TLS fixes for WebSocket under MicroPython

This commit is contained in:
Miguel Grinberg
2022-09-08 20:10:22 +01:00
parent b61f51f243
commit fe750feb03
6 changed files with 82 additions and 19 deletions

24
examples/tls/echo_tls.py Normal file
View File

@@ -0,0 +1,24 @@
import sys
from microdot import Microdot, send_file
from microdot_websocket import with_websocket
from microdot_ssl import create_ssl_context
app = Microdot()
@app.route('/')
def index(request):
return send_file('index.html')
@app.route('/echo')
@with_websocket
def echo(request, ws):
while True:
data = ws.receive()
ws.send(data)
ext = 'der' if sys.implementation.name == 'micropython' else 'pem'
sslctx = create_ssl_context('cert.' + ext, 'key.' + ext)
app.run(port=4443, debug=True, ssl=sslctx)

35
examples/tls/index.html Normal file
View File

@@ -0,0 +1,35 @@
<!doctype html>
<html>
<head>
<title>Microdot TLS WebSocket Demo</title>
</head>
<body>
<h1>Microdot TLS WebSocket Demo</h1>
<div id="log"></div>
<br>
<form id="form">
<label for="text">Input: </label>
<input type="text" id="text" autofocus>
</form>
<script>
const log = (text, color) => {
document.getElementById('log').innerHTML += `<span style="color: ${color}">${text}</span><br>`;
};
const socket = new WebSocket('wss://' + location.host + '/echo');
socket.addEventListener('message', ev => {
log('<<< ' + ev.data, 'blue');
});
socket.addEventListener('close', ev => {
log('<<< closed');
});
document.getElementById('form').onsubmit = ev => {
ev.preventDefault();
const textField = document.getElementById('text');
log('>>> ' + textField.value, 'red');
socket.send(textField.value);
textField.value = '';
};
</script>
</body>
</html>

View File

@@ -916,8 +916,11 @@ class Microdot():
if exc.errno == errno.ECONNABORTED:
break
else:
raise
create_thread(self.handle_request, sock, addr)
print_exception(exc)
except Exception as exc: # pragma: no cover
print_exception(exc)
else:
create_thread(self.handle_request, sock, addr)
def shutdown(self):
"""Request a server shutdown. The server will then exit its request
@@ -953,12 +956,13 @@ class Microdot():
stream = sock
req = None
res = None
try:
req = Request.create(self, stream, addr, sock)
res = self.dispatch_request(req)
except Exception as exc: # pragma: no cover
print_exception(exc)
res = self.dispatch_request(req)
if res != Response.already_handled: # pragma: no branch
if res and res != Response.already_handled: # pragma: no branch
res.write(stream)
try:
stream.close()

View File

@@ -260,7 +260,7 @@ class TestClient:
else WebSocket.BINARY
return WebSocket._encode_websocket_frame(opcode, data)
def recv(self, n):
def read(self, n):
self.started = True
if not self.buffer:
self.buffer = self._next()
@@ -268,7 +268,7 @@ class TestClient:
self.buffer = self.buffer[n:]
return data
def send(self, data):
def write(self, data):
if self.started:
h = WebSocket._parse_frame_header(data[0:2])
if h[3] < 0:

View File

@@ -17,10 +17,10 @@ class WebSocket:
def handshake(self):
response = self._handshake_response()
self.request.sock.send(b'HTTP/1.1 101 Switching Protocols\r\n')
self.request.sock.send(b'Upgrade: websocket\r\n')
self.request.sock.send(b'Connection: Upgrade\r\n')
self.request.sock.send(
self.request.sock.write(b'HTTP/1.1 101 Switching Protocols\r\n')
self.request.sock.write(b'Upgrade: websocket\r\n')
self.request.sock.write(b'Connection: Upgrade\r\n')
self.request.sock.write(
b'Sec-WebSocket-Accept: ' + response + b'\r\n\r\n')
def receive(self):
@@ -36,7 +36,7 @@ class WebSocket:
frame = self._encode_websocket_frame(
opcode or (self.TEXT if isinstance(data, str) else self.BINARY),
data)
self.request.sock.send(frame)
self.request.sock.write(frame)
def close(self):
if not self.closed: # pragma: no cover
@@ -110,16 +110,16 @@ class WebSocket:
return frame
def _read_frame(self):
header = self.request.sock.recv(2)
header = self.request.sock.read(2)
if len(header) != 2: # pragma: no cover
raise OSError(32, 'Websocket connection closed')
fin, opcode, has_mask, length = self._parse_frame_header(header)
if length < 0:
length = self.request.sock.recv(-length)
length = self.request.sock.read(-length)
length = int.from_bytes(length, 'big')
if has_mask: # pragma: no cover
mask = self.request.sock.recv(4)
payload = self.request.sock.recv(length)
mask = self.request.sock.read(4)
payload = self.request.sock.read(length)
if has_mask: # pragma: no cover
payload = bytes(x ^ mask[i % 4] for i, x in enumerate(payload))
return opcode, payload

View File

@@ -22,10 +22,10 @@ class WebSocket:
def handshake(self):
response = self._handshake_response()
self.request.sock.send(b'HTTP/1.1 101 Switching Protocols\r\n')
self.request.sock.send(b'Upgrade: websocket\r\n')
self.request.sock.send(b'Connection: Upgrade\r\n')
self.request.sock.send(
self.request.sock.write(b'HTTP/1.1 101 Switching Protocols\r\n')
self.request.sock.write(b'Upgrade: websocket\r\n')
self.request.sock.write(b'Connection: Upgrade\r\n')
self.request.sock.write(
b'Sec-WebSocket-Accept: ' + response + b'\r\n\r\n')
def receive(self):