Update uasyncio to include new TLS support

This commit is contained in:
Miguel Grinberg
2024-01-04 20:41:05 +00:00
parent 5d188e8c0d
commit c8c91e8345
3 changed files with 29 additions and 16 deletions

View File

@@ -172,10 +172,8 @@ configure the web server.
app.run(port=4443, debug=True, ssl=sslctx) app.run(port=4443, debug=True, ssl=sslctx)
.. note:: .. note::
The ``ssl`` argument can only be used with CPython at this time, because When using CPython, the certificate and key files must be given in PEM
MicroPython's asyncio module does not currently support SSL certificates or format. When using MicroPython, these files must be given in DER format.
TLS encryption. Work on this is
`in progress <https://github.com/micropython/micropython/pull/11897>`_.
Defining Routes Defining Routes
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~

View File

@@ -1,4 +1,5 @@
import ssl import ssl
import sys
from microdot import Microdot from microdot import Microdot
app = Microdot() app = Microdot()
@@ -31,6 +32,7 @@ async def shutdown(request):
return 'The server is shutting down...' return 'The server is shutting down...'
ext = 'der' if sys.implementation.name == 'micropython' else 'pem'
sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
sslctx.load_cert_chain('cert.pem', 'key.pem') sslctx.load_cert_chain('cert.' + ext, 'key.' + ext)
app.run(port=4443, debug=True, ssl=sslctx) app.run(port=4443, debug=True, ssl=sslctx)

View File

@@ -13,12 +13,6 @@ class Stream:
def get_extra_info(self, v): def get_extra_info(self, v):
return self.e[v] return self.e[v]
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
await self.close()
def close(self): def close(self):
pass pass
@@ -63,6 +57,8 @@ class Stream:
while True: while True:
yield core._io_queue.queue_read(self.s) yield core._io_queue.queue_read(self.s)
l2 = self.s.readline() # may do multiple reads but won't block l2 = self.s.readline() # may do multiple reads but won't block
if l2 is None:
continue
l += l2 l += l2
if not l2 or l[-1] == 10: # \n (check l in case l2 is str) if not l2 or l[-1] == 10: # \n (check l in case l2 is str)
return l return l
@@ -100,19 +96,29 @@ StreamWriter = Stream
# Create a TCP stream connection to a remote host # Create a TCP stream connection to a remote host
# #
# async # async
def open_connection(host, port): def open_connection(host, port, ssl=None, server_hostname=None):
from errno import EINPROGRESS from errno import EINPROGRESS
import socket import socket
ai = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0] # TODO this is blocking! ai = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0] # TODO this is blocking!
s = socket.socket(ai[0], ai[1], ai[2]) s = socket.socket(ai[0], ai[1], ai[2])
s.setblocking(False) s.setblocking(False)
ss = Stream(s)
try: try:
s.connect(ai[-1]) s.connect(ai[-1])
except OSError as er: except OSError as er:
if er.errno != EINPROGRESS: if er.errno != EINPROGRESS:
raise er raise er
# wrap with SSL, if requested
if ssl:
if ssl is True:
import ssl as _ssl
ssl = _ssl.SSLContext(_ssl.PROTOCOL_TLS_CLIENT)
if not server_hostname:
server_hostname = host
s = ssl.wrap_socket(s, server_hostname=server_hostname, do_handshake_on_connect=False)
s.setblocking(False)
ss = Stream(s)
yield core._io_queue.queue_write(s) yield core._io_queue.queue_write(s)
return ss, ss return ss, ss
@@ -135,7 +141,7 @@ class Server:
async def wait_closed(self): async def wait_closed(self):
await self.task await self.task
async def _serve(self, s, cb): async def _serve(self, s, cb, ssl):
self.state = False self.state = False
# Accept incoming connections # Accept incoming connections
while True: while True:
@@ -156,6 +162,13 @@ class Server:
except: except:
# Ignore a failed accept # Ignore a failed accept
continue continue
if ssl:
try:
s2 = ssl.wrap_socket(s2, server_side=True, do_handshake_on_connect=False)
except OSError as e:
core.sys.print_exception(e)
s2.close()
continue
s2.setblocking(False) s2.setblocking(False)
s2s = Stream(s2, {"peername": addr}) s2s = Stream(s2, {"peername": addr})
core.create_task(cb(s2s, s2s)) core.create_task(cb(s2s, s2s))
@@ -163,7 +176,7 @@ class Server:
# Helper function to start a TCP stream server, running as a new task # Helper function to start a TCP stream server, running as a new task
# TODO could use an accept-callback on socket read activity instead of creating a task # TODO could use an accept-callback on socket read activity instead of creating a task
async def start_server(cb, host, port, backlog=5): async def start_server(cb, host, port, backlog=5, ssl=None):
import socket import socket
# Create and bind server socket. # Create and bind server socket.
@@ -176,7 +189,7 @@ async def start_server(cb, host, port, backlog=5):
# Create and return server object and task. # Create and return server object and task.
srv = Server() srv = Server()
srv.task = core.create_task(srv._serve(s, cb)) srv.task = core.create_task(srv._serve(s, cb, ssl))
try: try:
# Ensure that the _serve task has been scheduled so that it gets to # Ensure that the _serve task has been scheduled so that it gets to
# handle cancellation. # handle cancellation.