ajdavis: Ben, do you suppose RedirectHandler should also convert named capture groups to keyword args like RequestHandler does?
diff --git a/docs/guide/structure.rst b/docs/guide/structure.rst
index f0829df0..715e080f 100644
--- a/docs/guide/structure.rst
+++ b/docs/guide/structure.rst
@@ -278,7 +278,7 @@ to the prefix ``/photos/`` instead::
     app = tornado.web.Application([
         url(r"/photos/(.*)", MyPhotoHandler),
         url(r"/pictures/(.*)", tornado.web.RedirectHandler,
-            dict(url=r"/photos/\1")),
+            dict(url=r"/photos/{0}")),
         ])
 
 Unlike `.RequestHandler.redirect`, `.RedirectHandler` uses permanent
diff --git a/tornado/httpserver.py b/tornado/httpserver.py
index c7b9c2f8..ff235fe4 100644
--- a/tornado/httpserver.py
+++ b/tornado/httpserver.py
@@ -179,45 +179,12 @@ class HTTPServer(TCPServer, Configurable,
         conn.start_serving(self)
 
     def start_request(self, server_conn, request_conn):
-        if isinstance(self.request_callback, httputil.HTTPServerConnectionDelegate):
-            delegate = self.request_callback.start_request(server_conn, request_conn)
-        else:
-            delegate = _CallableAdapter(self.request_callback, request_conn)
-
-        if self.xheaders:
-            delegate = _ProxyAdapter(delegate, request_conn)
-
-        return delegate
+        return _ServerRequestAdapter(self, server_conn, request_conn)
 
     def on_close(self, server_conn):
         self._connections.remove(server_conn)
 
 
-class _CallableAdapter(httputil.HTTPMessageDelegate):
-    def __init__(self, request_callback, request_conn):
-        self.connection = request_conn
-        self.request_callback = request_callback
-        self.request = None
-        self.delegate = None
-        self._chunks = []
-
-    def headers_received(self, start_line, headers):
-        self.request = httputil.HTTPServerRequest(
-            connection=self.connection, start_line=start_line,
-            headers=headers)
-
-    def data_received(self, chunk):
-        self._chunks.append(chunk)
-
-    def finish(self):
-        self.request.body = b''.join(self._chunks)
-        self.request._parse_body()
-        self.request_callback(self.request)
-
-    def on_connection_close(self):
-        self._chunks = None
-
-
 class _HTTPRequestContext(object):
     def __init__(self, stream, address, protocol):
         self.address = address
@@ -280,27 +247,58 @@ class _HTTPRequestContext(object):
         self.protocol = self._orig_protocol
 
 
-class _ProxyAdapter(httputil.HTTPMessageDelegate):
-    def __init__(self, delegate, request_conn):
+class _ServerRequestAdapter(httputil.HTTPMessageDelegate):
+    """Adapts the `HTTPMessageDelegate` interface to the interface expected
+    by our clients.
+    """
+    def __init__(self, server, server_conn, request_conn):
+        self.server = server
         self.connection = request_conn
-        self.delegate = delegate
+        self.request = None
+        if isinstance(server.request_callback,
+                      httputil.HTTPServerConnectionDelegate):
+            self.delegate = server.request_callback.start_request(
+                server_conn, request_conn)
+            self._chunks = None
+        else:
+            self.delegate = None
+            self._chunks = []
 
     def headers_received(self, start_line, headers):
-        self.connection.context._apply_xheaders(headers)
-        return self.delegate.headers_received(start_line, headers)
+        if self.server.xheaders:
+            self.connection.context._apply_xheaders(headers)
+        if self.delegate is None:
+            self.request = httputil.HTTPServerRequest(
+                connection=self.connection, start_line=start_line,
+                headers=headers)
+        else:
+            return self.delegate.headers_received(start_line, headers)
 
     def data_received(self, chunk):
-        return self.delegate.data_received(chunk)
+        if self.delegate is None:
+            self._chunks.append(chunk)
+        else:
+            return self.delegate.data_received(chunk)
 
     def finish(self):
-        self.delegate.finish()
+        if self.delegate is None:
+            self.request.body = b''.join(self._chunks)
+            self.request._parse_body()
+            self.server.request_callback(self.request)
+        else:
+            self.delegate.finish()
         self._cleanup()
 
     def on_connection_close(self):
-        self.delegate.on_connection_close()
+        if self.delegate is None:
+            self._chunks = None
+        else:
+            self.delegate.on_connection_close()
         self._cleanup()
 
     def _cleanup(self):
-        self.connection.context._unapply_xheaders()
+        if self.server.xheaders:
+            self.connection.context._unapply_xheaders()
+
 
 HTTPRequest = httputil.HTTPServerRequest
diff --git a/tornado/ioloop.py b/tornado/ioloop.py
index 1b1a07cd..d6183176 100644
--- a/tornado/ioloop.py
+++ b/tornado/ioloop.py
@@ -28,7 +28,6 @@ In addition to I/O events, the `IOLoop` can also schedule time-based events.
 
 from __future__ import absolute_import, division, print_function, with_statement
 
-import collections
 import datetime
 import errno
 import functools
@@ -694,7 +693,8 @@ class PollIOLoop(IOLoop):
         self.time_func = time_func or time.time
         self._handlers = {}
         self._events = {}
-        self._callbacks = collections.deque()
+        self._callbacks = []
+        self._callback_lock = threading.Lock()
         self._timeouts = []
         self._cancellations = 0
         self._running = False
@@ -712,7 +712,8 @@ class PollIOLoop(IOLoop):
                          self.READ)
 
     def close(self, all_fds=False):
