web.py 134.9 KB
Newer Older
B
Bret Taylor 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#
# Copyright 2009 Facebook
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

16 17 18 19
"""``tornado.web`` provides a simple web framework with asynchronous
features that allow it to scale to large numbers of open connections,
making it ideal for `long polling
<http://en.wikipedia.org/wiki/Push_technology#Long_polling>`_.
B
Bret Taylor 已提交
20

21 22 23
Here is a simple "Hello, world" example app:

.. testcode::
B
Bret Taylor 已提交
24 25 26 27 28 29 30 31 32 33 34 35

    import tornado.ioloop
    import tornado.web

    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            self.write("Hello, world")

    if __name__ == "__main__":
        application = tornado.web.Application([
            (r"/", MainHandler),
        ])
36
        application.listen(8888)
37
        tornado.ioloop.IOLoop.current().start()
B
Bret Taylor 已提交
38

39 40 41 42
.. testoutput::
   :hide:


B
Ben Darnell 已提交
43
See the :doc:`guide` for additional information.
44

B
Ben Darnell 已提交
45 46
Thread-safety notes
-------------------
47

48
In general, methods on `RequestHandler` and elsewhere in Tornado are
B
Ben Darnell 已提交
49
not thread-safe. In particular, methods such as
50
`~RequestHandler.write()`, `~RequestHandler.finish()`, and
B
Ben Darnell 已提交
51
`~RequestHandler.flush()` must only be called from the main thread. If
52 53
you use multiple threads it is important to use `.IOLoop.add_callback`
to transfer control back to the main thread before finishing the
B
Ben Darnell 已提交
54 55 56
request, or to limit your use of other threads to
`.IOLoop.run_in_executor` and ensure that your callbacks running in
the executor do not refer to Tornado objects.
B
Ben Darnell 已提交
57

B
Bret Taylor 已提交
58 59 60 61 62 63 64
"""

import base64
import binascii
import datetime
import email.utils
import functools
65
import gzip
B
Bret Taylor 已提交
66 67
import hashlib
import hmac
68 69 70
import http.cookies
from inspect import isclass
from io import BytesIO
B
Bret Taylor 已提交
71
import mimetypes
72
import numbers
B
Bret Taylor 已提交
73 74 75
import os.path
import re
import sys
76
import threading
B
Bret Taylor 已提交
77
import time
78
import tornado
79
import traceback
B
Bret Taylor 已提交
80
import types
81 82
import urllib.parse
from urllib.parse import urlencode
B
Bret Taylor 已提交
83

84
from tornado.concurrent import Future, future_set_result_unless_cancelled
85
from tornado import escape
86
from tornado import gen
87
from tornado.httpserver import HTTPServer
88
from tornado import httputil
89
from tornado import iostream
B
Ben Darnell 已提交
90
import tornado.locale
91
from tornado import locale
92
from tornado.log import access_log, app_log, gen_log
93
from tornado import template
94
from tornado.escape import utf8, _unicode
B
Ben Darnell 已提交
95 96 97 98 99 100 101 102 103 104
from tornado.routing import (
    AnyMatches,
    DefaultHostMatches,
    HostMatches,
    ReversibleRouter,
    Rule,
    ReversibleRuleRouter,
    URLSpec,
    _RuleList,
)
105
from tornado.util import ObjectDict, unicode_type, _websocket_mask
A
Andrey Sumin 已提交
106 107

url = URLSpec
108

B
Ben Darnell 已提交
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
from typing import (
    Dict,
    Any,
    Union,
    Optional,
    Awaitable,
    Tuple,
    List,
    Callable,
    Iterable,
    Generator,
    Type,
    cast,
    overload,
)
B
Ben Darnell 已提交
124 125
from types import TracebackType
import typing
B
Ben Darnell 已提交
126

B
Ben Darnell 已提交
127 128
if typing.TYPE_CHECKING:
    from typing import Set  # noqa: F401
129

B
Ben Darnell 已提交
130 131 132

# The following types are accepted by RequestHandler.set_header
# and related methods.
B
Ben Darnell 已提交
133
_HeaderTypes = Union[bytes, unicode_type, int, numbers.Integral, datetime.datetime]
B
Ben Darnell 已提交
134

B
Ben Darnell 已提交
135
_CookieSecretTypes = Union[str, bytes, Dict[int, str], Dict[int, bytes]]
136

137

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
MIN_SUPPORTED_SIGNED_VALUE_VERSION = 1
"""The oldest signed value version supported by this version of Tornado.

Signed values older than this version cannot be decoded.

.. versionadded:: 3.2.1
"""

MAX_SUPPORTED_SIGNED_VALUE_VERSION = 2
"""The newest signed value version supported by this version of Tornado.

Signed values newer than this version cannot be decoded.

.. versionadded:: 3.2.1
"""

DEFAULT_SIGNED_VALUE_VERSION = 2
"""The signed value version produced by `.RequestHandler.create_signed_value`.

May be overridden by passing a ``version`` keyword argument.

.. versionadded:: 3.2.1
"""

DEFAULT_SIGNED_VALUE_MIN_VERSION = 1
"""The oldest signed value accepted by `.RequestHandler.get_secure_cookie`.

165
May be overridden by passing a ``min_version`` keyword argument.
166 167 168 169

.. versionadded:: 3.2.1
"""

B
Ben Darnell 已提交
170

B
Ben Darnell 已提交
171 172 173 174 175 176 177
class _ArgDefaultMarker:
    pass


_ARG_DEFAULT = _ArgDefaultMarker()


B
Bret Taylor 已提交
178
class RequestHandler(object):
B
Ben Darnell 已提交
179
    """Base class for HTTP request handlers.
B
Bret Taylor 已提交
180

B
Ben Darnell 已提交
181 182
    Subclasses must define at least one of the methods defined in the
    "Entry points" section below.
B
Bret Taylor 已提交
183
    """
B
Ben Darnell 已提交
184 185

    SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT", "OPTIONS")
B
Bret Taylor 已提交
186

B
Ben Darnell 已提交
187
    _template_loaders = {}  # type: Dict[str, template.BaseLoader]
188
    _template_loader_lock = threading.Lock()
189
    _remove_control_chars_regex = re.compile(r"[\x00-\x08\x0e-\x1f]")
190

B
Ben Darnell 已提交
191 192 193 194 195 196 197
    _stream_request_body = False

    # Will be set in _execute.
    _transforms = None  # type: List[OutputTransform]
    path_args = None  # type: List[str]
    path_kwargs = None  # type: Dict[str, str]

B
Ben Darnell 已提交
198 199 200 201 202 203
    def __init__(
        self,
        application: "Application",
        request: httputil.HTTPServerRequest,
        **kwargs: Any
    ) -> None:
204
        super(RequestHandler, self).__init__()
205

B
Bret Taylor 已提交
206 207 208 209 210
        self.application = application
        self.request = request
        self._headers_written = False
        self._finished = False
        self._auto_finish = True
211
        self._prepared_future = None
B
Ben Darnell 已提交
212 213 214
        self.ui = ObjectDict(
            (n, self._ui_method(m)) for n, m in application.ui_methods.items()
        )
215
        # UIModules are available as both `modules` and `_tt_modules` in the
B
Ben Darnell 已提交
216 217
        # template namespace.  Historically only `modules` was available
        # but could be clobbered by user additions to the namespace.
218
        # The template {% module %} directive looks in `_tt_modules` to avoid
B
Ben Darnell 已提交
219
        # possible conflicts.
B
Ben Darnell 已提交
220
        self.ui["_tt_modules"] = _UIModuleNamespace(self, application.ui_modules)
221
        self.ui["modules"] = self.ui["_tt_modules"]
B
Bret Taylor 已提交
222
        self.clear()
B
Ben Darnell 已提交
223 224
        assert self.request.connection is not None
        # TODO: need to add set_close_callback to HTTPConnection interface
B
Ben Darnell 已提交
225 226 227
        self.request.connection.set_close_callback(  # type: ignore
            self.on_connection_close
        )
B
Ben Darnell 已提交
228
        self.initialize(**kwargs)  # type: ignore
229

230 231
    def _initialize(self) -> None:
        pass
A
afg 已提交
232

233 234
    initialize = _initialize  # type: Callable[..., None]
    """Hook for subclass initialization. Called for each request.
235

236 237
    A dictionary passed as the third argument of a url spec will be
    supplied as keyword arguments to initialize().
B
Ben Darnell 已提交
238

239
    Example::
240

241 242 243
        class ProfileHandler(RequestHandler):
            def initialize(self, database):
                self.database = database
244

245 246 247 248 249 250 251
            def get(self, username):
                ...

        app = Application([
            (r'/user/(.*)', ProfileHandler, dict(database=database)),
            ])
    """
B
Bret Taylor 已提交
252 253

    @property
B
Ben Darnell 已提交
254
    def settings(self) -> Dict[str, Any]:
255
        """An alias for `self.application.settings <Application.settings>`."""
B
Bret Taylor 已提交
256 257
        return self.application.settings

258
    def _unimplemented_method(self, *args: str, **kwargs: str) -> None:
B
Bret Taylor 已提交
259 260
        raise HTTPError(405)

261 262 263 264 265 266 267
    head = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
    get = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
    post = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
    delete = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
    patch = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
    put = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
    options = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
268

B
Ben Darnell 已提交
269
    def prepare(self) -> Optional[Awaitable[None]]:
270
        """Called at the beginning of a request before  `get`/`post`/etc.
B
Bret Taylor 已提交
271

272 273
        Override this method to perform common initialization regardless
        of the request method.
274 275

        Asynchronous support: Decorate this method with `.gen.coroutine`
B
Ben Darnell 已提交
276
        or use ``async def`` to make it asynchronous.
277 278
        If this method returns a `.Future` execution will not proceed
        until the `.Future` is done.
279 280 281

        .. versionadded:: 3.1
           Asynchronous support.
282 283 284
        """
        pass

B
Ben Darnell 已提交
285
    def on_finish(self) -> None:
286 287 288 289 290 291
        """Called after the end of a request.

        Override this method to perform cleanup, logging, etc.
        This method is a counterpart to `prepare`.  ``on_finish`` may
        not produce any output, as it is called after the response
        has been sent to the client.
B
Bret Taylor 已提交
292 293 294
        """
        pass

B
Ben Darnell 已提交
295
    def on_connection_close(self) -> None:
296 297
        """Called in async handlers if the client closed the connection.

298 299 300 301 302
        Override this to clean up resources associated with
        long-lived connections.  Note that this method is called only if
        the connection was closed during asynchronous processing; if you
        need to do cleanup after every request override `on_finish`
        instead.
303

304 305 306 307
        Proxies may keep a connection open for a time (perhaps
        indefinitely) after the client has gone away, so this method
        may not be called promptly after the end user closes their
        connection.
308
        """
309
        if _has_stream_request_body(self.__class__):
B
Ben Darnell 已提交
310 311 312
            if not self.request._body_future.done():
                self.request._body_future.set_exception(iostream.StreamClosedError())
                self.request._body_future.exception()
313

B
Ben Darnell 已提交
314
    def clear(self) -> None:
B
Bret Taylor 已提交
315
        """Resets all headers and content for this response."""
B
Ben Darnell 已提交
316 317 318 319 320 321 322
        self._headers = httputil.HTTPHeaders(
            {
                "Server": "TornadoServer/%s" % tornado.version,
                "Content-Type": "text/html; charset=UTF-8",
                "Date": httputil.format_timestamp(time.time()),
            }
        )
323
        self.set_default_headers()
B
Ben Darnell 已提交
324
        self._write_buffer = []  # type: List[bytes]
B
Bret Taylor 已提交
325
        self._status_code = 200
326
        self._reason = httputil.responses[200]
B
Bret Taylor 已提交
327

B
Ben Darnell 已提交
328
    def set_default_headers(self) -> None:
329 330 331 332 333 334 335 336 337
        """Override this to set HTTP headers at the beginning of the request.

        For example, this is the place to set a custom ``Server`` header.
        Note that setting such headers in the normal flow of request
        processing may not do what you want, since headers may be reset
        during error handling.
        """
        pass

B
Ben Darnell 已提交
338
    def set_status(self, status_code: int, reason: str = None) -> None:
339 340
        """Sets the status code for our response.

B
Ben Darnell 已提交
341
        :arg int status_code: Response status code.
B
Ben Darnell 已提交
342
        :arg str reason: Human-readable reason phrase describing the status
343
            code. If ``None``, it will be filled in from
B
Ben Darnell 已提交
344 345 346 347 348 349
            `http.client.responses` or "Unknown".

        .. versionchanged:: 5.0

           No longer validates that the response code is in
           `http.client.responses`.
350
        """
B
Bret Taylor 已提交
351
        self._status_code = status_code
352 353 354
        if reason is not None:
            self._reason = escape.native_str(reason)
        else:
355
            self._reason = httputil.responses.get(status_code, "Unknown")
B
Bret Taylor 已提交
356

B
Ben Darnell 已提交
357
    def get_status(self) -> int:
358 359 360
        """Returns the status code for our response."""
        return self._status_code

B
Ben Darnell 已提交
361
    def set_header(self, name: str, value: _HeaderTypes) -> None:
B
Bret Taylor 已提交
362 363 364 365 366 367
        """Sets the given response header name and value.

        If a datetime is given, we automatically format it according to the
        HTTP specification. If the value is not a string, we convert it to
        a string. All header values are then encoded as UTF-8.
        """
368 369
        self._headers[name] = self._convert_header_value(value)

B
Ben Darnell 已提交
370
    def add_header(self, name: str, value: _HeaderTypes) -> None:
371 372 373 374 375
        """Adds the given response header and value.

        Unlike `set_header`, `add_header` may be called multiple times
        to return multiple values for the same header.
        """
376
        self._headers.add(name, self._convert_header_value(value))
377

B
Ben Darnell 已提交
378
    def clear_header(self, name: str) -> None:
379 380 381 382 383 384 385 386
        """Clears an outgoing header, undoing a previous `set_header` call.

        Note that this method does not apply to multi-valued headers
        set by `add_header`.
        """
        if name in self._headers:
            del self._headers[name]

387
    _INVALID_HEADER_CHAR_RE = re.compile(r"[\x00-\x1f]")
388

B
Ben Darnell 已提交
389
    def _convert_header_value(self, value: _HeaderTypes) -> str:
390 391 392 393 394 395 396 397 398
        # Convert the input value to a str. This type check is a bit
        # subtle: The bytes case only executes on python 3, and the
        # unicode case only executes on python 2, because the other
        # cases are covered by the first match for str.
        if isinstance(value, str):
            retval = value
        elif isinstance(value, bytes):  # py3
            # Non-ascii characters in headers are not well supported,
            # but if you pass bytes, use latin1 so they pass through as-is.
B
Ben Darnell 已提交
399
            retval = value.decode("latin1")
400 401 402 403
        elif isinstance(value, unicode_type):  # py2
            # TODO: This is inconsistent with the use of latin1 above,
            # but it's been that way for a long time. Should it change?
            retval = escape.utf8(value)
404
        elif isinstance(value, numbers.Integral):
B
Ben Darnell 已提交
405 406
            # return immediately since we know the converted value will be safe
            return str(value)
B
Ben Darnell 已提交
407
        elif isinstance(value, datetime.datetime):
408
            return httputil.format_timestamp(value)
B
Ben Darnell 已提交
409 410
        else:
            raise TypeError("Unsupported header value %r" % value)
B
Ben Darnell 已提交
411
        # If \n is allowed into the header, it is possible to inject
412
        # additional headers or split the request.
413 414 415
        if RequestHandler._INVALID_HEADER_CHAR_RE.search(retval):
            raise ValueError("Unsafe header value %r", retval)
        return retval
416

B
Ben Darnell 已提交
417
    @overload
B
Ben Darnell 已提交
418
    def get_argument(self, name: str, default: str, strip: bool = True) -> str:
B
Ben Darnell 已提交
419 420 421
        pass

    @overload  # noqa: F811
B
Ben Darnell 已提交
422 423 424
    def get_argument(
        self, name: str, default: _ArgDefaultMarker = _ARG_DEFAULT, strip: bool = True
    ) -> str:
B
Ben Darnell 已提交
425 426 427
        pass

    @overload  # noqa: F811
B
Ben Darnell 已提交
428 429 430
    def get_argument(
        self, name: str, default: None, strip: bool = True
    ) -> Optional[str]:
B
Ben Darnell 已提交
431 432
        pass

B
Ben Darnell 已提交
433 434 435 436 437 438
    def get_argument(  # noqa: F811
        self,
        name: str,
        default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT,
        strip: bool = True,
    ) -> Optional[str]:
B
Bret Taylor 已提交
439 440 441
        """Returns the value of the argument with the given name.

        If default is not provided, the argument is considered to be
442
        required, and we raise a `MissingArgumentError` if it is missing.
B
Bret Taylor 已提交
443

444 445 446
        If the argument appears in the url more than once, we return the
        last value.

B
Bret Taylor 已提交
447 448
        The returned value is always unicode.
        """
T
Travis Beauvais 已提交
449 450
        return self._get_argument(name, default, self.request.arguments, strip)

B
Ben Darnell 已提交
451
    def get_arguments(self, name: str, strip: bool = True) -> List[str]:
T
Travis Beauvais 已提交
452 453 454 455 456 457
        """Returns a list of the arguments with the given name.

        If the argument is not present, returns an empty list.

        The returned values are always unicode.
        """
458 459 460 461 462 463

        # Make sure `get_arguments` isn't accidentally being called with a
        # positional argument that's assumed to be a default (like in
        # `get_argument`.)
        assert isinstance(strip, bool)

T
Travis Beauvais 已提交
464
        return self._get_arguments(name, self.request.arguments, strip)
T
Travis Beauvais 已提交
465

B
Ben Darnell 已提交
466 467 468 469 470 471
    def get_body_argument(
        self,
        name: str,
        default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT,
        strip: bool = True,
    ) -> Optional[str]:
T
Travis Beauvais 已提交
472 473 474 475 476 477 478 479 480 481
        """Returns the value of the argument with the given name
        from the request body.

        If default is not provided, the argument is considered to be
        required, and we raise a `MissingArgumentError` if it is missing.

        If the argument appears in the url more than once, we return the
        last value.

        The returned value is always unicode.
B
Ben Darnell 已提交
482 483

        .. versionadded:: 3.2
T
Travis Beauvais 已提交
484
        """
B
Ben Darnell 已提交
485
        return self._get_argument(name, default, self.request.body_arguments, strip)
T
Travis Beauvais 已提交
486

B
Ben Darnell 已提交
487
    def get_body_arguments(self, name: str, strip: bool = True) -> List[str]:
T
Travis Beauvais 已提交
488 489 490 491 492
        """Returns a list of the body arguments with the given name.

        If the argument is not present, returns an empty list.

        The returned values are always unicode.
B
Ben Darnell 已提交
493 494

        .. versionadded:: 3.2
T
Travis Beauvais 已提交
495 496
        """
        return self._get_arguments(name, self.request.body_arguments, strip)
T
Travis Beauvais 已提交
497

B
Ben Darnell 已提交
498 499 500 501 502 503
    def get_query_argument(
        self,
        name: str,
        default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT,
        strip: bool = True,
    ) -> Optional[str]:
T
Travis Beauvais 已提交
504 505 506 507 508 509 510 511 512 513
        """Returns the value of the argument with the given name
        from the request query string.

        If default is not provided, the argument is considered to be
        required, and we raise a `MissingArgumentError` if it is missing.

        If the argument appears in the url more than once, we return the
        last value.

        The returned value is always unicode.
B
Ben Darnell 已提交
514 515

        .. versionadded:: 3.2
T
Travis Beauvais 已提交
516
        """
B
Ben Darnell 已提交
517
        return self._get_argument(name, default, self.request.query_arguments, strip)
T
Travis Beauvais 已提交
518

