web.py 139.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
    import asyncio
26
    import tornado
B
Bret Taylor 已提交
27 28 29 30 31

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

32
    async def main():
B
Bret Taylor 已提交
33 34 35
        application = tornado.web.Application([
            (r"/", MainHandler),
        ])
36
        application.listen(8888)
37 38 39 40
        await asyncio.Event().wait()

    if __name__ == "__main__":
        asyncio.run(main())
B
Bret Taylor 已提交
41

42 43 44 45
.. testoutput::
   :hide:


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

B
Ben Darnell 已提交
48 49
Thread-safety notes
-------------------
50

51
In general, methods on `RequestHandler` and elsewhere in Tornado are
B
Ben Darnell 已提交
52
not thread-safe. In particular, methods such as
53
`~RequestHandler.write()`, `~RequestHandler.finish()`, and
B
Ben Darnell 已提交
54
`~RequestHandler.flush()` must only be called from the main thread. If
55 56
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 已提交
57 58 59
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 已提交
60

B
Bret Taylor 已提交
61 62 63 64 65 66 67
"""

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

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

url = URLSpec
112

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

B
Ben Darnell 已提交
132 133
if typing.TYPE_CHECKING:
    from typing import Set  # noqa: F401
134

B
Ben Darnell 已提交
135 136 137

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

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

142

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
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
168
"""The oldest signed value accepted by `.RequestHandler.get_signed_cookie`.
169

170
May be overridden by passing a ``min_version`` keyword argument.
171 172 173 174

.. versionadded:: 3.2.1
"""

B
Ben Darnell 已提交
175

B
Ben Darnell 已提交
176 177 178 179 180 181 182
class _ArgDefaultMarker:
    pass


_ARG_DEFAULT = _ArgDefaultMarker()


B
Bret Taylor 已提交
183
class RequestHandler(object):
B
Ben Darnell 已提交
184
    """Base class for HTTP request handlers.
B
Bret Taylor 已提交
185

B
Ben Darnell 已提交
186 187
    Subclasses must define at least one of the methods defined in the
    "Entry points" section below.
B
Ben Darnell 已提交
188 189 190 191 192

    Applications should not construct `RequestHandler` objects
    directly and subclasses should not override ``__init__`` (override
    `~RequestHandler.initialize` instead).

B
Bret Taylor 已提交
193
    """
B
Ben Darnell 已提交
194 195

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

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

B
Ben Darnell 已提交
201 202 203 204 205 206 207
    _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 已提交
208 209 210 211
    def __init__(
        self,
        application: "Application",
        request: httputil.HTTPServerRequest,
212
        **kwargs: Any,
B
Ben Darnell 已提交
213
    ) -> None:
P
Poruri Sai Rahul 已提交
214
        super().__init__()
215

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

240 241
    def _initialize(self) -> None:
        pass
A
afg 已提交
242

243 244
    initialize = _initialize  # type: Callable[..., None]
    """Hook for subclass initialization. Called for each request.
245

246 247
    A dictionary passed as the third argument of a ``URLSpec`` will be
    supplied as keyword arguments to ``initialize()``.
B
Ben Darnell 已提交
248

249
    Example::
250

251 252 253
        class ProfileHandler(RequestHandler):
            def initialize(self, database):
                self.database = database
254

255 256 257 258 259 260 261
            def get(self, username):
                ...

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

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

268
    def _unimplemented_method(self, *args: str, **kwargs: str) -> None:
B
Bret Taylor 已提交
269 270
        raise HTTPError(405)

271 272 273 274 275 276 277
    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]]]
278

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

282 283
        Override this method to perform common initialization regardless
        of the request method.
284

B
Ben Darnell 已提交
285 286 287 288
        Asynchronous support: Use ``async def`` or decorate this method with
        `.gen.coroutine` to make it asynchronous.
        If this method returns an  ``Awaitable`` execution will not proceed
        until the ``Awaitable`` is done.
289 290 291

        .. versionadded:: 3.1
           Asynchronous support.
292 293 294
        """
        pass

B
Ben Darnell 已提交
295
    def on_finish(self) -> None:
296 297 298 299 300 301
        """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 已提交
302 303 304
        """
        pass

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

308 309 310 311 312
        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.
313

314 315 316 317
        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.
318
        """
319
        if _has_stream_request_body(self.__class__):
B
Ben Darnell 已提交
320 321 322
            if not self.request._body_future.done():
                self.request._body_future.set_exception(iostream.StreamClosedError())
                self.request._body_future.exception()
323

B
Ben Darnell 已提交
324
    def clear(self) -> None:
B
Bret Taylor 已提交
325
        """Resets all headers and content for this response."""
B
Ben Darnell 已提交
326 327 328 329 330 331 332
        self._headers = httputil.HTTPHeaders(
            {
                "Server": "TornadoServer/%s" % tornado.version,
                "Content-Type": "text/html; charset=UTF-8",
                "Date": httputil.format_timestamp(time.time()),
            }
        )
333
        self.set_default_headers()
B
Ben Darnell 已提交
334
        self._write_buffer = []  # type: List[bytes]
B
Bret Taylor 已提交
335
        self._status_code = 200
336
        self._reason = httputil.responses[200]
B
Bret Taylor 已提交
337

B
Ben Darnell 已提交
338
    def set_default_headers(self) -> None:
339 340 341 342 343 344 345 346 347
        """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

R
Robin Roth 已提交
348
    def set_status(self, status_code: int, reason: Optional[str] = None) -> None:
349 350
        """Sets the status code for our response.

B
Ben Darnell 已提交
351
        :arg int status_code: Response status code.
B
Ben Darnell 已提交
352
        :arg str reason: Human-readable reason phrase describing the status
353
            code. If ``None``, it will be filled in from
B
Ben Darnell 已提交
354 355 356 357 358 359
            `http.client.responses` or "Unknown".

        .. versionchanged:: 5.0

           No longer validates that the response code is in
           `http.client.responses`.
360
        """
B
Bret Taylor 已提交
361
        self._status_code = status_code
362 363 364
        if reason is not None:
            self._reason = escape.native_str(reason)
        else:
365
            self._reason = httputil.responses.get(status_code, "Unknown")
B
Bret Taylor 已提交
366

B
Ben Darnell 已提交
367
    def get_status(self) -> int:
368 369 370
        """Returns the status code for our response."""
        return self._status_code

B
Ben Darnell 已提交
371
    def set_header(self, name: str, value: _HeaderTypes) -> None:
B
Bret Taylor 已提交
372 373
        """Sets the given response header name and value.

B
Ben Darnell 已提交
374 375 376 377
        All header values are converted to strings (`datetime` objects
        are formatted according to the HTTP specification for the
        ``Date`` header).

B
Bret Taylor 已提交
378
        """
379 380
        self._headers[name] = self._convert_header_value(value)

B
Ben Darnell 已提交
381
    def add_header(self, name: str, value: _HeaderTypes) -> None:
382 383 384 385 386
        """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.
        """
387
        self._headers.add(name, self._convert_header_value(value))
388

B
Ben Darnell 已提交
389
    def clear_header(self, name: str) -> None:
390 391 392 393 394 395 396 397
        """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]

398
    _INVALID_HEADER_CHAR_RE = re.compile(r"[\x00-\x1f]")
399

B
Ben Darnell 已提交
400
    def _convert_header_value(self, value: _HeaderTypes) -> str:
401 402 403 404 405 406
        # 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
B
Ben Darnell 已提交
407
        elif isinstance(value, bytes):
408 409
            # 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 已提交
410
            retval = value.decode("latin1")
411
        elif isinstance(value, numbers.Integral):
B
Ben Darnell 已提交
412 413
            # return immediately since we know the converted value will be safe
            return str(value)
B
Ben Darnell 已提交
414
        elif isinstance(value, datetime.datetime):
415
            return httputil.format_timestamp(value)
B
Ben Darnell 已提交
416 417
        else:
            raise TypeError("Unsupported header value %r" % value)
B
Ben Darnell 已提交
418
        # If \n is allowed into the header, it is possible to inject
419
        # additional headers or split the request.
420 421 422
        if RequestHandler._INVALID_HEADER_CHAR_RE.search(retval):
            raise ValueError("Unsafe header value %r", retval)
        return retval
423

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

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

B
Ben Darnell 已提交
434 435
    @overload
    def get_argument(  # noqa: F811
B
Ben Darnell 已提交
436 437
        self, name: str, default: None, strip: bool = True
    ) -> Optional[str]:
B
Ben Darnell 已提交
438 439
        pass

B
Ben Darnell 已提交
440 441 442 443 444 445
    def get_argument(  # noqa: F811
        self,
        name: str,
        default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT,
        strip: bool = True,
    ) -> Optional[str]:
B
Bret Taylor 已提交
446 447 448
        """Returns the value of the argument with the given name.

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

B
Ben Darnell 已提交
451
        If the argument appears in the request more than once, we return the
452 453
        last value.

B
Ben Darnell 已提交
454
        This method searches both the query and body arguments.
B
Bret Taylor 已提交
455
        """
T
Travis Beauvais 已提交
456 457
        return self._get_argument(name, default, self.request.arguments, strip)

B
Ben Darnell 已提交
458
    def get_arguments(self, name: str, strip: bool = True) -> List[str]:
T
Travis Beauvais 已提交
459 460 461 462
        """Returns a list of the arguments with the given name.

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

B
Ben Darnell 已提交
463
        This method searches both the query and body arguments.
T
Travis Beauvais 已提交
464
        """
465 466 467 468 469 470

        # 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 已提交
471
        return self._get_arguments(name, self.request.arguments, strip)
T
Travis Beauvais 已提交
472

B
Ben Darnell 已提交
473 474 475 476 477 478
    def get_body_argument(
        self,
        name: str,
        default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT,
        strip: bool = True,
    ) -> Optional[str]:
T
Travis Beauvais 已提交
479 480 481 482 483 484 485 486 487
        """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.

B
Ben Darnell 已提交
488
        .. versionadded:: 3.2
T
Travis Beauvais 已提交
489
        """
B
Ben Darnell 已提交
490
        return self._get_argument(name, default, self.request.body_arguments, strip)
T
Travis Beauvais 已提交
491

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

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

B
Ben Darnell 已提交
497
        .. versionadded:: 3.2
T
Travis Beauvais 已提交
498 499
        """
        return self._get_arguments(name, self.request.body_arguments, strip)
T
Travis Beauvais 已提交
500

B
Ben Darnell 已提交
501 502 503 504 505 506
    def get_query_argument(
        self,
        name: str,
        default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT,
        strip: bool = True,
    ) -> Optional[str]:
T
Travis Beauvais 已提交
507 508 509 510 511 512 513 514 515
        """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.

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

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

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

B
Ben Darnell 已提交
525
        .. versionadded:: 3.2
526
        """
T
Travis Beauvais 已提交
527 528
        return self._get_arguments(name, self.request.query_arguments, strip)

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

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

R
Robin Roth 已提交
558
    def decode_argument(self, value: bytes, name: Optional[str] = None) -> str:
559 560 561 562 563 564
        """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.

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

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

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

R
Robin Roth 已提交
584
    def get_cookie(self, name: str, default: Optional[str] = None) -> Optional[str]:
B
Ben Darnell 已提交
585 586 587 588 589 590 591 592
        """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.
        """
593
        if self.request.cookies is not None and name in self.request.cookies:
594
            return self.request.cookies[name].value
B
Bret Taylor 已提交
595 596
        return default

B
Ben Darnell 已提交
597 598 599 600
    def set_cookie(
        self,
        name: str,
        value: Union[str, bytes],
R
Robin Roth 已提交
601 602
        domain: Optional[str] = None,
        expires: Optional[Union[float, Tuple, datetime.datetime]] = None,
B
Ben Darnell 已提交
603
        path: str = "/",
604
        expires_days: Optional[float] = None,
605 606 607 608 609 610
        # Keyword-only args start here for historical reasons.
        *,
        max_age: Optional[int] = None,
        httponly: bool = False,
        secure: bool = False,
        samesite: Optional[str] = None,
611
        **kwargs: Any,
B
Ben Darnell 已提交
612
    ) -> None:
B
Ben Darnell 已提交
613 614 615 616
        """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 已提交
617

618 619 620 621 622
        Most arguments are passed directly to `http.cookies.Morsel` directly.
        See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
        for more information.

        ``expires`` may be a numeric timestamp as returned by `time.time`,
B
Ben Darnell 已提交
623
        a time tuple as returned by `time.gmtime`, or a
624 625 626
        `datetime.datetime` object. ``expires_days`` is provided as a convenience
        to set an expiration time in days from today (if both are set, ``expires``
        is used).
627

628 629 630 631
        .. deprecated:: 6.3
           Keyword arguments are currently accepted case-insensitively.
           In Tornado 7.0 this will be changed to only accept lowercase
           arguments.
632
        """
633 634 635
        # 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 已提交
636 637 638
        if re.search(r"[\x00-\x20]", name + value):
            # Don't let us accidentally inject bad stuff
            raise ValueError("Invalid cookie %r: %r" % (name, value))
639
        if not hasattr(self, "_new_cookie"):
B
Ben Darnell 已提交
640 641 642
            self._new_cookie = (
                http.cookies.SimpleCookie()
            )  # type: http.cookies.SimpleCookie
643 644 645 646
        if name in self._new_cookie:
            del self._new_cookie[name]
        self._new_cookie[name] = value
        morsel = self._new_cookie[name]
B
Bret Taylor 已提交
647
        if domain:
648
            morsel["domain"] = domain
B
Bret Taylor 已提交
649
        if expires_days is not None and not expires:
650 651 652
            expires = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
                days=expires_days
            )
B
Bret Taylor 已提交
653
        if expires:
654
            morsel["expires"] = httputil.format_timestamp(expires)
B
Bret Taylor 已提交
655
        if path:
656
            morsel["path"] = path
657 658 659 660 661 662 663 664 665 666 667
        if max_age:
            # Note change from _ to -.
            morsel["max-age"] = str(max_age)
        if httponly:
            # Note that SimpleCookie ignores the value here. The presense of an
            # httponly (or secure) key is treated as true.
            morsel["httponly"] = True
        if secure:
            morsel["secure"] = True
        if samesite:
            morsel["samesite"] = samesite
