]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
When following redirects, release connection resources immediately
authorBen Darnell <ben@bendarnell.com>
Sun, 21 Aug 2011 22:53:00 +0000 (15:53 -0700)
committerBen Darnell <ben@bendarnell.com>
Sun, 21 Aug 2011 22:55:17 +0000 (15:55 -0700)
tornado/simple_httpclient.py
tornado/test/httpserver_test.py
tornado/test/simple_httpclient_test.py

index c0d76aa66369cdc393ff1649499fa92186449ed2..e1a9ce40527875bb58559066fb8834f158d0c0c0 100644 (file)
@@ -110,13 +110,12 @@ class SimpleAsyncHTTPClient(AsyncHTTPClient):
                 key = object()
                 self.active[key] = (request, callback)
                 _HTTPConnection(self.io_loop, self, request,
-                                functools.partial(self._on_fetch_complete,
-                                                  key, callback),
+                                functools.partial(self._release_fetch, key),
+                                callback,
                                 self.max_buffer_size)
 
-    def _on_fetch_complete(self, key, callback, response):
+    def _release_fetch(self, key):
         del self.active[key]
-        callback(response)
         self._process_queue()
 
 
@@ -124,12 +123,14 @@ class SimpleAsyncHTTPClient(AsyncHTTPClient):
 class _HTTPConnection(object):
     _SUPPORTED_METHODS = set(["GET", "HEAD", "POST", "PUT", "DELETE"])
 
-    def __init__(self, io_loop, client, request, callback, max_buffer_size):
+    def __init__(self, io_loop, client, request, release_callback,
+                 final_callback, max_buffer_size):
         self.start_time = time.time()
         self.io_loop = io_loop
         self.client = client
         self.request = request
-        self.callback = callback
+        self.release_callback = release_callback
+        self.final_callback = final_callback
         self.code = None
         self.headers = None
         self.chunks = None
@@ -269,11 +270,18 @@ class _HTTPConnection(object):
             self.stream.write(self.request.body)
         self.stream.read_until(b("\r\n\r\n"), self._on_headers)
 
+    def _release(self):
+        if self.release_callback is not None:
+            release_callback = self.release_callback
+            self.release_callback = None
+            release_callback()
+
     def _run_callback(self, response):
-        if self.callback is not None:
-            callback = self.callback
-            self.callback = None
-            callback(response)
+        self._release()
+        if self.final_callback is not None:
+            final_callback = self.final_callback
+            self.final_callback = None
+            final_callback(response)
 
     @contextlib.contextmanager
     def cleanup(self):
@@ -346,9 +354,11 @@ class _HTTPConnection(object):
             new_request.max_redirects -= 1
             del new_request.headers["Host"]
             new_request.original_request = original_request
-            callback = self.callback
-            self.callback = None
-            self.client.fetch(new_request, callback)
+            final_callback = self.final_callback
+            self.final_callback = None
+            self._release()
+            self.client.fetch(new_request, final_callback)
+            self.stream.close()
             return
         response = HTTPResponse(original_request,
                                 self.code, headers=self.headers,
index 804acdeb3cf2946489990062012af63b29efaeb9..8ac365e217afa6f2519ed03590053e58f4646146 100644 (file)
@@ -105,7 +105,8 @@ class HTTPConnectionTest(AsyncHTTPTestCase, LogTrapTestCase):
     def raw_fetch(self, headers, body):
         conn = RawRequestHTTPConnection(self.io_loop, self.http_client,
                                         httpclient.HTTPRequest(self.get_url("/")),
-                                        self.stop, 1024*1024)
+                                        None, self.stop,
+                                        1024*1024)
         conn.set_request(
             b("\r\n").join(headers +
                            [utf8("Content-Length: %d\r\n" % len(body))]) +
index bdea4fc70f6ec99e186b047288c3527b33ce44d6..662806620727cfce330a2b8c7eb0f7cbdba1202a 100644 (file)
@@ -100,6 +100,15 @@ class SimpleHTTPClientTestCase(AsyncHTTPTestCase, LogTrapTestCase):
         self.assertEqual(set(seen), set([0, 1, 2, 3]))
         self.assertEqual(len(self.triggers), 0)
 
+    def test_redirect_connection_limit(self):
+        # following redirects should not consume additional connections
+        client = SimpleAsyncHTTPClient(self.io_loop, max_clients=1,
+                                       force_instance=True)
+        client.fetch(self.get_url('/countdown/3'), self.stop,
+                     max_redirects=3)
+        response = self.wait()
+        response.rethrow()
+
     def test_default_certificates_exist(self):
         open(_DEFAULT_CA_CERTS).close()