tornadoweb / tornadoweb/tornado
websocket: Async version of WebSocketHandler.close is needed
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 22.2k
- Forks
- 5.6k
- Avg merge
- 3h 42m
- Merged PRs (30d)
- 16
Description
NOTE: this is different from https://github.com/tornadoweb/tornado/issues/2763. possibly related to https://github.com/tornadoweb/tornado/issues/2448, but also distinct.
After starting a websocket server, I am getting
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending name='Task-5' coro=<RequestHandler._execute() running at /usr/local/lib/python3.8/site-packages/tornado/web.py:1703> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10acb2970>()]> cb=[_HandlerDelegate.execute.<locals>.<lambda>() at /usr/local/lib/python3.8/site-packages/tornado/web.py:2333]>
even after appropriately closing all connections. this is different from https://github.com/tornadoweb/tornado/issues/2763, where the unresolved task was WebSocketProtocol13._receive_frame_loop, and not all connections were closed.
to reproduce this easily, run the following tiny python3 program:
import asyncio
from abc import ABC
import tornado.websocket
import tornado.httpserver
import tornado.ioloop
import tornado.web
handlers = []
class Handler(tornado.websocket.WebSocketHandler, ABC):
def open(self): # making this async and awaiting write_message doesn't silence the tornado warnings.
handlers.append(self)
def on_message(self, message):
pass
def on_close(self):
pass
def check_origin(self, origin):
return True
application = tornado.web.Application([
(r'/', Handler),
])
http_server = tornado.httpserver.HTTPServer(application)
loop = tornado.ioloop.IOLoop.current()
async def my_function(): # closes the connections, stops the server, stops the loop after 20s.
await asyncio.sleep(20.0)
for handler in handlers:
handler.close()
http_server.stop()
loop.stop()
http_server.listen(8080)
loop.add_callback(my_function)
loop.start()
loop.close()
in a separate Node.js console (for example), run:
const WebSocket = require('ws');
new WebSocket('ws://localhost:8080')
the culprit appears to be the future fut here, which is never awaited:
https://github.com/tornadoweb/tornado/blob/79b9c4fcbb3728f1325c4f6120bec45dd5df9fd8/tornado/web.py#L2323-L2326
tornado version: tornado-6.0.4. Python version: 3.8.5.
i am unable to further diagnose the issue, or fix it. thanks for your attention and time.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with tornado/web.py around the cited lines 2323-2326 and inspect how the reproduced WebSocketHandler is closed. Run the provided Python server with a Node.js WebSocket client, then verify the connection shutdown leaves no pending-task warning after the server and loop stop. Done means an asynchronous close path is available and the reproduction completes without the reported warning.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, networking
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100