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):
|
async def wait_closed(self):
|
||||||
await self.task
|
await self.task
|
||||||
|
|
||||||
async def _serve(self, cb, host, port, backlog):
|
async def _serve(self, s, cb):
|
||||||
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)
|
|
||||||
# Accept incoming connections
|
# Accept incoming connections
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@ -137,9 +129,20 @@ 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):
|
||||||
s = Server()
|
import usocket as socket
|
||||||
s.task = core.create_task(s._serve(cb, host, port, backlog))
|
|
||||||
return s
|
# 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