-        self._closing = True
+        with self._callback_lock:
+            self._closing = True
         self.remove_handler(self._waker.fileno())
         if all_fds:
             for fd, handler in self._handlers.values():
@@ -799,7 +800,9 @@ class PollIOLoop(IOLoop):
             while True:
                 # Prevent IO event starvation by delaying new callbacks
                 # to the next iteration of the event loop.
-                ncallbacks = len(self._callbacks)
+                with self._callback_lock:
+                    callbacks = self._callbacks
+                    self._callbacks = []
 
                 # Add any timeouts that have come due to the callback list.
                 # Do not run anything until we have determined which ones
@@ -828,14 +831,14 @@ class PollIOLoop(IOLoop):
                                           if x.callback is not None]
                         heapq.heapify(self._timeouts)
 
-                for i in range(ncallbacks):
-                    self._run_callback(self._callbacks.popleft())
+                for callback in callbacks:
+                    self._run_callback(callback)
                 for timeout in due_timeouts:
                     if timeout.callback is not None:
                         self._run_callback(timeout.callback)
                 # Closures may be holding on to a lot of memory, so allow
                 # them to be freed before we go into our poll wait.
-                due_timeouts = timeout = None
+                callbacks = callback = due_timeouts = timeout = None
 
                 if self._callbacks:
                     # If any callbacks or timeouts called add_callback,
@@ -931,20 +934,36 @@ class PollIOLoop(IOLoop):
         self._cancellations += 1
 
     def add_callback(self, callback, *args, **kwargs):
-        if self._closing:
-            return
-        # Blindly insert into self._callbacks. This is safe even
-        # from signal handlers because deque.append is atomic.
-        self._callbacks.append(functools.partial(
-            stack_context.wrap(callback), *args, **kwargs))
         if thread.get_ident() != self._thread_ident:
-            # This will write one byte but Waker.consume() reads many
-            # at once, so it's ok to write even when not strictly
-            # necessary.
-            self._waker.wake()
+            # If we're not on the IOLoop's thread, we need to synchronize
+            # with other threads, or waking logic will induce a race.
+            with self._callback_lock:
+                if self._closing:
+                    return
+                list_empty = not self._callbacks
+                self._callbacks.append(functools.partial(
+                    stack_context.wrap(callback), *args, **kwargs))
+                if list_empty:
+                    # If we're not in the IOLoop's thread, and we added the
+                    # first callback to an empty list, we may need to wake it
+                    # up (it may wake up on its own, but an occasional extra
+                    # wake is harmless).  Waking up a polling IOLoop is
+                    # relatively expensive, so we try to avoid it when we can.
+                    self._waker.wake()
         else:
-            # If we're on the IOLoop's thread, we don't need to wake anyone.
-            pass
+            if self._closing:
+                return
+            # If we're on the IOLoop's thread, we don't need the lock,
+            # since we don't need to wake anyone, just add the
+            # callback. Blindly insert into self._callbacks. This is
+            # safe even from signal handlers because the GIL makes
+            # list.append atomic. One subtlety is that if the signal
+            # is interrupting another thread holding the
+            # _callback_lock block in IOLoop.start, we may modify
+            # either the old or new version of self._callbacks, but
+            # either way will work.
+            self._callbacks.append(functools.partial(
+                stack_context.wrap(callback), *args, **kwargs))
 
     def add_callback_from_signal(self, callback, *args, **kwargs):
         with stack_context.NullContext():