B
Ben Darnell 已提交
519
    def get_query_arguments(self, name: str, strip: bool = True) -> List[str]:
T
Travis Beauvais 已提交
520
        """Returns a list of the query arguments with the given name.
521 522 523 524

        If the argument is not present, returns an empty list.

        The returned values are always unicode.
B
Ben Darnell 已提交
525 526

        .. versionadded:: 3.2
527
        """
T
Travis Beauvais 已提交
528 529
        return self._get_arguments(name, self.request.query_arguments, strip)

B
Ben Darnell 已提交
530 531 532 533 534 535 536
    def _get_argument(
        self,
        name: str,
        default: Union[None, str, _ArgDefaultMarker],
        source: Dict[str, List[bytes]],
        strip: bool = True,
    ) -> Optional[str]:
T
Travis Beauvais 已提交
537 538
        args = self._get_arguments(name, source, strip=strip)
        if not args:
B
Ben Darnell 已提交
539
            if isinstance(default, _ArgDefaultMarker):
T
Travis Beauvais 已提交
540 541 542
                raise MissingArgumentError(name)
            return default
        return args[-1]
543

B
Ben Darnell 已提交
544 545 546
    def _get_arguments(
        self, name: str, source: Dict[str, List[bytes]], strip: bool = True
    ) -> List[str]:
547
        values = []
T
Travis Beauvais 已提交
548
        for v in source.get(name, []):
B
Ben Darnell 已提交
549 550
            s = self.decode_argument(v, name=name)
            if isinstance(s, unicode_type):
551 552
                # Get rid of any weird control chars (unless decoding gave
                # us bytes, in which case leave it alone)
B
Ben Darnell 已提交
553
                s = RequestHandler._remove_control_chars_regex.sub(" ", s)
554
            if strip:
B
Ben Darnell 已提交
555 556
                s = s.strip()
            values.append(s)
557 558
        return values

B
Ben Darnell 已提交
559
    def decode_argument(self, value: bytes, name: str = None) -> str:
560 561 562 563 564 565
        """Decodes an argument from the request.

        The argument has been percent-decoded and is now a byte string.
        By default, this method decodes the argument as utf-8 and returns
        a unicode string, but this may be overridden in subclasses.

566 567
        This method is used as a filter for both `get_argument()` and for
        values extracted from the url and passed to `get()`/`post()`/etc.
568 569 570 571

        The name of the argument is provided if known, but may be None
        (e.g. for unnamed groups in the url regex).
        """
572 573 574
        try:
            return _unicode(value)
        except UnicodeDecodeError:
B
Ben Darnell 已提交
575 576 577
            raise HTTPError(
                400, "Invalid unicode in %s: %r" % (name or "url", value[:40])
            )
B
Bret Taylor 已提交
578

579
    @property
B
Ben Darnell 已提交
580
    def cookies(self) -> Dict[str, http.cookies.Morsel]:
G
Gonzalo Rafuls 已提交
581 582
        """An alias for
        `self.request.cookies <.httputil.HTTPServerRequest.cookies>`."""
583 584
        return self.request.cookies

B
Ben Darnell 已提交
585
    def get_cookie(self, name: str, default: str = None) -> Optional[str]:
B
Ben Darnell 已提交
586 587 588 589 590 591 592 593
        """Returns the value of the request cookie with the given name.

        If the named cookie is not present, returns ``default``.

        This method only returns cookies that were present in the request.
        It does not see the outgoing cookies set by `set_cookie` in this
        handler.
        """
594
        if self.request.cookies is not None and name in self.request.cookies:
595
            return self.request.cookies[name].value
B
Bret Taylor 已提交
596 597
        return default

B
Ben Darnell 已提交
598 599 600 601 602 603 604 605 606 607
    def set_cookie(
        self,
        name: str,
        value: Union[str, bytes],
        domain: str = None,
        expires: Union[float, Tuple, datetime.datetime] = None,
        path: str = "/",
        expires_days: int = None,
        **kwargs: Any
    ) -> None:
B
Ben Darnell 已提交
608 609 610 611
        """Sets an outgoing cookie name/value with the given options.

        Newly-set cookies are not immediately visible via `get_cookie`;
        they are not present until the next request.
B
Ben Darnell 已提交
612

P
peacetara 已提交
613
        expires may be a numeric timestamp as returned by `time.time`,
B
Ben Darnell 已提交
614
        a time tuple as returned by `time.gmtime`, or a
P
peacetara 已提交
615
        `datetime.datetime` object.
616

B
Ben Darnell 已提交
617
        Additional keyword arguments are set on the cookies.Morsel
618
        directly.
B
Ben Darnell 已提交
619
        See https://docs.python.org/3/library/http.cookies.html#http.cookies.Morsel
620 621
        for available attributes.
        """
622 623 624
        # The cookie library only accepts type str, in both python 2 and 3
        name = escape.native_str(name)
        value = escape.native_str(value)
B
Bret Taylor 已提交
625 626 627
        if re.search(r"[\x00-\x20]", name + value):
            # Don't let us accidentally inject bad stuff
            raise ValueError("Invalid cookie %r: %r" % (name, value))
628
        if not hasattr(self, "_new_cookie"):
629
            self._new_cookie = http.cookies.SimpleCookie()
630 631 632 633
        if name in self._new_cookie:
            del self._new_cookie[name]
        self._new_cookie[name] = value
        morsel = self._new_cookie[name]
B
Bret Taylor 已提交
634
        if domain:
635
            morsel["domain"] = domain
B
Bret Taylor 已提交
636
        if expires_days is not None and not expires:
B
Ben Darnell 已提交
637
            expires = datetime.datetime.utcnow() + datetime.timedelta(days=expires_days)
B
Bret Taylor 已提交
638
        if expires:
639
            morsel["expires"] = httputil.format_timestamp(expires)
B
Bret Taylor 已提交
640
        if path:
641
            morsel["path"] = path
642
        for k, v in kwargs.items():
B
Ben Darnell 已提交
643 644
            if k == "max_age":
                k = "max-age"
645 646 647

            # skip falsy values for httponly and secure flags because
            # SimpleCookie sets them regardless
B
Ben Darnell 已提交
648
            if k in ["httponly", "secure"] and not v:
649 650
                continue

651
            morsel[k] = v
B
Bret Taylor 已提交
652

B
Ben Darnell 已提交
653
    def clear_cookie(self, name: str, path: str = "/", domain: str = None) -> None:
654 655 656 657 658 659
        """Deletes the cookie with the given name.

        Due to limitations of the cookie protocol, you must pass the same
        path and domain to clear a cookie as were used when that cookie
        was set (but there is no way to find out on the server side
        which values were used for a given cookie).
B
Ben Darnell 已提交
660 661 662

        Similar to `set_cookie`, the effect of this method will not be
        seen until the following request.
663
        """
B
Bret Taylor 已提交
664
        expires = datetime.datetime.utcnow() - datetime.timedelta(days=365)
B
Ben Darnell 已提交
665
        self.set_cookie(name, value="", path=path, expires=expires, domain=domain)
B
Bret Taylor 已提交
666

B
Ben Darnell 已提交
667
    def clear_all_cookies(self, path: str = "/", domain: str = None) -> None:
668 669 670 671
        """Deletes all the cookies the user sent with this request.

        See `clear_cookie` for more information on the path and domain
        parameters.
B
Ben Darnell 已提交
672

B
Ben Darnell 已提交
673 674 675
        Similar to `set_cookie`, the effect of this method will not be
        seen until the following request.

B
Ben Darnell 已提交
676 677 678
        .. versionchanged:: 3.2

           Added the ``path`` and ``domain`` parameters.
679
        """
680
        for name in self.request.cookies:
681
            self.clear_cookie(name, path=path, domain=domain)
B
Bret Taylor 已提交
682

B
Ben Darnell 已提交
683 684 685 686 687 688 689 690
    def set_secure_cookie(
        self,
        name: str,
        value: Union[str, bytes],
        expires_days: int = 30,
        version: int = None,
        **kwargs: Any
    ) -> None:
B
Bret Taylor 已提交
691 692
        """Signs and timestamps a cookie so it cannot be forged.

693
        You must specify the ``cookie_secret`` setting in your Application
B
Bret Taylor 已提交
694 695 696
        to use this method. It should be a long, random sequence of bytes
        to be used as the HMAC secret for the signature.

697 698 699 700 701
        To read a cookie set with this method, use `get_secure_cookie()`.

        Note that the ``expires_days`` parameter sets the lifetime of the
        cookie in the browser, but is independent of the ``max_age_days``
        parameter to `get_secure_cookie`.
702 703 704

        Secure cookies may contain arbitrary byte values, not just unicode
        strings (unlike regular cookies)
705

B
Ben Darnell 已提交
706 707 708
        Similar to `set_cookie`, the effect of this method will not be
        seen until the following request.

709 710 711 712
        .. versionchanged:: 3.2.1

           Added the ``version`` argument.  Introduced cookie version 2
           and made it the default.
B
Bret Taylor 已提交
713
        """
B
Ben Darnell 已提交
714 715 716 717 718 719
        self.set_cookie(
            name,
            self.create_signed_value(name, value, version=version),
            expires_days=expires_days,
            **kwargs
        )
720

B
Ben Darnell 已提交
721 722 723
    def create_signed_value(
        self, name: str, value: Union[str, bytes], version: int = None
    ) -> bytes:
724 725 726 727 728
        """Signs and timestamps a string so it cannot be forged.

        Normally used via set_secure_cookie, but provided as a separate
        method for non-cookie uses.  To decode a value not stored
        as a cookie use the optional value argument to get_secure_cookie.
729 730 731 732 733

        .. versionchanged:: 3.2.1

           Added the ``version`` argument.  Introduced cookie version 2
           and made it the default.
734
        """
735
        self.require_setting("cookie_secret", "secure cookies")
736 737 738 739 740 741 742
        secret = self.application.settings["cookie_secret"]
        key_version = None
        if isinstance(secret, dict):
            if self.application.settings.get("key_version") is None:
                raise Exception("key_version setting must be used for secret_key dicts")
            key_version = self.application.settings["key_version"]

B
Ben Darnell 已提交
743 744 745
        return create_signed_value(
            secret, name, value, version=version, key_version=key_version
        )
B
Bret Taylor 已提交
746

B
Ben Darnell 已提交
747 748 749 750 751 752 753
    def get_secure_cookie(
        self,
        name: str,
        value: str = None,
        max_age_days: int = 31,
        min_version: int = None,
    ) -> Optional[bytes]:
754 755 756 757
        """Returns the given signed cookie if it validates, or None.

        The decoded cookie value is returned as a byte string (unlike
        `get_cookie`).
758

B
Ben Darnell 已提交
759 760 761 762
        Similar to `get_cookie`, this method only returns cookies that
        were present in the request. It does not see outgoing cookies set by
        `set_secure_cookie` in this handler.

763 764 765 766
        .. versionchanged:: 3.2.1

           Added the ``min_version`` argument.  Introduced cookie version 2;
           both versions 1 and 2 are accepted by default.
767
        """
B
Bret Taylor 已提交
768
        self.require_setting("cookie_secret", "secure cookies")
769 770
        if value is None:
            value = self.get_cookie(name)
B
Ben Darnell 已提交
771 772 773 774 775 776 777
        return decode_signed_value(
            self.application.settings["cookie_secret"],
            name,
            value,
            max_age_days=max_age_days,
            min_version=min_version,
        )
B
Bret Taylor 已提交
778

B
Ben Darnell 已提交
779 780 781
    def get_secure_cookie_key_version(
        self, name: str, value: str = None
    ) -> Optional[int]:
782 783 784 785 786 787 788
        """Returns the signing key version of the secure cookie.

        The version is returned as int.
        """
        self.require_setting("cookie_secret", "secure cookies")
        if value is None:
            value = self.get_cookie(name)
B
Ben Darnell 已提交
789 790
        if value is None:
            return None
791 792
        return get_signature_key_version(value)

B
Ben Darnell 已提交
793
    def redirect(self, url: str, permanent: bool = False, status: int = None) -> None:
794 795 796 797 798 799 800
        """Sends a redirect to the given (optionally relative) URL.

        If the ``status`` argument is specified, that value is used as the
        HTTP status code; otherwise either 301 (permanent) or 302
        (temporary) is chosen based on the ``permanent`` argument.
        The default is 302 (temporary).
        """
B
Bret Taylor 已提交
801 802
        if self._headers_written:
            raise Exception("Cannot redirect after headers have been written")
803 804 805 806 807
        if status is None:
            status = 301 if permanent else 302
        else:
            assert isinstance(status, int) and 300 <= status <= 399
        self.set_status(status)
808
        self.set_header("Location", utf8(url))
B
Bret Taylor 已提交
809 810
        self.finish()

B
Ben Darnell 已提交
811
    def write(self, chunk: Union[str, bytes, dict]) -> None:
B
Bret Taylor 已提交
812 813 814 815 816
        """Writes the given chunk to the output buffer.

        To write the output to the network, use the flush() method below.

        If the given chunk is a dictionary, we write it as JSON and set
817 818
        the Content-Type of the response to be ``application/json``.
        (if you want to send JSON as a different ``Content-Type``, call
819
        set_header *after* calling write()).
820 821 822 823

        Note that lists are not converted to JSON because of a potential
        cross-site security vulnerability.  All JSON output should be
        wrapped in a dictionary.  More details at
824 825
        http://haacked.com/archive/2009/06/25/json-hijacking.aspx/ and
        https://github.com/facebook/tornado/issues/1009
B
Bret Taylor 已提交
826
        """
827
        if self._finished:
828
            raise RuntimeError("Cannot write() after finish()")
829
        if not isinstance(chunk, (bytes, unicode_type, dict)):
830 831
            message = "write() only accepts bytes, unicode, and dict objects"
            if isinstance(chunk, list):
B
Ben Darnell 已提交
832 833 834 835
                message += (
                    ". Lists not accepted for security reasons; see "
                    + "http://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.write"  # noqa: E501
                )
836
            raise TypeError(message)
B
Bret Taylor 已提交
837 838
        if isinstance(chunk, dict):
            chunk = escape.json_encode(chunk)
839
            self.set_header("Content-Type", "application/json; charset=UTF-8")
840
        chunk = utf8(chunk)
B
Bret Taylor 已提交
841 842
        self._write_buffer.append(chunk)

B
Ben Darnell 已提交
843
    def render(self, template_name: str, **kwargs: Any) -> "Future[None]":
844 845 846 847 848 849 850 851 852 853 854 855
        """Renders the template with the given arguments as the response.

        ``render()`` calls ``finish()``, so no other output methods can be called
        after it.

        Returns a `.Future` with the same semantics as the one returned by `finish`.
        Awaiting this `.Future` is optional.

        .. versionchanged:: 5.1

           Now returns a `.Future` instead of ``None``.
        """
856 857
        if self._finished:
            raise RuntimeError("Cannot render() after finish()")
B
Bret Taylor 已提交
858 859 860 861 862 863 864 865
        html = self.render_string(template_name, **kwargs)

        # Insert the additional JS and CSS added by the modules on the page
        js_embed = []
        js_files = []
        css_embed = []
        css_files = []
        html_heads = []
866
        html_bodies = []
867
        for module in getattr(self, "_active_modules", {}).values():
B
Bret Taylor 已提交
868
            embed_part = module.embedded_javascript()
869 870
            if embed_part:
                js_embed.append(utf8(embed_part))
B
Bret Taylor 已提交
871 872
            file_part = module.javascript_files()
            if file_part:
873
                if isinstance(file_part, (unicode_type, bytes)):
B
Ben Darnell 已提交
874
                    js_files.append(_unicode(file_part))
B
Bret Taylor 已提交
875 876 877
                else:
                    js_files.extend(file_part)
            embed_part = module.embedded_css()
878 879
            if embed_part:
                css_embed.append(utf8(embed_part))
B
Bret Taylor 已提交
880 881
            file_part = module.css_files()
            if file_part:
882
                if isinstance(file_part, (unicode_type, bytes)):
B
Ben Darnell 已提交
883
                    css_files.append(_unicode(file_part))
B
Bret Taylor 已提交
884 885 886
                else:
                    css_files.extend(file_part)
            head_part = module.html_head()
887 888
            if head_part:
                html_heads.append(utf8(head_part))
889
            body_part = module.html_body()
890 891 892
            if body_part:
                html_bodies.append(utf8(body_part))

B
Bret Taylor 已提交
893
        if js_files:
894
            # Maintain order of JavaScript files given by modules
895
            js = self.render_linked_js(js_files)
B
Ben Darnell 已提交
896 897
            sloc = html.rindex(b"</body>")
            html = html[:sloc] + utf8(js) + b"\n" + html[sloc:]
B
Bret Taylor 已提交
898
        if js_embed:
B
Ben Darnell 已提交
899
            js_bytes = self.render_embed_js(js_embed)
B
Ben Darnell 已提交
900 901
            sloc = html.rindex(b"</body>")
            html = html[:sloc] + js_bytes + b"\n" + html[sloc:]
B
Bret Taylor 已提交
902
        if css_files:
903
            css = self.render_linked_css(css_files)
B
Ben Darnell 已提交
904 905
            hloc = html.index(b"</head>")
            html = html[:hloc] + utf8(css) + b"\n" + html[hloc:]
B
Bret Taylor 已提交
906
        if css_embed:
B
Ben Darnell 已提交
907
            css_bytes = self.render_embed_css(css_embed)
B
Ben Darnell 已提交
908 909
            hloc = html.index(b"</head>")
            html = html[:hloc] + css_bytes + b"\n" + html[hloc:]
B
Bret Taylor 已提交
910
        if html_heads:
B
Ben Darnell 已提交
911 912
            hloc = html.index(b"</head>")
            html = html[:hloc] + b"".join(html_heads) + b"\n" + html[hloc:]
913
        if html_bodies:
B
Ben Darnell 已提交
914 915
            hloc = html.index(b"</body>")
            html = html[:hloc] + b"".join(html_bodies) + b"\n" + html[hloc:]
916
        return self.finish(html)
B
Bret Taylor 已提交
917

B
Ben Darnell 已提交
918
    def render_linked_js(self, js_files: Iterable[str]) -> str:
919 920 921 922 923 924
        """Default method used to render the final js links for the
        rendered webpage.

        Override this method in a sub-classed controller to change the output.
        """
        paths = []
B
Ben Darnell 已提交
925
        unique_paths = set()  # type: Set[str]
926 927 928 929 930 931 932 933

        for path in js_files:
            if not is_absolute(path):
                path = self.static_url(path)
            if path not in unique_paths:
                paths.append(path)
                unique_paths.add(path)

B
Ben Darnell 已提交
934 935 936 937 938 939
        return "".join(
            '<script src="'
            + escape.xhtml_escape(p)
            + '" type="text/javascript"></script>'
            for p in paths
        )
940

B
Ben Darnell 已提交
941
    def render_embed_js(self, js_embed: Iterable[bytes]) -> bytes:
942 943 944 945 946
        """Default method used to render the final embedded js for the
        rendered webpage.

        Override this method in a sub-classed controller to change the output.
        """
B
Ben Darnell 已提交
947 948 949 950 951
        return (
            b'<script type="text/javascript">\n//<![CDATA[\n'
            + b"\n".join(js_embed)
            + b"\n//]]>\n</script>"
        )
952

B
Ben Darnell 已提交
953
    def render_linked_css(self, css_files: Iterable[str]) -> str:
954 955 956 957 958 959
        """Default method used to render the final css links for the
        rendered webpage.

        Override this method in a sub-classed controller to change the output.
        """
        paths = []
B
Ben Darnell 已提交
960
        unique_paths = set()  # type: Set[str]
961 962 963 964 965 966 967 968

        for path in css_files:
            if not is_absolute(path):
                path = self.static_url(path)
            if path not in unique_paths:
                paths.append(path)
                unique_paths.add(path)

