Repository: tornadoweb/tornado

Issue:
RequestHandler.finish() future not resolving
Just tried Tornado 5.1 for the first time and am wondering if I'm doing something wrong when waiting for the future of the `finish` method:

```python
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    async def get(self):
        print('before')
        await self.finish("Hello, world")
        print('after')

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()
```

It seems like the future is never resolved. `after` is not printed and interrupting the application outputs:

```
KeyboardInterrupt
ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<MainHandler.get() running at main.py:7> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f0cb0b25e28>()]> cb=[IOLoop.add_future.<locals>.<lambda>() at /home/sebastian/.virtualenvs/tempenv-2fc3210213e1f/lib/python3.6/site-packages/tornado/ioloop.py:719]>
```

Python 3.6, Ubuntu


Discussion:
I give a PR to solve this problem in #2449.
@garenchan Thanks!
Fixed by #2449 
@garenchan This bug hasn't been completely fixed in #2449 
If you try with the following modification of the code above, the future `RequestHandler.finish()` is still not resolving
```
import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    async def get(self):
        self.write('first')
        await self.flush('first')
        print('before finish')
        await self.finish('second')
        print('finished')


def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])


if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()
```
The fix on `HTTP1Connection.write_headers()` should also be applied to `HTTP1Connection.write()` since the latter is the one called in the second call to `RequestHandler.flush()` (after the headers have been written).
@cramosc, Thank you very much! You are right and I left out the changes to the other branch.

Resolving patch:
diff --git a/tornado/http1connection.py b/tornado/http1connection.py
--- a/tornado/http1connection.py
+++ b/tornado/http1connection.py
@@ -492,7 +492,7 @@ def write(self, chunk: bytes) -> "Future[None]":
         else:
             future = self._write_future = Future()
             self._pending_write = self.stream.write(self._format_chunk(chunk))
-            self._pending_write.add_done_callback(self._on_write_complete)
+            future_add_done_callback(self._pending_write, self._on_write_complete)
         return future
 
     def finish(self) -> None:


Test patch:
diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py
--- a/tornado/test/web_test.py
+++ b/tornado/test/web_test.py
@@ -224,6 +224,13 @@ def get(self):
                 test.final_return = self.finish()
                 yield test.final_return
 
+            @gen.coroutine
+            def post(self):
+                self.write("hello,")
+                yield self.flush()
+                test.final_return = self.finish("world")
+                yield test.final_return
+
         class RenderHandler(RequestHandler):
             def create_template_loader(self, path):
                 return DictLoader({"foo.html": "hi"})
@@ -243,6 +250,11 @@ def test_finish_method_return_future(self):
         self.assertIsInstance(self.final_return, Future)
         self.assertTrue(self.final_return.done())
 
+        response = self.fetch(self.get_url("/finish"), method="POST", body=b"")
+        self.assertEqual(response.code, 200)
+        self.assertIsInstance(self.final_return, Future)
+        self.assertTrue(self.final_return.done())
+
     def test_render_method_return_future(self):
         response = self.fetch(self.get_url("/render"))
         self.assertEqual(response.code, 200)