diff --git a/tornado/platform/common.py b/tornado/platform/common.py
index 2d4065ca..b409a903 100644
--- a/tornado/platform/common.py
+++ b/tornado/platform/common.py
@@ -3,26 +3,10 @@ from __future__ import absolute_import, division, print_function, with_statement
 
 import errno
 import socket
-import time
 
 from tornado.platform import interface
 
 
-def try_close(f):
-    # Avoid issue #875 (race condition when using the file in another
-    # thread).
-    for i in range(10):
-        try:
-            f.close()
-        except IOError:
-            # Yield to another thread
-            time.sleep(1e-3)
-        else:
-            break
-    # Try a last time and let raise
-    f.close()
-
-
 class Waker(interface.Waker):
     """Create an OS independent asynchronous pipe.
 
@@ -91,7 +75,7 @@ class Waker(interface.Waker):
     def wake(self):
         try:
             self.writer.send(b"x")
-        except (IOError, socket.error, ValueError):
+        except (IOError, socket.error):
             pass
 
     def consume(self):
@@ -105,4 +89,4 @@ class Waker(interface.Waker):
 
     def close(self):
         self.reader.close()
-        try_close(self.writer)
+        self.writer.close()
diff --git a/tornado/platform/posix.py b/tornado/platform/posix.py
index 572c0139..41a5794c 100644
--- a/tornado/platform/posix.py
+++ b/tornado/platform/posix.py
@@ -21,7 +21,7 @@ from __future__ import absolute_import, division, print_function, with_statement
 import fcntl
 import os
 
-from tornado.platform import common, interface
+from tornado.platform import interface
 
 
 def set_close_exec(fd):
@@ -53,7 +53,7 @@ class Waker(interface.Waker):
     def wake(self):
         try:
             self.writer.write(b"x")
-        except (IOError, ValueError):
+        except IOError:
             pass
 
     def consume(self):
@@ -67,4 +67,4 @@ class Waker(interface.Waker):
 
     def close(self):
         self.reader.close()
-        common.try_close(self.writer)
+        self.writer.close()
diff --git a/tornado/web.py b/tornado/web.py
index a0cb0e8e..f4c50e3c 100644
--- a/tornado/web.py
+++ b/tornado/web.py
@@ -2191,13 +2191,29 @@ class RedirectHandler(RequestHandler):
         application = web.Application([
             (r"/oldpath", web.RedirectHandler, {"url": "/newpath"}),
         ])
+
+    `RedirectHandler` supports regular expression substitutions. E.g., to
+    swap the first and second parts of a path while preserving the remainder::
+
+        application = web.Application([
+            (r"/(.*?)/(.*?)/(.*)", web.RedirectHandler, {"url": "/{1}/{0}/{2}"}),
+        ])
+
+    The final URL is formatted with `str.format` and the substrings that match
+    the capturing groups. In the above example, a request to "/a/b/c" would be
+    formatted like::
+
+        str.format("/{1}/{0}/{2}", "a", "b", "c")  # -> "/b/a/c"
+
+    Use Python's :ref:`format string syntax <formatstrings>` to customize how
+    values are substituted.
     """
     def initialize(self, url, permanent=True):
         self._url = url
         self._permanent = permanent
 
-    def get(self):
-        self.redirect(self._url, permanent=self._permanent)
+    def get(self, *args):
+        self.redirect(self._url.format(*args), permanent=self._permanent)
 
 
 class StaticFileHandler(RequestHandler):

RedirectHandler re substitution doesn't work
### Code

``` python
import tornado.web
from tornado.web import url
import tornado.ioloop

app = tornado.web.Application([
    url(r"/pictures/(.*)", tornado.web.RedirectHandler,
        dict(url=r"/photos/\1")),
])

app.listen(8765)
tornado.ioloop.IOLoop.current().start()
```
### visit `localhost:8765/picture/1` will raise TypeError: get() takes 1 positional argument but 2 were given

