How do I properly close uasyncio tcp connections? #9695
-
For my own education, I have created a web server using uasyncio.start_server() and though it's serving files, I don't believe it's closing tcp connections properly. I'm hoping someone can help.
This make me think I'm doing something wrong when closing the connection at the end of the request. Here is the relevant code: @staticmethod
def read_file_chunk(file):
while True:
chunk = file.read(64) # small chunks to avoid out of memory errors
if chunk:
yield chunk
else: # empty chunk means end of the file
return
async def on_connect(self, reader, writer):
client_ip = writer.get_extra_info('peername')[0]
if (self.debug): print(f'Connection from client: {client_ip}')
try:
req_buffer = await reader.read(self.req_buffer_size)
req = await TinyHTTPD.parse_http_request(req_buffer)
if (self.debug): print(f'Request: {req}')
except Exception as ex:
print(f'Unable to parse request: {ex}')
writer.write(await TinyHTTPD.http_status_line(400))
writer.write(await TinyHTTPD.http_headers())
writer.write('Bad request\r\n')
else:
file_path = self.static_folder + req['path']
file_size = await TinyHTTPD.file_size(file_path)
file_type = await TinyHTTPD.file_type(file_path)
if (self.debug): print(f'{file_path} [{file_size}] {file_type}')
if (file_size == None): # None means there was an error, most likely the file doesn't exist
writer.write(await TinyHTTPD.http_status_line(404))
writer.write(await TinyHTTPD.http_headers())
writer.write('File not found\r\n')
else:
writer.write(await TinyHTTPD.http_status_line(200))
writer.write(await TinyHTTPD.http_headers(content_length=file_size, content_type=file_type))
with open(file_path, 'rb') as file:
for chunk in TinyHTTPD.read_file_chunk(file):
writer.write(chunk)
await writer.drain() # drain immediately after write to avoid memory allocation errors
reader.close()
reader.wait_closed()
await writer.drain()
writer.close()
writer.wait_closed()
if (self.debug): print(f'Connection closed for {client_ip}')
def run(self, host='0.0.0.0', port=80, loop=None, debug=False):
self.debug = debug
print(f'Listening on {host}:{port}')
if (loop == None):
loop = get_event_loop()
server = start_server(self.on_connect, host, port, 5)
loop.create_task(server)
loop.run_forever()
else:
server = start_server(self.on_connect, host, port, 5)
loop.create_task(server)
return loop
app = TinyHTTPD()
app.run(debug=True) From the code above, as the last step in my connection handler reader.close()
reader.wait_closed()
await writer.drain()
writer.close()
writer.wait_closed() I'm assuming that's enough to make the connection close. Is there more I need to do? Is it the wrong order of closing reader and writer? I can create the same stuck connections regardless of connecting with a web browser or using Putty as a telnet client, so I don't think it's a problem with the client software. What am I missing? What is the proper way to terminate the connection once data has been sent? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
|
Beta Was this translation helpful? Give feedback.
await writer_closed()
is required, because it's a coroutine: https://docs.micropython.org/en/latest/library/uasyncio.html#uasyncio.Stream.wait_closed