B
Ben Darnell 已提交
969 970 971 972 973
        return "".join(
            '<link href="' + escape.xhtml_escape(p) + '" '
            'type="text/css" rel="stylesheet"/>'
            for p in paths
        )
974

B
Ben Darnell 已提交
975
    def render_embed_css(self, css_embed: Iterable[bytes]) -> bytes:
976 977 978 979 980
        """Default method used to render the final embedded css for the
        rendered webpage.

        Override this method in a sub-classed controller to change the output.
        """
B
Ben Darnell 已提交
981
        return b'<style type="text/css">\n' + b"\n".join(css_embed) + b"\n</style>"
982

B
Ben Darnell 已提交
983
    def render_string(self, template_name: str, **kwargs: Any) -> bytes:
B
Bret Taylor 已提交
984 985
        """Generate the given template with the given arguments.

986 987
        We return the generated byte string (in utf8). To generate and
        write a template as a response, use render() above.
B
Bret Taylor 已提交
988 989
        """
        # If no template_path is specified, use the path of the calling file
990
        template_path = self.get_template_path()
B
Bret Taylor 已提交
991 992 993 994 995
        if not template_path:
            frame = sys._getframe(0)
            web_file = frame.f_code.co_filename
            while frame.f_code.co_filename == web_file:
                frame = frame.f_back
B
Ben Darnell 已提交
996
            assert frame.f_code.co_filename is not None
B
Bret Taylor 已提交
997
            template_path = os.path.dirname(frame.f_code.co_filename)
998 999 1000 1001 1002 1003 1004
        with RequestHandler._template_loader_lock:
            if template_path not in RequestHandler._template_loaders:
                loader = self.create_template_loader(template_path)
                RequestHandler._template_loaders[template_path] = loader
            else:
                loader = RequestHandler._template_loaders[template_path]
        t = loader.load(template_name)
1005 1006 1007 1008
        namespace = self.get_template_namespace()
        namespace.update(kwargs)
        return t.generate(**namespace)

B
Ben Darnell 已提交
1009
    def get_template_namespace(self) -> Dict[str, Any]:
1010 1011 1012 1013 1014 1015 1016 1017 1018
        """Returns a dictionary to be used as the default template namespace.

        May be overridden by subclasses to add or modify values.

        The results of this method will be combined with additional
        defaults in the `tornado.template` module and keyword arguments
        to `render` or `render_string`.
        """
        namespace = dict(
B
Bret Taylor 已提交
1019 1020 1021 1022 1023
            handler=self,
            request=self.request,
            current_user=self.current_user,
            locale=self.locale,
            _=self.locale.translate,
1024
            pgettext=self.locale.pgettext,
B
Bret Taylor 已提交
1025 1026
            static_url=self.static_url,
            xsrf_form_html=self.xsrf_form_html,
B
Ben Darnell 已提交
1027
            reverse_url=self.reverse_url,
B
Bret Taylor 已提交
1028
        )
1029 1030
        namespace.update(self.ui)
        return namespace
B
Bret Taylor 已提交
1031

B
Ben Darnell 已提交
1032
    def create_template_loader(self, template_path: str) -> template.BaseLoader:
1033 1034 1035 1036
        """Returns a new template loader for the given path.

        May be overridden by subclasses.  By default returns a
        directory-based loader on the given path, using the
1037 1038 1039
        ``autoescape`` and ``template_whitespace`` application
        settings.  If a ``template_loader`` application setting is
        supplied, uses that instead.
1040
        """
1041 1042 1043 1044 1045 1046 1047 1048
        settings = self.application.settings
        if "template_loader" in settings:
            return settings["template_loader"]
        kwargs = {}
        if "autoescape" in settings:
            # autoescape=None means "no escaping", so we have to be sure
            # to only pass this kwarg if the user asked for it.
            kwargs["autoescape"] = settings["autoescape"]
1049 1050
        if "template_whitespace" in settings:
            kwargs["whitespace"] = settings["template_whitespace"]
1051 1052
        return template.Loader(template_path, **kwargs)

B
Ben Darnell 已提交
1053
    def flush(self, include_footers: bool = False) -> "Future[None]":
1054
        """Flushes the current output buffer to the network.
1055

1056 1057 1058 1059 1060
        The ``callback`` argument, if given, can be used for flow control:
        it will be run when all flushed data has been written to the socket.
        Note that only one flush callback can be outstanding at a time;
        if another flush occurs before the previous flush's callback
        has been run, the previous callback will be discarded.
B
Ben Darnell 已提交
1061

1062
        .. versionchanged:: 4.0
B
Ben Darnell 已提交
1063
           Now returns a `.Future` if no callback is given.
1064

1065
        .. versionchanged:: 6.0
1066

1067
           The ``callback`` argument was removed.
1068
        """
B
Ben Darnell 已提交
1069
        assert self.request.connection is not None
1070
        chunk = b"".join(self._write_buffer)
1071
        self._write_buffer = []
B
Bret Taylor 已提交
1072 1073
        if not self._headers_written:
            self._headers_written = True
1074
            for transform in self._transforms:
B
Ben Darnell 已提交
1075
                assert chunk is not None
B
Ben Darnell 已提交
1076 1077 1078
                self._status_code, self._headers, chunk = transform.transform_first_chunk(
                    self._status_code, self._headers, chunk, include_footers
                )
1079 1080
            # Ignore the chunk and only write the headers for HEAD requests
            if self.request.method == "HEAD":
B
Ben Darnell 已提交
1081
                chunk = b""
1082

1083 1084 1085 1086 1087 1088 1089
            # Finalize the cookie headers (which have been stored in a side
            # object so an outgoing cookie could be overwritten before it
            # is sent).
            if hasattr(self, "_new_cookie"):
                for cookie in self._new_cookie.values():
                    self.add_header("Set-Cookie", cookie.OutputString(None))

B
Ben Darnell 已提交
1090
            start_line = httputil.ResponseStartLine("", self._status_code, self._reason)
1091
            return self.request.connection.write_headers(
B
Ben Darnell 已提交
1092 1093
                start_line, self._headers, chunk
            )
B
Bret Taylor 已提交
1094
        else:
1095 1096
            for transform in self._transforms:
                chunk = transform.transform_chunk(chunk, include_footers)
1097 1098
            # Ignore the chunk and only write the headers for HEAD requests
            if self.request.method != "HEAD":
1099
                return self.request.connection.write(chunk)
1100
            else:
B
Ben Darnell 已提交
1101
                future = Future()  # type: Future[None]
1102 1103
                future.set_result(None)
                return future
B
Bret Taylor 已提交
1104

B
Ben Darnell 已提交
1105
    def finish(self, chunk: Union[str, bytes, dict] = None) -> "Future[None]":
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
        """Finishes this response, ending the HTTP request.

        Passing a ``chunk`` to ``finish()`` is equivalent to passing that
        chunk to ``write()`` and then calling ``finish()`` with no arguments.

        Returns a `.Future` which may optionally be awaited to track the sending
        of the response to the client. This `.Future` resolves when all the response
        data has been sent, and raises an error if the connection is closed before all
        data can be sent.

        .. versionchanged:: 5.1

           Now returns a `.Future` instead of ``None``.
        """
1120
        if self._finished:
1121
            raise RuntimeError("finish() called twice")
1122

1123 1124
        if chunk is not None:
            self.write(chunk)
B
Bret Taylor 已提交
1125 1126 1127 1128

        # Automatically support ETags and add the Content-Length header if
        # we have not flushed any content yet.
        if not self._headers_written:
B
Ben Darnell 已提交
1129 1130 1131 1132 1133
            if (
                self._status_code == 200
                and self.request.method in ("GET", "HEAD")
                and "Etag" not in self._headers
            ):
1134 1135 1136 1137
                self.set_etag_header()
                if self.check_etag_header():
                    self._write_buffer = []
                    self.set_status(304)
B
Ben Darnell 已提交
1138 1139 1140 1141 1142 1143
            if self._status_code in (204, 304) or (
                self._status_code >= 100 and self._status_code < 200
            ):
                assert not self._write_buffer, (
                    "Cannot send body with %s" % self._status_code
                )
1144
                self._clear_headers_for_304()
S
Sebastien Boving 已提交
1145
            elif "Content-Length" not in self._headers:
B
Bret Taylor 已提交
1146 1147 1148
                content_length = sum(len(part) for part in self._write_buffer)
                self.set_header("Content-Length", content_length)

B
Ben Darnell 已提交
1149 1150 1151 1152 1153 1154
        assert self.request.connection is not None
        # Now that the request is finished, clear the callback we
        # set on the HTTPConnection (which would otherwise prevent the
        # garbage collection of the RequestHandler when there
        # are keepalive connections)
        self.request.connection.set_close_callback(None)  # type: ignore
1155

1156
        future = self.flush(include_footers=True)
B
Ben Darnell 已提交
1157
        self.request.connection.finish()
1158
        self._log()
B
Bret Taylor 已提交
1159
        self._finished = True
1160
        self.on_finish()
1161
        self._break_cycles()
1162
        return future
1163

B
Ben Darnell 已提交
1164
    def detach(self) -> iostream.IOStream:
B
Ben Darnell 已提交
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175
        """Take control of the underlying stream.

        Returns the underlying `.IOStream` object and stops all
        further HTTP processing. Intended for implementing protocols
        like websockets that tunnel over an HTTP handshake.

        This method is only supported when HTTP/1.1 is used.

        .. versionadded:: 5.1
        """
        self._finished = True
B
Ben Darnell 已提交
1176 1177
        # TODO: add detach to HTTPConnection?
        return self.request.connection.detach()  # type: ignore
B
Ben Darnell 已提交
1178

B
Ben Darnell 已提交
1179
    def _break_cycles(self) -> None:
1180 1181
        # Break up a reference cycle between this handler and the
        # _ui_module closures to allow for faster GC on CPython.
B
Ben Darnell 已提交
1182
        self.ui = None  # type: ignore
B
Bret Taylor 已提交
1183

B
Ben Darnell 已提交
1184
    def send_error(self, status_code: int = 500, **kwargs: Any) -> None:
B
Bret Taylor 已提交
1185 1186
        """Sends the given HTTP error code to the browser.

1187 1188 1189 1190 1191 1192 1193
        If `flush()` has already been called, it is not possible to send
        an error, so this method will simply terminate the response.
        If output has been written but not yet flushed, it will be discarded
        and replaced with the error page.

        Override `write_error()` to customize the error page that is returned.
        Additional keyword arguments are passed through to `write_error`.
B
Bret Taylor 已提交
1194 1195
        """
        if self._headers_written:
1196
            gen_log.error("Cannot send error response after headers written")
B
Bret Taylor 已提交
1197
            if not self._finished:
1198 1199 1200 1201 1202 1203 1204
                # If we get an error between writing headers and finishing,
                # we are unlikely to be able to finish due to a
                # Content-Length mismatch. Try anyway to release the
                # socket.
                try:
                    self.finish()
                except Exception:
B
Ben Darnell 已提交
1205
                    gen_log.error("Failed to flush partial response", exc_info=True)
B
Bret Taylor 已提交
1206 1207
            return
        self.clear()
1208

B
Ben Darnell 已提交
1209 1210 1211
        reason = kwargs.get("reason")
        if "exc_info" in kwargs:
            exception = kwargs["exc_info"][1]
1212 1213 1214
            if isinstance(exception, HTTPError) and exception.reason:
                reason = exception.reason
        self.set_status(status_code, reason=reason)
1215 1216 1217
        try:
            self.write_error(status_code, **kwargs)
        except Exception:
1218
            app_log.error("Uncaught exception in write_error", exc_info=True)
1219 1220
        if not self._finished:
            self.finish()
B
Bret Taylor 已提交
1221

B
Ben Darnell 已提交
1222
    def write_error(self, status_code: int, **kwargs: Any) -> None:
1223 1224
        """Override to implement custom error pages.

1225 1226 1227
        ``write_error`` may call `write`, `render`, `set_header`, etc
        to produce output as usual.

1228 1229 1230 1231 1232
        If this error was caused by an uncaught exception (including
        HTTPError), an ``exc_info`` triple will be available as
        ``kwargs["exc_info"]``.  Note that this exception may not be
        the "current" exception for purposes of methods like
        ``sys.exc_info()`` or ``traceback.format_exc``.
1233
        """
1234
        if self.settings.get("serve_traceback") and "exc_info" in kwargs:
1235
            # in debug mode, try to send a traceback
B
Ben Darnell 已提交
1236
            self.set_header("Content-Type", "text/plain")
1237 1238 1239
            for line in traceback.format_exception(*kwargs["exc_info"]):
                self.write(line)
            self.finish()
1240
        else:
B
Ben Darnell 已提交
1241 1242 1243 1244 1245
            self.finish(
                "<html><title>%(code)d: %(message)s</title>"
                "<body>%(code)d: %(message)s</body></html>"
                % {"code": status_code, "message": self._reason}
            )
B
Bret Taylor 已提交
1246 1247

    @property
B
Ben Darnell 已提交
1248
    def locale(self) -> tornado.locale.Locale:
1249
        """The locale for the current session.
B
Bret Taylor 已提交
1250

1251
        Determined by either `get_user_locale`, which you can override to
B
Bret Taylor 已提交
1252
        set the locale based on, e.g., a user preference stored in a
1253
        database, or `get_browser_locale`, which uses the ``Accept-Language``
B
Bret Taylor 已提交
1254
        header.
1255 1256 1257

        .. versionchanged: 4.1
           Added a property setter.
B
Bret Taylor 已提交
1258 1259
        """
        if not hasattr(self, "_locale"):
B
Ben Darnell 已提交
1260 1261 1262 1263
            loc = self.get_user_locale()
            if loc is not None:
                self._locale = loc
            else:
B
Bret Taylor 已提交
1264 1265 1266
                self._locale = self.get_browser_locale()
                assert self._locale
        return self._locale
1267

1268
    @locale.setter
B
Ben Darnell 已提交
1269
    def locale(self, value: tornado.locale.Locale) -> None:
1270 1271
        self._locale = value

B
Ben Darnell 已提交
1272
    def get_user_locale(self) -> Optional[tornado.locale.Locale]:
B
Bret Taylor 已提交
1273 1274
        """Override to determine the locale from the authenticated user.

1275
        If None is returned, we fall back to `get_browser_locale()`.
1276

1277 1278
        This method should return a `tornado.locale.Locale` object,
        most likely obtained via a call like ``tornado.locale.get("en")``
B
Bret Taylor 已提交
1279 1280 1281
        """
        return None

B
Ben Darnell 已提交
1282
    def get_browser_locale(self, default: str = "en_US") -> tornado.locale.Locale:
1283
        """Determines the user's locale from ``Accept-Language`` header.
B
Bret Taylor 已提交
1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300

        See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
        """
        if "Accept-Language" in self.request.headers:
            languages = self.request.headers["Accept-Language"].split(",")
            locales = []
            for language in languages:
                parts = language.strip().split(";")
                if len(parts) > 1 and parts[1].startswith("q="):
                    try:
                        score = float(parts[1][2:])
                    except (ValueError, TypeError):
                        score = 0.0
                else:
                    score = 1.0
                locales.append((parts[0], score))
            if locales:
1301
                locales.sort(key=lambda pair: pair[1], reverse=True)
B
Bret Taylor 已提交
1302 1303 1304 1305 1306
                codes = [l[0] for l in locales]
                return locale.get(*codes)
        return locale.get(default)

    @property
B
Ben Darnell 已提交
1307
    def current_user(self) -> Any:
B
Bret Taylor 已提交
1308 1309
        """The authenticated user for this request.

1310
        This is set in one of two ways:
B
Bret Taylor 已提交
1311

1312 1313 1314 1315 1316 1317 1318
        * A subclass may override `get_current_user()`, which will be called
          automatically the first time ``self.current_user`` is accessed.
          `get_current_user()` will only be called once per request,
          and is cached for future access::

              def get_current_user(self):
                  user_cookie = self.get_secure_cookie("user")
1319 1320
                  if user_cookie:
                      return json.loads(user_cookie)
1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
                  return None

        * It may be set as a normal variable, typically from an overridden
          `prepare()`::

              @gen.coroutine
              def prepare(self):
                  user_id_cookie = self.get_secure_cookie("user_id")
                  if user_id_cookie:
                      self.current_user = yield load_user(user_id_cookie)

        Note that `prepare()` may be a coroutine while `get_current_user()`
        may not, so the latter form is necessary if loading the user requires
        asynchronous operations.

A
Andrew Rabert 已提交
1336
        The user object may be any type of the application's choosing.
B
Bret Taylor 已提交
1337 1338 1339 1340 1341
        """
        if not hasattr(self, "_current_user"):
            self._current_user = self.get_current_user()
        return self._current_user

1342
    @current_user.setter
B
Ben Darnell 已提交
1343
    def current_user(self, value: Any) -> None:
1344 1345
        self._current_user = value

B
Ben Darnell 已提交
1346
    def get_current_user(self) -> Any:
1347 1348 1349 1350
        """Override to determine the current user from, e.g., a cookie.

        This method may not be a coroutine.
        """
B
Bret Taylor 已提交
1351 1352
        return None

B
Ben Darnell 已提交
1353
    def get_login_url(self) -> str:
B
Bret Taylor 已提交
1354 1355
        """Override to customize the login URL based on the request.

1356
        By default, we use the ``login_url`` application setting.
B
Bret Taylor 已提交
1357 1358 1359 1360
        """
        self.require_setting("login_url", "@tornado.web.authenticated")
        return self.application.settings["login_url"]

B
Ben Darnell 已提交
1361
    def get_template_path(self) -> Optional[str]:
1362 1363
        """Override to customize template path for each handler.

1364
        By default, we use the ``template_path`` application setting.
1365 1366 1367 1368
        Return None to load templates relative to the calling file.
        """
        return self.application.settings.get("template_path")

B
Bret Taylor 已提交
1369
    @property
B
Ben Darnell 已提交
1370
    def xsrf_token(self) -> bytes:
B
Bret Taylor 已提交
1371 1372 1373 1374 1375 1376 1377 1378
        """The XSRF-prevention token for the current user/session.

        To prevent cross-site request forgery, we set an '_xsrf' cookie
        and include the same '_xsrf' value as an argument with all POST
        requests. If the two do not match, we reject the form submission
        as a potential forgery.

        See http://en.wikipedia.org/wiki/Cross-site_request_forgery
1379

1380 1381 1382 1383 1384
        This property is of type `bytes`, but it contains only ASCII
        characters. If a character string is required, there is no
        need to base64-encode it; just decode the byte string as
        UTF-8.

1385 1386 1387 1388 1389 1390 1391 1392
        .. versionchanged:: 3.2.2
           The xsrf token will now be have a random mask applied in every
           request, which makes it safe to include the token in pages
           that are compressed.  See http://breachattack.com for more
           information on the issue fixed by this change.  Old (version 1)
           cookies will be converted to version 2 when this method is called
           unless the ``xsrf_cookie_version`` `Application` setting is
           set to 1.
1393 1394 1395 1396 1397 1398 1399 1400

        .. versionchanged:: 4.3
           The ``xsrf_cookie_kwargs`` `Application` setting may be
           used to supply additional cookie options (which will be
           passed directly to `set_cookie`). For example,
           ``xsrf_cookie_kwargs=dict(httponly=True, secure=True)``
           will set the ``secure`` and ``httponly`` flags on the
           ``_xsrf`` cookie.
B
Bret Taylor 已提交
1401 1402
        """
        if not hasattr(self, "_xsrf_token"):
1403
            version, token, timestamp = self._get_raw_xsrf_token()
1404
            output_version = self.settings.get("xsrf_cookie_version", 2)
1405
            cookie_kwargs = self.settings.get("xsrf_cookie_kwargs", {})