668 669 670 671 672 673 674 675 676 677 678
        if kwargs:
            # The setitem interface is case-insensitive, so continue to support
            # kwargs for backwards compatibility until we can remove deprecated
            # features.
            for k, v in kwargs.items():
                morsel[k] = v
            warnings.warn(
                f"Deprecated arguments to set_cookie: {set(kwargs.keys())} "
                "(should be lowercase)",
                DeprecationWarning,
            )
B
Bret Taylor 已提交
679

680
    def clear_cookie(self, name: str, **kwargs: Any) -> None:
681 682
        """Deletes the cookie with the given name.

683 684 685 686 687
        This method accepts the same arguments as `set_cookie`, except for
        ``expires`` and ``max_age``. Clearing a cookie requires the same
        ``domain`` and ``path`` arguments as when it was set. In some cases the
        ``samesite`` and ``secure`` arguments are also required to match. Other
        arguments are ignored.
B
Ben Darnell 已提交
688 689 690

        Similar to `set_cookie`, the effect of this method will not be
        seen until the following request.
691 692 693 694 695 696

        .. versionchanged:: 6.3

           Now accepts all keyword arguments that ``set_cookie`` does.
           The ``samesite`` and ``secure`` flags have recently become
           required for clearing ``samesite="none"`` cookies.
697
        """
698 699 700 701 702
        for excluded_arg in ["expires", "max_age"]:
            if excluded_arg in kwargs:
                raise TypeError(
                    f"clear_cookie() got an unexpected keyword argument '{excluded_arg}'"
                )
703 704 705
        expires = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(
            days=365
        )
706
        self.set_cookie(name, value="", expires=expires, **kwargs)
B
Bret Taylor 已提交
707

708 709
    def clear_all_cookies(self, **kwargs: Any) -> None:
        """Attempt to delete all the cookies the user sent with this request.
710

711 712 713 714 715 716
        See `clear_cookie` for more information on keyword arguments. Due to
        limitations of the cookie protocol, it is impossible to determine on the
        server side which values are necessary for the ``domain``, ``path``,
        ``samesite``, or ``secure`` arguments, this method can only be
        successful if you consistently use the same values for these arguments
        when setting cookies.
B
Ben Darnell 已提交
717

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

B
Ben Darnell 已提交
721 722 723
        .. versionchanged:: 3.2

           Added the ``path`` and ``domain`` parameters.
724 725 726 727 728 729 730 731 732 733 734

        .. versionchanged:: 6.3

           Now accepts all keyword arguments that ``set_cookie`` does.

        .. deprecated:: 6.3

           The increasingly complex rules governing cookies have made it
           impossible for a ``clear_all_cookies`` method to work reliably
           since all we know about cookies are their names. Applications
           should generally use ``clear_cookie`` one at a time instead.
735
        """
736
        for name in self.request.cookies:
737
            self.clear_cookie(name, **kwargs)
B
Bret Taylor 已提交
738

739
    def set_signed_cookie(
B
Ben Darnell 已提交
740 741 742
        self,
        name: str,
        value: Union[str, bytes],
743
        expires_days: Optional[float] = 30,
R
Robin Roth 已提交
744
        version: Optional[int] = None,
745
        **kwargs: Any,
B
Ben Darnell 已提交
746
    ) -> None:
B
Bret Taylor 已提交
747 748
        """Signs and timestamps a cookie so it cannot be forged.

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

753
        To read a cookie set with this method, use `get_signed_cookie()`.
754 755 756

        Note that the ``expires_days`` parameter sets the lifetime of the
        cookie in the browser, but is independent of the ``max_age_days``
757
        parameter to `get_signed_cookie`.
R
Robin Roth 已提交
758
        A value of None limits the lifetime to the current browser session.
759 760 761

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

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

766 767 768 769
        .. versionchanged:: 3.2.1

           Added the ``version`` argument.  Introduced cookie version 2
           and made it the default.
770 771 772 773 774 775

        .. versionchanged:: 6.3

           Renamed from ``set_secure_cookie`` to ``set_signed_cookie`` to
           avoid confusion with other uses of "secure" in cookie attributes
           and prefixes. The old name remains as an alias.
B
Bret Taylor 已提交
776
        """
B
Ben Darnell 已提交
777 778 779 780
        self.set_cookie(
            name,
            self.create_signed_value(name, value, version=version),
            expires_days=expires_days,
781
            **kwargs,
B
Ben Darnell 已提交
782
        )
783

784 785
    set_secure_cookie = set_signed_cookie

B
Ben Darnell 已提交
786
    def create_signed_value(
R
Robin Roth 已提交
787
        self, name: str, value: Union[str, bytes], version: Optional[int] = None
B
Ben Darnell 已提交
788
    ) -> bytes:
789 790
        """Signs and timestamps a string so it cannot be forged.

791
        Normally used via set_signed_cookie, but provided as a separate
792
        method for non-cookie uses.  To decode a value not stored
793
        as a cookie use the optional value argument to get_signed_cookie.
794 795 796 797 798

        .. versionchanged:: 3.2.1

           Added the ``version`` argument.  Introduced cookie version 2
           and made it the default.
799
        """
800
        self.require_setting("cookie_secret", "secure cookies")
801 802 803 804 805 806 807
        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 已提交
808 809 810
        return create_signed_value(
            secret, name, value, version=version, key_version=key_version
        )
B
Bret Taylor 已提交
811

812
    def get_signed_cookie(
B
Ben Darnell 已提交
813 814
        self,
        name: str,
R
Robin Roth 已提交
815
        value: Optional[str] = None,
816
        max_age_days: float = 31,
R
Robin Roth 已提交
817
        min_version: Optional[int] = None,
B
Ben Darnell 已提交
818
    ) -> Optional[bytes]:
819 820 821 822
        """Returns the given signed cookie if it validates, or None.

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

B
Ben Darnell 已提交
824 825
        Similar to `get_cookie`, this method only returns cookies that
        were present in the request. It does not see outgoing cookies set by
826
        `set_signed_cookie` in this handler.
B
Ben Darnell 已提交
827

828 829 830 831
        .. versionchanged:: 3.2.1

           Added the ``min_version`` argument.  Introduced cookie version 2;
           both versions 1 and 2 are accepted by default.
832 833 834 835 836 837 838

         .. versionchanged:: 6.3

           Renamed from ``get_secure_cookie`` to ``get_signed_cookie`` to
           avoid confusion with other uses of "secure" in cookie attributes
           and prefixes. The old name remains as an alias.

839
        """
B
Bret Taylor 已提交
840
        self.require_setting("cookie_secret", "secure cookies")
841 842
        if value is None:
            value = self.get_cookie(name)
B
Ben Darnell 已提交
843 844 845 846 847 848 849
        return decode_signed_value(
            self.application.settings["cookie_secret"],
            name,
            value,
            max_age_days=max_age_days,
            min_version=min_version,
        )
B
Bret Taylor 已提交
850

851 852 853
    get_secure_cookie = get_signed_cookie

    def get_signed_cookie_key_version(
R
Robin Roth 已提交
854
        self, name: str, value: Optional[str] = None
B
Ben Darnell 已提交
855
    ) -> Optional[int]:
856 857 858
        """Returns the signing key version of the secure cookie.

        The version is returned as int.
859 860 861 862 863 864 865 866

        .. versionchanged:: 6.3

           Renamed from ``get_secure_cookie_key_version`` to
           ``set_signed_cookie_key_version`` to avoid confusion with other
           uses of "secure" in cookie attributes and prefixes. The old name
           remains as an alias.

867 868 869 870
        """
        self.require_setting("cookie_secret", "secure cookies")
        if value is None:
            value = self.get_cookie(name)
B
Ben Darnell 已提交
871 872
        if value is None:
            return None
873 874
        return get_signature_key_version(value)

875 876
    get_secure_cookie_key_version = get_signed_cookie_key_version

R
Robin Roth 已提交
877 878 879
    def redirect(
        self, url: str, permanent: bool = False, status: Optional[int] = None
    ) -> None:
880 881 882 883 884 885 886
        """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 已提交
887 888
        if self._headers_written:
            raise Exception("Cannot redirect after headers have been written")
889 890 891 892 893
        if status is None:
            status = 301 if permanent else 302
        else:
            assert isinstance(status, int) and 300 <= status <= 399
        self.set_status(status)
894
        self.set_header("Location", utf8(url))
B
Bret Taylor 已提交
895 896
        self.finish()

B
Ben Darnell 已提交
897
    def write(self, chunk: Union[str, bytes, dict]) -> None:
B
Bret Taylor 已提交
898 899
        """Writes the given chunk to the output buffer.

B
Ben Darnell 已提交
900
        To write the output to the network, use the `flush()` method below.
B
Bret Taylor 已提交
901 902

        If the given chunk is a dictionary, we write it as JSON and set
903 904
        the Content-Type of the response to be ``application/json``.
        (if you want to send JSON as a different ``Content-Type``, call
905
        ``set_header`` *after* calling ``write()``).
906 907 908 909

        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
910 911
        http://haacked.com/archive/2009/06/25/json-hijacking.aspx/ and
        https://github.com/facebook/tornado/issues/1009
B
Bret Taylor 已提交
912
        """
913
        if self._finished:
914
            raise RuntimeError("Cannot write() after finish()")
915
        if not isinstance(chunk, (bytes, unicode_type, dict)):
916 917
            message = "write() only accepts bytes, unicode, and dict objects"
            if isinstance(chunk, list):
B
Ben Darnell 已提交
918 919 920 921
                message += (
                    ". Lists not accepted for security reasons; see "
                    + "http://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.write"  # noqa: E501
                )
922
            raise TypeError(message)
B
Bret Taylor 已提交
923 924
        if isinstance(chunk, dict):
            chunk = escape.json_encode(chunk)
925
            self.set_header("Content-Type", "application/json; charset=UTF-8")
926
        chunk = utf8(chunk)
B
Bret Taylor 已提交
927 928
        self._write_buffer.append(chunk)

B
Ben Darnell 已提交
929
    def render(self, template_name: str, **kwargs: Any) -> "Future[None]":
930 931 932 933 934 935 936 937 938 939 940 941
        """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``.
        """
942 943
        if self._finished:
            raise RuntimeError("Cannot render() after finish()")
B
Bret Taylor 已提交
944 945 946 947 948 949 950 951
        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 = []
952
        html_bodies = []
953
        for module in getattr(self, "_active_modules", {}).values():
B
Bret Taylor 已提交
954
            embed_part = module.embedded_javascript()
955 956
            if embed_part:
                js_embed.append(utf8(embed_part))
B
Bret Taylor 已提交
957 958
            file_part = module.javascript_files()
            if file_part:
959
                if isinstance(file_part, (unicode_type, bytes)):
B
Ben Darnell 已提交
960
                    js_files.append(_unicode(file_part))
B
Bret Taylor 已提交
961 962 963
                else:
                    js_files.extend(file_part)
            embed_part = module.embedded_css()
964 965
            if embed_part:
                css_embed.append(utf8(embed_part))
B
Bret Taylor 已提交
966 967
            file_part = module.css_files()
            if file_part:
968
                if isinstance(file_part, (unicode_type, bytes)):
B
Ben Darnell 已提交
969
                    css_files.append(_unicode(file_part))
B
Bret Taylor 已提交
970 971 972
                else:
                    css_files.extend(file_part)
            head_part = module.html_head()
973 974
            if head_part:
                html_heads.append(utf8(head_part))
975
            body_part = module.html_body()
976 977 978
            if body_part:
                html_bodies.append(utf8(body_part))

B
Bret Taylor 已提交
979
        if js_files:
980
            # Maintain order of JavaScript files given by modules
981
            js = self.render_linked_js(js_files)
B
Ben Darnell 已提交
982 983
            sloc = html.rindex(b"</body>")
            html = html[:sloc] + utf8(js) + b"\n" + html[sloc:]
B
Bret Taylor 已提交
984
        if js_embed:
B
Ben Darnell 已提交
985
            js_bytes = self.render_embed_js(js_embed)
B
Ben Darnell 已提交
986 987
            sloc = html.rindex(b"</body>")
            html = html[:sloc] + js_bytes + b"\n" + html[sloc:]
B
Bret Taylor 已提交
988
        if css_files:
989
            css = self.render_linked_css(css_files)
B
Ben Darnell 已提交
990 991
            hloc = html.index(b"</head>")
            html = html[:hloc] + utf8(css) + b"\n" + html[hloc:]
B
Bret Taylor 已提交
992
        if css_embed:
B
Ben Darnell 已提交
993
            css_bytes = self.render_embed_css(css_embed)
B
Ben Darnell 已提交
994 995
            hloc = html.index(b"</head>")
            html = html[:hloc] + css_bytes + b"\n" + html[hloc:]
B
Bret Taylor 已提交
996
        if html_heads:
B
Ben Darnell 已提交
997 998
            hloc = html.index(b"</head>")
            html = html[:hloc] + b"".join(html_heads) + b"\n" + html[hloc:]
999
        if html_bodies:
B
Ben Darnell 已提交
1000 1001
            hloc = html.index(b"</body>")
            html = html[:hloc] + b"".join(html_bodies) + b"\n" + html[hloc:]
1002
        return self.finish(html)
B
Bret Taylor 已提交
1003

B
Ben Darnell 已提交
1004
    def render_linked_js(self, js_files: Iterable[str]) -> str:
1005 1006 1007 1008 1009 1010
        """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 已提交
1011
        unique_paths = set()  # type: Set[str]
1012 1013 1014 1015 1016 1017 1018 1019

        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 已提交
1020 1021 1022 1023 1024 1025
        return "".join(
            '<script src="'
            + escape.xhtml_escape(p)
            + '" type="text/javascript"></script>'
            for p in paths
        )
1026

B
Ben Darnell 已提交
1027
    def render_embed_js(self, js_embed: Iterable[bytes]) -> bytes:
