]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Fix a memory leak and test timeout in websockets.
authorBen Darnell <ben@bendarnell.com>
Tue, 5 Aug 2014 03:46:03 +0000 (23:46 -0400)
committerBen Darnell <ben@bendarnell.com>
Tue, 5 Aug 2014 03:46:03 +0000 (23:46 -0400)
In some cases (primarily when prepare() is asynchronous), an
HTTP1ServerConnection could be left waiting forever for a connection
to finish when that connection has been detached and handed off to a
WebSocketHandler.  This would manifest as a leak and a timeout in
tests as the HTTPServer waited for all its existing connections to
finish.

Also fix a bug in the test tearDown method that would actually
wait forever for connections to finish instead of timing out.

Closes #1133.

tornado/http1connection.py
tornado/test/websocket_test.py
tornado/testing.py

index 2ac139a64cecdacc0c48206fd81b85bdfb801e4e..6ed74e2ad04d6bb0cb595634f387d0dabee25e97 100644 (file)
@@ -294,6 +294,8 @@ class HTTP1Connection(httputil.HTTPConnection):
         self._clear_callbacks()
         stream = self.stream
         self.stream = None
+        if not self._finish_future.done():
+            self._finish_future.set_result(None)
         return stream
 
     def set_body_timeout(self, timeout):
index f8cd163bd33b661726491963fc4e832840a8b922..e1e3ea7005d1c396a9f47dcdb65daf4c2fd1aec6 100644 (file)
@@ -78,6 +78,15 @@ class CloseReasonHandler(TestWebSocketHandler):
         self.close(1001, "goodbye")
 
 
+class AsyncPrepareHandler(TestWebSocketHandler):
+    @gen.coroutine
+    def prepare(self):
+        yield gen.moment
+
+    def on_message(self, message):
+        self.write_message(message)
+
+
 class WebSocketBaseTestCase(AsyncHTTPTestCase):
     @gen.coroutine
     def ws_connect(self, path, compression_options=None):
@@ -107,6 +116,8 @@ class WebSocketTest(WebSocketBaseTestCase):
              dict(close_future=self.close_future)),
             ('/error_in_on_message', ErrorInOnMessageHandler,
              dict(close_future=self.close_future)),
+            ('/async_prepare', AsyncPrepareHandler,
+             dict(close_future=self.close_future)),
         ])
 
     def test_http_request(self):
@@ -219,6 +230,15 @@ class WebSocketTest(WebSocketBaseTestCase):
         self.assertEqual(code, 1001)
         self.assertEqual(reason, 'goodbye')
 
+    @gen_test
+    def test_async_prepare(self):
+        # Previously, an async prepare method triggered a bug that would
+        # result in a timeout on test shutdown (and a memory leak).
+        ws = yield self.ws_connect('/async_prepare')
+        ws.write_message('hello')
+        res = yield ws.read_message()
+        self.assertEqual(res, 'hello')
+
     @gen_test
     def test_check_origin_valid_no_path(self):
         port = self.get_http_port()
index b4bfb274a23350fd1f2c4b89c61a8601ddab96d3..b3c47a03bf456b0165ac7ce16b3bf0a1db2e2415 100644 (file)
@@ -395,7 +395,8 @@ class AsyncHTTPTestCase(AsyncTestCase):
 
     def tearDown(self):
         self.http_server.stop()
-        self.io_loop.run_sync(self.http_server.close_all_connections)
+        self.io_loop.run_sync(self.http_server.close_all_connections,
+                              timeout=get_async_test_timeout())
         if (not IOLoop.initialized() or
                 self.http_client.io_loop is not IOLoop.instance()):
             self.http_client.close()