1406 1407 1408 1409
            if output_version == 1:
                self._xsrf_token = binascii.b2a_hex(token)
            elif output_version == 2:
                mask = os.urandom(4)
B
Ben Darnell 已提交
1410 1411 1412 1413 1414 1415 1416 1417
                self._xsrf_token = b"|".join(
                    [
                        b"2",
                        binascii.b2a_hex(mask),
                        binascii.b2a_hex(_websocket_mask(mask, token)),
                        utf8(str(int(timestamp))),
                    ]
                )
1418
            else:
B
Ben Darnell 已提交
1419
                raise ValueError("unknown xsrf cookie version %d", output_version)
1420
            if version is None:
B
Bret Taylor 已提交
1421
                expires_days = 30 if self.current_user else None
B
Ben Darnell 已提交
1422 1423 1424 1425 1426 1427
                self.set_cookie(
                    "_xsrf",
                    self._xsrf_token,
                    expires_days=expires_days,
                    **cookie_kwargs
                )
B
Bret Taylor 已提交
1428 1429
        return self._xsrf_token

B
Ben Darnell 已提交
1430
    def _get_raw_xsrf_token(self) -> Tuple[Optional[int], bytes, float]:
1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
        """Read or generate the xsrf token in its raw form.

        The raw_xsrf_token is a tuple containing:

        * version: the version of the cookie from which this token was read,
          or None if we generated a new token in this request.
        * token: the raw token data; random (non-ascii) bytes.
        * timestamp: the time this token was generated (will not be accurate
          for version 1 cookies)
        """
B
Ben Darnell 已提交
1441
        if not hasattr(self, "_raw_xsrf_token"):
1442 1443 1444 1445 1446 1447 1448 1449 1450
            cookie = self.get_cookie("_xsrf")
            if cookie:
                version, token, timestamp = self._decode_xsrf_token(cookie)
            else:
                version, token, timestamp = None, None, None
            if token is None:
                version = None
                token = os.urandom(16)
                timestamp = time.time()
B
Ben Darnell 已提交
1451 1452
            assert token is not None
            assert timestamp is not None
1453 1454 1455
            self._raw_xsrf_token = (version, token, timestamp)
        return self._raw_xsrf_token

B
Ben Darnell 已提交
1456 1457 1458
    def _decode_xsrf_token(
        self, cookie: str
    ) -> Tuple[Optional[int], Optional[bytes], Optional[float]]:
1459 1460 1461
        """Convert a cookie string into a the tuple form returned by
        _get_raw_xsrf_token.
        """
1462 1463 1464 1465 1466 1467 1468

        try:
            m = _signed_value_version_re.match(utf8(cookie))

            if m:
                version = int(m.group(1))
                if version == 2:
B
Ben Darnell 已提交
1469
                    _, mask_str, masked_token, timestamp_str = cookie.split("|")
1470

B
Ben Darnell 已提交
1471
                    mask = binascii.a2b_hex(utf8(mask_str))
B
Ben Darnell 已提交
1472
                    token = _websocket_mask(mask, binascii.a2b_hex(utf8(masked_token)))
B
Ben Darnell 已提交
1473
                    timestamp = int(timestamp_str)
1474 1475 1476 1477
                    return version, token, timestamp
                else:
                    # Treat unknown versions as not present instead of failing.
                    raise Exception("Unknown xsrf cookie version")
1478
            else:
1479 1480 1481 1482 1483 1484 1485 1486 1487 1488
                version = 1
                try:
                    token = binascii.a2b_hex(utf8(cookie))
                except (binascii.Error, TypeError):
                    token = utf8(cookie)
                # We don't have a usable timestamp in older versions.
                timestamp = int(time.time())
                return (version, token, timestamp)
        except Exception:
            # Catch exceptions and return nothing instead of failing.
B
Ben Darnell 已提交
1489
            gen_log.debug("Uncaught exception in _decode_xsrf_token", exc_info=True)
1490
            return None, None, None
1491

B
Ben Darnell 已提交
1492
    def check_xsrf_cookie(self) -> None:
1493
        """Verifies that the ``_xsrf`` cookie matches the ``_xsrf`` argument.
B
Bret Taylor 已提交
1494

1495
        To prevent cross-site request forgery, we set an ``_xsrf``
1496
        cookie and include the same value as a non-cookie
1497
        field with all ``POST`` requests. If the two do not match, we
1498 1499
        reject the form submission as a potential forgery.

1500 1501
        The ``_xsrf`` value may be set as either a form field named ``_xsrf``
        or in a custom HTTP header named ``X-XSRFToken`` or ``X-CSRFToken``
1502
        (the latter is accepted for compatibility with Django).
B
Bret Taylor 已提交
1503 1504

        See http://en.wikipedia.org/wiki/Cross-site_request_forgery
1505 1506

        Prior to release 1.1.1, this check was ignored if the HTTP header
1507
        ``X-Requested-With: XMLHTTPRequest`` was present.  This exception
1508 1509 1510 1511
        has been shown to be insecure and has been removed.  For more
        information please see
        http://www.djangoproject.com/weblog/2011/feb/08/security/
        http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails
1512 1513 1514 1515

        .. versionchanged:: 3.2.2
           Added support for cookie version 2.  Both versions 1 and 2 are
           supported.
B
Bret Taylor 已提交
1516
        """
B
Ben Darnell 已提交
1517 1518 1519 1520 1521
        token = (
            self.get_argument("_xsrf", None)
            or self.request.headers.get("X-Xsrftoken")
            or self.request.headers.get("X-Csrftoken")
        )
B
Bret Taylor 已提交
1522 1523
        if not token:
            raise HTTPError(403, "'_xsrf' argument missing from POST")
1524 1525
        _, token, _ = self._decode_xsrf_token(token)
        _, expected_token, _ = self._get_raw_xsrf_token()
1526 1527
        if not token:
            raise HTTPError(403, "'_xsrf' argument has invalid format")
B
Ben Darnell 已提交
1528
        if not hmac.compare_digest(utf8(token), utf8(expected_token)):
B
Bret Taylor 已提交
1529 1530
            raise HTTPError(403, "XSRF cookie does not match POST argument")

B
Ben Darnell 已提交
1531
    def xsrf_form_html(self) -> str:
1532
        """An HTML ``<input/>`` element to be included with all POST forms.
B
Bret Taylor 已提交
1533

1534
        It defines the ``_xsrf`` input value, which we check on all POST
B
Bret Taylor 已提交
1535
        requests to prevent cross-site request forgery. If you have set
1536
        the ``xsrf_cookies`` application setting, you must include this
B
Bret Taylor 已提交
1537 1538
        HTML within all of your HTML forms.

1539 1540 1541 1542
        In a template, this method should be called with ``{% module
        xsrf_form_html() %}``

        See `check_xsrf_cookie()` above for more information.
B
Bret Taylor 已提交
1543
        """
B
Ben Darnell 已提交
1544 1545 1546 1547 1548
        return (
            '<input type="hidden" name="_xsrf" value="'
            + escape.xhtml_escape(self.xsrf_token)
            + '"/>'
        )
B
Bret Taylor 已提交
1549

B
Ben Darnell 已提交
1550
    def static_url(self, path: str, include_host: bool = None, **kwargs: Any) -> str:
B
Bret Taylor 已提交
1551 1552
        """Returns a static URL for the given relative static file path.

1553
        This method requires you set the ``static_path`` setting in your
B
Bret Taylor 已提交
1554 1555 1556
        application (which specifies the root directory of your static
        files).

1557 1558 1559 1560 1561 1562
        This method returns a versioned url (by default appending
        ``?v=<signature>``), which allows the static files to be
        cached indefinitely.  This can be disabled by passing
        ``include_version=False`` (in the default implementation;
        other static file implementations are not required to support
        this, but they may support other options).
B
Bret Taylor 已提交
1563

1564 1565 1566 1567 1568
        By default this method returns URLs relative to the current
        host, but if ``include_host`` is true the URL returned will be
        absolute.  If this handler has an ``include_host`` attribute,
        that value will be used as the default for all `static_url`
        calls that do not pass ``include_host`` as a keyword argument.
1569

B
Bret Taylor 已提交
1570 1571
        """
        self.require_setting("static_path", "static_url")
B
Ben Darnell 已提交
1572 1573 1574
        get_url = self.settings.get(
            "static_handler_class", StaticFileHandler
        ).make_static_url
1575 1576 1577 1578 1579

        if include_host is None:
            include_host = getattr(self, "include_host", False)

        if include_host:
1580
            base = self.request.protocol + "://" + self.request.host
B
Bret Taylor 已提交
1581
        else:
1582
            base = ""
1583

1584
        return base + get_url(self.settings, path, **kwargs)
B
Bret Taylor 已提交
1585

B
Ben Darnell 已提交
1586
    def require_setting(self, name: str, feature: str = "this feature") -> None:
B
Bret Taylor 已提交
1587 1588
        """Raises an exception if the given app setting is not defined."""
        if not self.application.settings.get(name):
B
Ben Darnell 已提交
1589 1590 1591 1592
            raise Exception(
                "You must define the '%s' setting in your "
                "application to use %s" % (name, feature)
            )
B
Bret Taylor 已提交
1593

B
Ben Darnell 已提交
1594
    def reverse_url(self, name: str, *args: Any) -> str:
1595
        """Alias for `Application.reverse_url`."""
1596 1597
        return self.application.reverse_url(name, *args)

B
Ben Darnell 已提交
1598
    def compute_etag(self) -> Optional[str]:
1599 1600
        """Computes the etag header to be used for this request.

1601 1602
        By default uses a hash of the content written so far.

1603 1604 1605 1606 1607 1608 1609 1610
        May be overridden to provide custom etag implementations,
        or may return None to disable tornado's default etag support.
        """
        hasher = hashlib.sha1()
        for part in self._write_buffer:
            hasher.update(part)
        return '"%s"' % hasher.hexdigest()

B
Ben Darnell 已提交
1611
    def set_etag_header(self) -> None:
1612 1613 1614
        """Sets the response's Etag header using ``self.compute_etag()``.

        Note: no header will be set if ``compute_etag()`` returns ``None``.
1615 1616

        This method is called automatically when the request is finished.
1617 1618 1619 1620 1621
        """
        etag = self.compute_etag()
        if etag is not None:
            self.set_header("Etag", etag)

B
Ben Darnell 已提交
1622
    def check_etag_header(self) -> bool:
1623 1624 1625 1626 1627 1628 1629 1630 1631
        """Checks the ``Etag`` header against requests's ``If-None-Match``.

        Returns ``True`` if the request's Etag matches and a 304 should be
        returned. For example::

            self.set_etag_header()
            if self.check_etag_header():
                self.set_status(304)
                return
1632 1633 1634 1635 1636 1637

        This method is called automatically when the request is finished,
        but may be called earlier for applications that override
        `compute_etag` and want to do an early check for ``If-None-Match``
        before completing the request.  The ``Etag`` header should be set
        (perhaps with `set_etag_header`) before calling this method.
1638
        """
1639
        computed_etag = utf8(self._headers.get("Etag", ""))
D
daftshady 已提交
1640 1641 1642
        # Find all weak and strong etag values from If-None-Match header
        # because RFC 7232 allows multiple etag values in a single header.
        etags = re.findall(
B
Ben Darnell 已提交
1643
            br'\*|(?:W/)?"[^"]*"', utf8(self.request.headers.get("If-None-Match", ""))
D
daftshady 已提交
1644
        )
1645 1646 1647 1648
        if not computed_etag or not etags:
            return False

        match = False
B
Ben Darnell 已提交
1649
        if etags[0] == b"*":
1650 1651 1652
            match = True
        else:
            # Use a weak comparison when comparing entity-tags.
B
Ben Darnell 已提交
1653
            def val(x: bytes) -> bytes:
B
Ben Darnell 已提交
1654
                return x[2:] if x.startswith(b"W/") else x
1655

1656 1657 1658 1659 1660
            for etag in etags:
                if val(etag) == val(computed_etag):
                    match = True
                    break
        return match
1661

1662
    async def _execute(
B
Ben Darnell 已提交
1663
        self, transforms: List["OutputTransform"], *args: bytes, **kwargs: bytes
1664
    ) -> None:
B
Bret Taylor 已提交
1665 1666
        """Executes this request with the given output transforms."""
        self._transforms = transforms
1667
        try:
B
Bret Taylor 已提交
1668 1669
            if self.request.method not in self.SUPPORTED_METHODS:
                raise HTTPError(405)
1670
            self.path_args = [self.decode_argument(arg) for arg in args]
B
Ben Darnell 已提交
1671 1672 1673
            self.path_kwargs = dict(
                (k, self.decode_argument(v, name=k)) for (k, v) in kwargs.items()
            )
B
Bret Taylor 已提交
1674 1675
            # If XSRF cookies are turned on, reject form submissions without
            # the proper cookie
B
Ben Darnell 已提交
1676 1677 1678 1679 1680
            if self.request.method not in (
                "GET",
                "HEAD",
                "OPTIONS",
            ) and self.application.settings.get("xsrf_cookies"):
B
Bret Taylor 已提交
1681
                self.check_xsrf_cookie()
1682

1683
            result = self.prepare()
1684
            if result is not None:
1685
                result = await result
1686 1687 1688
            if self._prepared_future is not None:
                # Tell the Application we've finished with prepare()
                # and are ready for the body to arrive.
1689
                future_set_result_unless_cancelled(self._prepared_future, None)
1690 1691
            if self._finished:
                return
B
Bret Taylor 已提交
1692

1693 1694 1695 1696 1697
            if _has_stream_request_body(self.__class__):
                # In streaming mode request.body is a Future that signals
                # the body has been completely received.  The Future has no
                # result; the data has been passed to self.data_received
                # instead.
1698
                try:
1699
                    await self.request._body_future
1700 1701
                except iostream.StreamClosedError:
                    return
1702

1703
            method = getattr(self, self.request.method.lower())
1704
            result = method(*self.path_args, **self.path_kwargs)
1705
            if result is not None:
1706
                result = await result
1707 1708 1709
            if self._auto_finish and not self._finished:
                self.finish()
        except Exception as e:
1710 1711 1712 1713
            try:
                self._handle_request_exception(e)
            except Exception:
                app_log.error("Exception in exception handler", exc_info=True)
1714 1715 1716
            finally:
                # Unset result to avoid circular references
                result = None
B
Ben Darnell 已提交
1717
            if self._prepared_future is not None and not self._prepared_future.done():
1718 1719 1720 1721
                # In case we failed before setting _prepared_future, do it
                # now (to unblock the HTTP server).  Note that this is not
                # in a finally block to avoid GC issues prior to Python 3.4.
                self._prepared_future.set_result(None)
1722

B
Ben Darnell 已提交
1723
    def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:
B
Ben Darnell 已提交
1724 1725 1726 1727 1728 1729
        """Implement this method to handle streamed request data.

        Requires the `.stream_request_body` decorator.
        """
        raise NotImplementedError()

B
Ben Darnell 已提交
1730
    def _log(self) -> None:
1731 1732 1733 1734 1735 1736 1737
        """Logs the current request.

        Sort of deprecated since this functionality was moved to the
        Application, but left in place for the benefit of existing apps
        that have overridden this method.
        """
        self.application.log_request(self)
B
Bret Taylor 已提交
1738

B
Ben Darnell 已提交
1739
    def _request_summary(self) -> str:
B
Ben Darnell 已提交
1740 1741 1742 1743 1744
        return "%s %s (%s)" % (
            self.request.method,
            self.request.uri,
            self.request.remote_ip,
        )
B
Bret Taylor 已提交
1745

B
Ben Darnell 已提交
1746
    def _handle_request_exception(self, e: BaseException) -> None:
1747 1748 1749
        if isinstance(e, Finish):
            # Not an error; just finish the request without logging.
            if not self._finished:
1750
                self.finish(*e.args)
1751
            return
1752 1753 1754 1755 1756 1757
        try:
            self.log_exception(*sys.exc_info())
        except Exception:
            # An error here should still get a best-effort send_error()
            # to avoid leaking the connection.
            app_log.error("Error in exception logger", exc_info=True)
1758 1759 1760 1761 1762
        if self._finished:
            # Extra errors after the request has been finished should
            # be logged, but there is no reason to continue to try and
            # send a response.
            return
B
Bret Taylor 已提交
1763
        if isinstance(e, HTTPError):
1764
            self.send_error(e.status_code, exc_info=sys.exc_info())
B
Bret Taylor 已提交
1765
        else:
1766
            self.send_error(500, exc_info=sys.exc_info())
B
Bret Taylor 已提交
1767

B
Ben Darnell 已提交
1768 1769 1770 1771 1772 1773
    def log_exception(
        self,
        typ: Optional[Type[BaseException]],
        value: Optional[BaseException],
        tb: Optional[TracebackType],
    ) -> None:
1774 1775 1776 1777 1778 1779
        """Override to customize logging of uncaught exceptions.

        By default logs instances of `HTTPError` as warnings without
        stack traces (on the ``tornado.general`` logger), and all
        other exceptions as errors with stack traces (on the
        ``tornado.application`` logger).
1780 1781

        .. versionadded:: 3.1
1782 1783 1784 1785
        """
        if isinstance(value, HTTPError):
            if value.log_message:
                format = "%d %s: " + value.log_message
B
Ben Darnell 已提交
1786
                args = [value.status_code, self._request_summary()] + list(value.args)
1787 1788
                gen_log.warning(format, *args)
        else:
B
Ben Darnell 已提交
1789 1790 1791 1792 1793 1794 1795 1796
            app_log.error(  # type: ignore
                "Uncaught exception %s\n%r",
                self._request_summary(),
                self.request,
                exc_info=(typ, value, tb),
            )

    def _ui_module(self, name: str, module: Type["UIModule"]) -> Callable[..., str]:
1797
        def render(*args, **kwargs) -> str:  # type: ignore
B
Bret Taylor 已提交
1798
            if not hasattr(self, "_active_modules"):
B
Ben Darnell 已提交
1799
                self._active_modules = {}  # type: Dict[str, UIModule]
B
Bret Taylor 已提交
1800 1801 1802 1803
            if name not in self._active_modules:
                self._active_modules[name] = module(self)
            rendered = self._active_modules[name].render(*args, **kwargs)
            return rendered
B
Ben Darnell 已提交
1804

B
Bret Taylor 已提交
1805 1806
        return render

B
Ben Darnell 已提交
1807
    def _ui_method(self, method: Callable[..., str]) -> Callable[..., str]:
B
Bret Taylor 已提交
1808 1809
        return lambda *args, **kwargs: method(self, *args, **kwargs)

B
Ben Darnell 已提交
1810
    def _clear_headers_for_304(self) -> None:
1811 1812 1813 1814
        # 304 responses should not contain entity headers (defined in
        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.1)
        # not explicitly allowed by
        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
B
Ben Darnell 已提交
1815 1816 1817 1818 1819 1820 1821 1822 1823 1824
        headers = [
            "Allow",
            "Content-Encoding",
            "Content-Language",
            "Content-Length",
            "Content-MD5",
            "Content-Range",
            "Content-Type",
            "Last-Modified",
        ]
1825 1826 1827
        for h in headers:
            self.clear_header(h)

B
Bret Taylor 已提交
1828

B
Ben Darnell 已提交
1829
def stream_request_body(cls: Type[RequestHandler]) -> Type[RequestHandler]:
1830 1831 1832
    """Apply to `RequestHandler` subclasses to enable streaming body support.

    This decorator implies the following changes:
B
Ben Darnell 已提交
1833

1834 1835 1836 1837 1838 1839 1840
    * `.HTTPServerRequest.body` is undefined, and body arguments will not
      be included in `RequestHandler.get_argument`.
    * `RequestHandler.prepare` is called when the request headers have been
      read instead of after the entire body has been read.
    * The subclass must define a method ``data_received(self, data):``, which
      will be called zero or more times as data is available.  Note that
      if the request has an empty body, ``data_received`` may not be called.
1841 1842 1843
    * ``prepare`` and ``data_received`` may return Futures (such as via
      ``@gen.coroutine``, in which case the next method will not be called
      until those futures have completed.
1844 1845 1846
    * The regular HTTP method (``post``, ``put``, etc) will be called after
      the entire body has been read.

1847 1848
    See the `file receiver demo <https://github.com/tornadoweb/tornado/tree/master/demos/file_upload/>`_
    for example usage.
1849
    """  # noqa: E501