1028 1029 1030 1031 1032
        """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 已提交
1033 1034 1035 1036 1037
        return (
            b'<script type="text/javascript">\n//<![CDATA[\n'
            + b"\n".join(js_embed)
            + b"\n//]]>\n</script>"
        )
1038

B
Ben Darnell 已提交
1039
    def render_linked_css(self, css_files: Iterable[str]) -> str:
1040 1041 1042 1043 1044 1045
        """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 已提交
1046
        unique_paths = set()  # type: Set[str]
1047 1048 1049 1050 1051 1052 1053 1054

        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 已提交
1055 1056 1057 1058 1059
        return "".join(
            '<link href="' + escape.xhtml_escape(p) + '" '
            'type="text/css" rel="stylesheet"/>'
            for p in paths
        )
1060

B
Ben Darnell 已提交
1061
    def render_embed_css(self, css_embed: Iterable[bytes]) -> bytes:
1062 1063 1064 1065 1066
        """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 已提交
1067
        return b'<style type="text/css">\n' + b"\n".join(css_embed) + b"\n</style>"
1068

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

1072 1073
        We return the generated byte string (in utf8). To generate and
        write a template as a response, use render() above.
B
Bret Taylor 已提交
1074 1075
        """
        # If no template_path is specified, use the path of the calling file
1076
        template_path = self.get_template_path()
B
Bret Taylor 已提交
1077 1078 1079
        if not template_path:
            frame = sys._getframe(0)
            web_file = frame.f_code.co_filename
B
Ben Darnell 已提交
1080
            while frame.f_code.co_filename == web_file and frame.f_back is not None:
B
Bret Taylor 已提交
1081
                frame = frame.f_back
B
Ben Darnell 已提交
1082
            assert frame.f_code.co_filename is not None
B
Bret Taylor 已提交
1083
            template_path = os.path.dirname(frame.f_code.co_filename)
1084 1085 1086 1087 1088 1089 1090
        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)
1091 1092 1093 1094
        namespace = self.get_template_namespace()
        namespace.update(kwargs)
        return t.generate(**namespace)

B
Ben Darnell 已提交
1095
    def get_template_namespace(self) -> Dict[str, Any]:
1096 1097 1098 1099 1100 1101 1102 1103 1104
        """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 已提交
1105 1106 1107 1108 1109
            handler=self,
            request=self.request,
            current_user=self.current_user,
            locale=self.locale,
            _=self.locale.translate,
1110
            pgettext=self.locale.pgettext,
B
Bret Taylor 已提交
1111 1112
            static_url=self.static_url,
            xsrf_form_html=self.xsrf_form_html,
B
Ben Darnell 已提交
1113
            reverse_url=self.reverse_url,
B
Bret Taylor 已提交
1114
        )
1115 1116
        namespace.update(self.ui)
        return namespace
B
Bret Taylor 已提交
1117

B
Ben Darnell 已提交
1118
    def create_template_loader(self, template_path: str) -> template.BaseLoader:
1119 1120 1121 1122
        """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
1123 1124 1125
        ``autoescape`` and ``template_whitespace`` application
        settings.  If a ``template_loader`` application setting is
        supplied, uses that instead.
1126
        """
1127 1128 1129 1130 1131 1132 1133 1134
        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"]
1135 1136
        if "template_whitespace" in settings:
            kwargs["whitespace"] = settings["template_whitespace"]
1137 1138
        return template.Loader(template_path, **kwargs)

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

1142
        .. versionchanged:: 4.0
B
Ben Darnell 已提交
1143
           Now returns a `.Future` if no callback is given.
1144

1145
        .. versionchanged:: 6.0
1146

1147
           The ``callback`` argument was removed.
1148
        """
B
Ben Darnell 已提交
1149
        assert self.request.connection is not None
1150
        chunk = b"".join(self._write_buffer)
1151
        self._write_buffer = []
B
Bret Taylor 已提交
1152 1153
        if not self._headers_written:
            self._headers_written = True
1154
            for transform in self._transforms:
B
Ben Darnell 已提交
1155
                assert chunk is not None
1156 1157 1158 1159 1160
                (
                    self._status_code,
                    self._headers,
                    chunk,
                ) = transform.transform_first_chunk(
B
Ben Darnell 已提交
1161 1162
                    self._status_code, self._headers, chunk, include_footers
                )
1163 1164
            # Ignore the chunk and only write the headers for HEAD requests
            if self.request.method == "HEAD":
B
Ben Darnell 已提交
1165
                chunk = b""
1166

1167 1168 1169 1170 1171 1172 1173
            # 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 已提交
1174
            start_line = httputil.ResponseStartLine("", self._status_code, self._reason)
1175
            return self.request.connection.write_headers(
B
Ben Darnell 已提交
1176 1177
                start_line, self._headers, chunk
            )
B
Bret Taylor 已提交
1178
        else:
1179 1180
            for transform in self._transforms:
                chunk = transform.transform_chunk(chunk, include_footers)
1181 1182
            # Ignore the chunk and only write the headers for HEAD requests
            if self.request.method != "HEAD":
1183
                return self.request.connection.write(chunk)
1184
            else:
B
Ben Darnell 已提交
1185
                future = Future()  # type: Future[None]
1186 1187
                future.set_result(None)
                return future
B
Bret Taylor 已提交
1188

R
Robin Roth 已提交
1189
    def finish(self, chunk: Optional[Union[str, bytes, dict]] = None) -> "Future[None]":
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
        """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``.
        """
1204
        if self._finished:
1205
            raise RuntimeError("finish() called twice")
1206

1207 1208
        if chunk is not None:
            self.write(chunk)
B
Bret Taylor 已提交
1209 1210 1211 1212

        # 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 已提交
1213 1214 1215 1216 1217
            if (
                self._status_code == 200
                and self.request.method in ("GET", "HEAD")
                and "Etag" not in self._headers
            ):
1218 1219 1220 1221
                self.set_etag_header()
                if self.check_etag_header():
                    self._write_buffer = []
                    self.set_status(304)
J
John Bampton 已提交
1222
            if self._status_code in (204, 304) or (100 <= self._status_code < 200):
B
Ben Darnell 已提交
1223 1224 1225
                assert not self._write_buffer, (
                    "Cannot send body with %s" % self._status_code
                )
1226
                self._clear_representation_headers()
S
Sebastien Boving 已提交
1227
            elif "Content-Length" not in self._headers:
B
Bret Taylor 已提交
1228 1229 1230
                content_length = sum(len(part) for part in self._write_buffer)
                self.set_header("Content-Length", content_length)

B
Ben Darnell 已提交
1231 1232 1233 1234 1235 1236
        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
1237

1238
        future = self.flush(include_footers=True)
B
Ben Darnell 已提交
1239
        self.request.connection.finish()
1240
        self._log()
B
Bret Taylor 已提交
1241
        self._finished = True
1242
        self.on_finish()
1243
        self._break_cycles()
1244
        return future
1245

B
Ben Darnell 已提交
1246
    def detach(self) -> iostream.IOStream:
B
Ben Darnell 已提交
1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
        """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 已提交
1258 1259
        # TODO: add detach to HTTPConnection?
        return self.request.connection.detach()  # type: ignore
B
Ben Darnell 已提交
1260

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

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

1269 1270 1271 1272 1273 1274 1275
        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 已提交
1276 1277
        """
        if self._headers_written:
1278
            gen_log.error("Cannot send error response after headers written")
B
Bret Taylor 已提交
1279
            if not self._finished:
1280 1281 1282 1283 1284 1285 1286
                # 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 已提交
1287
                    gen_log.error("Failed to flush partial response", exc_info=True)
B
Bret Taylor 已提交
1288 1289
            return
        self.clear()
1290

B
Ben Darnell 已提交
1291 1292 1293
        reason = kwargs.get("reason")
        if "exc_info" in kwargs:
            exception = kwargs["exc_info"][1]
1294 1295 1296
            if isinstance(exception, HTTPError) and exception.reason:
                reason = exception.reason
        self.set_status(status_code, reason=reason)
1297 1298 1299
        try:
            self.write_error(status_code, **kwargs)
        except Exception:
1300
            app_log.error("Uncaught exception in write_error", exc_info=True)
1301 1302
        if not self._finished:
            self.finish()
B
Bret Taylor 已提交
1303

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

1307 1308 1309
        ``write_error`` may call `write`, `render`, `set_header`, etc
        to produce output as usual.

1310 1311 1312 1313 1314
        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``.
1315
        """
1316
        if self.settings.get("serve_traceback") and "exc_info" in kwargs:
1317
            # in debug mode, try to send a traceback
B
Ben Darnell 已提交
1318
            self.set_header("Content-Type", "text/plain")
1319 1320 1321
            for line in traceback.format_exception(*kwargs["exc_info"]):
                self.write(line)
            self.finish()
1322
        else:
B
Ben Darnell 已提交
1323 1324 1325 1326 1327
            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 已提交
1328 1329

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

1333
        Determined by either `get_user_locale`, which you can override to
B
Bret Taylor 已提交
1334
        set the locale based on, e.g., a user preference stored in a
1335
        database, or `get_browser_locale`, which uses the ``Accept-Language``
B
Bret Taylor 已提交
1336
        header.
1337 1338 1339

        .. versionchanged: 4.1
           Added a property setter.
B
Bret Taylor 已提交
1340 1341
        """
        if not hasattr(self, "_locale"):
B
Ben Darnell 已提交
1342 1343 1344 1345
            loc = self.get_user_locale()
            if loc is not None:
                self._locale = loc
            else:
B
Bret Taylor 已提交
1346 1347 1348
                self._locale = self.get_browser_locale()
                assert self._locale
        return self._locale
1349

1350
    @locale.setter
B
Ben Darnell 已提交
1351
    def locale(self, value: tornado.locale.Locale) -> None:
1352 1353
        self._locale = value

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

1357
        If None is returned, we fall back to `get_browser_locale()`.
1358

1359 1360
        This method should return a `tornado.locale.Locale` object,
        most likely obtained via a call like ``tornado.locale.get("en")``
B
Bret Taylor 已提交
1361 1362 1363
        """
        return None

B
Ben Darnell 已提交
1364
    def get_browser_locale(self, default: str = "en_US") -> tornado.locale.Locale:
1365
        """Determines the user's locale from ``Accept-Language`` header.
B
Bret Taylor 已提交
1366 1367 1368 1369 1370 1371 1372 1373

        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(";")
1374
                if len(parts) > 1 and parts[1].strip().startswith("q="):
B
Bret Taylor 已提交
1375
                    try:
1376
                        score = float(parts[1].strip()[2:])
1377 1378
                        if score < 0:
                            raise ValueError()
B
Bret Taylor 已提交
1379 1380 1381 1382
                    except (ValueError, TypeError):
                        score = 0.0
                else:
                    score = 1.0
1383 1384
                if score > 0:
                    locales.append((parts[0], score))
B
Bret Taylor 已提交
1385
            if locales:
1386
                locales.sort(key=lambda pair: pair[1], reverse=True)
M
marc 已提交
1387
                codes = [loc[0] for loc in locales]
B
Bret Taylor 已提交
1388 1389 1390 1391
                return locale.get(*codes)
        return locale.get(default)

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

1395
        This is set in one of two ways:
B
Bret Taylor 已提交
1396

1397 1398 1399 1400 1401 1402
        * 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):
1403
                  user_cookie = self.get_signed_cookie("user")
1404 1405
                  if user_cookie:
                      return json.loads(user_cookie)
1406 1407 1408 1409 1410 1411 1412
                  return None

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

              @gen.coroutine
              def prepare(self):
1413
                  user_id_cookie = self.get_signed_cookie("user_id")
1414 1415 1416 1417 1418 1419 1420
                  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 已提交
1421
        The user object may be any type of the application's choosing.
B
Bret Taylor 已提交
1422 1423 1424 1425 1426
        """
        if not hasattr(self, "_current_user"):
            self._current_user = self.get_current_user()
        return self._current_user

1427
    @current_user.setter
B
Ben Darnell 已提交
1428
    def current_user(self, value: Any) -> None:
1429 1430
        self._current_user = value

B
Ben Darnell 已提交
1431
    def get_current_user(self) -> Any:
1432 1433 1434 1435
        """Override to determine the current user from, e.g., a cookie.

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

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

1441
        By default, we use the ``login_url`` application setting.
B
Bret Taylor 已提交
1442 1443 1444 1445
        """
        self.require_setting("login_url", "@tornado.web.authenticated")
        return self.application.settings["login_url"]

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

1449
        By default, we use the ``template_path`` application setting.
1450 1451 1452 1453
        Return None to load templates relative to the calling file.
        """
        return self.application.settings.get("template_path")

B
Bret Taylor 已提交
1454
    @property
B
Ben Darnell 已提交
1455
    def xsrf_token(self) -> bytes:
B
Bret Taylor 已提交
1456 1457 1458 1459 1460 1461 1462 1463
        """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
1464

1465 1466 1467 1468 1469
        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.

1470 1471 1472 1473 1474 1475 1476 1477
        .. 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.
1478 1479 1480 1481 1482 1483 1484 1485

        .. 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 已提交
1486 1487
        """
        if not hasattr(self, "_xsrf_token"):
1488
            version, token, timestamp = self._get_raw_xsrf_token()
1489
            output_version = self.settings.get("xsrf_cookie_version", 2)
1490
            cookie_kwargs = self.settings.get("xsrf_cookie_kwargs", {})
1491 1492 1493 1494
            if output_version == 1:
                self._xsrf_token = binascii.b2a_hex(token)
            elif output_version == 2:
                mask = os.urandom(4)
B
Ben Darnell 已提交
1495 1496 1497 1498 1499 1500 1501 1502
                self._xsrf_token = b"|".join(
                    [
                        b"2",
                        binascii.b2a_hex(mask),
                        binascii.b2a_hex(_websocket_mask(mask, token)),
                        utf8(str(int(timestamp))),
                    ]
                )
1503
            else:
B
Ben Darnell 已提交
1504
                raise ValueError("unknown xsrf cookie version %d", output_version)
1505
            if version is None:
1506 1507
                if self.current_user and "expires_days" not in cookie_kwargs:
                    cookie_kwargs["expires_days"] = 30
