extmod/uasyncio: Get addr and bind server socket before creating task.
Currently when using uasyncio.start_server() the socket configuration is done inside a uasyncio.create_task() background function. If the address and port are already in use however this throws an OSError which cannot be cleanly caught behind the create_task(). This commit moves the getaddrinfo and socket binding to the start_server() function, and only creates the task if that succeeds. This means that any OSError from the initial socket configuration is propagated directly up the call stack, compatible with CPython behaviour. See #7444. Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
parent
cbc9a591a4
commit
7ec95c2768
@ -107,15 +107,7 @@ class Server:
|
||||
async def wait_closed(self):
|
||||
await self.task
|
||||
|
||||
async def _serve(self, cb, host, port, backlog):
|
||||
import usocket as socket
|
||||
|
||||
ai = socket.getaddrinfo(host, port)[0] # TODO this is blocking!
|
||||
s = socket.socket()
|
||||
s.setblocking(False)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(ai[-1])
|
||||
s.listen(backlog)
|
||||
async def _serve(self, s, cb):
|
||||
# Accept incoming connections
|
||||
while True:
|
||||
try:
|
||||
@ -137,9 +129,20 @@ class Server:
|
||||
# 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
|
||||
async def start_server(cb, host, port, backlog=5):
|
||||
s = Server()
|
||||
s.task = core.create_task(s._serve(cb, host, port, backlog))
|
||||
return s
|
||||
import usocket as socket
|
||||
|
||||
# Create and bind server socket.
|
||||
host = socket.getaddrinfo(host, port)[0] # TODO this is blocking!
|
||||
s = socket.socket()
|
||||
s.setblocking(False)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(host[-1])
|
||||
s.listen(backlog)
|
||||
|
||||
# Create and return server object and task.
|
||||
srv = Server()
|
||||
srv.task = core.create_task(srv._serve(s, cb))
|
||||
return srv
|
||||
|
||||
|
||||
################################################################################
|
||||
|
31
tests/net_hosted/uasyncio_start_server.py
Normal file
31
tests/net_hosted/uasyncio_start_server.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Test basic behaviour of uasyncio.start_server()
|
||||
|
||||
try:
|
||||
import uasyncio as asyncio
|
||||
except ImportError:
|
||||
try:
|
||||
import asyncio
|
||||
except ImportError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
|
||||
async def test():
|
||||
# Test creating 2 servers using the same address
|
||||
print("create server1")
|
||||
server1 = await asyncio.start_server(None, "0.0.0.0", 8000)
|
||||
try:
|
||||
print("create server2")
|
||||
await asyncio.start_server(None, "0.0.0.0", 8000)
|
||||
except OSError as er:
|
||||
print("OSError")
|
||||
|
||||
# Wait for server to close.
|
||||
async with server1:
|
||||
print("sleep")
|
||||
await asyncio.sleep(0)
|
||||
|
||||
print("done")
|
||||
|
||||
|
||||
asyncio.run(test())
|
5
tests/net_hosted/uasyncio_start_server.py.exp
Normal file
5
tests/net_hosted/uasyncio_start_server.py.exp
Normal file
@ -0,0 +1,5 @@
|
||||
create server1
|
||||
create server2
|
||||
OSError
|
||||
sleep
|
||||
done
|
Loading…
Reference in New Issue
Block a user