1850 1851 1852 1853 1854 1855
    if not issubclass(cls, RequestHandler):
        raise TypeError("expected subclass of RequestHandler, got %r", cls)
    cls._stream_request_body = True
    return cls


B
Ben Darnell 已提交
1856
def _has_stream_request_body(cls: Type[RequestHandler]) -> bool:
1857 1858
    if not issubclass(cls, RequestHandler):
        raise TypeError("expected subclass of RequestHandler, got %r", cls)
B
Ben Darnell 已提交
1859
    return cls._stream_request_body
1860 1861


B
Ben Darnell 已提交
1862
def removeslash(
B
Ben Darnell 已提交
1863
    method: Callable[..., Optional[Awaitable[None]]]
B
Ben Darnell 已提交
1864
) -> Callable[..., Optional[Awaitable[None]]]:
1865 1866
    """Use this decorator to remove trailing slashes from the request path.

1867
    For example, a request to ``/foo/`` would redirect to ``/foo`` with this
1868
    decorator. Your request handler mapping should use a regular expression
B
Ben Darnell 已提交
1869
    like ``r'/foo/*'`` in conjunction with using the decorator.
1870
    """
B
Ben Darnell 已提交
1871

1872
    @functools.wraps(method)
1873 1874
    def wrapper(  # type: ignore
        self: RequestHandler, *args, **kwargs
B
Ben Darnell 已提交
1875
    ) -> Optional[Awaitable[None]]:
1876
        if self.request.path.endswith("/"):
1877
            if self.request.method in ("GET", "HEAD"):
1878
                uri = self.request.path.rstrip("/")
1879
                if uri:  # don't try to redirect '/' to ''
1880 1881
                    if self.request.query:
                        uri += "?" + self.request.query
1882
                    self.redirect(uri, permanent=True)
B
Ben Darnell 已提交
1883
                    return None
1884 1885
            else:
                raise HTTPError(404)
1886
        return method(self, *args, **kwargs)
B
Ben Darnell 已提交
1887

1888 1889 1890
    return wrapper


B
Ben Darnell 已提交
1891
def addslash(
B
Ben Darnell 已提交
1892
    method: Callable[..., Optional[Awaitable[None]]]
B
Ben Darnell 已提交
1893
) -> Callable[..., Optional[Awaitable[None]]]:
1894 1895
    """Use this decorator to add a missing trailing slash to the request path.

1896
    For example, a request to ``/foo`` would redirect to ``/foo/`` with this
1897
    decorator. Your request handler mapping should use a regular expression
1898
    like ``r'/foo/?'`` in conjunction with using the decorator.
1899
    """
B
Ben Darnell 已提交
1900

1901
    @functools.wraps(method)
1902 1903
    def wrapper(  # type: ignore
        self: RequestHandler, *args, **kwargs
B
Ben Darnell 已提交
1904
    ) -> Optional[Awaitable[None]]:
1905
        if not self.request.path.endswith("/"):
1906
            if self.request.method in ("GET", "HEAD"):
1907
                uri = self.request.path + "/"
1908 1909
                if self.request.query:
                    uri += "?" + self.request.query
1910
                self.redirect(uri, permanent=True)
B
Ben Darnell 已提交
1911
                return None
1912 1913
            raise HTTPError(404)
        return method(self, *args, **kwargs)
B
Ben Darnell 已提交
1914

1915 1916 1917
    return wrapper


A
Andrey Sumin 已提交
1918
class _ApplicationRouter(ReversibleRuleRouter):
1919
    """Routing implementation used internally by `Application`.
A
Andrey Sumin 已提交
1920

1921
    Provides a binding between `Application` and `RequestHandler`.
A
Andrey Sumin 已提交
1922 1923
    This implementation extends `~.routing.ReversibleRuleRouter` in a couple of ways:
        * it allows to use `RequestHandler` subclasses as `~.routing.Rule` target and
1924 1925 1926
        * it allows to use a list/tuple of rules as `~.routing.Rule` target.
        ``process_rule`` implementation will substitute this list with an appropriate
        `_ApplicationRouter` instance.
A
Andrey Sumin 已提交
1927 1928
    """

B
Ben Darnell 已提交
1929
    def __init__(self, application: "Application", rules: _RuleList = None) -> None:
A
Andrey Sumin 已提交
1930 1931
        assert isinstance(application, Application)
        self.application = application
A
Andrey Sumin 已提交
1932
        super(_ApplicationRouter, self).__init__(rules)
A
Andrey Sumin 已提交
1933

B
Ben Darnell 已提交
1934
    def process_rule(self, rule: Rule) -> Rule:
A
Andrey Sumin 已提交
1935
        rule = super(_ApplicationRouter, self).process_rule(rule)
A
Andrey Sumin 已提交
1936 1937

        if isinstance(rule.target, (list, tuple)):
B
Ben Darnell 已提交
1938 1939 1940
            rule.target = _ApplicationRouter(  # type: ignore
                self.application, rule.target
            )
A
Andrey Sumin 已提交
1941 1942 1943

        return rule

B
Ben Darnell 已提交
1944 1945 1946
    def get_target_delegate(
        self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any
    ) -> Optional[httputil.HTTPMessageDelegate]:
A
Andrey Sumin 已提交
1947
        if isclass(target) and issubclass(target, RequestHandler):
B
Ben Darnell 已提交
1948 1949 1950
            return self.application.get_handler_delegate(
                request, target, **target_params
            )
A
Andrey Sumin 已提交
1951

B
Ben Darnell 已提交
1952 1953 1954
        return super(_ApplicationRouter, self).get_target_delegate(
            target, request, **target_params
        )
A
Andrey Sumin 已提交
1955 1956 1957


class Application(ReversibleRouter):
1958
    r"""A collection of request handlers that make up a web application.
B
Bret Taylor 已提交
1959 1960

    Instances of this class are callable and can be passed directly to
B
Ben Darnell 已提交
1961
    HTTPServer to serve the application::
B
Bret Taylor 已提交
1962 1963 1964 1965 1966 1967

        application = web.Application([
            (r"/", MainPageHandler),
        ])
        http_server = httpserver.HTTPServer(application)
        http_server.listen(8080)
1968
        ioloop.IOLoop.current().start()
B
Bret Taylor 已提交
1969

A
Andrey Sumin 已提交
1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985
    The constructor for this class takes in a list of `~.routing.Rule`
    objects or tuples of values corresponding to the arguments of
    `~.routing.Rule` constructor: ``(matcher, target, [target_kwargs], [name])``,
    the values in square brackets being optional. The default matcher is
    `~.routing.PathMatches`, so ``(regexp, target)`` tuples can also be used
    instead of ``(PathMatches(regexp), target)``.

    A common routing target is a `RequestHandler` subclass, but you can also
    use lists of rules as a target, which create a nested routing configuration::

        application = web.Application([
            (HostMatches("example.com"), [
                (r"/", MainPageHandler),
                (r"/feed", FeedHandler),
            ]),
        ])
B
Bret Taylor 已提交
1986

A
Andrey Sumin 已提交
1987 1988 1989
    In addition to this you can use nested `~.routing.Router` instances,
    `~.httputil.HTTPMessageDelegate` subclasses and callables as routing targets
    (see `~.routing` module docs for more information).
B
Bret Taylor 已提交
1990

A
Andrey Sumin 已提交
1991 1992 1993 1994
    When we receive requests, we iterate over the list in order and
    instantiate an instance of the first request class whose regexp
    matches the request path. The request class can be specified as
    either a class object or a (fully-qualified) name.
1995

A
Andrey Sumin 已提交
1996 1997 1998
    A dictionary may be passed as the third element (``target_kwargs``)
    of the tuple, which will be used as keyword arguments to the handler's
    constructor and `~RequestHandler.initialize` method. This pattern
1999 2000 2001
    is used for the `StaticFileHandler` in this example (note that a
    `StaticFileHandler` can be installed automatically with the
    static_path setting described below)::
B
Bret Taylor 已提交
2002 2003 2004 2005 2006

        application = web.Application([
            (r"/static/(.*)", web.StaticFileHandler, {"path": "/var/www"}),
        ])

2007
    We support virtual hosts with the `add_handlers` method, which takes in
B
Ben Darnell 已提交
2008
    a host regular expression as the first argument::
B
Bret Taylor 已提交
2009 2010 2011 2012 2013

        application.add_handlers(r"www\.myhost\.com", [
            (r"/article/([0-9]+)", ArticleHandler),
        ])

2014 2015 2016
    If there's no match for the current request's host, then ``default_host``
    parameter value is matched against host regular expressions.

2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027

    .. warning::

       Applications that do not use TLS may be vulnerable to :ref:`DNS
       rebinding <dnsrebinding>` attacks. This attack is especially
       relevant to applications that only listen on ``127.0.0.1` or
       other private networks. Appropriate host patterns must be used
       (instead of the default of ``r'.*'``) to prevent this risk. The
       ``default_host`` argument must not be used in applications that
       may be vulnerable to DNS rebinding.

2028 2029 2030 2031 2032 2033 2034
    You can serve static files by sending the ``static_path`` setting
    as a keyword argument. We will serve those files from the
    ``/static/`` URI (this is configurable with the
    ``static_url_prefix`` setting), and we will serve ``/favicon.ico``
    and ``/robots.txt`` from the same directory.  A custom subclass of
    `StaticFileHandler` can be specified with the
    ``static_handler_class`` setting.
2035

2036 2037
    .. versionchanged:: 4.5
       Integration with the new `tornado.routing` module.
2038

B
Bret Taylor 已提交
2039
    """
B
Ben Darnell 已提交
2040 2041 2042 2043 2044 2045 2046 2047

    def __init__(
        self,
        handlers: _RuleList = None,
        default_host: str = None,
        transforms: List[Type["OutputTransform"]] = None,
        **settings: Any
    ) -> None:
B
Bret Taylor 已提交
2048
        if transforms is None:
B
Ben Darnell 已提交
2049
            self.transforms = []  # type: List[Type[OutputTransform]]
2050
            if settings.get("compress_response") or settings.get("gzip"):
2051
                self.transforms.append(GZipContentEncoding)
B
Bret Taylor 已提交
2052 2053 2054 2055
        else:
            self.transforms = transforms
        self.default_host = default_host
        self.settings = settings
B
Ben Darnell 已提交
2056 2057 2058 2059 2060
        self.ui_modules = {
            "linkify": _linkify,
            "xsrf_form_html": _xsrf_form_html,
            "Template": TemplateModule,
        }
B
Ben Darnell 已提交
2061
        self.ui_methods = {}  # type: Dict[str, Callable[..., str]]
B
Bret Taylor 已提交
2062 2063 2064 2065 2066
        self._load_ui_modules(settings.get("ui_modules", {}))
        self._load_ui_methods(settings.get("ui_methods", {}))
        if self.settings.get("static_path"):
            path = self.settings["static_path"]
            handlers = list(handlers or [])
B
Ben Darnell 已提交
2067 2068 2069 2070
            static_url_prefix = settings.get("static_url_prefix", "/static/")
            static_handler_class = settings.get(
                "static_handler_class", StaticFileHandler
            )
2071
            static_handler_args = settings.get("static_handler_args", {})
B
Ben Darnell 已提交
2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084
            static_handler_args["path"] = path
            for pattern in [
                re.escape(static_url_prefix) + r"(.*)",
                r"/(favicon\.ico)",
                r"/(robots\.txt)",
            ]:
                handlers.insert(0, (pattern, static_handler_class, static_handler_args))

        if self.settings.get("debug"):
            self.settings.setdefault("autoreload", True)
            self.settings.setdefault("compiled_template_cache", False)
            self.settings.setdefault("static_hash_cache", False)
            self.settings.setdefault("serve_traceback", True)
2085

A
Andrey Sumin 已提交
2086
        self.wildcard_router = _ApplicationRouter(self, handlers)
B
Ben Darnell 已提交
2087 2088 2089
        self.default_router = _ApplicationRouter(
            self, [Rule(AnyMatches(), self.wildcard_router)]
        )
A
Andrey Sumin 已提交
2090

2091
        # Automatically reload modified modules
B
Ben Darnell 已提交
2092
        if self.settings.get("autoreload"):
B
Ben Darnell 已提交
2093
            from tornado import autoreload
B
Ben Darnell 已提交
2094

B
Bret Taylor 已提交
2095
            autoreload.start()
2096

B
Ben Darnell 已提交
2097
    def listen(self, port: int, address: str = "", **kwargs: Any) -> HTTPServer:
2098 2099
        """Starts an HTTP server for this application on the given port.

2100 2101 2102 2103 2104 2105 2106
        This is a convenience alias for creating an `.HTTPServer`
        object and calling its listen method.  Keyword arguments not
        supported by `HTTPServer.listen <.TCPServer.listen>` are passed to the
        `.HTTPServer` constructor.  For advanced uses
        (e.g. multi-process mode), do not use this method; create an
        `.HTTPServer` and call its
        `.TCPServer.bind`/`.TCPServer.start` methods directly.
2107 2108

        Note that after calling this method you still need to call
2109
        ``IOLoop.current().start()`` to start the server.
2110 2111 2112 2113 2114

        Returns the `.HTTPServer` object.

        .. versionchanged:: 4.3
           Now returns the `.HTTPServer` object.
2115 2116
        """
        server = HTTPServer(self, **kwargs)
2117
        server.listen(port, address)
2118
        return server
2119

B
Ben Darnell 已提交
2120
    def add_handlers(self, host_pattern: str, host_handlers: _RuleList) -> None:
2121 2122
        """Appends the given handlers to our handler list.

2123 2124
        Host patterns are processed sequentially in the order they were
        added. All matching patterns will be considered.
2125
        """
A
Andrey Sumin 已提交
2126
        host_matcher = HostMatches(host_pattern)
A
Andrey Sumin 已提交
2127
        rule = Rule(host_matcher, _ApplicationRouter(self, host_handlers))
A
Andrey Sumin 已提交
2128 2129 2130 2131

        self.default_router.rules.insert(-1, rule)

        if self.default_host is not None:
B
Ben Darnell 已提交
2132 2133 2134
            self.wildcard_router.add_rules(
                [(DefaultHostMatches(self, host_matcher.host_pattern), host_handlers)]
            )
B
Bret Taylor 已提交
2135

B
Ben Darnell 已提交
2136
    def add_transform(self, transform_class: Type["OutputTransform"]) -> None:
B
Bret Taylor 已提交
2137 2138
        self.transforms.append(transform_class)

B
Ben Darnell 已提交
2139
    def _load_ui_methods(self, methods: Any) -> None:
2140
        if isinstance(methods, types.ModuleType):
B
Ben Darnell 已提交
2141
            self._load_ui_methods(dict((n, getattr(methods, n)) for n in dir(methods)))
B
Bret Taylor 已提交
2142
        elif isinstance(methods, list):
2143 2144
            for m in methods:
                self._load_ui_methods(m)
B
Bret Taylor 已提交
2145
        else:
2146
            for name, fn in methods.items():
B
Ben Darnell 已提交
2147 2148 2149 2150 2151
                if (
                    not name.startswith("_")
                    and hasattr(fn, "__call__")
                    and name[0].lower() == name[0]
                ):
B
Bret Taylor 已提交
2152 2153
                    self.ui_methods[name] = fn

B
Ben Darnell 已提交
2154
    def _load_ui_modules(self, modules: Any) -> None:
2155
        if isinstance(modules, types.ModuleType):
B
Ben Darnell 已提交
2156
            self._load_ui_modules(dict((n, getattr(modules, n)) for n in dir(modules)))
B
Bret Taylor 已提交
2157
        elif isinstance(modules, list):
2158 2159
            for m in modules:
                self._load_ui_modules(m)
B
Bret Taylor 已提交
2160 2161
        else:
            assert isinstance(modules, dict)
2162
            for name, cls in modules.items():
B
Bret Taylor 已提交
2163 2164 2165 2166 2167 2168
                try:
                    if issubclass(cls, UIModule):
                        self.ui_modules[name] = cls
                except TypeError:
                    pass

B
Ben Darnell 已提交
2169 2170 2171
    def __call__(
        self, request: httputil.HTTPServerRequest
    ) -> Optional[Awaitable[None]]:
2172
        # Legacy HTTPServer interface
A
Andrey Sumin 已提交
2173
        dispatcher = self.find_handler(request)
2174
        return dispatcher.execute()
B
Bret Taylor 已提交
2175

B
Ben Darnell 已提交
2176 2177 2178
    def find_handler(
        self, request: httputil.HTTPServerRequest, **kwargs: Any
    ) -> "_HandlerDelegate":
A
Andrey Sumin 已提交
2179 2180
        route = self.default_router.find_handler(request)
        if route is not None:
B
Ben Darnell 已提交
2181
            return cast("_HandlerDelegate", route)
A
Andrey Sumin 已提交
2182

B
Ben Darnell 已提交
2183
        if self.settings.get("default_handler_class"):
A
Andrey Sumin 已提交
2184 2185
            return self.get_handler_delegate(
                request,
B
Ben Darnell 已提交
2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199
                self.settings["default_handler_class"],
                self.settings.get("default_handler_args", {}),
            )

        return self.get_handler_delegate(request, ErrorHandler, {"status_code": 404})

    def get_handler_delegate(
        self,
        request: httputil.HTTPServerRequest,
        target_class: Type[RequestHandler],
        target_kwargs: Dict[str, Any] = None,
        path_args: List[bytes] = None,
        path_kwargs: Dict[str, bytes] = None,
    ) -> "_HandlerDelegate":
2200 2201 2202 2203 2204 2205 2206 2207 2208 2209
        """Returns `~.httputil.HTTPMessageDelegate` that can serve a request
        for application and `RequestHandler` subclass.

        :arg httputil.HTTPServerRequest request: current HTTP request.
        :arg RequestHandler target_class: a `RequestHandler` class.
        :arg dict target_kwargs: keyword arguments for ``target_class`` constructor.
        :arg list path_args: positional arguments for ``target_class`` HTTP method that
            will be executed while handling a request (``get``, ``post`` or any other).
        :arg dict path_kwargs: keyword arguments for ``target_class`` HTTP method.
        """
A
Andrey Sumin 已提交
2210
        return _HandlerDelegate(
B
Ben Darnell 已提交
2211 2212
            self, request, target_class, target_kwargs, path_args, path_kwargs
        )
A
Andrey Sumin 已提交
2213

B
Ben Darnell 已提交
2214
    def reverse_url(self, name: str, *args: Any) -> str:
2215
        """Returns a URL path for handler named ``name``
2216

2217
        The handler must be added to the application as a named `URLSpec`.
2218

2219
        Args will be substituted for capturing groups in the `URLSpec` regex.
2220 2221
        They will be converted to strings if necessary, encoded as utf8,
        and url-escaped.
2222
        """
A
Andrey Sumin 已提交
2223 2224 2225 2226
        reversed_url = self.default_router.reverse_url(name, *args)
        if reversed_url is not None:
            return reversed_url

2227
        raise KeyError("%s not found in named urls" % name)
B
Bret Taylor 已提交
2228

B
Ben Darnell 已提交
2229
    def log_request(self, handler: RequestHandler) -> None:
2230 2231 2232 2233 2234
        """Writes a completed HTTP request to the logs.

        By default writes to the python root logger.  To change
        this behavior either subclass Application and override this method,
        or pass a function in the application settings dictionary as
2235
        ``log_function``.
2236 2237 2238 2239 2240
        """
        if "log_function" in self.settings:
            self.settings["log_function"](handler)
            return
        if handler.get_status() < 400:
2241
            log_method = access_log.info
2242
        elif handler.get_status() < 500:
2243
            log_method = access_log.warning
2244
        else:
2245
            log_method = access_log.error
2246
        request_time = 1000.0 * handler.request.request_time()
B
Ben Darnell 已提交
2247 2248 2249 2250 2251 2252
        log_method(
            "%d %s %.2fms",
            handler.get_status(),
            handler._request_summary(),
            request_time,
        )
2253 2254


A
Andrey Sumin 已提交
2255
class _HandlerDelegate(httputil.HTTPMessageDelegate):
B
Ben Darnell 已提交
2256 2257 2258 2259 2260 2261 2262 2263 2264
    def __init__(
        self,
        application: Application,
        request: httputil.HTTPServerRequest,
        handler_class: Type[RequestHandler],
        handler_kwargs: Optional[Dict[str, Any]],
        path_args: Optional[List[bytes]],
        path_kwargs: Optional[Dict[str, bytes]],
    ) -> None:
2265
        self.application = application
A
Andrey Sumin 已提交
2266 2267 2268 2269 2270 2271
        self.connection = request.connection
        self.request = request
        self.handler_class = handler_class
        self.handler_kwargs = handler_kwargs or {}
        self.path_args = path_args or []
        self.path_kwargs = path_kwargs or {}
B
Ben Darnell 已提交
2272
        self.chunks = []  # type: List[bytes]
A
Andrey Sumin 已提交
2273
        self.stream_request_body = _has_stream_request_body(self.handler_class)
2274

B
Ben Darnell 已提交
2275 2276 2277 2278 2279
    def headers_received(
        self,
        start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine],
        headers: httputil.HTTPHeaders,
    ) -> Optional[Awaitable[None]]:
2280
        if self.stream_request_body:
B
Ben Darnell 已提交
2281
            self.request._body_future = Future()
2282
            return self.execute()
B
Ben Darnell 已提交
2283
        return None
2284

B
Ben Darnell 已提交
2285
    def data_received(self, data: bytes) -> Optional[Awaitable[None]]:
2286
        if self.stream_request_body:
2287
            return self.handler.data_received(data)
2288 2289
        else:
            self.chunks.append(data)
B
Ben Darnell 已提交
2290
            return None
2291

B
Ben Darnell 已提交
2292
    def finish(self) -> None:
2293
        if self.stream_request_body:
B
Ben Darnell 已提交
2294
            future_set_result_unless_cancelled(self.request._body_future, None)
2295
        else:
B
Ben Darnell 已提交
2296
            self.request.body = b"".join(self.chunks)
2297 2298
            self.request._parse_body()
            self.execute()
2299

B
Ben Darnell 已提交
2300
    def on_connection_close(self) -> None:
2301 2302 2303
        if self.stream_request_body:
            self.handler.on_connection_close()
        else:
B
Ben Darnell 已提交
2304
            self.chunks = None  # type: ignore
2305

B
Ben Darnell 已提交
2306
    def execute(self) -> Optional[Awaitable[None]]:
2307 2308 2309 2310 2311 2312 2313
        # If template cache is disabled (usually in the debug mode),
        # re-compile templates and reload static files on every
        # request so you don't need to restart to see changes
        if not self.application.settings.get("compiled_template_cache", True):
            with RequestHandler._template_loader_lock:
                for loader in RequestHandler._template_loaders.values():
                    loader.reset()
B
Ben Darnell 已提交
2314
        if not self.application.settings.get("static_hash_cache", True):
2315 2316
            StaticFileHandler.reset()

B
Ben Darnell 已提交
2317 2318 2319
        self.handler = self.handler_class(
            self.application, self.request, **self.handler_kwargs
        )
2320
        transforms = [t(self.request) for t in self.application.transforms]
2321 2322 2323

        if self.stream_request_body:
            self.handler._prepared_future = Future()
2324
        # Note that if an exception escapes handler._execute it will be
2325 2326
        # trapped in the Future it returns (which we are ignoring here,
        # leaving it to be logged when the Future is GC'd).
2327 2328
        # However, that shouldn't happen because _execute has a blanket
        # except handler, and we cannot easily access the IOLoop here to
2329 2330
        # call add_future (because of the requirement to remain compatible
        # with WSGI)
2331 2332 2333 2334
        fut = gen.convert_yielded(
            self.handler._execute(transforms, *self.path_args, **self.path_kwargs)
        )
        fut.add_done_callback(lambda f: f.result())
2335 2336 2337 2338 2339
        # If we are streaming the request body, then execute() is finished
        # when the handler has prepared to receive the body.  If not,
        # it doesn't matter when execute() finishes (so we return None)
        return self.handler._prepared_future

2340

B
Bret Taylor 已提交
2341
class HTTPError(Exception):
2342 2343
    """An exception that will turn into an HTTP error response.

2344 2345 2346 2347
    Raising an `HTTPError` is a convenient alternative to calling
    `RequestHandler.send_error` since it automatically ends the
    current function.

2348 2349 2350
    To customize the response sent with an `HTTPError`, override
    `RequestHandler.write_error`.

2351
    :arg int status_code: HTTP status code.  Must be listed in
2352 2353
        `httplib.responses <http.client.responses>` unless the ``reason``
        keyword argument is given.
B
Ben Darnell 已提交
2354
    :arg str log_message: Message to be written to the log for this error
2355 2356 2357
        (will not be shown to the user unless the `Application` is in debug
        mode).  May contain ``%s``-style placeholders, which will be filled
        in with remaining positional parameters.
B
Ben Darnell 已提交
2358
    :arg str reason: Keyword-only argument.  The HTTP "reason" phrase
2359 2360 2361 2362
        to pass in the status line along with ``status_code``.  Normally
        determined automatically from ``status_code``, but can be used
        to use a non-standard numeric code.
    """
B
Ben Darnell 已提交
2363 2364 2365 2366

    def __init__(
        self, status_code: int = 500, log_message: str = None, *args: Any, **kwargs: Any
    ) -> None:
B
Bret Taylor 已提交
2367 2368 2369
        self.status_code = status_code
        self.log_message = log_message
        self.args = args
B
Ben Darnell 已提交
2370
        self.reason = kwargs.get("reason", None)
2371
        if log_message and not args:
B
Ben Darnell 已提交
2372
            self.log_message = log_message.replace("%", "%%")
B
Bret Taylor 已提交
2373

B
Ben Darnell 已提交
2374
    def __str__(self) -> str:
B
Bret Taylor 已提交
2375
        message = "HTTP %d: %s" % (
2376
            self.status_code,
B
Ben Darnell 已提交
2377 2378
            self.reason or httputil.responses.get(self.status_code, "Unknown"),
        )
B
Bret Taylor 已提交
2379 2380 2381 2382 2383 2384
        if self.log_message:
            return message + " (" + (self.log_message % self.args) + ")"
        else:
            return message


2385 2386 2387
class Finish(Exception):
    """An exception that ends the request without producing an error response.

2388 2389 2390 2391 2392 2393 2394 2395
    When `Finish` is raised in a `RequestHandler`, the request will
    end (calling `RequestHandler.finish` if it hasn't already been
    called), but the error-handling methods (including
    `RequestHandler.write_error`) will not be called.

    If `Finish()` was created with no arguments, the pending response
    will be sent as-is. If `Finish()` was given an argument, that
    argument will be passed to `RequestHandler.finish()`.
2396 2397 2398 2399 2400 2401 2402 2403

    This can be a more convenient way to implement custom error pages
    than overriding ``write_error`` (especially in library code)::

        if self.current_user is None:
            self.set_status(401)
            self.set_header('WWW-Authenticate', 'Basic realm="something"')
            raise Finish()
2404 2405 2406 2407

    .. versionchanged:: 4.3
       Arguments passed to ``Finish()`` will be passed on to
       `RequestHandler.finish`.
2408
    """
B
Ben Darnell 已提交
2409

2410 2411 2412
    pass


2413 2414 2415 2416 2417
class MissingArgumentError(HTTPError):
    """Exception raised by `RequestHandler.get_argument`.

    This is a subclass of `HTTPError`, so if it is uncaught a 400 response
    code will be used instead of 500 (and a stack trace will not be logged).
2418 2419

    .. versionadded:: 3.1
2420
    """
B
Ben Darnell 已提交
2421

B
Ben Darnell 已提交
2422
    def __init__(self, arg_name: str) -> None:
2423
        super(MissingArgumentError, self).__init__(
B
Ben Darnell 已提交
2424 2425
            400, "Missing argument %s" % arg_name
        )
2426 2427 2428
        self.arg_name = arg_name


B
Bret Taylor 已提交
2429
class ErrorHandler(RequestHandler):
2430
    """Generates an error response with ``status_code`` for all requests."""
B
Ben Darnell 已提交
2431

2432
    def initialize(self, status_code: int) -> None:
B
Bret Taylor 已提交
2433 2434
        self.set_status(status_code)

B
Ben Darnell 已提交
2435
    def prepare(self) -> None:
B
Bret Taylor 已提交
2436 2437
        raise HTTPError(self._status_code)

B
Ben Darnell 已提交
2438
    def check_xsrf_cookie(self) -> None:
2439 2440 2441 2442 2443
        # POSTs to an ErrorHandler don't actually have side effects,
        # so we don't need to check the xsrf token.  This allows POSTs
        # to the wrong url to return a 404 instead of 403.
        pass

B
Bret Taylor 已提交
2444 2445 2446 2447

class RedirectHandler(RequestHandler):
    """Redirects the client to the given URL for all GET requests.

2448
    You should provide the keyword argument ``url`` to the handler, e.g.::
B
Bret Taylor 已提交
2449 2450 2451 2452

        application = web.Application([
            (r"/oldpath", web.RedirectHandler, {"url": "/newpath"}),
        ])
2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468

    `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.
2469 2470 2471

    .. versionchanged:: 4.5
       Added support for substitutions into the destination URL.
B
Ben Darnell 已提交
2472 2473 2474 2475

    .. versionchanged:: 5.0
       If any query arguments are present, they will be copied to the
       destination URL.
B
Bret Taylor 已提交
2476
    """
B
Ben Darnell 已提交
2477

2478
    def initialize(self, url: str, permanent: bool = True) -> None:
B
Bret Taylor 已提交
2479 2480
        self._url = url
        self._permanent = permanent
2481

2482
    def get(self, *args: Any) -> None:
2483 2484
        to_url = self._url.format(*args)
        if self.request.query_arguments:
B
Ben Darnell 已提交
2485
            # TODO: figure out typing for the next line.
2486
            to_url = httputil.url_concat(
B
Ben Darnell 已提交
2487 2488 2489
                to_url,
                list(httputil.qs_to_qsl(self.request.query_arguments)),  # type: ignore
            )
2490
        self.redirect(to_url, permanent=self._permanent)
B
Bret Taylor 已提交
2491 2492 2493 2494 2495


class StaticFileHandler(RequestHandler):
    """A simple handler that can serve static content from a directory.

2496 2497 2498 2499 2500 2501
    A `StaticFileHandler` is configured automatically if you pass the
    ``static_path`` keyword argument to `Application`.  This handler
    can be customized with the ``static_url_prefix``, ``static_handler_class``,
    and ``static_handler_args`` settings.

    To map an additional path to this handler for a static data directory
B
Ben Darnell 已提交
2502
    you would add a line to your application like::
B
Bret Taylor 已提交
2503 2504

        application = web.Application([
2505
            (r"/content/(.*)", web.StaticFileHandler, {"path": "/var/www"}),
B
Bret Taylor 已提交
2506 2507
        ])

2508 2509 2510 2511
    The handler constructor requires a ``path`` argument, which specifies the
    local root directory of the content to be served.

    Note that a capture group in the regex is required to parse the value for
2512
    the ``path`` argument to the get() method (different than the constructor
2513
    argument above); see `URLSpec` for details.
B
Bret Taylor 已提交
2514

2515 2516 2517 2518 2519
    To serve a file like ``index.html`` automatically when a directory is
    requested, set ``static_handler_args=dict(default_filename="index.html")``
    in your application settings, or add ``default_filename`` as an initializer
    argument for your ``StaticFileHandler``.

2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530
    To maximize the effectiveness of browser caching, this class supports
    versioned urls (by default using the argument ``?v=``).  If a version
    is given, we instruct the browser to cache this file indefinitely.
    `make_static_url` (also available as `RequestHandler.static_url`) can
    be used to construct a versioned url.

    This handler is intended primarily for use in development and light-duty
    file serving; for heavy traffic it will be more efficient to use
    a dedicated static file server (such as nginx or Apache).  We support
    the HTTP ``Accept-Ranges`` mechanism to return partial content (because
    some browsers require this functionality to be present to seek in
2531
    HTML5 audio or video).
2532 2533 2534 2535 2536 2537 2538 2539 2540 2541

    **Subclassing notes**

    This class is designed to be extensible by subclassing, but because
    of the way static urls are generated with class methods rather than
    instance methods, the inheritance patterns are somewhat unusual.
    Be sure to use the ``@classmethod`` decorator when overriding a
    class method.  Instance methods may use the attributes ``self.path``
    ``self.absolute_path``, and ``self.modified``.

2542 2543 2544 2545 2546
    Subclasses should only override methods discussed in this section;
    overriding other methods is error-prone.  Overriding
    ``StaticFileHandler.get`` is particularly problematic due to the
    tight coupling with ``compute_etag`` and other methods.

2547 2548 2549 2550
    To change the way static urls are generated (e.g. to match the behavior
    of another server or CDN), override `make_static_url`, `parse_url_path`,
    `get_cache_time`, and/or `get_version`.

B
Ben Darnell 已提交
2551 2552 2553 2554
    To replace all interaction with the filesystem (e.g. to serve
    static content from a database), override `get_content`,
    `get_content_size`, `get_modified_time`, `get_absolute_path`, and
    `validate_absolute_path`.
2555 2556 2557

    .. versionchanged:: 3.1
       Many of the methods for subclasses were added in Tornado 3.1.
B
Bret Taylor 已提交
2558
    """
B
Ben Darnell 已提交
2559

2560
    CACHE_MAX_AGE = 86400 * 365 * 10  # 10 years
2561

B
Ben Darnell 已提交
2562
    _static_hashes = {}  # type: Dict[str, Optional[str]]
2563
    _lock = threading.Lock()  # protects _static_hashes
2564

2565
    def initialize(self, path: str, default_filename: str = None) -> None:
2566
        self.root = path
2567
        self.default_filename = default_filename
B
Bret Taylor 已提交
2568

2569
    @classmethod
B
Ben Darnell 已提交
2570
    def reset(cls) -> None:
2571 2572 2573
        with cls._lock:
            cls._static_hashes = {}

2574
    def head(self, path: str) -> Awaitable[None]:
2575
        return self.get(path, include_body=False)
B
Bret Taylor 已提交
2576

2577
    async def get(self, path: str, include_body: bool = True) -> None:
2578 2579 2580
        # Set up our path instance variables.
        self.path = self.parse_url_path(path)
        del path  # make sure we don't refer to path instead of self.path again
2581
        absolute_path = self.get_absolute_path(self.root, self.path)
B
Ben Darnell 已提交
2582
        self.absolute_path = self.validate_absolute_path(self.root, absolute_path)
2583 2584
        if self.absolute_path is None:
            return
2585

2586 2587
        self.modified = self.get_modified_time()
        self.set_headers()
2588

2589
        if self.should_return_304():
2590 2591 2592 2593 2594 2595
            self.set_status(304)
            return

        request_range = None
        range_header = self.request.headers.get("Range")
        if range_header:
2596 2597
            # As per RFC 2616 14.16, if an invalid Range header is specified,
            # the request will be treated as if the header didn't exist.
2598
            request_range = httputil._parse_request_range(range_header)
B
Bret Taylor 已提交
2599

2600
        size = self.get_content_size()
2601
        if request_range:
2602
            start, end = request_range
2603 2604 2605 2606 2607 2608 2609 2610
            if start is not None and start < 0:
                start += size
                if start < 0:
                    start = 0
            if (
                start is not None
                and (start >= size or (end is not None and start >= end))
            ) or end == 0:
2611 2612
                # As per RFC 2616 14.35.1, a range is not satisfiable only: if
                # the first requested byte is equal to or greater than the
2613 2614 2615 2616
                # content, or when a suffix with length 0 is specified.
                # https://tools.ietf.org/html/rfc7233#section-2.1
                # A byte-range-spec is invalid if the last-byte-pos value is present
                # and less than the first-byte-pos.
2617 2618
                self.set_status(416)  # Range Not Satisfiable
                self.set_header("Content-Type", "text/plain")
B
Ben Darnell 已提交
2619
                self.set_header("Content-Range", "bytes */%s" % (size,))
2620
                return
2621 2622 2623 2624
            if end is not None and end > size:
                # Clients sometimes blindly use a large range to limit their
                # download size; cap the endpoint at the actual file size.
                end = size
2625 2626 2627 2628
            # Note: only return HTTP 206 if less than the entire range has been
            # requested. Not only is this semantically correct, but Chrome
            # refuses to play audio if it gets an HTTP 206 in response to
            # ``Range: bytes=0-``.
D
David Wolever 已提交
2629 2630
            if size != (end or size) - (start or 0):
                self.set_status(206)  # Partial Content
B
Ben Darnell 已提交
2631 2632 2633
                self.set_header(
                    "Content-Range", httputil._get_content_range(start, end, size)
                )
2634
        else:
2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645
            start = end = None

        if start is not None and end is not None:
            content_length = end - start
        elif end is not None:
            content_length = end
        elif start is not None:
            content_length = size - start
        else:
            content_length = size
        self.set_header("Content-Length", content_length)
2646 2647 2648

        if include_body:
            content = self.get_content(self.absolute_path, start, end)
2649
            if isinstance(content, bytes):
2650 2651
                content = [content]
            for chunk in content:
2652 2653
                try:
                    self.write(chunk)
2654
                    await self.flush()
2655 2656
                except iostream.StreamClosedError:
                    return
2657
        else:
2658
            assert self.request.method == "HEAD"
2659

B
Ben Darnell 已提交
2660
    def compute_etag(self) -> Optional[str]:
2661
        """Sets the ``Etag`` header based on static url version.
2662

2663 2664 2665
        This allows efficient ``If-None-Match`` checks against cached
        versions, and sends the correct ``Etag`` for a partial response
        (i.e. the same ``Etag`` as the full file).
2666 2667

        .. versionadded:: 3.1
2668
        """
B
Ben Darnell 已提交
2669
        assert self.absolute_path is not None
2670
        version_hash = self._get_cached_version(self.absolute_path)
2671 2672
        if not version_hash:
            return None
B
Ben Darnell 已提交
2673
        return '"%s"' % (version_hash,)
B
Bret Taylor 已提交
2674

B
Ben Darnell 已提交
2675
    def set_headers(self) -> None:
2676 2677 2678 2679
        """Sets the content and caching headers on the response.

        .. versionadded:: 3.1
        """
2680 2681
        self.set_header("Accept-Ranges", "bytes")
        self.set_etag_header()
2682

2683 2684
        if self.modified is not None:
            self.set_header("Last-Modified", self.modified)
B
Bret Taylor 已提交
2685

2686 2687 2688
        content_type = self.get_content_type()
        if content_type:
            self.set_header("Content-Type", content_type)
2689

B
Ben Darnell 已提交
2690
        cache_time = self.get_cache_time(self.path, self.modified, content_type)
2691
        if cache_time > 0:
B
Ben Darnell 已提交
2692 2693 2694 2695
            self.set_header(
                "Expires",
                datetime.datetime.utcnow() + datetime.timedelta(seconds=cache_time),
            )