1508 1509
                cookie_name = self.settings.get("xsrf_cookie_name", "_xsrf")
                self.set_cookie(cookie_name, self._xsrf_token, **cookie_kwargs)
B
Bret Taylor 已提交
1510 1511
        return self._xsrf_token

B
Ben Darnell 已提交
1512
    def _get_raw_xsrf_token(self) -> Tuple[Optional[int], bytes, float]:
1513 1514 1515 1516 1517 1518 1519 1520 1521 1522
        """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 已提交
1523
        if not hasattr(self, "_raw_xsrf_token"):
1524 1525
            cookie_name = self.settings.get("xsrf_cookie_name", "_xsrf")
            cookie = self.get_cookie(cookie_name)
1526 1527 1528 1529 1530 1531 1532 1533
            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 已提交
1534 1535
            assert token is not None
            assert timestamp is not None
1536 1537 1538
            self._raw_xsrf_token = (version, token, timestamp)
        return self._raw_xsrf_token

B
Ben Darnell 已提交
1539 1540 1541
    def _decode_xsrf_token(
        self, cookie: str
    ) -> Tuple[Optional[int], Optional[bytes], Optional[float]]:
1542 1543 1544
        """Convert a cookie string into a the tuple form returned by
        _get_raw_xsrf_token.
        """
1545 1546 1547 1548 1549 1550 1551

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

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

B
Ben Darnell 已提交
1554
                    mask = binascii.a2b_hex(utf8(mask_str))
B
Ben Darnell 已提交
1555
                    token = _websocket_mask(mask, binascii.a2b_hex(utf8(masked_token)))
B
Ben Darnell 已提交
1556
                    timestamp = int(timestamp_str)
1557 1558 1559 1560
                    return version, token, timestamp
                else:
                    # Treat unknown versions as not present instead of failing.
                    raise Exception("Unknown xsrf cookie version")
1561
            else:
1562 1563 1564 1565 1566 1567 1568 1569 1570 1571
                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 已提交
1572
            gen_log.debug("Uncaught exception in _decode_xsrf_token", exc_info=True)
1573
            return None, None, None
1574

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

1578
        To prevent cross-site request forgery, we set an ``_xsrf``
1579
        cookie and include the same value as a non-cookie
1580
        field with all ``POST`` requests. If the two do not match, we
1581 1582
        reject the form submission as a potential forgery.

1583 1584
        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``
1585
        (the latter is accepted for compatibility with Django).
B
Bret Taylor 已提交
1586 1587

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

1589 1590 1591
        .. versionchanged:: 3.2.2
           Added support for cookie version 2.  Both versions 1 and 2 are
           supported.
B
Bret Taylor 已提交
1592
        """
B
Ben Darnell 已提交
1593 1594 1595 1596 1597 1598
        # Prior to release 1.1.1, this check was ignored if the HTTP header
        # ``X-Requested-With: XMLHTTPRequest`` was present.  This exception
        # 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
B
Ben Darnell 已提交
1599 1600 1601 1602 1603
        token = (
            self.get_argument("_xsrf", None)
            or self.request.headers.get("X-Xsrftoken")
            or self.request.headers.get("X-Csrftoken")
        )
B
Bret Taylor 已提交
1604 1605
        if not token:
            raise HTTPError(403, "'_xsrf' argument missing from POST")
1606 1607
        _, token, _ = self._decode_xsrf_token(token)
        _, expected_token, _ = self._get_raw_xsrf_token()
1608 1609
        if not token:
            raise HTTPError(403, "'_xsrf' argument has invalid format")
B
Ben Darnell 已提交
1610
        if not hmac.compare_digest(utf8(token), utf8(expected_token)):
B
Bret Taylor 已提交
1611 1612
            raise HTTPError(403, "XSRF cookie does not match POST argument")

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

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

1621 1622 1623 1624
        In a template, this method should be called with ``{% module
        xsrf_form_html() %}``

        See `check_xsrf_cookie()` above for more information.
B
Bret Taylor 已提交
1625
        """
B
Ben Darnell 已提交
1626 1627 1628 1629 1630
        return (
            '<input type="hidden" name="_xsrf" value="'
            + escape.xhtml_escape(self.xsrf_token)
            + '"/>'
        )
B
Bret Taylor 已提交
1631

R
Robin Roth 已提交
1632 1633 1634
    def static_url(
        self, path: str, include_host: Optional[bool] = None, **kwargs: Any
    ) -> str:
B
Bret Taylor 已提交
1635 1636
        """Returns a static URL for the given relative static file path.

1637
        This method requires you set the ``static_path`` setting in your
B
Bret Taylor 已提交
1638 1639 1640
        application (which specifies the root directory of your static
        files).

1641 1642 1643 1644 1645 1646
        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 已提交
1647

1648 1649 1650 1651 1652
        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.
1653

B
Bret Taylor 已提交
1654 1655
        """
        self.require_setting("static_path", "static_url")
B
Ben Darnell 已提交
1656 1657 1658
        get_url = self.settings.get(
            "static_handler_class", StaticFileHandler
        ).make_static_url
1659 1660 1661 1662 1663

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

        if include_host:
1664
            base = self.request.protocol + "://" + self.request.host
B
Bret Taylor 已提交
1665
        else:
1666
            base = ""
1667

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

B
Ben Darnell 已提交
1670
    def require_setting(self, name: str, feature: str = "this feature") -> None:
B
Bret Taylor 已提交
1671 1672
        """Raises an exception if the given app setting is not defined."""
        if not self.application.settings.get(name):
B
Ben Darnell 已提交
1673 1674 1675 1676
            raise Exception(
                "You must define the '%s' setting in your "
                "application to use %s" % (name, feature)
            )
B
Bret Taylor 已提交
1677

B
Ben Darnell 已提交
1678
    def reverse_url(self, name: str, *args: Any) -> str:
1679
        """Alias for `Application.reverse_url`."""
1680 1681
        return self.application.reverse_url(name, *args)

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

1685 1686
        By default uses a hash of the content written so far.

1687 1688 1689 1690 1691 1692 1693 1694
        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 已提交
1695
    def set_etag_header(self) -> None:
1696 1697 1698
        """Sets the response's Etag header using ``self.compute_etag()``.

        Note: no header will be set if ``compute_etag()`` returns ``None``.
1699 1700

        This method is called automatically when the request is finished.
1701 1702 1703 1704 1705
        """
        etag = self.compute_etag()
        if etag is not None:
            self.set_header("Etag", etag)

B
Ben Darnell 已提交
1706
    def check_etag_header(self) -> bool:
1707 1708 1709 1710 1711 1712 1713 1714 1715
        """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
1716 1717 1718 1719 1720 1721

        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.
1722
        """
1723
        computed_etag = utf8(self._headers.get("Etag", ""))
D
daftshady 已提交
1724 1725 1726
        # 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 已提交
1727
            rb'\*|(?:W/)?"[^"]*"', utf8(self.request.headers.get("If-None-Match", ""))
D
daftshady 已提交
1728
        )
1729 1730 1731 1732
        if not computed_etag or not etags:
            return False

        match = False
B
Ben Darnell 已提交
1733
        if etags[0] == b"*":
1734 1735 1736
            match = True
        else:
            # Use a weak comparison when comparing entity-tags.
B
Ben Darnell 已提交
1737
            def val(x: bytes) -> bytes:
B
Ben Darnell 已提交
1738
                return x[2:] if x.startswith(b"W/") else x
1739

1740 1741 1742 1743 1744
            for etag in etags:
                if val(etag) == val(computed_etag):
                    match = True
                    break
        return match
1745

1746
    async def _execute(
B
Ben Darnell 已提交
1747
        self, transforms: List["OutputTransform"], *args: bytes, **kwargs: bytes
1748
    ) -> None:
B
Bret Taylor 已提交
1749 1750
        """Executes this request with the given output transforms."""
        self._transforms = transforms
1751
        try:
B
Bret Taylor 已提交
1752 1753
            if self.request.method not in self.SUPPORTED_METHODS:
                raise HTTPError(405)
1754
            self.path_args = [self.decode_argument(arg) for arg in args]
B
Ben Darnell 已提交
1755 1756 1757
            self.path_kwargs = dict(
                (k, self.decode_argument(v, name=k)) for (k, v) in kwargs.items()
            )
B
Bret Taylor 已提交
1758 1759
            # If XSRF cookies are turned on, reject form submissions without
            # the proper cookie
B
Ben Darnell 已提交
1760 1761 1762 1763 1764
            if self.request.method not in (
                "GET",
                "HEAD",
                "OPTIONS",
            ) and self.application.settings.get("xsrf_cookies"):
B
Bret Taylor 已提交
1765
                self.check_xsrf_cookie()
1766

1767
            result = self.prepare()
1768
            if result is not None:
B
Ben Darnell 已提交
1769
                result = await result  # type: ignore
1770 1771 1772
            if self._prepared_future is not None:
                # Tell the Application we've finished with prepare()
                # and are ready for the body to arrive.
1773
                future_set_result_unless_cancelled(self._prepared_future, None)
1774 1775
            if self._finished:
                return
B
Bret Taylor 已提交
1776

1777 1778 1779 1780 1781
            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.
1782
                try:
1783
                    await self.request._body_future
1784 1785
                except iostream.StreamClosedError:
                    return
1786

1787
            method = getattr(self, self.request.method.lower())
1788
            result = method(*self.path_args, **self.path_kwargs)
1789
            if result is not None:
1790
                result = await result
1791 1792 1793
            if self._auto_finish and not self._finished:
                self.finish()
        except Exception as e:
1794 1795 1796 1797
            try:
                self._handle_request_exception(e)
            except Exception:
                app_log.error("Exception in exception handler", exc_info=True)
1798 1799 1800
            finally:
                # Unset result to avoid circular references
                result = None
B
Ben Darnell 已提交
1801
            if self._prepared_future is not None and not self._prepared_future.done():
1802 1803 1804 1805
                # 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)
1806

B
Ben Darnell 已提交
1807
    def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:
B
Ben Darnell 已提交
1808 1809 1810
        """Implement this method to handle streamed request data.

        Requires the `.stream_request_body` decorator.
B
Ben Darnell 已提交
1811 1812

        May be a coroutine for flow control.
B
Ben Darnell 已提交
1813 1814 1815
        """
        raise NotImplementedError()

B
Ben Darnell 已提交
1816
    def _log(self) -> None:
1817 1818 1819 1820 1821 1822 1823
        """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 已提交
1824

B
Ben Darnell 已提交
1825
    def _request_summary(self) -> str:
B
Ben Darnell 已提交
1826 1827 1828 1829 1830
        return "%s %s (%s)" % (
            self.request.method,
            self.request.uri,
            self.request.remote_ip,
        )
B
Bret Taylor 已提交
1831

B
Ben Darnell 已提交
1832
    def _handle_request_exception(self, e: BaseException) -> None:
1833 1834 1835
        if isinstance(e, Finish):
            # Not an error; just finish the request without logging.
            if not self._finished:
1836
                self.finish(*e.args)
1837
            return
1838 1839 1840 1841 1842 1843
        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)
1844 1845 1846 1847 1848
        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 已提交
1849
        if isinstance(e, HTTPError):
1850
            self.send_error(e.status_code, exc_info=sys.exc_info())
B
Bret Taylor 已提交
1851
        else:
1852
            self.send_error(500, exc_info=sys.exc_info())
B
Bret Taylor 已提交
1853

B
Ben Darnell 已提交
1854 1855
    def log_exception(
        self,
B
Ben Darnell 已提交
1856
        typ: "Optional[Type[BaseException]]",
B
Ben Darnell 已提交
1857 1858 1859
        value: Optional[BaseException],
        tb: Optional[TracebackType],
    ) -> None:
1860 1861 1862 1863 1864 1865
        """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).
1866 1867

        .. versionadded:: 3.1
1868 1869 1870 1871
        """
        if isinstance(value, HTTPError):
            if value.log_message:
                format = "%d %s: " + value.log_message
B
Ben Darnell 已提交
1872
                args = [value.status_code, self._request_summary()] + list(value.args)
1873 1874
                gen_log.warning(format, *args)
        else:
B
Ben Darnell 已提交
1875
            app_log.error(
B
Ben Darnell 已提交
1876 1877 1878
                "Uncaught exception %s\n%r",
                self._request_summary(),
                self.request,
B
Ben Darnell 已提交
1879
                exc_info=(typ, value, tb),  # type: ignore
B
Ben Darnell 已提交
1880 1881 1882
            )

    def _ui_module(self, name: str, module: Type["UIModule"]) -> Callable[..., str]:
1883
        def render(*args, **kwargs) -> str:  # type: ignore
B
Bret Taylor 已提交
1884
            if not hasattr(self, "_active_modules"):
B
Ben Darnell 已提交
1885
                self._active_modules = {}  # type: Dict[str, UIModule]
B
Bret Taylor 已提交
1886 1887 1888 1889
            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 已提交
1890

B
Bret Taylor 已提交
1891 1892
        return render

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

1896 1897 1898 1899
    def _clear_representation_headers(self) -> None:
        # 304 responses should not contain representation metadata
        # headers (defined in
        # https://tools.ietf.org/html/rfc7231#section-3.1)
1900
        # not explicitly allowed by
1901 1902
        # https://tools.ietf.org/html/rfc7232#section-4.1
        headers = ["Content-Encoding", "Content-Language", "Content-Type"]
1903 1904 1905
        for h in headers:
            self.clear_header(h)

B
Bret Taylor 已提交
1906

1907
_RequestHandlerType = TypeVar("_RequestHandlerType", bound=RequestHandler)
1908 1909


1910
def stream_request_body(cls: Type[_RequestHandlerType]) -> Type[_RequestHandlerType]:
1911 1912 1913
    """Apply to `RequestHandler` subclasses to enable streaming body support.

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

1915 1916 1917 1918 1919 1920 1921
    * `.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.
1922 1923 1924
    * ``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.
1925 1926 1927
    * The regular HTTP method (``post``, ``put``, etc) will be called after
      the entire body has been read.

1928
    See the `file receiver demo <https://github.com/tornadoweb/tornado/tree/stable/demos/file_upload/>`_
1929
    for example usage.
1930
    """  # noqa: E501