```
ERROR:tornado.application:Uncaught exception GET /pictures/1 (::1)
HTTPServerRequest(protocol='http', host='localhost:8765', method='GET', uri='/pictures/1', version='HTTP/1.1', remote_ip='::1', headers={'Host': 'localhost:8765', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Dnt': '1', 'Upgrade-Insecure-Requests': '1', 'Connection': 'keep-alive', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36', 'Cookie': 'Pycharm-73b7632f=f7202b97-d898-4046-8aae-ae6536d1d931; _xsrf=2|ca7ba18c|041b228c03a99e0221d548c50c478a96|1476528491', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Accept-Encoding': 'gzip, deflate, sdch, br'})
Traceback (most recent call last):
  File "C:\Users\Jeff\AppData\Local\Programs\Python\Python35-32\lib\site-packages\tornado\web.py", line 1467, in _execute
    result = method(*self.path_args, **self.path_kwargs)
TypeError: get() takes 1 positional argument but 2 were given
ERROR:tornado.access:500 GET /pictures/1 (::1) 1532.03ms
```

diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py
index 89f34071..b09bb9ab 100644
--- a/tornado/test/web_test.py
+++ b/tornado/test/web_test.py
@@ -2866,3 +2866,20 @@ class URLSpecReverseTest(unittest.TestCase):
     def test_reverse_arguments(self):
         self.assertEqual('/api/v1/foo/bar',
                          url(r'^/api/v1/foo/(\w+)$', None).reverse('bar'))
+
+
+class RedirectHandlerTest(WebTestCase):
+    def get_handlers(self):
+        return [
+            ('/src', WebRedirectHandler, {'url': '/dst'}),
+            (r'/(.*?)/(.*?)/(.*)', WebRedirectHandler, {'url': '/{1}/{0}/{2}'})]
+
+    def test_basic_redirect(self):
+        response = self.fetch('/src', follow_redirects=False)
+        self.assertEqual(response.code, 301)
+        self.assertEqual(response.headers['Location'], '/dst')
+
+    def test_redirect_pattern(self):
+        response = self.fetch('/a/b/c', follow_redirects=False)
+        self.assertEqual(response.code, 301)
+        self.assertEqual(response.headers['Location'], '/b/a/c')

pip install -e .[dev]
parse_log_pytest
pytest
sphinx sphinx_rtd_theme pytest
apt-get update apt-get install -y gcc
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
Babel==2.11.0
certifi==2021.5.30
charset-normalizer==2.0.12
docutils==0.18.1
idna==3.10
imagesize==1.4.1
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
Jinja2==3.0.3
MarkupSafe==2.0.1
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
Pygments==2.14.0
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
pytz==2025.2
requests==2.27.1
snowballstemmer==2.2.0
Sphinx==5.3.0
sphinx-rtd-theme==2.0.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
-e git+https://github.com/tornadoweb/tornado.git@3d2bcbedbd2eaab9d221c5e8129240702f2287e7#egg=tornado
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
urllib3==1.26.20
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: tornado
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - babel==2.11.0
      - charset-normalizer==2.0.12
      - docutils==0.18.1
      - idna==3.10
      - imagesize==1.4.1
      - jinja2==3.0.3
      - markupsafe==2.0.1
      - pygments==2.14.0
      - pytz==2025.2
      - requests==2.27.1
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinx-rtd-theme==2.0.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - urllib3==1.26.20
prefix: /opt/conda/envs/tornado