2696 2697
            self.set_header("Cache-Control", "max-age=" + str(cache_time))

2698 2699
        self.set_extra_headers(self.path)

B
Ben Darnell 已提交
2700
    def should_return_304(self) -> bool:
2701 2702 2703 2704
        """Returns True if the headers indicate that we should return 304.

        .. versionadded:: 3.1
        """
2705
        # If client sent If-None-Match, use it, ignore If-Modified-Since
B
Ben Darnell 已提交
2706
        if self.request.headers.get("If-None-Match"):
2707
            return self.check_etag_header()
2708

2709 2710 2711 2712 2713
        # Check the If-Modified-Since, and don't send the result if the
        # content has not been modified
        ims_value = self.request.headers.get("If-Modified-Since")
        if ims_value is not None:
            date_tuple = email.utils.parsedate(ims_value)
2714 2715
            if date_tuple is not None:
                if_since = datetime.datetime(*date_tuple[:6])
B
Ben Darnell 已提交
2716
                assert self.modified is not None
2717 2718
                if if_since >= self.modified:
                    return True
2719

2720
        return False
2721

2722
    @classmethod
B
Ben Darnell 已提交
2723
    def get_absolute_path(cls, root: str, path: str) -> str:
2724 2725 2726 2727
        """Returns the absolute location of ``path`` relative to ``root``.

        ``root`` is the path configured for this `StaticFileHandler`
        (in most cases the ``static_path`` `Application` setting).
2728 2729 2730 2731 2732

        This class method may be overridden in subclasses.  By default
        it returns a filesystem path, but other strings may be used
        as long as they are unique and understood by the subclass's
        overridden `get_content`.
2733 2734

        .. versionadded:: 3.1
2735 2736 2737 2738
        """
        abspath = os.path.abspath(os.path.join(root, path))
        return abspath

B
Ben Darnell 已提交
2739
    def validate_absolute_path(self, root: str, absolute_path: str) -> Optional[str]:
2740 2741
        """Validate and return the absolute path.

2742 2743 2744
        ``root`` is the configured path for the `StaticFileHandler`,
        and ``path`` is the result of `get_absolute_path`

2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755
        This is an instance method called during request processing,
        so it may raise `HTTPError` or use methods like
        `RequestHandler.redirect` (return None after redirecting to
        halt further processing).  This is where 404 errors for missing files
        are generated.

        This method may modify the path before returning it, but note that
        any such modifications will not be understood by `make_static_url`.

        In instance methods, this method's result is available as
        ``self.absolute_path``.
2756 2757

        .. versionadded:: 3.1
2758
        """
2759 2760 2761 2762
        # os.path.abspath strips a trailing /.
        # We must add it back to `root` so that we only match files
        # in a directory named `root` instead of files starting with
        # that prefix.
B
Ben Darnell 已提交
2763 2764 2765 2766 2767 2768 2769 2770
        root = os.path.abspath(root)
        if not root.endswith(os.path.sep):
            # abspath always removes a trailing slash, except when
            # root is '/'. This is an unusual case, but several projects
            # have independently discovered this technique to disable
            # Tornado's path validation and (hopefully) do their own,
            # so we need to support it.
            root += os.path.sep
2771 2772
        # The trailing slash also needs to be temporarily added back
        # the requested path so a request to root/ will match.
2773
        if not (absolute_path + os.path.sep).startswith(root):
B
Ben Darnell 已提交
2774 2775
            raise HTTPError(403, "%s is not in root static directory", self.path)
        if os.path.isdir(absolute_path) and self.default_filename is not None:
2776 2777 2778 2779
            # need to look at the request.path here for when path is empty
            # but there is some prefix to the path that was already
            # trimmed by the routing
            if not self.request.path.endswith("/"):
2780
                self.redirect(self.request.path + "/", permanent=True)
B
Ben Darnell 已提交
2781
                return None
2782 2783 2784 2785 2786 2787
            absolute_path = os.path.join(absolute_path, self.default_filename)
        if not os.path.exists(absolute_path):
            raise HTTPError(404)
        if not os.path.isfile(absolute_path):
            raise HTTPError(403, "%s is not a file", self.path)
        return absolute_path
2788

2789
    @classmethod
B
Ben Darnell 已提交
2790 2791 2792
    def get_content(
        cls, abspath: str, start: int = None, end: int = None
    ) -> Generator[bytes, None, None]:
2793
        """Retrieve the content of the requested resource which is located
2794 2795 2796 2797 2798 2799
        at the given absolute path.

        This class method may be overridden by subclasses.  Note that its
        signature is different from other overridable class methods
        (no ``settings`` argument); this is deliberate to ensure that
        ``abspath`` is able to stand on its own as a cache key.
B
Ben Darnell 已提交
2800 2801 2802 2803

        This method should either return a byte string or an iterator
        of byte strings.  The latter is preferred for large files
        as it helps reduce memory fragmentation.
2804 2805

        .. versionadded:: 3.1
2806
        """
2807
        with open(abspath, "rb") as file:
2808 2809 2810
            if start is not None:
                file.seek(start)
            if end is not None:
B
Ben Darnell 已提交
2811
                remaining = end - (start or 0)  # type: Optional[int]
2812
            else:
B
Ben Darnell 已提交
2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826
                remaining = None
            while True:
                chunk_size = 64 * 1024
                if remaining is not None and remaining < chunk_size:
                    chunk_size = remaining
                chunk = file.read(chunk_size)
                if chunk:
                    if remaining is not None:
                        remaining -= len(chunk)
                    yield chunk
                else:
                    if remaining is not None:
                        assert remaining == 0
                    return
B
Bret Taylor 已提交
2827

2828
    @classmethod
B
Ben Darnell 已提交
2829
    def get_content_version(cls, abspath: str) -> str:
2830 2831 2832 2833
        """Returns a version string for the resource at the given path.

        This class method may be overridden by subclasses.  The
        default implementation is a hash of the file's contents.
2834 2835

        .. versionadded:: 3.1
2836 2837
        """
        data = cls.get_content(abspath)
B
Ben Darnell 已提交
2838
        hasher = hashlib.md5()
2839
        if isinstance(data, bytes):
B
Ben Darnell 已提交
2840 2841 2842 2843 2844
            hasher.update(data)
        else:
            for chunk in data:
                hasher.update(chunk)
        return hasher.hexdigest()
2845

B
Ben Darnell 已提交
2846 2847
    def _stat(self) -> os.stat_result:
        assert self.absolute_path is not None
B
Ben Darnell 已提交
2848
        if not hasattr(self, "_stat_result"):
2849 2850 2851
            self._stat_result = os.stat(self.absolute_path)
        return self._stat_result

B
Ben Darnell 已提交
2852
    def get_content_size(self) -> int:
2853 2854
        """Retrieve the total size of the resource at the given path.

2855
        This method may be overridden by subclasses.
2856 2857

        .. versionadded:: 3.1
2858

2859
        .. versionchanged:: 4.0
2860 2861
           This method is now always called, instead of only when
           partial results are requested.
2862 2863
        """
        stat_result = self._stat()
B
Ben Darnell 已提交
2864
        return stat_result.st_size
2865

B
Ben Darnell 已提交
2866
    def get_modified_time(self) -> Optional[datetime.datetime]:
2867 2868 2869 2870
        """Returns the time that ``self.absolute_path`` was last modified.

        May be overridden in subclasses.  Should return a `~datetime.datetime`
        object or None.
2871 2872

        .. versionadded:: 3.1
2873
        """
2874
        stat_result = self._stat()
B
Ben Darnell 已提交
2875 2876 2877 2878 2879 2880 2881 2882
        # NOTE: Historically, this used stat_result[stat.ST_MTIME],
        # which truncates the fractional portion of the timestamp. It
        # was changed from that form to stat_result.st_mtime to
        # satisfy mypy (which disallows the bracket operator), but the
        # latter form returns a float instead of an int. For
        # consistency with the past (and because we have a unit test
        # that relies on this), we truncate the float here, although
        # I'm not sure that's the right thing to do.
B
Ben Darnell 已提交
2883
        modified = datetime.datetime.utcfromtimestamp(int(stat_result.st_mtime))
2884 2885
        return modified

B
Ben Darnell 已提交
2886
    def get_content_type(self) -> str:
2887 2888 2889 2890
        """Returns the ``Content-Type`` header to be used for this request.

        .. versionadded:: 3.1
        """
B
Ben Darnell 已提交
2891
        assert self.absolute_path is not None
2892
        mime_type, encoding = mimetypes.guess_type(self.absolute_path)
2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905
        # per RFC 6713, use the appropriate type for a gzip compressed file
        if encoding == "gzip":
            return "application/gzip"
        # As of 2015-07-21 there is no bzip2 encoding defined at
        # http://www.iana.org/assignments/media-types/media-types.xhtml
        # So for that (and any other encoding), use octet-stream.
        elif encoding is not None:
            return "application/octet-stream"
        elif mime_type is not None:
            return mime_type
        # if mime_type not detected, use application/octet-stream
        else:
            return "application/octet-stream"
B
Bret Taylor 已提交
2906

B
Ben Darnell 已提交
2907
    def set_extra_headers(self, path: str) -> None:
B
Ben Darnell 已提交
2908 2909
        """For subclass to add extra headers to the response"""
        pass
2910

B
Ben Darnell 已提交
2911 2912 2913
    def get_cache_time(
        self, path: str, modified: Optional[datetime.datetime], mime_type: str
    ) -> int:
2914 2915
        """Override to customize cache control behavior.

2916 2917 2918 2919
        Return a positive number of seconds to make the result
        cacheable for that amount of time or 0 to mark resource as
        cacheable for an unspecified amount of time (subject to
        browser heuristics).
2920 2921

        By default returns cache expiry of 10 years for resources requested
2922
        with ``v`` argument.
2923 2924
        """
        return self.CACHE_MAX_AGE if "v" in self.request.arguments else 0
2925

2926
    @classmethod
B
Ben Darnell 已提交
2927 2928 2929
    def make_static_url(
        cls, settings: Dict[str, Any], path: str, include_version: bool = True
    ) -> str:
2930 2931
        """Constructs a versioned url for the given path.

2932 2933 2934
        This method may be overridden in subclasses (but note that it
        is a class method rather than an instance method).  Subclasses
        are only required to implement the signature
2935 2936 2937
        ``make_static_url(cls, settings, path)``; other keyword
        arguments may be passed through `~RequestHandler.static_url`
        but are not standard.
2938

2939 2940 2941
        ``settings`` is the `Application.settings` dictionary.  ``path``
        is the static path being requested.  The url returned should be
        relative to the current host.
2942 2943 2944 2945

        ``include_version`` determines whether the generated URL should
        include the query string containing the version hash of the
        file corresponding to the given ``path``.
2946

2947
        """
B
Ben Darnell 已提交
2948
        url = settings.get("static_url_prefix", "/static/") + path
2949 2950 2951
        if not include_version:
            return url

2952
        version_hash = cls.get_version(settings, path)
2953 2954 2955
        if not version_hash:
            return url

B
Ben Darnell 已提交
2956
        return "%s?v=%s" % (url, version_hash)
2957

B
Ben Darnell 已提交
2958
    def parse_url_path(self, url_path: str) -> str:
2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969
        """Converts a static URL path into a filesystem path.

        ``url_path`` is the path component of the URL with
        ``static_url_prefix`` removed.  The return value should be
        filesystem path relative to ``static_path``.

        This is the inverse of `make_static_url`.
        """
        if os.path.sep != "/":
            url_path = url_path.replace("/", os.path.sep)
        return url_path
2970 2971

    @classmethod
B
Ben Darnell 已提交
2972
    def get_version(cls, settings: Dict[str, Any], path: str) -> Optional[str]:
2973 2974 2975
        """Generate the version string to be used in static URLs.

        ``settings`` is the `Application.settings` dictionary and ``path``
2976
        is the relative location of the requested asset on the filesystem.
2977 2978
        The returned value should be a string, or ``None`` if no version
        could be determined.
2979

2980 2981 2982 2983
        .. versionchanged:: 3.1
           This method was previously recommended for subclasses to override;
           `get_content_version` is now preferred as it allows the base
           class to handle caching of the result.
2984
        """
B
Ben Darnell 已提交
2985
        abs_path = cls.get_absolute_path(settings["static_path"], path)
2986 2987 2988
        return cls._get_cached_version(abs_path)

    @classmethod
B
Ben Darnell 已提交
2989
    def _get_cached_version(cls, abs_path: str) -> Optional[str]:
2990 2991 2992 2993
        with cls._lock:
            hashes = cls._static_hashes
            if abs_path not in hashes:
                try:
2994
                    hashes[abs_path] = cls.get_content_version(abs_path)
2995
                except Exception:
2996
                    gen_log.error("Could not open static file %r", abs_path)
2997 2998
                    hashes[abs_path] = None
            hsh = hashes.get(abs_path)
2999
            if hsh:
3000
                return hsh
3001
        return None
3002

B
Bret Taylor 已提交
3003

3004
class FallbackHandler(RequestHandler):
3005
    """A `RequestHandler` that wraps another HTTP server callback.
3006

3007
    The fallback is a callable object that accepts an
B
Ben Darnell 已提交
3008
    `~.httputil.HTTPServerRequest`, such as an `Application` or
3009 3010 3011
    `tornado.wsgi.WSGIContainer`.  This is most useful to use both
    Tornado ``RequestHandlers`` and WSGI in the same server.  Typical
    usage::
B
Ben Darnell 已提交
3012

3013 3014 3015 3016 3017 3018 3019
        wsgi_app = tornado.wsgi.WSGIContainer(
            django.core.handlers.wsgi.WSGIHandler())
        application = tornado.web.Application([
            (r"/foo", FooHandler),
            (r".*", FallbackHandler, dict(fallback=wsgi_app),
        ])
    """
B
Ben Darnell 已提交
3020

3021
    def initialize(
B
Ben Darnell 已提交
3022 3023
        self, fallback: Callable[[httputil.HTTPServerRequest], None]
    ) -> None:
3024 3025
        self.fallback = fallback

B
Ben Darnell 已提交
3026
    def prepare(self) -> None:
3027 3028
        self.fallback(self.request)
        self._finished = True
3029
        self.on_finish()
3030 3031


B
Bret Taylor 已提交
3032 3033 3034
class OutputTransform(object):
    """A transform modifies the result of an HTTP request (e.g., GZip encoding)

3035 3036 3037
    Applications are not expected to create their own OutputTransforms
    or interact with them directly; the framework chooses which transforms
    (if any) to apply.
B
Bret Taylor 已提交
3038
    """
B
Ben Darnell 已提交
3039

B
Ben Darnell 已提交
3040
    def __init__(self, request: httputil.HTTPServerRequest) -> None:
B
Bret Taylor 已提交
3041 3042
        pass

B
Ben Darnell 已提交
3043
    def transform_first_chunk(
B
Ben Darnell 已提交
3044 3045 3046 3047 3048
        self,
        status_code: int,
        headers: httputil.HTTPHeaders,
        chunk: bytes,
        finishing: bool,
B
Ben Darnell 已提交
3049
    ) -> Tuple[int, httputil.HTTPHeaders, bytes]:
3050
        return status_code, headers, chunk
B
Bret Taylor 已提交
3051

B
Ben Darnell 已提交
3052
    def transform_chunk(self, chunk: bytes, finishing: bool) -> bytes:
3053
        return chunk
B
Bret Taylor 已提交
3054

3055 3056 3057 3058 3059

class GZipContentEncoding(OutputTransform):
    """Applies the gzip content encoding to the response.

    See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
B
Ben Darnell 已提交
3060

3061
    .. versionchanged:: 4.0
B
Ben Darnell 已提交
3062 3063 3064
        Now compresses all mime types beginning with ``text/``, instead
        of just a whitelist. (the whitelist is still used for certain
        non-text mime types).
3065
    """
B
Ben Darnell 已提交
3066

3067 3068
    # Whitelist of compressible mime types (in addition to any types
    # beginning with "text/").
B
Ben Darnell 已提交
3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079
    CONTENT_TYPES = set(
        [
            "application/javascript",
            "application/x-javascript",
            "application/xml",
            "application/atom+xml",
            "application/json",
            "application/xhtml+xml",
            "image/svg+xml",
        ]
    )
3080 3081 3082 3083 3084 3085 3086 3087 3088 3089
    # Python's GzipFile defaults to level 9, while most other gzip
    # tools (including gzip itself) default to 6, which is probably a
    # better CPU/size tradeoff.
    GZIP_LEVEL = 6
    # Responses that are too short are unlikely to benefit from gzipping
    # after considering the "Content-Encoding: gzip" header and the header
    # inside the gzip encoding.
    # Note that responses written in multiple chunks will be compressed
    # regardless of size.
    MIN_LENGTH = 1024
3090

B
Ben Darnell 已提交
3091
    def __init__(self, request: httputil.HTTPServerRequest) -> None:
3092
        self._gzipping = "gzip" in request.headers.get("Accept-Encoding", "")
3093

B
Ben Darnell 已提交
3094
    def _compressible_type(self, ctype: str) -> bool:
B
Ben Darnell 已提交
3095
        return ctype.startswith("text/") or ctype in self.CONTENT_TYPES
3096

B
Ben Darnell 已提交
3097
    def transform_first_chunk(
B
Ben Darnell 已提交
3098 3099 3100 3101 3102
        self,
        status_code: int,
        headers: httputil.HTTPHeaders,
        chunk: bytes,
        finishing: bool,
B
Ben Darnell 已提交
3103
    ) -> Tuple[int, httputil.HTTPHeaders, bytes]:
3104
        # TODO: can/should this type be inherited from the superclass?
B
Ben Darnell 已提交
3105 3106
        if "Vary" in headers:
            headers["Vary"] += ", Accept-Encoding"
3107
        else:
B
Ben Darnell 已提交
3108
            headers["Vary"] = "Accept-Encoding"
3109
        if self._gzipping:
3110
            ctype = _unicode(headers.get("Content-Type", "")).split(";")[0]
B
Ben Darnell 已提交
3111 3112 3113 3114 3115
            self._gzipping = (
                self._compressible_type(ctype)
                and (not finishing or len(chunk) >= self.MIN_LENGTH)
                and ("Content-Encoding" not in headers)
            )
3116 3117
        if self._gzipping:
            headers["Content-Encoding"] = "gzip"
3118
            self._gzip_value = BytesIO()
B
Ben Darnell 已提交
3119 3120 3121
            self._gzip_file = gzip.GzipFile(
                mode="w", fileobj=self._gzip_value, compresslevel=self.GZIP_LEVEL
            )
3122 3123
            chunk = self.transform_chunk(chunk, finishing)
            if "Content-Length" in headers:
3124 3125 3126 3127 3128 3129 3130 3131
                # The original content length is no longer correct.
                # If this is the last (and only) chunk, we can set the new
                # content-length; otherwise we remove it and fall back to
                # chunked encoding.
                if finishing:
                    headers["Content-Length"] = str(len(chunk))
                else:
                    del headers["Content-Length"]
3132
        return status_code, headers, chunk
3133

B
Ben Darnell 已提交
3134
    def transform_chunk(self, chunk: bytes, finishing: bool) -> bytes:
3135 3136 3137 3138 3139 3140 3141
        if self._gzipping:
            self._gzip_file.write(chunk)
            if finishing:
                self._gzip_file.close()
            else:
                self._gzip_file.flush()
            chunk = self._gzip_value.getvalue()
3142 3143
            self._gzip_value.truncate(0)
            self._gzip_value.seek(0)
3144
        return chunk
B
Bret Taylor 已提交
3145 3146