1931 1932 1933 1934 1935 1936
    if not issubclass(cls, RequestHandler):
        raise TypeError("expected subclass of RequestHandler, got %r", cls)
    cls._stream_request_body = True
    return cls


B
Ben Darnell 已提交
1937
def _has_stream_request_body(cls: Type[RequestHandler]) -> bool:
1938 1939
    if not issubclass(cls, RequestHandler):
        raise TypeError("expected subclass of RequestHandler, got %r", cls)
B
Ben Darnell 已提交
1940
    return cls._stream_request_body
1941 1942


B
Ben Darnell 已提交
1943
def removeslash(
B
Ben Darnell 已提交
1944
    method: Callable[..., Optional[Awaitable[None]]]
B
Ben Darnell 已提交
1945
) -> Callable[..., Optional[Awaitable[None]]]:
1946 1947
    """Use this decorator to remove trailing slashes from the request path.

1948
    For example, a request to ``/foo/`` would redirect to ``/foo`` with this
1949
    decorator. Your request handler mapping should use a regular expression
B
Ben Darnell 已提交
1950
    like ``r'/foo/*'`` in conjunction with using the decorator.
1951
    """
B
Ben Darnell 已提交
1952

1953
    @functools.wraps(method)
1954 1955
    def wrapper(  # type: ignore
        self: RequestHandler, *args, **kwargs
B
Ben Darnell 已提交
1956
    ) -> Optional[Awaitable[None]]:
1957
        if self.request.path.endswith("/"):
1958
            if self.request.method in ("GET", "HEAD"):
1959
                uri = self.request.path.rstrip("/")
1960
                if uri:  # don't try to redirect '/' to ''
1961 1962
                    if self.request.query:
                        uri += "?" + self.request.query
1963
                    self.redirect(uri, permanent=True)
B
Ben Darnell 已提交
1964
                    return None
1965 1966
            else:
                raise HTTPError(404)
1967
        return method(self, *args, **kwargs)
B
Ben Darnell 已提交
1968

1969 1970 1971
    return wrapper


B
Ben Darnell 已提交
1972
def addslash(
B
Ben Darnell 已提交
1973
    method: Callable[..., Optional[Awaitable[None]]]
B
Ben Darnell 已提交
1974
) -> Callable[..., Optional[Awaitable[None]]]:
1975 1976
    """Use this decorator to add a missing trailing slash to the request path.

1977
    For example, a request to ``/foo`` would redirect to ``/foo/`` with this
1978
    decorator. Your request handler mapping should use a regular expression
1979
    like ``r'/foo/?'`` in conjunction with using the decorator.
1980
    """
B
Ben Darnell 已提交
1981

1982
    @functools.wraps(method)
1983 1984
    def wrapper(  # type: ignore
        self: RequestHandler, *args, **kwargs
B
Ben Darnell 已提交
1985
    ) -> Optional[Awaitable[None]]:
1986
        if not self.request.path.endswith("/"):
1987
            if self.request.method in ("GET", "HEAD"):
1988
                uri = self.request.path + "/"
1989 1990
                if self.request.query:
                    uri += "?" + self.request.query
1991
                self.redirect(uri, permanent=True)
B
Ben Darnell 已提交
1992
                return None
1993 1994
            raise HTTPError(404)
        return method(self, *args, **kwargs)
B
Ben Darnell 已提交
1995

1996 1997 1998
    return wrapper


A
Andrey Sumin 已提交
1999
class _ApplicationRouter(ReversibleRuleRouter):
2000
    """Routing implementation used internally by `Application`.
A
Andrey Sumin 已提交
2001

2002
    Provides a binding between `Application` and `RequestHandler`.
A
Andrey Sumin 已提交
2003 2004
    This implementation extends `~.routing.ReversibleRuleRouter` in a couple of ways:
        * it allows to use `RequestHandler` subclasses as `~.routing.Rule` target and
2005 2006 2007
        * 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 已提交
2008 2009
    """

R
Robin Roth 已提交
2010 2011 2012
    def __init__(
        self, application: "Application", rules: Optional[_RuleList] = None
    ) -> None:
A
Andrey Sumin 已提交
2013 2014
        assert isinstance(application, Application)
        self.application = application
P
Poruri Sai Rahul 已提交
2015
        super().__init__(rules)
A
Andrey Sumin 已提交
2016

B
Ben Darnell 已提交
2017
    def process_rule(self, rule: Rule) -> Rule:
P
Poruri Sai Rahul 已提交
2018
        rule = super().process_rule(rule)
A
Andrey Sumin 已提交
2019 2020

        if isinstance(rule.target, (list, tuple)):
B
Ben Darnell 已提交
2021 2022
            rule.target = _ApplicationRouter(
                self.application, rule.target  # type: ignore
B
Ben Darnell 已提交
2023
            )
A
Andrey Sumin 已提交
2024 2025 2026

        return rule

B
Ben Darnell 已提交
2027 2028 2029
    def get_target_delegate(
        self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any
    ) -> Optional[httputil.HTTPMessageDelegate]:
A
Andrey Sumin 已提交
2030
        if isclass(target) and issubclass(target, RequestHandler):
B
Ben Darnell 已提交
2031 2032 2033
            return self.application.get_handler_delegate(
                request, target, **target_params
            )
A
Andrey Sumin 已提交
2034

P
Poruri Sai Rahul 已提交
2035
        return super().get_target_delegate(target, request, **target_params)
A
Andrey Sumin 已提交
2036 2037 2038


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

    Instances of this class are callable and can be passed directly to
B
Ben Darnell 已提交
2042
    HTTPServer to serve the application::
B
Bret Taylor 已提交
2043 2044 2045 2046 2047 2048 2049

        application = web.Application([
            (r"/", MainPageHandler),
        ])
        http_server = httpserver.HTTPServer(application)
        http_server.listen(8080)

A
Andrey Sumin 已提交
2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065
    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 已提交
2066

A
Andrey Sumin 已提交
2067 2068 2069
    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 已提交
2070

A
Andrey Sumin 已提交
2071 2072 2073 2074
    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.
2075

A
Andrey Sumin 已提交
2076 2077 2078
    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
2079 2080 2081
    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 已提交
2082 2083 2084 2085 2086

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

2087
    We support virtual hosts with the `add_handlers` method, which takes in
B
Ben Darnell 已提交
2088
    a host regular expression as the first argument::
B
Bret Taylor 已提交
2089 2090 2091 2092 2093

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

2094 2095 2096
    If there's no match for the current request's host, then ``default_host``
    parameter value is matched against host regular expressions.

2097 2098 2099 2100 2101

    .. warning::

       Applications that do not use TLS may be vulnerable to :ref:`DNS
       rebinding <dnsrebinding>` attacks. This attack is especially
B
Ben Darnell 已提交
2102
       relevant to applications that only listen on ``127.0.0.1`` or
2103 2104 2105 2106 2107
       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.

2108 2109 2110 2111 2112 2113 2114
    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.
2115

2116 2117
    .. versionchanged:: 4.5
       Integration with the new `tornado.routing` module.
2118

B
Bret Taylor 已提交
2119
    """
B
Ben Darnell 已提交
2120 2121 2122

    def __init__(
        self,
R
Robin Roth 已提交
2123 2124 2125
        handlers: Optional[_RuleList] = None,
        default_host: Optional[str] = None,
        transforms: Optional[List[Type["OutputTransform"]]] = None,
2126
        **settings: Any,
B
Ben Darnell 已提交
2127
    ) -> None:
B
Bret Taylor 已提交
2128
        if transforms is None:
B
Ben Darnell 已提交
2129
            self.transforms = []  # type: List[Type[OutputTransform]]
2130
            if settings.get("compress_response") or settings.get("gzip"):
2131
                self.transforms.append(GZipContentEncoding)
B
Bret Taylor 已提交
2132 2133 2134 2135
        else:
            self.transforms = transforms
        self.default_host = default_host
        self.settings = settings
B
Ben Darnell 已提交
2136 2137 2138 2139 2140
        self.ui_modules = {
            "linkify": _linkify,
            "xsrf_form_html": _xsrf_form_html,
            "Template": TemplateModule,
        }
B
Ben Darnell 已提交
2141
        self.ui_methods = {}  # type: Dict[str, Callable[..., str]]
B
Bret Taylor 已提交
2142 2143 2144 2145 2146
        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 已提交
2147 2148 2149 2150
            static_url_prefix = settings.get("static_url_prefix", "/static/")
            static_handler_class = settings.get(
                "static_handler_class", StaticFileHandler
            )
2151
            static_handler_args = settings.get("static_handler_args", {})
B
Ben Darnell 已提交
2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164
            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)
2165

A
Andrey Sumin 已提交
2166
        self.wildcard_router = _ApplicationRouter(self, handlers)
B
Ben Darnell 已提交
2167 2168 2169
        self.default_router = _ApplicationRouter(
            self, [Rule(AnyMatches(), self.wildcard_router)]
        )
A
Andrey Sumin 已提交
2170

2171
        # Automatically reload modified modules
B
Ben Darnell 已提交
2172
        if self.settings.get("autoreload"):
B
Ben Darnell 已提交
2173
            from tornado import autoreload
B
Ben Darnell 已提交
2174

B
Bret Taylor 已提交
2175
            autoreload.start()
2176

2177 2178 2179 2180 2181 2182 2183 2184 2185
    def listen(
        self,
        port: int,
        address: Optional[str] = None,
        *,
        family: socket.AddressFamily = socket.AF_UNSPEC,
        backlog: int = tornado.netutil._DEFAULT_BACKLOG,
        flags: Optional[int] = None,
        reuse_port: bool = False,
2186
        **kwargs: Any,
2187
    ) -> HTTPServer:
2188 2189
        """Starts an HTTP server for this application on the given port.

2190 2191 2192 2193 2194
        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
2195
        `.TCPServer.bind`/`.TCPServer.start` methods directly.
2196 2197

        Note that after calling this method you still need to call
2198 2199
        ``IOLoop.current().start()`` (or run within ``asyncio.run``) to start
        the server.
2200 2201 2202 2203 2204

        Returns the `.HTTPServer` object.

        .. versionchanged:: 4.3
           Now returns the `.HTTPServer` object.
2205 2206 2207 2208

        .. versionchanged:: 6.2
           Added support for new keyword arguments in `.TCPServer.listen`,
           including ``reuse_port``.
2209 2210
        """
        server = HTTPServer(self, **kwargs)
2211 2212 2213 2214 2215 2216 2217 2218
        server.listen(
            port,
            address=address,
            family=family,
            backlog=backlog,
            flags=flags,
            reuse_port=reuse_port,
        )
2219
        return server
2220

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

2224 2225
        Host patterns are processed sequentially in the order they were
        added. All matching patterns will be considered.
2226
        """
A
Andrey Sumin 已提交
2227
        host_matcher = HostMatches(host_pattern)
A
Andrey Sumin 已提交
2228
        rule = Rule(host_matcher, _ApplicationRouter(self, host_handlers))
A
Andrey Sumin 已提交
2229 2230 2231 2232

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

        if self.default_host is not None:
B
Ben Darnell 已提交
2233 2234 2235
            self.wildcard_router.add_rules(
                [(DefaultHostMatches(self, host_matcher.host_pattern), host_handlers)]
            )
B
Bret Taylor 已提交
2236

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

B
Ben Darnell 已提交
2240
    def _load_ui_methods(self, methods: Any) -> None:
2241
        if isinstance(methods, types.ModuleType):
B
Ben Darnell 已提交
2242
            self._load_ui_methods(dict((n, getattr(methods, n)) for n in dir(methods)))
B
Bret Taylor 已提交
2243
        elif isinstance(methods, list):
2244 2245
            for m in methods:
                self._load_ui_methods(m)
B
Bret Taylor 已提交
2246
        else:
2247
            for name, fn in methods.items():
B
Ben Darnell 已提交
2248 2249 2250 2251 2252
                if (
                    not name.startswith("_")
                    and hasattr(fn, "__call__")
                    and name[0].lower() == name[0]
                ):
B
Bret Taylor 已提交
2253 2254
                    self.ui_methods[name] = fn

B
Ben Darnell 已提交
2255
    def _load_ui_modules(self, modules: Any) -> None:
2256
        if isinstance(modules, types.ModuleType):
B
Ben Darnell 已提交
2257
            self._load_ui_modules(dict((n, getattr(modules, n)) for n in dir(modules)))
B
Bret Taylor 已提交
2258
        elif isinstance(modules, list):
2259 2260
            for m in modules:
                self._load_ui_modules(m)
B
Bret Taylor 已提交
2261 2262
        else:
            assert isinstance(modules, dict)
2263
            for name, cls in modules.items():
B
Bret Taylor 已提交
2264 2265 2266 2267 2268 2269
                try:
                    if issubclass(cls, UIModule):
                        self.ui_modules[name] = cls
                except TypeError:
                    pass

B
Ben Darnell 已提交
2270 2271 2272
    def __call__(
        self, request: httputil.HTTPServerRequest
    ) -> Optional[Awaitable[None]]:
2273
        # Legacy HTTPServer interface
A
Andrey Sumin 已提交
2274
        dispatcher = self.find_handler(request)
2275
        return dispatcher.execute()
B
Bret Taylor 已提交
2276

B
Ben Darnell 已提交
2277 2278 2279
    def find_handler(
        self, request: httputil.HTTPServerRequest, **kwargs: Any
    ) -> "_HandlerDelegate":
A
Andrey Sumin 已提交
2280 2281
        route = self.default_router.find_handler(request)
        if route is not None:
B
Ben Darnell 已提交
2282
            return cast("_HandlerDelegate", route)
A
Andrey Sumin 已提交
2283

B
Ben Darnell 已提交
2284
        if self.settings.get("default_handler_class"):
A
Andrey Sumin 已提交
2285 2286
            return self.get_handler_delegate(
                request,
B
Ben Darnell 已提交
2287 2288 2289 2290 2291 2292 2293 2294 2295 2296
                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],