tornado/test/web_test.py::RedirectHandlerTest::test_redirect_pattern
tornado/test/web_test.py::SecureCookieV1Test::test_arbitrary_bytes tornado/test/web_test.py::SecureCookieV1Test::test_cookie_tampering_future_timestamp tornado/test/web_test.py::SecureCookieV1Test::test_round_trip tornado/test/web_test.py::SecureCookieV2Test::test_key_version_increment_version tornado/test/web_test.py::SecureCookieV2Test::test_key_version_invalidate_version tornado/test/web_test.py::SecureCookieV2Test::test_key_version_roundtrip tornado/test/web_test.py::SecureCookieV2Test::test_key_version_roundtrip_differing_version tornado/test/web_test.py::SecureCookieV2Test::test_round_trip tornado/test/web_test.py::CookieTest::test_cookie_special_char tornado/test/web_test.py::CookieTest::test_get_cookie tornado/test/web_test.py::CookieTest::test_set_cookie tornado/test/web_test.py::CookieTest::test_set_cookie_domain tornado/test/web_test.py::CookieTest::test_set_cookie_expires_days tornado/test/web_test.py::CookieTest::test_set_cookie_false_flags tornado/test/web_test.py::CookieTest::test_set_cookie_max_age tornado/test/web_test.py::CookieTest::test_set_cookie_overwrite tornado/test/web_test.py::AuthRedirectTest::test_absolute_auth_redirect tornado/test/web_test.py::AuthRedirectTest::test_relative_auth_redirect tornado/test/web_test.py::ConnectionCloseTest::test_connection_close tornado/test/web_test.py::RequestEncodingTest::test_error tornado/test/web_test.py::RequestEncodingTest::test_group_encoding tornado/test/web_test.py::RequestEncodingTest::test_group_question_mark tornado/test/web_test.py::RequestEncodingTest::test_slashes tornado/test/web_test.py::WSGISafeWebTest::test_decode_argument tornado/test/web_test.py::WSGISafeWebTest::test_decode_argument_invalid_unicode tornado/test/web_test.py::WSGISafeWebTest::test_decode_argument_plus tornado/test/web_test.py::WSGISafeWebTest::test_get_argument tornado/test/web_test.py::WSGISafeWebTest::test_get_body_arguments tornado/test/web_test.py::WSGISafeWebTest::test_get_query_arguments tornado/test/web_test.py::WSGISafeWebTest::test_header_injection tornado/test/web_test.py::WSGISafeWebTest::test_multi_header tornado/test/web_test.py::WSGISafeWebTest::test_no_gzip tornado/test/web_test.py::WSGISafeWebTest::test_optional_path tornado/test/web_test.py::WSGISafeWebTest::test_redirect tornado/test/web_test.py::WSGISafeWebTest::test_reverse_url tornado/test/web_test.py::WSGISafeWebTest::test_types tornado/test/web_test.py::WSGISafeWebTest::test_uimodule_resources tornado/test/web_test.py::WSGISafeWebTest::test_uimodule_unescaped tornado/test/web_test.py::WSGISafeWebTest::test_web_redirect tornado/test/web_test.py::WSGISafeWebTest::test_web_redirect_double_slash tornado/test/web_test.py::NonWSGIWebTests::test_empty_flush tornado/test/web_test.py::NonWSGIWebTests::test_flow_control tornado/test/web_test.py::ErrorResponseTest::test_default tornado/test/web_test.py::ErrorResponseTest::test_failed_write_error tornado/test/web_test.py::ErrorResponseTest::test_write_error tornado/test/web_test.py::StaticFileTest::test_absolute_static_url tornado/test/web_test.py::StaticFileTest::test_absolute_version_exclusion tornado/test/web_test.py::StaticFileTest::test_include_host_override tornado/test/web_test.py::StaticFileTest::test_path_traversal_protection tornado/test/web_test.py::StaticFileTest::test_relative_version_exclusion tornado/test/web_test.py::StaticFileTest::test_root_static_path tornado/test/web_test.py::StaticFileTest::test_static_304_if_modified_since tornado/test/web_test.py::StaticFileTest::test_static_304_if_none_match tornado/test/web_test.py::StaticFileTest::test_static_404 tornado/test/web_test.py::StaticFileTest::test_static_compressed_files tornado/test/web_test.py::StaticFileTest::test_static_etag tornado/test/web_test.py::StaticFileTest::test_static_files tornado/test/web_test.py::StaticFileTest::test_static_head tornado/test/web_test.py::StaticFileTest::test_static_head_range tornado/test/web_test.py::StaticFileTest::test_static_if_modified_since_pre_epoch tornado/test/web_test.py::StaticFileTest::test_static_if_modified_since_time_zone tornado/test/web_test.py::StaticFileTest::test_static_invalid_range tornado/test/web_test.py::StaticFileTest::test_static_range_if_none_match tornado/test/web_test.py::StaticFileTest::test_static_unsatisfiable_range_invalid_start tornado/test/web_test.py::StaticFileTest::test_static_unsatisfiable_range_zero_suffix tornado/test/web_test.py::StaticFileTest::test_static_url tornado/test/web_test.py::StaticFileTest::test_static_with_range tornado/test/web_test.py::StaticFileTest::test_static_with_range_end_edge tornado/test/web_test.py::StaticFileTest::test_static_with_range_full_file tornado/test/web_test.py::StaticFileTest::test_static_with_range_full_past_end tornado/test/web_test.py::StaticFileTest::test_static_with_range_neg_end tornado/test/web_test.py::StaticFileTest::test_static_with_range_partial_past_end tornado/test/web_test.py::StaticDefaultFilenameTest::test_static_default_filename tornado/test/web_test.py::StaticDefaultFilenameTest::test_static_default_redirect tornado/test/web_test.py::StaticFileWithPathTest::test_serve tornado/test/web_test.py::CustomStaticFileTest::test_serve tornado/test/web_test.py::CustomStaticFileTest::test_static_url tornado/test/web_test.py::HostMatchingTest::test_host_matching tornado/test/web_test.py::DefaultHostMatchingTest::test_default_host_matching tornado/test/web_test.py::NamedURLSpecGroupsTest::test_named_urlspec_groups tornado/test/web_test.py::ClearHeaderTest::test_clear_header tornado/test/web_test.py::Header204Test::test_204_headers tornado/test/web_test.py::Header304Test::test_304_headers tornado/test/web_test.py::StatusReasonTest::test_status tornado/test/web_test.py::DateHeaderTest::test_date_header tornado/test/web_test.py::RaiseWithReasonTest::test_httperror_str tornado/test/web_test.py::RaiseWithReasonTest::test_httperror_str_from_httputil tornado/test/web_test.py::RaiseWithReasonTest::test_raise_with_reason tornado/test/web_test.py::ErrorHandlerXSRFTest::test_404_xsrf tornado/test/web_test.py::ErrorHandlerXSRFTest::test_error_xsrf tornado/test/web_test.py::GzipTestCase::test_gzip tornado/test/web_test.py::GzipTestCase::test_gzip_not_requested tornado/test/web_test.py::GzipTestCase::test_gzip_static tornado/test/web_test.py::GzipTestCase::test_vary_already_present tornado/test/web_test.py::GzipTestCase::test_vary_already_present_multiple tornado/test/web_test.py::PathArgsInPrepareTest::test_kw tornado/test/web_test.py::PathArgsInPrepareTest::test_pos tornado/test/web_test.py::ClearAllCookiesTest::test_clear_all_cookies tornado/test/web_test.py::ExceptionHandlerTest::test_http_error tornado/test/web_test.py::ExceptionHandlerTest::test_known_error tornado/test/web_test.py::ExceptionHandlerTest::test_unknown_error tornado/test/web_test.py::BuggyLoggingTest::test_buggy_log_exception tornado/test/web_test.py::UIMethodUIModuleTest::test_ui_method tornado/test/web_test.py::GetArgumentErrorTest::test_catch_error tornado/test/web_test.py::MultipleExceptionTest::test_multi_exception tornado/test/web_test.py::SetLazyPropertiesTest::test_set_properties tornado/test/web_test.py::GetCurrentUserTest::test_get_current_user_from_ui_module_is_lazy tornado/test/web_test.py::GetCurrentUserTest::test_get_current_user_from_ui_module_works tornado/test/web_test.py::GetCurrentUserTest::test_get_current_user_works tornado/test/web_test.py::UnimplementedHTTPMethodsTest::test_unimplemented_standard_methods tornado/test/web_test.py::UnimplementedNonStandardMethodsTest::test_unimplemented_other tornado/test/web_test.py::UnimplementedNonStandardMethodsTest::test_unimplemented_patch tornado/test/web_test.py::AllHTTPMethodsTest::test_standard_methods tornado/test/web_test.py::PatchMethodTest::test_other tornado/test/web_test.py::PatchMethodTest::test_patch tornado/test/web_test.py::FinishInPrepareTest::test_finish_in_prepare tornado/test/web_test.py::Default404Test::test_404 tornado/test/web_test.py::Custom404Test::test_404 tornado/test/web_test.py::DefaultHandlerArgumentsTest::test_403 tornado/test/web_test.py::HandlerByNameTest::test_handler_by_name tornado/test/web_test.py::StreamingRequestBodyTest::test_close_during_upload tornado/test/web_test.py::StreamingRequestBodyTest::test_early_return tornado/test/web_test.py::StreamingRequestBodyTest::test_early_return_with_data tornado/test/web_test.py::StreamingRequestBodyTest::test_streaming_body tornado/test/web_test.py::DecoratedStreamingRequestFlowControlTest::test_flow_control_chunked_body tornado/test/web_test.py::DecoratedStreamingRequestFlowControlTest::test_flow_control_compressed_body tornado/test/web_test.py::DecoratedStreamingRequestFlowControlTest::test_flow_control_fixed_body tornado/test/web_test.py::NativeStreamingRequestFlowControlTest::test_flow_control_chunked_body tornado/test/web_test.py::NativeStreamingRequestFlowControlTest::test_flow_control_compressed_body tornado/test/web_test.py::NativeStreamingRequestFlowControlTest::test_flow_control_fixed_body tornado/test/web_test.py::IncorrectContentLengthTest::test_content_length_too_high tornado/test/web_test.py::IncorrectContentLengthTest::test_content_length_too_low tornado/test/web_test.py::ClientCloseTest::test_client_close tornado/test/web_test.py::SignedValueTest::test_expired tornado/test/web_test.py::SignedValueTest::test_key_version_retrieval tornado/test/web_test.py::SignedValueTest::test_key_versioning_invalid_key tornado/test/web_test.py::SignedValueTest::test_key_versioning_read_write_default_key tornado/test/web_test.py::SignedValueTest::test_key_versioning_read_write_non_default_key tornado/test/web_test.py::SignedValueTest::test_known_values tornado/test/web_test.py::SignedValueTest::test_name_swap tornado/test/web_test.py::SignedValueTest::test_non_ascii tornado/test/web_test.py::SignedValueTest::test_payload_tampering tornado/test/web_test.py::SignedValueTest::test_signature_tampering tornado/test/web_test.py::XSRFTest::test_cross_user tornado/test/web_test.py::XSRFTest::test_distinct_tokens tornado/test/web_test.py::XSRFTest::test_refresh_token tornado/test/web_test.py::XSRFTest::test_versioning tornado/test/web_test.py::XSRFTest::test_xsrf_fail_argument_invalid_format tornado/test/web_test.py::XSRFTest::test_xsrf_fail_body_no_cookie tornado/test/web_test.py::XSRFTest::test_xsrf_fail_cookie_invalid_format tornado/test/web_test.py::XSRFTest::test_xsrf_fail_cookie_no_body tornado/test/web_test.py::XSRFTest::test_xsrf_fail_no_token tornado/test/web_test.py::XSRFTest::test_xsrf_success_header tornado/test/web_test.py::XSRFTest::test_xsrf_success_non_hex_token tornado/test/web_test.py::XSRFTest::test_xsrf_success_post_body tornado/test/web_test.py::XSRFTest::test_xsrf_success_query_string tornado/test/web_test.py::XSRFTest::test_xsrf_success_short_token tornado/test/web_test.py::XSRFCookieKwargsTest::test_xsrf_httponly tornado/test/web_test.py::FinishExceptionTest::test_finish_exception tornado/test/web_test.py::DecoratorTest::test_addslash tornado/test/web_test.py::DecoratorTest::test_removeslash tornado/test/web_test.py::CacheTest::test_multiple_strong_etag_match tornado/test/web_test.py::CacheTest::test_multiple_strong_etag_not_match tornado/test/web_test.py::CacheTest::test_multiple_weak_etag_match tornado/test/web_test.py::CacheTest::test_multiple_weak_etag_not_match tornado/test/web_test.py::CacheTest::test_strong_etag_match tornado/test/web_test.py::CacheTest::test_strong_etag_not_match tornado/test/web_test.py::CacheTest::test_weak_etag_match tornado/test/web_test.py::CacheTest::test_weak_etag_not_match tornado/test/web_test.py::CacheTest::test_wildcard_etag tornado/test/web_test.py::RequestSummaryTest::test_missing_remote_ip tornado/test/web_test.py::HTTPErrorTest::test_copy tornado/test/web_test.py::ApplicationTest::test_listen tornado/test/web_test.py::URLSpecReverseTest::test_non_reversible tornado/test/web_test.py::URLSpecReverseTest::test_reverse tornado/test/web_test.py::URLSpecReverseTest::test_reverse_arguments tornado/test/web_test.py::RedirectHandlerTest::test_basic_redirect
tornado/ioloop.py docs/guide/structure.rst tornado/web.py tornado/platform/posix.py tornado/httpserver.py tornado/platform/common.py
tornado/ioloop.py docs/guide/structure.rst tornado/web.py tornado/platform/posix.py tornado/httpserver.py tornado/platform/common.py