B
Ben Darnell 已提交
3147
def authenticated(
B
Ben Darnell 已提交
3148
    method: Callable[..., Optional[Awaitable[None]]]
B
Ben Darnell 已提交
3149
) -> Callable[..., Optional[Awaitable[None]]]:
3150 3151 3152 3153
    """Decorate methods with this to require that the user be logged in.

    If the user is not logged in, they will be redirected to the configured
    `login url <RequestHandler.get_login_url>`.
3154 3155 3156 3157 3158

    If you configure a login url with a query parameter, Tornado will
    assume you know what you're doing and use it as-is.  If not, it
    will add a `next` parameter so the login page knows where to send
    you once you're logged in.
3159
    """
B
Ben Darnell 已提交
3160

B
Bret Taylor 已提交
3161
    @functools.wraps(method)
3162 3163
    def wrapper(  # type: ignore
        self: RequestHandler, *args, **kwargs
B
Ben Darnell 已提交
3164
    ) -> Optional[Awaitable[None]]:
B
Bret Taylor 已提交
3165
        if not self.current_user:
3166
            if self.request.method in ("GET", "HEAD"):
B
Bret Taylor 已提交
3167 3168
                url = self.get_login_url()
                if "?" not in url:
3169
                    if urllib.parse.urlsplit(url).scheme:
3170 3171 3172
                        # if login url is absolute, make next absolute too
                        next_url = self.request.full_url()
                    else:
B
Ben Darnell 已提交
3173
                        assert self.request.uri is not None
3174
                        next_url = self.request.uri
3175
                    url += "?" + urlencode(dict(next=next_url))
B
Bret Taylor 已提交
3176
                self.redirect(url)
B
Ben Darnell 已提交
3177
                return None
B
Bret Taylor 已提交
3178 3179
            raise HTTPError(403)
        return method(self, *args, **kwargs)
B
Ben Darnell 已提交
3180

B
Bret Taylor 已提交
3181 3182 3183 3184
    return wrapper


class UIModule(object):
3185
    """A re-usable, modular UI unit on a page.
B
Bret Taylor 已提交
3186 3187 3188 3189

    UI modules often execute additional queries, and they can include
    additional CSS and JavaScript that will be included in the output
    page, which is automatically inserted on page render.
B
Ben Darnell 已提交
3190 3191

    Subclasses of UIModule must override the `render` method.
B
Bret Taylor 已提交
3192
    """
B
Ben Darnell 已提交
3193

B
Ben Darnell 已提交
3194
    def __init__(self, handler: RequestHandler) -> None:
B
Bret Taylor 已提交
3195 3196 3197 3198 3199
        self.handler = handler
        self.request = handler.request
        self.ui = handler.ui
        self.locale = handler.locale

3200
    @property
B
Ben Darnell 已提交
3201
    def current_user(self) -> Any:
3202 3203
        return self.handler.current_user

B
Ben Darnell 已提交
3204
    def render(self, *args: Any, **kwargs: Any) -> str:
B
Ben Darnell 已提交
3205
        """Override in subclasses to return this module's output."""
B
Bret Taylor 已提交
3206 3207
        raise NotImplementedError()

B
Ben Darnell 已提交
3208
    def embedded_javascript(self) -> Optional[str]:
G
Gonzalo Rafuls 已提交
3209 3210
        """Override to return a JavaScript string
        to be embedded in the page."""
B
Bret Taylor 已提交
3211 3212
        return None

B
Ben Darnell 已提交
3213
    def javascript_files(self) -> Optional[Iterable[str]]:
B
Ben Darnell 已提交
3214 3215 3216 3217 3218
        """Override to return a list of JavaScript files needed by this module.

        If the return values are relative paths, they will be passed to
        `RequestHandler.static_url`; otherwise they will be used as-is.
        """
B
Bret Taylor 已提交
3219 3220
        return None

B
Ben Darnell 已提交
3221
    def embedded_css(self) -> Optional[str]:
G
Gonzalo Rafuls 已提交
3222 3223
        """Override to return a CSS string
        that will be embedded in the page."""
B
Bret Taylor 已提交
3224 3225
        return None

B
Ben Darnell 已提交
3226
    def css_files(self) -> Optional[Iterable[str]]:
B
Ben Darnell 已提交
3227 3228 3229 3230 3231
        """Override to returns a list of CSS files required by this module.

        If the return values are relative paths, they will be passed to
        `RequestHandler.static_url`; otherwise they will be used as-is.
        """
B
Bret Taylor 已提交
3232 3233
        return None

B
Ben Darnell 已提交
3234
    def html_head(self) -> Optional[str]:
B
Ben Darnell 已提交
3235 3236 3237
        """Override to return an HTML string that will be put in the <head/>
        element.
        """
B
Bret Taylor 已提交
3238 3239
        return None

B
Ben Darnell 已提交
3240
    def html_body(self) -> Optional[str]:
B
Ben Darnell 已提交
3241 3242 3243
        """Override to return an HTML string that will be put at the end of
        the <body/> element.
        """
3244 3245
        return None

B
Ben Darnell 已提交
3246
    def render_string(self, path: str, **kwargs: Any) -> bytes:
3247
        """Renders a template and returns it as a string."""
B
Bret Taylor 已提交
3248 3249
        return self.handler.render_string(path, **kwargs)

3250

3251
class _linkify(UIModule):
B
Ben Darnell 已提交
3252
    def render(self, text: str, **kwargs: Any) -> str:  # type: ignore
3253 3254
        return escape.linkify(text, **kwargs)

3255

3256
class _xsrf_form_html(UIModule):
B
Ben Darnell 已提交
3257
    def render(self) -> str:  # type: ignore
3258 3259
        return self.handler.xsrf_form_html()

3260

3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272
class TemplateModule(UIModule):
    """UIModule that simply renders the given template.

    {% module Template("foo.html") %} is similar to {% include "foo.html" %},
    but the module version gets its own namespace (with kwargs passed to
    Template()) instead of inheriting the outer template's namespace.

    Templates rendered through this module also get access to UIModule's
    automatic javascript/css features.  Simply call set_resources
    inside the template and give it keyword arguments corresponding to
    the methods on UIModule: {{ set_resources(js_files=static_url("my.js")) }}
    Note that these resources are output once per template file, not once
3273
    per instantiation of the template, so they must not depend on
3274 3275
    any arguments to the template.
    """
B
Ben Darnell 已提交
3276

B
Ben Darnell 已提交
3277
    def __init__(self, handler: RequestHandler) -> None:
3278 3279
        super(TemplateModule, self).__init__(handler)
        # keep resources in both a list and a dict to preserve order
B
Ben Darnell 已提交
3280 3281
        self._resource_list = []  # type: List[Dict[str, Any]]
        self._resource_dict = {}  # type: Dict[str, Dict[str, Any]]
3282

B
Ben Darnell 已提交
3283
    def render(self, path: str, **kwargs: Any) -> bytes:  # type: ignore
3284
        def set_resources(**kwargs) -> str:  # type: ignore
3285 3286 3287 3288 3289
            if path not in self._resource_dict:
                self._resource_list.append(kwargs)
                self._resource_dict[path] = kwargs
            else:
                if self._resource_dict[path] != kwargs:
B
Ben Darnell 已提交
3290 3291 3292 3293
                    raise ValueError(
                        "set_resources called with different "
                        "resources for the same template"
                    )
3294
            return ""
B
Ben Darnell 已提交
3295 3296

        return self.render_string(path, set_resources=set_resources, **kwargs)
3297

B
Ben Darnell 已提交
3298
    def _get_resources(self, key: str) -> Iterable[str]:
3299 3300
        return (r[key] for r in self._resource_list if key in r)

B
Ben Darnell 已提交
3301
    def embedded_javascript(self) -> str:
3302 3303
        return "\n".join(self._get_resources("embedded_javascript"))

B
Ben Darnell 已提交
3304
    def javascript_files(self) -> Iterable[str]:
3305 3306
        result = []
        for f in self._get_resources("javascript_files"):
3307
            if isinstance(f, (unicode_type, bytes)):
3308 3309 3310 3311 3312
                result.append(f)
            else:
                result.extend(f)
        return result

B
Ben Darnell 已提交
3313
    def embedded_css(self) -> str:
3314 3315
        return "\n".join(self._get_resources("embedded_css"))

B
Ben Darnell 已提交
3316
    def css_files(self) -> Iterable[str]:
3317 3318
        result = []
        for f in self._get_resources("css_files"):
3319
            if isinstance(f, (unicode_type, bytes)):
3320 3321 3322 3323 3324
                result.append(f)
            else:
                result.extend(f)
        return result

B
Ben Darnell 已提交
3325
    def html_head(self) -> str:
3326 3327
        return "".join(self._get_resources("html_head"))

B
Ben Darnell 已提交
3328
    def html_body(self) -> str:
3329 3330 3331
        return "".join(self._get_resources("html_body"))


3332 3333
class _UIModuleNamespace(object):
    """Lazy namespace which creates UIModule proxies bound to a handler."""
B
Ben Darnell 已提交
3334 3335 3336 3337

    def __init__(
        self, handler: RequestHandler, ui_modules: Dict[str, Type[UIModule]]
    ) -> None:
3338 3339 3340
        self.handler = handler
        self.ui_modules = ui_modules

B
Ben Darnell 已提交
3341
    def __getitem__(self, key: str) -> Callable[..., str]:
3342 3343
        return self.handler._ui_module(key, self.ui_modules[key])

B
Ben Darnell 已提交
3344
    def __getattr__(self, key: str) -> Callable[..., str]:
3345 3346 3347 3348 3349 3350
        try:
            return self[key]
        except KeyError as e:
            raise AttributeError(str(e))


B
Ben Darnell 已提交
3351 3352 3353 3354 3355 3356 3357 3358
def create_signed_value(
    secret: _CookieSecretTypes,
    name: str,
    value: Union[str, bytes],
    version: int = None,
    clock: Callable[[], float] = None,
    key_version: int = None,
) -> bytes:
3359
    if version is None:
3360
        version = DEFAULT_SIGNED_VALUE_VERSION
B
Ben Darnell 已提交
3361 3362
    if clock is None:
        clock = time.time
3363

B
Ben Darnell 已提交
3364
    timestamp = utf8(str(int(clock())))
3365
    value = base64.b64encode(utf8(value))
3366
    if version == 1:
B
Ben Darnell 已提交
3367
        assert not isinstance(secret, dict)
B
Ben Darnell 已提交
3368
        signature = _create_signature_v1(secret, name, value, timestamp)
3369 3370
        value = b"|".join([value, timestamp, signature])
        return value
B
Ben Darnell 已提交
3371 3372 3373 3374 3375 3376 3377 3378 3379 3380
    elif version == 2:
        # The v2 format consists of a version number and a series of
        # length-prefixed fields "%d:%s", the last of which is a
        # signature, all separated by pipes.  All numbers are in
        # decimal format with no leading zeros.  The signature is an
        # HMAC-SHA256 of the whole string up to that point, including
        # the final pipe.
        #
        # The fields are:
        # - format version (i.e. 2; no length prefix)
3381
        # - key version (integer, default is 0)
B
Ben Darnell 已提交
3382 3383 3384 3385
        # - timestamp (integer seconds since epoch)
        # - name (not encoded; assumed to be ~alphanumeric)
        # - value (base64-encoded)
        # - signature (hex-encoded; no length prefix)
B
Ben Darnell 已提交
3386
        def format_field(s: Union[str, bytes]) -> bytes:
B
Ben Darnell 已提交
3387
            return utf8("%d:" % len(s)) + utf8(s)
B
Ben Darnell 已提交
3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398

        to_sign = b"|".join(
            [
                b"2",
                format_field(str(key_version or 0)),
                format_field(timestamp),
                format_field(name),
                format_field(value),
                b"",
            ]
        )
3399 3400

        if isinstance(secret, dict):
B
Ben Darnell 已提交
3401 3402 3403 3404
            assert (
                key_version is not None
            ), "Key version must be set when sign key dict is used"
            assert version >= 2, "Version must be at least 2 for key version support"
3405 3406
            secret = secret[key_version]

B
Ben Darnell 已提交
3407 3408
        signature = _create_signature_v2(secret, to_sign)
        return to_sign + signature
3409 3410
    else:
        raise ValueError("Unsupported version %d" % version)
3411

B
Ben Darnell 已提交
3412

G
Gonzalo Rafuls 已提交
3413 3414
# A leading version number in decimal
# with no leading zeros, followed by a pipe.
B
Ben Darnell 已提交
3415
_signed_value_version_re = re.compile(br"^([1-9][0-9]*)\|(.*)$")
3416

B
Ben Darnell 已提交
3417

B
Ben Darnell 已提交
3418
def _get_version(value: bytes) -> int:
3419
    # Figures out what version value is.  Version 1 did not include an
3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437
    # explicit version field and started with arbitrary base64 data,
    # which makes this tricky.
    m = _signed_value_version_re.match(value)
    if m is None:
        version = 1
    else:
        try:
            version = int(m.group(1))
            if version > 999:
                # Certain payloads from the version-less v1 format may
                # be parsed as valid integers.  Due to base64 padding
                # restrictions, this can only happen for numbers whose
                # length is a multiple of 4, so we can treat all
                # numbers up to 999 as versions, and for the rest we
                # fall back to v1 format.
                version = 1
        except ValueError:
            version = 1
3438 3439 3440
    return version


B
Ben Darnell 已提交
3441 3442 3443 3444 3445 3446 3447 3448
def decode_signed_value(
    secret: _CookieSecretTypes,
    name: str,
    value: Union[None, str, bytes],
    max_age_days: int = 31,
    clock: Callable[[], float] = None,
    min_version: int = None,
) -> Optional[bytes]:
3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459
    if clock is None:
        clock = time.time
    if min_version is None:
        min_version = DEFAULT_SIGNED_VALUE_MIN_VERSION
    if min_version > 2:
        raise ValueError("Unsupported min_version %d" % min_version)
    if not value:
        return None

    value = utf8(value)
    version = _get_version(value)
3460 3461

    if version < min_version:
B
Ben Darnell 已提交
3462
        return None
3463
    if version == 1:
B
Ben Darnell 已提交
3464
        assert not isinstance(secret, dict)
B
Ben Darnell 已提交
3465
        return _decode_signed_value_v1(secret, name, value, max_age_days, clock)
B
Ben Darnell 已提交
3466
    elif version == 2:
B
Ben Darnell 已提交
3467
        return _decode_signed_value_v2(secret, name, value, max_age_days, clock)
3468
    else:
B
Ben Darnell 已提交
3469
        return None
3470

B
Ben Darnell 已提交
3471

B
Ben Darnell 已提交
3472 3473 3474 3475 3476 3477 3478
def _decode_signed_value_v1(
    secret: Union[str, bytes],
    name: str,
    value: bytes,
    max_age_days: int,
    clock: Callable[[], float],
) -> Optional[bytes]:
3479
    parts = utf8(value).split(b"|")
3480 3481
    if len(parts) != 3:
        return None
B
Ben Darnell 已提交
3482
    signature = _create_signature_v1(secret, name, parts[0], parts[1])
B
Ben Darnell 已提交
3483
    if not hmac.compare_digest(parts[2], signature):
3484
        gen_log.warning("Invalid cookie signature %r", value)
3485 3486
        return None
    timestamp = int(parts[1])
B
Ben Darnell 已提交
3487
    if timestamp < clock() - max_age_days * 86400:
3488
        gen_log.warning("Expired cookie %r", value)
3489
        return None
B
Ben Darnell 已提交
3490
    if timestamp > clock() + 31 * 86400:
3491 3492 3493 3494 3495
        # _cookie_signature does not hash a delimiter between the
        # parts of the cookie, so an attacker could transfer trailing
        # digits from the payload to the timestamp without altering the
        # signature.  For backwards compatibility, sanity-check timestamp
        # here instead of modifying _cookie_signature.
B
Ben Darnell 已提交
3496
        gen_log.warning("Cookie timestamp in future; possible tampering %r", value)
3497
        return None
3498
    if parts[1].startswith(b"0"):
3499
        gen_log.warning("Tampered cookie %r", value)
3500
        return None
3501 3502 3503 3504 3505
    try:
        return base64.b64decode(parts[0])
    except Exception:
        return None

3506

B
Ben Darnell 已提交
3507 3508
def _decode_fields_v2(value: bytes) -> Tuple[int, bytes, bytes, bytes, bytes]:
    def _consume_field(s: bytes) -> Tuple[bytes, bytes]:
B
Ben Darnell 已提交
3509
        length, _, rest = s.partition(b":")
B
Ben Darnell 已提交
3510 3511 3512 3513
        n = int(length)
        field_value = rest[:n]
        # In python 3, indexing bytes returns small integers; we must
        # use a slice to get a byte string as in python 2.
B
Ben Darnell 已提交
3514
        if rest[n : n + 1] != b"|":
B
Ben Darnell 已提交
3515
            raise ValueError("malformed v2 signed value field")
B
Ben Darnell 已提交
3516
        rest = rest[n + 1 :]
B
Ben Darnell 已提交
3517
        return field_value, rest
3518

B
Ben Darnell 已提交
3519
    rest = value[2:]  # remove version number
3520 3521 3522 3523 3524 3525 3526
    key_version, rest = _consume_field(rest)
    timestamp, rest = _consume_field(rest)
    name_field, rest = _consume_field(rest)
    value_field, passed_sig = _consume_field(rest)
    return int(key_version), timestamp, name_field, value_field, passed_sig


B
Ben Darnell 已提交
3527 3528 3529 3530 3531 3532 3533
def _decode_signed_value_v2(
    secret: _CookieSecretTypes,
    name: str,
    value: bytes,
    max_age_days: int,
    clock: Callable[[], float],
) -> Optional[bytes]:
B
Ben Darnell 已提交
3534
    try:
B
Ben Darnell 已提交
3535 3536 3537
        key_version, timestamp_bytes, name_field, value_field, passed_sig = _decode_fields_v2(
            value
        )
B
Ben Darnell 已提交
3538 3539
    except ValueError:
        return None
B
Ben Darnell 已提交
3540
    signed_string = value[: -len(passed_sig)]
3541 3542 3543 3544 3545 3546 3547

    if isinstance(secret, dict):
        try:
            secret = secret[key_version]
        except KeyError:
            return None

B
Ben Darnell 已提交
3548
    expected_sig = _create_signature_v2(secret, signed_string)
B
Ben Darnell 已提交
3549
    if not hmac.compare_digest(passed_sig, expected_sig):
B
Ben Darnell 已提交
3550 3551 3552
        return None
    if name_field != utf8(name):
        return None
B
Ben Darnell 已提交
3553
    timestamp = int(timestamp_bytes)
B
Ben Darnell 已提交
3554 3555 3556 3557 3558 3559 3560 3561 3562
    if timestamp < clock() - max_age_days * 86400:
        # The signature has expired.
        return None
    try:
        return base64.b64decode(value_field)
    except Exception:
        return None


B
Ben Darnell 已提交
3563
def get_signature_key_version(value: Union[str, bytes]) -> Optional[int]:
3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575
    value = utf8(value)
    version = _get_version(value)
    if version < 2:
        return None
    try:
        key_version, _, _, _, _ = _decode_fields_v2(value)
    except ValueError:
        return None

    return key_version


B
Ben Darnell 已提交
3576
def _create_signature_v1(secret: Union[str, bytes], *parts: Union[str, bytes]) -> bytes:
3577
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
3578 3579
    for part in parts:
        hash.update(utf8(part))
3580
    return utf8(hash.hexdigest())
3581

B
Ben Darnell 已提交
3582

B
Ben Darnell 已提交
3583
def _create_signature_v2(secret: Union[str, bytes], s: bytes) -> bytes:
B
Ben Darnell 已提交
3584 3585 3586
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
    hash.update(utf8(s))
    return utf8(hash.hexdigest())
B
Ben Darnell 已提交
3587

B
Ben Darnell 已提交
3588

B
Ben Darnell 已提交
3589
def is_absolute(path: str) -> bool:
3590
    return any(path.startswith(x) for x in ["/", "http:", "https:"])