{"action":"created","issue":{"url":"https://api.github.com/repos/aaugustin/websockets/issues/69","labels_url":"https://api.github.com/repos/aaugustin/websockets/issues/69/labels{/name}","comments_url":"https://api.github.com/repos/aaugustin/websockets/issues/69/comments","events_url":"https://api.github.com/repos/aaugustin/websockets/issues/69/events","html_url":"https://github.com/aaugustin/websockets/issues/69","id":103110239,"number":69,"title":"Handler for closed connections not allowed to finish","user":{"login":"anderslundstedt","id":4514101,"avatar_url":"https://avatars.githubusercontent.com/u/4514101?v=3","gravatar_id":"","url":"https://api.github.com/users/anderslundstedt","html_url":"https://github.com/anderslundstedt","followers_url":"https://api.github.com/users/anderslundstedt/followers","following_url":"https://api.github.com/users/anderslundstedt/following{/other_user}","gists_url":"https://api.github.com/users/anderslundstedt/gists{/gist_id}","starred_url":"https://api.github.com/users/anderslundstedt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/anderslundstedt/subscriptions","organizations_url":"https://api.github.com/users/anderslundstedt/orgs","repos_url":"https://api.github.com/users/anderslundstedt/repos","events_url":"https://api.github.com/users/anderslundstedt/events{/privacy}","received_events_url":"https://api.github.com/users/anderslundstedt/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-08-25T20:32:42Z","updated_at":"2015-08-25T21:06:08Z","closed_at":null,"body":"During shutdown I wait for clients to close connections. When this is done, the handler is not allowed to finish.\r\n\r\nTo trigger the error, run the following script, interrupt it (Ctrl+C) with a connection open, then close the connection.\r\n\r\n    #!/usr/bin/env python\r\n    \r\n    import asyncio\r\n    import logging\r\n    import signal\r\n    \r\n    import websockets\r\n    \r\n    logging.getLogger().setLevel(logging.DEBUG)\r\n    \r\n    connectionClosedEvents = []\r\n    \r\n    @asyncio.coroutine\r\n    def echo(websocket, path):\r\n        print(\"Connection opened.\")\r\n        connectionClosedEvent = asyncio.Event()\r\n        connectionClosedEvents.append(connectionClosedEvent)\r\n        s = yield from websocket.recv()\r\n        if s is not None:\r\n            yield from websocket.send(s)\r\n        print(\"Connection closed.\")\r\n        connectionClosedEvent.set()\r\n    \r\n    @asyncio.coroutine\r\n    def shutdown():\r\n        print(\"Shutdown initialized.\")\r\n        print(\"Waiting for open connections to close ...\")\r\n        for e in connectionClosedEvents:\r\n            yield from e.wait()\r\n        print(\"All connections closed.\")\r\n        echoServer.close()\r\n        print(\"Waiting for server to close ...\")\r\n        yield from echoServer.wait_closed()\r\n        print(\"Server closed.\")\r\n        print(\"Stopping event loop ...\")\r\n        eventLoop.stop()\r\n        print(\"Event loop stopped.\")\r\n        print(\"Shutdown complete.\")\r\n    \r\n    eventLoop = asyncio.get_event_loop()\r\n    eventLoop.add_signal_handler(signal.SIGINT, asyncio.async, shutdown())\r\n    echoServer =\\\r\n        eventLoop.run_until_complete(websockets.serve(echo, \"0.0.0.0\", 8765))\r\n    eventLoop.run_forever()\r\n\r\nTypical output:\r\n\r\n    Connection opened.\r\n    ^CShutdown initialized.\r\n    Waiting for open connections to close ...\r\n    Connection closed.\r\n    All connections closed.\r\n    Waiting for server to close ...\r\n    Server closed.\r\n    Stopping event loop ...\r\n    Event loop stopped.\r\n    Shutdown complete.\r\n    Task was destroyed but it is pending!\r\n    task: <Task pending coro=<handler() running at /tmp/tmp.OhmZJ4uKUJ/venv/lib/python3.4/site-packages/websockets/server.py:77> wait_for=<Future finished result=None>>\r\n\r\nThe reason for the error is that `websockets.server.connection_lost` removes the connection from `self.websockets`. Thus in `websockets.server.wait_closed` the connection is not waited for:\r\n\r\n                yield from asyncio.wait(\r\n                    [ws.handler_task for ws in self.websockets], loop=self.loop)\r\n\r\nI will submit a proposed fix for this shortly."},"comment":{"url":"https://api.github.com/repos/aaugustin/websockets/issues/comments/134742014","html_url":"https://github.com/aaugustin/websockets/issues/69#issuecomment-134742014","issue_url":"https://api.github.com/repos/aaugustin/websockets/issues/69","id":134742014,"user":{"login":"aaugustin","id":788910,"avatar_url":"https://avatars.githubusercontent.com/u/788910?v=3","gravatar_id":"","url":"https://api.github.com/users/aaugustin","html_url":"https://github.com/aaugustin","followers_url":"https://api.github.com/users/aaugustin/followers","following_url":"https://api.github.com/users/aaugustin/following{/other_user}","gists_url":"https://api.github.com/users/aaugustin/gists{/gist_id}","starred_url":"https://api.github.com/users/aaugustin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aaugustin/subscriptions","organizations_url":"https://api.github.com/users/aaugustin/orgs","repos_url":"https://api.github.com/users/aaugustin/repos","events_url":"https://api.github.com/users/aaugustin/events{/privacy}","received_events_url":"https://api.github.com/users/aaugustin/received_events","type":"User","site_admin":false},"created_at":"2015-08-25T21:06:08Z","updated_at":"2015-08-25T21:06:08Z","body":"Can you test if the following patch resolves your problem?\r\n\r\n```diff\r\ndiff --git a/websockets/server.py b/websockets/server.py\r\nindex 94135d1..5adf4ae 100644\r\n--- a/websockets/server.py\r\n+++ b/websockets/server.py\r\n@@ -48,6 +48,8 @@ class WebSocketServerProtocol(WebSocketCommonProtocol):\r\n     def handler(self):\r\n         # Since this method doesn't have a caller able to handle exceptions,\r\n         # it attemps to log relevant ones and close the connection properly.\r\n+\r\n+        self.ws_server.register(self)\r\n         try:\r\n \r\n             try:\r\n@@ -86,6 +88,9 @@ class WebSocketServerProtocol(WebSocketCommonProtocol):\r\n             except Exception:                               # pragma: no cover\r\n                 pass\r\n \r\n+        finally:\r\n+            self.ws_server.unregister(self)\r\n+\r\n     @asyncio.coroutine\r\n     def handshake(self, origins=None, subprotocols=None, extra_headers=None):\r\n         \"\"\"\r\n@@ -170,14 +175,6 @@ class WebSocketServerProtocol(WebSocketCommonProtocol):\r\n         priority = lambda p: client_protos.index(p) + server_protos.index(p)\r\n         return sorted(common_protos, key=priority)[0]\r\n \r\n-    def client_connected(self, reader, writer):\r\n-        super().client_connected(reader, writer)\r\n-        self.ws_server.register(self)\r\n-\r\n-    def connection_lost(self, exc):\r\n-        self.ws_server.unregister(self)\r\n-        super().connection_lost(exc)\r\n-\r\n \r\n class WebSocketServer(asyncio.AbstractServer):\r\n     \"\"\"\r\n```"}}