]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
The RequestHandler.flush callback should be run even if there is no data.
authorBen Darnell <ben@bendarnell.com>
Thu, 19 Apr 2012 05:22:06 +0000 (22:22 -0700)
committerBen Darnell <ben@bendarnell.com>
Thu, 19 Apr 2012 05:22:06 +0000 (22:22 -0700)
Also IOStream._try_inline_read needs the same fake-callback change
as _handle_read (I can't seem to come up with a good isolated test for this,
but it's tickled by the empty-flush test).

Closes #492.

tornado/iostream.py
tornado/test/web_test.py
tornado/web.py

index 4e8ce0505ea8c81c55ad12db8d704bd3ed7ba705..f8fadf6892e035266c533fc67e2fa276237cd1f2 100644 (file)
@@ -362,10 +362,15 @@ class IOStream(object):
         if self._read_from_buffer():
             return
         self._check_closed()
-        while True:
-            if self._read_to_buffer() == 0:
-                break
-            self._check_closed()
+        try:
+            # See comments in _handle_read about incrementing _pending_callbacks
+            self._pending_callbacks += 1
+            while True:
+                if self._read_to_buffer() == 0:
+                    break
+                self._check_closed()
+        finally:
+            self._pending_callbacks -= 1
         if self._read_from_buffer():
             return
         self._add_io_state(self.io_loop.READ)
index f65b1f68bc2743a51daf615e7b847502a6ac4c22..96b4d662f1a1e2f18147f61f31dae5905b0909a3 100644 (file)
@@ -1,4 +1,5 @@
 from __future__ import absolute_import, division, with_statement
+from tornado import gen
 from tornado.escape import json_decode, utf8, to_unicode, recursive_unicode, native_str
 from tornado.iostream import IOStream
 from tornado.template import DictLoader
@@ -366,6 +367,19 @@ class RedirectHandler(RequestHandler):
         else:
             raise Exception("didn't get permanent or status arguments")
 
+class EmptyFlushCallbackHandler(RequestHandler):
+    @gen.engine
+    @asynchronous
+    def get(self):
+        # Ensure that the flush callback is run whether or not there
+        # was any output.
+        yield gen.Task(self.flush)  # "empty" flush, but writes headers
+        yield gen.Task(self.flush)  # empty flush
+        self.write("o")
+        yield gen.Task(self.flush)  # flushes the "o"
+        yield gen.Task(self.flush)  # empty flush
+        self.finish("k")
+
 
 class WebTest(AsyncHTTPTestCase, LogTrapTestCase):
     def get_app(self):
@@ -391,6 +405,7 @@ class WebTest(AsyncHTTPTestCase, LogTrapTestCase):
             url("/flow_control", FlowControlHandler),
             url("/multi_header", MultiHeaderHandler),
             url("/redirect", RedirectHandler),
+            url("/empty_flush", EmptyFlushCallbackHandler),
             ]
         return Application(urls,
                            template_loader=loader,
@@ -484,6 +499,10 @@ js_embed()
         response = self.fetch("/redirect?status=307", follow_redirects=False)
         self.assertEqual(response.code, 307)
 
+    def test_empty_flush(self):
+        response = self.fetch("/empty_flush")
+        self.assertEqual(response.body, b("ok"))
+
 
 class ErrorResponseTest(AsyncHTTPTestCase, LogTrapTestCase):
     def get_app(self):
index c66fa4e029b93396013bb0e662bc24fd932c8d97..f71ca1727606c5684ceefb3df22ee54c6cb7db6f 100644 (file)
@@ -636,8 +636,7 @@ class RequestHandler(object):
                 self.request.write(headers, callback=callback)
             return
 
-        if headers or chunk:
-            self.request.write(headers + chunk, callback=callback)
+        self.request.write(headers + chunk, callback=callback)
 
     def finish(self, chunk=None):
         """Finishes this response, ending the HTTP request."""