R
Robin Roth 已提交
2297 2298 2299
        target_kwargs: Optional[Dict[str, Any]] = None,
        path_args: Optional[List[bytes]] = None,
        path_kwargs: Optional[Dict[str, bytes]] = None,
B
Ben Darnell 已提交
2300
    ) -> "_HandlerDelegate":
2301 2302 2303 2304 2305 2306 2307 2308 2309 2310
        """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 已提交
2311
        return _HandlerDelegate(
B
Ben Darnell 已提交
2312 2313
            self, request, target_class, target_kwargs, path_args, path_kwargs
        )
A
Andrey Sumin 已提交
2314

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

2318
        The handler must be added to the application as a named `URLSpec`.
2319

2320
        Args will be substituted for capturing groups in the `URLSpec` regex.
2321 2322
        They will be converted to strings if necessary, encoded as utf8,
        and url-escaped.
2323
        """
A
Andrey Sumin 已提交
2324 2325 2326 2327
        reversed_url = self.default_router.reverse_url(name, *args)
        if reversed_url is not None:
            return reversed_url

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

B
Ben Darnell 已提交
2330
    def log_request(self, handler: RequestHandler) -> None:
2331 2332 2333 2334 2335
        """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
2336
        ``log_function``.
2337 2338 2339 2340 2341
        """
        if "log_function" in self.settings:
            self.settings["log_function"](handler)
            return
        if handler.get_status() < 400:
2342
            log_method = access_log.info
2343
        elif handler.get_status() < 500:
2344
            log_method = access_log.warning
2345
        else:
2346
            log_method = access_log.error
2347
        request_time = 1000.0 * handler.request.request_time()
B
Ben Darnell 已提交
2348 2349 2350 2351 2352 2353
        log_method(
            "%d %s %.2fms",
            handler.get_status(),
            handler._request_summary(),
            request_time,
        )
2354 2355


A
Andrey Sumin 已提交
2356
class _HandlerDelegate(httputil.HTTPMessageDelegate):
B
Ben Darnell 已提交
2357 2358 2359 2360 2361 2362 2363 2364 2365
    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:
2366
        self.application = application
A
Andrey Sumin 已提交
2367 2368 2369 2370 2371 2372
        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 已提交
2373
        self.chunks = []  # type: List[bytes]
A
Andrey Sumin 已提交
2374
        self.stream_request_body = _has_stream_request_body(self.handler_class)
2375

B
Ben Darnell 已提交
2376 2377 2378 2379 2380
    def headers_received(
        self,
        start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine],
        headers: httputil.HTTPHeaders,
    ) -> Optional[Awaitable[None]]:
2381
        if self.stream_request_body:
B
Ben Darnell 已提交
2382
            self.request._body_future = Future()
2383
            return self.execute()
B
Ben Darnell 已提交
2384
        return None
2385

B
Ben Darnell 已提交
2386
    def data_received(self, data: bytes) -> Optional[Awaitable[None]]:
2387
        if self.stream_request_body:
2388
            return self.handler.data_received(data)
2389 2390
        else:
            self.chunks.append(data)
B
Ben Darnell 已提交
2391
            return None
2392

B
Ben Darnell 已提交
2393
    def finish(self) -> None:
2394
        if self.stream_request_body:
B
Ben Darnell 已提交
2395
            future_set_result_unless_cancelled(self.request._body_future, None)
2396
        else:
B
Ben Darnell 已提交
2397
            self.request.body = b"".join(self.chunks)
2398 2399
            self.request._parse_body()
            self.execute()
2400

B
Ben Darnell 已提交
2401
    def on_connection_close(self) -> None:
2402 2403 2404
        if self.stream_request_body:
            self.handler.on_connection_close()
        else:
B
Ben Darnell 已提交
2405
            self.chunks = None  # type: ignore
2406

B
Ben Darnell 已提交
2407
    def execute(self) -> Optional[Awaitable[None]]:
2408 2409 2410 2411 2412 2413 2414
        # 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 已提交
2415
        if not self.application.settings.get("static_hash_cache", True):
2416 2417 2418 2419
            static_handler_class = self.application.settings.get(
                "static_handler_class", StaticFileHandler
            )
            static_handler_class.reset()
2420

B
Ben Darnell 已提交
2421 2422 2423
        self.handler = self.handler_class(
            self.application, self.request, **self.handler_kwargs
        )
2424
        transforms = [t(self.request) for t in self.application.transforms]
2425 2426 2427

        if self.stream_request_body:
            self.handler._prepared_future = Future()
2428
        # Note that if an exception escapes handler._execute it will be
2429 2430
        # trapped in the Future it returns (which we are ignoring here,
        # leaving it to be logged when the Future is GC'd).
2431 2432
        # However, that shouldn't happen because _execute has a blanket
        # except handler, and we cannot easily access the IOLoop here to
2433 2434
        # call add_future (because of the requirement to remain compatible
        # with WSGI)
2435 2436 2437 2438
        fut = gen.convert_yielded(
            self.handler._execute(transforms, *self.path_args, **self.path_kwargs)
        )
        fut.add_done_callback(lambda f: f.result())
2439 2440 2441 2442 2443
        # 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

2444

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

2448 2449 2450 2451
    Raising an `HTTPError` is a convenient alternative to calling
    `RequestHandler.send_error` since it automatically ends the
    current function.

2452 2453 2454
    To customize the response sent with an `HTTPError`, override
    `RequestHandler.write_error`.

2455
    :arg int status_code: HTTP status code.  Must be listed in
2456 2457
        `httplib.responses <http.client.responses>` unless the ``reason``
        keyword argument is given.
B
Ben Darnell 已提交
2458
    :arg str log_message: Message to be written to the log for this error
2459 2460 2461
        (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 已提交
2462
    :arg str reason: Keyword-only argument.  The HTTP "reason" phrase
2463 2464 2465 2466
        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 已提交
2467 2468

    def __init__(
R
Robin Roth 已提交
2469 2470 2471 2472
        self,
        status_code: int = 500,
        log_message: Optional[str] = None,
        *args: Any,
2473
        **kwargs: Any,
B
Ben Darnell 已提交
2474
    ) -> None:
B
Bret Taylor 已提交
2475 2476 2477
        self.status_code = status_code
        self.log_message = log_message
        self.args = args
B
Ben Darnell 已提交
2478
        self.reason = kwargs.get("reason", None)
2479
        if log_message and not args:
B
Ben Darnell 已提交
2480
            self.log_message = log_message.replace("%", "%%")
B
Bret Taylor 已提交
2481

B
Ben Darnell 已提交
2482
    def __str__(self) -> str:
B
Bret Taylor 已提交
2483
        message = "HTTP %d: %s" % (
2484
            self.status_code,
B
Ben Darnell 已提交
2485 2486
            self.reason or httputil.responses.get(self.status_code, "Unknown"),
        )
B
Bret Taylor 已提交
2487 2488 2489 2490 2491 2492
        if self.log_message:
            return message + " (" + (self.log_message % self.args) + ")"
        else:
            return message


2493 2494 2495
class Finish(Exception):
    """An exception that ends the request without producing an error response.

2496 2497 2498 2499 2500 2501 2502 2503
    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()`.
2504 2505 2506 2507 2508 2509 2510 2511

    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()
2512 2513 2514 2515

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

2518 2519 2520
    pass


2521 2522 2523 2524 2525
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).
2526 2527

    .. versionadded:: 3.1
2528
    """
B
Ben Darnell 已提交
2529

B
Ben Darnell 已提交
2530
    def __init__(self, arg_name: str) -> None:
P
Poruri Sai Rahul 已提交
2531
        super().__init__(400, "Missing argument %s" % arg_name)
2532 2533 2534
        self.arg_name = arg_name


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

2538
    def initialize(self, status_code: int) -> None:
B
Bret Taylor 已提交
2539 2540
        self.set_status(status_code)

B
Ben Darnell 已提交
2541
    def prepare(self) -> None:
B
Bret Taylor 已提交
2542 2543
        raise HTTPError(self._status_code)

B
Ben Darnell 已提交
2544
    def check_xsrf_cookie(self) -> None:
2545 2546 2547 2548 2549
        # 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 已提交
2550 2551 2552 2553

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

2554
    You should provide the keyword argument ``url`` to the handler, e.g.::
B
Bret Taylor 已提交
2555 2556 2557 2558

        application = web.Application([
            (r"/oldpath", web.RedirectHandler, {"url": "/newpath"}),
        ])
2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574

    `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.
2575 2576 2577

    .. versionchanged:: 4.5
       Added support for substitutions into the destination URL.
B
Ben Darnell 已提交
2578 2579 2580 2581

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

2584
    def initialize(self, url: str, permanent: bool = True) -> None:
B
Bret Taylor 已提交
2585 2586
        self._url = url
        self._permanent = permanent
2587

2588 2589
    def get(self, *args: Any, **kwargs: Any) -> None:
        to_url = self._url.format(*args, **kwargs)
2590
        if self.request.query_arguments:
B
Ben Darnell 已提交
2591
            # TODO: figure out typing for the next line.
2592
            to_url = httputil.url_concat(
B
Ben Darnell 已提交
2593 2594 2595
                to_url,
                list(httputil.qs_to_qsl(self.request.query_arguments)),  # type: ignore
            )
2596
        self.redirect(to_url, permanent=self._permanent)
B
Bret Taylor 已提交
2597 2598 2599 2600 2601


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

2602 2603 2604 2605 2606 2607
    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 已提交
2608
    you would add a line to your application like::
B
Bret Taylor 已提交
2609 2610

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

2614 2615 2616 2617
    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
2618
    the ``path`` argument to the get() method (different than the constructor
2619
    argument above); see `URLSpec` for details.
B
Bret Taylor 已提交
2620

2621 2622 2623 2624 2625
    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``.

2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636
    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
2637
    HTML5 audio or video).
2638 2639 2640 2641 2642 2643 2644 2645 2646 2647

    **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``.

2648 2649 2650 2651 2652
    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.

2653 2654 2655 2656
    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 已提交
2657 2658 2659 2660
    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`.
2661 2662 2663

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

2666
    CACHE_MAX_AGE = 86400 * 365 * 10  # 10 years
2667

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

R
Robin Roth 已提交
2671
    def initialize(self, path: str, default_filename: Optional[str] = None) -> None:
2672
        self.root = path
2673
        self.default_filename = default_filename
B
Bret Taylor 已提交
2674

2675
    @classmethod
B
Ben Darnell 已提交
2676
    def reset(cls) -> None:
2677 2678 2679
        with cls._lock:
            cls._static_hashes = {}

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

2683
    async def get(self, path: str, include_body: bool = True) -> None:
2684 2685 2686
        # 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
2687
        absolute_path = self.get_absolute_path(self.root, self.path)
B
Ben Darnell 已提交
2688
        self.absolute_path = self.validate_absolute_path(self.root, absolute_path)
2689 2690
        if self.absolute_path is None:
            return
2691

2692 2693
        self.modified = self.get_modified_time()
        self.set_headers()
2694

2695
        if self.should_return_304():
2696 2697 2698 2699 2700 2701
            self.set_status(304)
            return

        request_range = None
        range_header = self.request.headers.get("Range")
        if range_header:
2702 2703
            # 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.
2704
            request_range = httputil._parse_request_range(range_header)
B
Bret Taylor 已提交
2705

2706
        size = self.get_content_size()
2707
        if request_range:
2708
            start, end = request_range
2709 2710 2711 2712 2713 2714 2715 2716
            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:
2717 2718
                # 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
2719 2720 2721 2722
                # 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.
2723 2724
                self.set_status(416)  # Range Not Satisfiable
                self.set_header("Content-Type", "text/plain")
B
Ben Darnell 已提交
2725
                self.set_header("Content-Range", "bytes */%s" % (size,))
2726
                return
2727 2728 2729 2730
            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
2731 2732 2733 2734
            # 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 已提交
2735 2736
            if size != (end or size) - (start or 0):
                self.set_status(206)  # Partial Content
B
Ben Darnell 已提交
2737 2738 2739
                self.set_header(
                    "Content-Range", httputil._get_content_range(start, end, size)
                )
2740
        else:
2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751
            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)
2752 2753 2754

        if include_body:
            content = self.get_content(self.absolute_path, start, end)
2755
            if isinstance(content, bytes):
2756 2757
                content = [content]
            for chunk in content:
2758 2759
                try:
                    self.write(chunk)
2760
                    await self.flush()
2761 2762
                except iostream.StreamClosedError:
                    return
2763
        else:
2764
            assert self.request.method == "HEAD"
2765

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

2769 2770 2771
        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).
2772 2773

        .. versionadded:: 3.1
2774
        """
B
Ben Darnell 已提交
2775
        assert self.absolute_path is not None
2776
        version_hash = self._get_cached_version(self.absolute_path)
2777 2778
        if not version_hash:
            return None
B
Ben Darnell 已提交
2779
        return '"%s"' % (version_hash,)
B
Bret Taylor 已提交
2780

B
Ben Darnell 已提交
2781
    def set_headers(self) -> None:
2782 2783 2784 2785
        """Sets the content and caching headers on the response.

        .. versionadded:: 3.1
        """
2786 2787
        self.set_header("Accept-Ranges", "bytes")
        self.set_etag_header()
2788

2789 2790
        if self.modified is not None:
            self.set_header("Last-Modified", self.modified)
B
Bret Taylor 已提交
2791

2792 2793 2794
        content_type = self.get_content_type()
        if content_type:
            self.set_header("Content-Type", content_type)
2795

B
Ben Darnell 已提交
2796
        cache_time = self.get_cache_time(self.path, self.modified, content_type)
2797
        if cache_time > 0:
B
Ben Darnell 已提交
2798 2799 2800 2801
            self.set_header(
                "Expires",
                datetime.datetime.utcnow() + datetime.timedelta(seconds=cache_time),
            )
2802 2803
            self.set_header("Cache-Control", "max-age=" + str(cache_time))

2804 2805
        self.set_extra_headers(self.path)

B
Ben Darnell 已提交
2806
    def should_return_304(self) -> bool:
2807 2808 2809 2810
        """Returns True if the headers indicate that we should return 304.

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

2815 2816 2817 2818
        # 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:
2819 2820 2821 2822 2823 2824
            if_since = email.utils.parsedate_to_datetime(ims_value)
            if if_since.tzinfo is None:
                if_since = if_since.replace(tzinfo=datetime.timezone.utc)
            assert self.modified is not None
            if if_since >= self.modified:
                return True
2825

2826
        return False
2827

2828
    @classmethod
B
Ben Darnell 已提交
2829
    def get_absolute_path(cls, root: str, path: str) -> str:
2830 2831 2832 2833
        """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).
2834 2835 2836 2837 2838

        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`.
2839 2840

        .. versionadded:: 3.1
2841 2842 2843 2844
        """
        abspath = os.path.abspath(os.path.join(root, path))
        return abspath

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

2848 2849 2850
        ``root`` is the configured path for the `StaticFileHandler`,
        and ``path`` is the result of `get_absolute_path`

2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861
        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``.
2862 2863

        .. versionadded:: 3.1
2864
        """
2865 2866 2867 2868
        # 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 已提交
2869 2870 2871 2872 2873 2874 2875 2876
        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
2877 2878
        # The trailing slash also needs to be temporarily added back
        # the requested path so a request to root/ will match.
2879
        if not (absolute_path + os.path.sep).startswith(root):
B
Ben Darnell 已提交
2880 2881
            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:
2882 2883 2884 2885
            # 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("/"):
2886 2887 2888 2889 2890 2891 2892 2893 2894
                if self.request.path.startswith("//"):
                    # A redirect with two initial slashes is a "protocol-relative" URL.
                    # This means the next path segment is treated as a hostname instead
                    # of a part of the path, making this effectively an open redirect.
                    # Reject paths starting with two slashes to prevent this.
                    # This is only reachable under certain configurations.
                    raise HTTPError(
                        403, "cannot redirect path with two initial slashes"
                    )
2895
                self.redirect(self.request.path + "/", permanent=True)
B
Ben Darnell 已提交
2896
                return None
2897 2898 2899 2900 2901 2902
            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
2903

2904
    @classmethod
B
Ben Darnell 已提交
2905
    def get_content(
R
Robin Roth 已提交
2906
        cls, abspath: str, start: Optional[int] = None, end: Optional[int] = None
B
Ben Darnell 已提交
2907
    ) -> Generator[bytes, None, None]:
2908
        """Retrieve the content of the requested resource which is located
2909 2910 2911 2912 2913 2914
        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 已提交
2915 2916 2917 2918

        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.
2919 2920

        .. versionadded:: 3.1
2921
        """
2922
        with open(abspath, "rb") as file:
2923 2924 2925
            if start is not None:
                file.seek(start)
            if end is not None:
B
Ben Darnell 已提交
2926
                remaining = end - (start or 0)  # type: Optional[int]
2927
            else:
B
Ben Darnell 已提交
2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941
                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 已提交
2942

2943
    @classmethod
B
Ben Darnell 已提交
2944
    def get_content_version(cls, abspath: str) -> str:
2945 2946 2947
        """Returns a version string for the resource at the given path.

        This class method may be overridden by subclasses.  The
2948
        default implementation is a SHA-512 hash of the file's contents.
2949 2950

        .. versionadded:: 3.1
2951 2952
        """
        data = cls.get_content(abspath)
2953
        hasher = hashlib.sha512()
2954
        if isinstance(data, bytes):
B
Ben Darnell 已提交
2955 2956 2957 2958 2959
            hasher.update(data)
        else:
            for chunk in data:
                hasher.update(chunk)
        return hasher.hexdigest()
2960

B
Ben Darnell 已提交
2961 2962
    def _stat(self) -> os.stat_result:
        assert self.absolute_path is not None
B
Ben Darnell 已提交
2963
        if not hasattr(self, "_stat_result"):
2964 2965 2966
            self._stat_result = os.stat(self.absolute_path)
        return self._stat_result

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

2970
        This method may be overridden by subclasses.
2971 2972

        .. versionadded:: 3.1
2973

2974
        .. versionchanged:: 4.0
2975 2976
           This method is now always called, instead of only when
           partial results are requested.
2977 2978
        """
        stat_result = self._stat()
B
Ben Darnell 已提交
2979
        return stat_result.st_size
2980

B
Ben Darnell 已提交
2981
    def get_modified_time(self) -> Optional[datetime.datetime]:
2982 2983 2984 2985
        """Returns the time that ``self.absolute_path`` was last modified.

        May be overridden in subclasses.  Should return a `~datetime.datetime`
        object or None.
2986 2987

        .. versionadded:: 3.1
2988 2989 2990 2991

        .. versionchanged:: 6.4
           Now returns an aware datetime object instead of a naive one.
           Subclasses that override this method may return either kind.
2992
        """
2993
        stat_result = self._stat()
B
Ben Darnell 已提交
2994 2995 2996 2997 2998 2999 3000 3001
        # 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.
3002 3003 3004
        modified = datetime.datetime.fromtimestamp(
            int(stat_result.st_mtime), datetime.timezone.utc
        )
3005 3006
        return modified

B
Ben Darnell 已提交
3007
    def get_content_type(self) -> str:
3008 3009 3010 3011
        """Returns the ``Content-Type`` header to be used for this request.

        .. versionadded:: 3.1
        """
B
Ben Darnell 已提交
3012
        assert self.absolute_path is not None
3013
        mime_type, encoding = mimetypes.guess_type(self.absolute_path)
3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026
        # 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 已提交
3027

B
Ben Darnell 已提交
3028
    def set_extra_headers(self, path: str) -> None:
B
Ben Darnell 已提交
3029 3030
        """For subclass to add extra headers to the response"""
        pass
3031

B
Ben Darnell 已提交
3032 3033 3034
    def get_cache_time(
        self, path: str, modified: Optional[datetime.datetime], mime_type: str
    ) -> int:
3035 3036
        """Override to customize cache control behavior.

3037 3038 3039 3040
        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).
3041 3042

        By default returns cache expiry of 10 years for resources requested
3043
        with ``v`` argument.
3044 3045
        """
        return self.CACHE_MAX_AGE if "v" in self.request.arguments else 0
3046

3047
    @classmethod
B
Ben Darnell 已提交
3048 3049 3050
    def make_static_url(
        cls, settings: Dict[str, Any], path: str, include_version: bool = True
    ) -> str:
3051 3052
        """Constructs a versioned url for the given path.

3053 3054 3055
        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
3056 3057 3058
        ``make_static_url(cls, settings, path)``; other keyword
        arguments may be passed through `~RequestHandler.static_url`
        but are not standard.
3059

3060 3061 3062
        ``settings`` is the `Application.settings` dictionary.  ``path``
        is the static path being requested.  The url returned should be
        relative to the current host.
3063 3064 3065 3066

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

3068
        """
B
Ben Darnell 已提交
3069
        url = settings.get("static_url_prefix", "/static/") + path
3070 3071 3072
        if not include_version:
            return url

3073
        version_hash = cls.get_version(settings, path)
3074 3075 3076
        if not version_hash:
            return url

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

B
Ben Darnell 已提交
3079
    def parse_url_path(self, url_path: str) -> str:
3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090
        """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
3091 3092

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

        ``settings`` is the `Application.settings` dictionary and ``path``
3097
        is the relative location of the requested asset on the filesystem.
3098 3099
        The returned value should be a string, or ``None`` if no version
        could be determined.
3100

3101 3102 3103 3104
        .. 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.
3105
        """
B
Ben Darnell 已提交
3106
        abs_path = cls.get_absolute_path(settings["static_path"], path)
3107 3108 3109
        return cls._get_cached_version(abs_path)

    @classmethod
B
Ben Darnell 已提交
3110
    def _get_cached_version(cls, abs_path: str) -> Optional[str]:
3111 3112 3113 3114
        with cls._lock:
            hashes = cls._static_hashes
            if abs_path not in hashes:
                try:
3115
                    hashes[abs_path] = cls.get_content_version(abs_path)
3116
                except Exception:
3117
                    gen_log.error("Could not open static file %r", abs_path)
3118 3119
                    hashes[abs_path] = None
            hsh = hashes.get(abs_path)
3120
            if hsh:
3121
                return hsh
3122
        return None
3123

B
Bret Taylor 已提交
3124

3125
class FallbackHandler(RequestHandler):
3126
    """A `RequestHandler` that wraps another HTTP server callback.
3127

3128
    The fallback is a callable object that accepts an
B
Ben Darnell 已提交
3129
    `~.httputil.HTTPServerRequest`, such as an `Application` or
3130 3131 3132
    `tornado.wsgi.WSGIContainer`.  This is most useful to use both
    Tornado ``RequestHandlers`` and WSGI in the same server.  Typical
    usage::
B
Ben Darnell 已提交
3133

3134 3135 3136 3137
        wsgi_app = tornado.wsgi.WSGIContainer(
            django.core.handlers.wsgi.WSGIHandler())
        application = tornado.web.Application([
            (r"/foo", FooHandler),
S
Shinichi Hemmi 已提交
3138
            (r".*", FallbackHandler, dict(fallback=wsgi_app)),
3139 3140
        ])
    """
B
Ben Darnell 已提交
3141

3142
    def initialize(
B
Ben Darnell 已提交
3143 3144
        self, fallback: Callable[[httputil.HTTPServerRequest], None]
    ) -> None:
3145 3146
        self.fallback = fallback

B
Ben Darnell 已提交
3147
    def prepare(self) -> None:
3148 3149
        self.fallback(self.request)
        self._finished = True
3150
        self.on_finish()
3151 3152


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

3156 3157 3158
    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 已提交
3159
    """
B
Ben Darnell 已提交
3160

B
Ben Darnell 已提交
3161
    def __init__(self, request: httputil.HTTPServerRequest) -> None:
B
Bret Taylor 已提交
3162 3163
        pass

B
Ben Darnell 已提交
3164
    def transform_first_chunk(
B
Ben Darnell 已提交
3165 3166 3167 3168 3169
        self,
        status_code: int,
        headers: httputil.HTTPHeaders,
        chunk: bytes,
        finishing: bool,
B
Ben Darnell 已提交
3170
    ) -> Tuple[int, httputil.HTTPHeaders, bytes]:
3171
        return status_code, headers, chunk
B
Bret Taylor 已提交
3172

B
Ben Darnell 已提交
3173
    def transform_chunk(self, chunk: bytes, finishing: bool) -> bytes:
3174
        return chunk
B
Bret Taylor 已提交
3175

3176 3177 3178 3179 3180

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 已提交
3181

3182
    .. versionchanged:: 4.0
B
Ben Darnell 已提交
3183 3184 3185
        Now compresses all mime types beginning with ``text/``, instead
        of just a whitelist. (the whitelist is still used for certain
        non-text mime types).
3186
    """
B
Ben Darnell 已提交
3187

3188 3189
    # Whitelist of compressible mime types (in addition to any types
    # beginning with "text/").
B
Ben Darnell 已提交
3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200
    CONTENT_TYPES = set(
        [
            "application/javascript",
            "application/x-javascript",
            "application/xml",
            "application/atom+xml",
            "application/json",
            "application/xhtml+xml",
            "image/svg+xml",
        ]
    )
3201 3202 3203 3204 3205 3206 3207 3208 3209 3210
    # 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
3211

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

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

B
Ben Darnell 已提交
3218
    def transform_first_chunk(
B
Ben Darnell 已提交
3219 3220 3221 3222 3223
        self,
        status_code: int,
        headers: httputil.HTTPHeaders,
        chunk: bytes,
        finishing: bool,
B
Ben Darnell 已提交
3224
    ) -> Tuple[int, httputil.HTTPHeaders, bytes]:
3225
        # TODO: can/should this type be inherited from the superclass?
B
Ben Darnell 已提交
3226 3227
        if "Vary" in headers:
            headers["Vary"] += ", Accept-Encoding"
3228
        else:
B
Ben Darnell 已提交
3229
            headers["Vary"] = "Accept-Encoding"
3230
        if self._gzipping:
3231
            ctype = _unicode(headers.get("Content-Type", "")).split(";")[0]
B
Ben Darnell 已提交
3232 3233 3234 3235 3236
            self._gzipping = (
                self._compressible_type(ctype)
                and (not finishing or len(chunk) >= self.MIN_LENGTH)
                and ("Content-Encoding" not in headers)
            )
3237 3238
        if self._gzipping:
            headers["Content-Encoding"] = "gzip"
3239
            self._gzip_value = BytesIO()
B
Ben Darnell 已提交
3240 3241 3242
            self._gzip_file = gzip.GzipFile(
                mode="w", fileobj=self._gzip_value, compresslevel=self.GZIP_LEVEL
            )
3243 3244
            chunk = self.transform_chunk(chunk, finishing)
            if "Content-Length" in headers:
3245 3246 3247 3248 3249 3250 3251 3252
                # 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"]
3253
        return status_code, headers, chunk
3254

B
Ben Darnell 已提交
3255
    def transform_chunk(self, chunk: bytes, finishing: bool) -> bytes:
3256 3257 3258 3259 3260 3261 3262
        if self._gzipping:
            self._gzip_file.write(chunk)
            if finishing:
                self._gzip_file.close()
            else:
                self._gzip_file.flush()
            chunk = self._gzip_value.getvalue()
3263 3264
            self._gzip_value.truncate(0)
            self._gzip_value.seek(0)
3265
        return chunk
B
Bret Taylor 已提交
3266 3267


B
Ben Darnell 已提交
3268
def authenticated(
B
Ben Darnell 已提交
3269
    method: Callable[..., Optional[Awaitable[None]]]
B
Ben Darnell 已提交
3270
) -> Callable[..., Optional[Awaitable[None]]]:
3271 3272 3273 3274
    """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>`.
3275 3276 3277 3278 3279

    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.
3280
    """
B
Ben Darnell 已提交
3281

B
Bret Taylor 已提交
3282
    @functools.wraps(method)
3283 3284
    def wrapper(  # type: ignore
        self: RequestHandler, *args, **kwargs
B
Ben Darnell 已提交
3285
    ) -> Optional[Awaitable[None]]:
B
Bret Taylor 已提交
3286
        if not self.current_user:
3287
            if self.request.method in ("GET", "HEAD"):
B
Bret Taylor 已提交
3288 3289
                url = self.get_login_url()
                if "?" not in url:
3290
                    if urllib.parse.urlsplit(url).scheme:
3291 3292 3293
                        # if login url is absolute, make next absolute too
                        next_url = self.request.full_url()
                    else:
B
Ben Darnell 已提交
3294
                        assert self.request.uri is not None
3295
                        next_url = self.request.uri
3296
                    url += "?" + urlencode(dict(next=next_url))
B
Bret Taylor 已提交
3297
                self.redirect(url)
B
Ben Darnell 已提交
3298
                return None
B
Bret Taylor 已提交
3299 3300
            raise HTTPError(403)
        return method(self, *args, **kwargs)
B
Ben Darnell 已提交
3301

B
Bret Taylor 已提交
3302 3303 3304 3305
    return wrapper


class UIModule(object):
3306
    """A re-usable, modular UI unit on a page.
B
Bret Taylor 已提交
3307 3308 3309 3310

    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 已提交
3311 3312

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

B
Ben Darnell 已提交
3315
    def __init__(self, handler: RequestHandler) -> None:
B
Bret Taylor 已提交
3316 3317 3318 3319 3320
        self.handler = handler
        self.request = handler.request
        self.ui = handler.ui
        self.locale = handler.locale

3321
    @property
B
Ben Darnell 已提交
3322
    def current_user(self) -> Any:
3323 3324
        return self.handler.current_user

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

B
Ben Darnell 已提交
3329
    def embedded_javascript(self) -> Optional[str]:
G
Gonzalo Rafuls 已提交
3330 3331
        """Override to return a JavaScript string
        to be embedded in the page."""
B
Bret Taylor 已提交
3332 3333
        return None

B
Ben Darnell 已提交
3334
    def javascript_files(self) -> Optional[Iterable[str]]:
B
Ben Darnell 已提交
3335 3336 3337 3338 3339
        """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 已提交
3340 3341
        return None

B
Ben Darnell 已提交
3342
    def embedded_css(self) -> Optional[str]:
G
Gonzalo Rafuls 已提交
3343 3344
        """Override to return a CSS string
        that will be embedded in the page."""
B
Bret Taylor 已提交
3345 3346
        return None

B
Ben Darnell 已提交
3347
    def css_files(self) -> Optional[Iterable[str]]:
B
Ben Darnell 已提交
3348 3349 3350 3351 3352
        """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 已提交
3353 3354
        return None

B
Ben Darnell 已提交
3355
    def html_head(self) -> Optional[str]:
B
Ben Darnell 已提交
3356 3357 3358
        """Override to return an HTML string that will be put in the <head/>
        element.
        """
B
Bret Taylor 已提交
3359 3360
        return None

B
Ben Darnell 已提交
3361
    def html_body(self) -> Optional[str]:
B
Ben Darnell 已提交
3362 3363 3364
        """Override to return an HTML string that will be put at the end of
        the <body/> element.
        """
3365 3366
        return None

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

3371

3372
class _linkify(UIModule):
B
Ben Darnell 已提交
3373
    def render(self, text: str, **kwargs: Any) -> str:  # type: ignore
3374 3375
        return escape.linkify(text, **kwargs)

3376

3377
class _xsrf_form_html(UIModule):
B
Ben Darnell 已提交
3378
    def render(self) -> str:  # type: ignore
3379 3380
        return self.handler.xsrf_form_html()

3381

3382 3383 3384 3385 3386 3387 3388 3389
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
3390
    automatic JavaScript/CSS features.  Simply call set_resources
3391 3392 3393
    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
3394
    per instantiation of the template, so they must not depend on
3395 3396
    any arguments to the template.
    """
B
Ben Darnell 已提交
3397

B
Ben Darnell 已提交
3398
    def __init__(self, handler: RequestHandler) -> None:
P
Poruri Sai Rahul 已提交
3399
        super().__init__(handler)
3400
        # keep resources in both a list and a dict to preserve order
B
Ben Darnell 已提交
3401 3402
        self._resource_list = []  # type: List[Dict[str, Any]]
        self._resource_dict = {}  # type: Dict[str, Dict[str, Any]]
3403

B
Ben Darnell 已提交
3404
    def render(self, path: str, **kwargs: Any) -> bytes:  # type: ignore
3405
        def set_resources(**kwargs) -> str:  # type: ignore
3406 3407 3408 3409 3410
            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 已提交
3411 3412 3413 3414
                    raise ValueError(
                        "set_resources called with different "
                        "resources for the same template"
                    )
3415
            return ""
B
Ben Darnell 已提交
3416 3417

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

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

B
Ben Darnell 已提交
3422
    def embedded_javascript(self) -> str:
3423 3424
        return "\n".join(self._get_resources("embedded_javascript"))

B
Ben Darnell 已提交
3425
    def javascript_files(self) -> Iterable[str]:
3426 3427
        result = []
        for f in self._get_resources("javascript_files"):
3428
            if isinstance(f, (unicode_type, bytes)):
3429 3430 3431 3432 3433
                result.append(f)
            else:
                result.extend(f)
        return result

B
Ben Darnell 已提交
3434
    def embedded_css(self) -> str:
3435 3436
        return "\n".join(self._get_resources("embedded_css"))

B
Ben Darnell 已提交
3437
    def css_files(self) -> Iterable[str]:
3438 3439
        result = []
        for f in self._get_resources("css_files"):
3440
            if isinstance(f, (unicode_type, bytes)):
3441 3442 3443 3444 3445
                result.append(f)
            else:
                result.extend(f)
        return result

B
Ben Darnell 已提交
3446
    def html_head(self) -> str:
3447 3448
        return "".join(self._get_resources("html_head"))

B
Ben Darnell 已提交
3449
    def html_body(self) -> str:
3450 3451 3452
        return "".join(self._get_resources("html_body"))


3453 3454
class _UIModuleNamespace(object):
    """Lazy namespace which creates UIModule proxies bound to a handler."""
B
Ben Darnell 已提交
3455 3456 3457 3458

    def __init__(
        self, handler: RequestHandler, ui_modules: Dict[str, Type[UIModule]]
    ) -> None:
3459 3460 3461
        self.handler = handler
        self.ui_modules = ui_modules

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

B
Ben Darnell 已提交
3465
    def __getattr__(self, key: str) -> Callable[..., str]:
3466 3467 3468 3469 3470 3471
        try:
            return self[key]
        except KeyError as e:
            raise AttributeError(str(e))


B
Ben Darnell 已提交
3472 3473 3474 3475
def create_signed_value(
    secret: _CookieSecretTypes,
    name: str,
    value: Union[str, bytes],
R
Robin Roth 已提交
3476 3477 3478
    version: Optional[int] = None,
    clock: Optional[Callable[[], float]] = None,
    key_version: Optional[int] = None,
B
Ben Darnell 已提交
3479
) -> bytes:
3480
    if version is None:
3481
        version = DEFAULT_SIGNED_VALUE_VERSION
B
Ben Darnell 已提交
3482 3483
    if clock is None:
        clock = time.time
3484

B
Ben Darnell 已提交
3485
    timestamp = utf8(str(int(clock())))
3486
    value = base64.b64encode(utf8(value))
3487
    if version == 1:
B
Ben Darnell 已提交
3488
        assert not isinstance(secret, dict)
B
Ben Darnell 已提交
3489
        signature = _create_signature_v1(secret, name, value, timestamp)
3490 3491
        value = b"|".join([value, timestamp, signature])
        return value
B
Ben Darnell 已提交
3492 3493 3494 3495 3496 3497 3498 3499 3500 3501
    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)
3502
        # - key version (integer, default is 0)
B
Ben Darnell 已提交
3503 3504 3505 3506
        # - timestamp (integer seconds since epoch)
        # - name (not encoded; assumed to be ~alphanumeric)
        # - value (base64-encoded)
        # - signature (hex-encoded; no length prefix)
B
Ben Darnell 已提交
3507
        def format_field(s: Union[str, bytes]) -> bytes:
B
Ben Darnell 已提交
3508
            return utf8("%d:" % len(s)) + utf8(s)
B
Ben Darnell 已提交
3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519

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

        if isinstance(secret, dict):
B
Ben Darnell 已提交
3522 3523 3524 3525
            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"
3526 3527
            secret = secret[key_version]

B
Ben Darnell 已提交
3528 3529
        signature = _create_signature_v2(secret, to_sign)
        return to_sign + signature
3530 3531
    else:
        raise ValueError("Unsupported version %d" % version)
3532

B
Ben Darnell 已提交
3533

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

B
Ben Darnell 已提交
3538

B
Ben Darnell 已提交
3539
def _get_version(value: bytes) -> int:
3540
    # Figures out what version value is.  Version 1 did not include an
3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558
    # 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
3559 3560 3561
    return version


B
Ben Darnell 已提交
3562 3563 3564 3565
def decode_signed_value(
    secret: _CookieSecretTypes,
    name: str,
    value: Union[None, str, bytes],
3566
    max_age_days: float = 31,
R
Robin Roth 已提交
3567 3568
    clock: Optional[Callable[[], float]] = None,
    min_version: Optional[int] = None,
B
Ben Darnell 已提交
3569
) -> Optional[bytes]:
3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580
    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)
3581 3582

    if version < min_version:
B
Ben Darnell 已提交
3583
        return None
3584
    if version == 1:
B
Ben Darnell 已提交
3585
        assert not isinstance(secret, dict)
B
Ben Darnell 已提交
3586
        return _decode_signed_value_v1(secret, name, value, max_age_days, clock)
B
Ben Darnell 已提交
3587
    elif version == 2:
B
Ben Darnell 已提交
3588
        return _decode_signed_value_v2(secret, name, value, max_age_days, clock)
3589
    else:
B
Ben Darnell 已提交
3590
        return None
3591

B
Ben Darnell 已提交
3592

B
Ben Darnell 已提交
3593 3594 3595 3596
def _decode_signed_value_v1(
    secret: Union[str, bytes],
    name: str,
    value: bytes,
3597
    max_age_days: float,
B
Ben Darnell 已提交
3598 3599
    clock: Callable[[], float],
) -> Optional[bytes]:
3600
    parts = utf8(value).split(b"|")
3601 3602
    if len(parts) != 3:
        return None
B
Ben Darnell 已提交
3603
    signature = _create_signature_v1(secret, name, parts[0], parts[1])
B
Ben Darnell 已提交
3604
    if not hmac.compare_digest(parts[2], signature):
3605
        gen_log.warning("Invalid cookie signature %r", value)
3606 3607
        return None
    timestamp = int(parts[1])
B
Ben Darnell 已提交
3608
    if timestamp < clock() - max_age_days * 86400:
3609
        gen_log.warning("Expired cookie %r", value)
3610
        return None
B
Ben Darnell 已提交
3611
    if timestamp > clock() + 31 * 86400:
3612 3613 3614 3615 3616
        # _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 已提交
3617
        gen_log.warning("Cookie timestamp in future; possible tampering %r", value)
3618
        return None
3619
    if parts[1].startswith(b"0"):
3620
        gen_log.warning("Tampered cookie %r", value)
3621
        return None
3622 3623 3624 3625 3626
    try:
        return base64.b64decode(parts[0])
    except Exception:
        return None

3627

B
Ben Darnell 已提交
3628 3629
def _decode_fields_v2(value: bytes) -> Tuple[int, bytes, bytes, bytes, bytes]:
    def _consume_field(s: bytes) -> Tuple[bytes, bytes]:
B
Ben Darnell 已提交
3630
        length, _, rest = s.partition(b":")
B
Ben Darnell 已提交
3631 3632 3633 3634
        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 已提交
3635
        if rest[n : n + 1] != b"|":
B
Ben Darnell 已提交
3636
            raise ValueError("malformed v2 signed value field")
B
Ben Darnell 已提交
3637
        rest = rest[n + 1 :]
B
Ben Darnell 已提交
3638
        return field_value, rest
3639

B
Ben Darnell 已提交
3640
    rest = value[2:]  # remove version number
3641 3642 3643 3644 3645 3646 3647
    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 已提交
3648 3649 3650 3651
def _decode_signed_value_v2(
    secret: _CookieSecretTypes,
    name: str,
    value: bytes,
3652
    max_age_days: float,
B
Ben Darnell 已提交
3653 3654
    clock: Callable[[], float],
) -> Optional[bytes]:
B
Ben Darnell 已提交
3655
    try:
3656 3657 3658 3659 3660 3661 3662
        (
            key_version,
            timestamp_bytes,
            name_field,
            value_field,
            passed_sig,
        ) = _decode_fields_v2(value)
B
Ben Darnell 已提交
3663 3664
    except ValueError:
        return None
B
Ben Darnell 已提交
3665
    signed_string = value[: -len(passed_sig)]
3666 3667 3668 3669 3670 3671 3672

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

B
Ben Darnell 已提交
3673
    expected_sig = _create_signature_v2(secret, signed_string)
B
Ben Darnell 已提交
3674
    if not hmac.compare_digest(passed_sig, expected_sig):
B
Ben Darnell 已提交
3675 3676 3677
        return None
    if name_field != utf8(name):
        return None
B
Ben Darnell 已提交
3678
    timestamp = int(timestamp_bytes)
B
Ben Darnell 已提交
3679 3680 3681 3682 3683 3684 3685 3686 3687
    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 已提交
3688
def get_signature_key_version(value: Union[str, bytes]) -> Optional[int]:
3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700
    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 已提交
3701
def _create_signature_v1(secret: Union[str, bytes], *parts: Union[str, bytes]) -> bytes:
3702
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
3703 3704
    for part in parts:
        hash.update(utf8(part))
3705
    return utf8(hash.hexdigest())
3706

B
Ben Darnell 已提交
3707

B
Ben Darnell 已提交
3708
def _create_signature_v2(secret: Union[str, bytes], s: bytes) -> bytes:
B
Ben Darnell 已提交
3709 3710 3711
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
    hash.update(utf8(s))
    return utf8(hash.hexdigest())
B
Ben Darnell 已提交
3712

B
Ben Darnell 已提交
3713

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