]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Run AsyncHTTPClient callbacks on the IOLoop for a clean stack.
authorBen Darnell <ben@bendarnell.com>
Sat, 8 Dec 2012 23:37:45 +0000 (18:37 -0500)
committerBen Darnell <ben@bendarnell.com>
Sun, 9 Dec 2012 00:17:55 +0000 (19:17 -0500)
Closes #652.

tornado/simple_httpclient.py
tornado/test/httpclient_test.py
website/sphinx/releases/next.rst

index faff83c8f06eb35c4eddbb016a8a470c38a2ade9..45a11a2a5bcb8f8f72e7bce756f8235fce4ece2f 100644 (file)
@@ -309,7 +309,7 @@ class _HTTPConnection(object):
         if self.final_callback is not None:
             final_callback = self.final_callback
             self.final_callback = None
-            final_callback(response)
+            self.io_loop.add_callback(final_callback, response)
 
     @contextlib.contextmanager
     def cleanup(self):
index 6cc43171523abd4b4c935cd6642cca3035692cab..4513666c8659e55c6d4a50db7affa61467a3c8f4 100644 (file)
@@ -7,12 +7,13 @@ import binascii
 from contextlib import closing
 import functools
 import re
+import sys
 
 from tornado.escape import utf8
 from tornado.httpclient import HTTPRequest, _RequestProxy
 from tornado.iostream import IOStream
 from tornado import netutil
-from tornado.stack_context import ExceptionStackContext
+from tornado.stack_context import ExceptionStackContext, NullContext
 from tornado.testing import AsyncHTTPTestCase, bind_unused_port
 from tornado.test.util import unittest
 from tornado.util import b, bytes_type
@@ -289,6 +290,26 @@ Transfer-Encoding: chunked
         self.assertEqual(response.code, 304)
         self.assertEqual(response.headers['Content-Length'], '42')
 
+    def test_final_callback_stack_context(self):
+        # The final callback should be run outside of the httpclient's
+        # stack_context.  We want to ensure that there is not stack_context
+        # between the user's callback and the IOLoop, so monkey-patch
+        # IOLoop.handle_callback_exception and disable the test harness's
+        # context with a NullContext.
+        # Note that this does not apply to secondary callbacks (header
+        # and streaming_callback), as errors there must be seen as errors
+        # by the http client so it can clean up the connection.
+        exc_info = []
+        def handle_callback_exception(callback):
+            exc_info.append(sys.exc_info())
+            self.stop()
+        self.io_loop.handle_callback_exception = handle_callback_exception
+        with NullContext():
+            self.http_client.fetch(self.get_url('/hello'),
+                                   lambda response: 1 / 0)
+        self.wait()
+        self.assertEqual(exc_info[0][0], ZeroDivisionError)
+
 class RequestProxyTest(unittest.TestCase):
     def test_request_set(self):
         proxy = _RequestProxy(HTTPRequest('http://example.com/',
index 215fb028f1850ef392afc78684a364242c690926..49468ddcc985b34c615c020fb83f1b33e0cef1b8 100644 (file)
@@ -198,3 +198,5 @@ In progress
   ``*args, **kwargs`` to pass along to the callback.
 * When gzip is enabled in a `tornado.web.Application`, appropriate
   ``Vary: Accept-Encoding`` headers are now sent.
+* Fixed a bug in which `SimpleAsyncHTTPClient` callbacks were being run in the
+  client's ``stack_context``.