1#
2# Copyright 2009 Facebook
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16"""``tornado.web`` provides a simple web framework with asynchronous
17features that allow it to scale to large numbers of open connections,
18making it ideal for `long polling
19<http://en.wikipedia.org/wiki/Push_technology#Long_polling>`_.
20
21Here is a simple "Hello, world" example app:
22
23.. testcode::
24
25    import tornado.ioloop
26    import tornado.web
27
28    class MainHandler(tornado.web.RequestHandler):
29        def get(self):
30            self.write("Hello, world")
31
32    if __name__ == "__main__":
33        application = tornado.web.Application([
34            (r"/", MainHandler),
35        ])
36        application.listen(8888)
37        tornado.ioloop.IOLoop.current().start()
38
39.. testoutput::
40   :hide:
41
42
43See the :doc:`guide` for additional information.
44
45Thread-safety notes
46-------------------
47
48In general, methods on `RequestHandler` and elsewhere in Tornado are
49not thread-safe. In particular, methods such as
50`~RequestHandler.write()`, `~RequestHandler.finish()`, and
51`~RequestHandler.flush()` must only be called from the main thread. If
52you use multiple threads it is important to use `.IOLoop.add_callback`
53to transfer control back to the main thread before finishing the
54request, or to limit your use of other threads to
55`.IOLoop.run_in_executor` and ensure that your callbacks running in
56the executor do not refer to Tornado objects.
57
58"""
59
60import base64
61import binascii
62import datetime
63import email.utils
64import functools
65import gzip
66import hashlib
67import hmac
68import http.cookies
69from inspect import isclass
70from io import BytesIO
71import mimetypes
72import numbers
73import os.path
74import re
75import sys
76import threading
77import time
78import tornado
79import traceback
80import types
81import urllib.parse
82from urllib.parse import urlencode
83
84from tornado.concurrent import Future, future_set_result_unless_cancelled
85from tornado import escape
86from tornado import gen
87from tornado.httpserver import HTTPServer
88from tornado import httputil
89from tornado import iostream
90import tornado.locale
91from tornado import locale
92from tornado.log import access_log, app_log, gen_log
93from tornado import template
94from tornado.escape import utf8, _unicode
95from tornado.routing import (
96    AnyMatches,
97    DefaultHostMatches,
98    HostMatches,
99    ReversibleRouter,
100    Rule,
101    ReversibleRuleRouter,
102    URLSpec,
103    _RuleList,
104)
105from tornado.util import ObjectDict, unicode_type, _websocket_mask
106
107url = URLSpec
108
109from typing import (
110    Dict,
111    Any,
112    Union,
113    Optional,
114    Awaitable,
115    Tuple,
116    List,
117    Callable,
118    Iterable,
119    Generator,
120    Type,
121    cast,
122    overload,
123)
124from types import TracebackType
125import typing
126
127if typing.TYPE_CHECKING:
128    from typing import Set  # noqa: F401
129
130
131# The following types are accepted by RequestHandler.set_header
132# and related methods.
133_HeaderTypes = Union[bytes, unicode_type, int, numbers.Integral, datetime.datetime]
134
135_CookieSecretTypes = Union[str, bytes, Dict[int, str], Dict[int, bytes]]
136
137
138MIN_SUPPORTED_SIGNED_VALUE_VERSION = 1
139"""The oldest signed value version supported by this version of Tornado.
140
141Signed values older than this version cannot be decoded.
142
143.. versionadded:: 3.2.1
144"""
145
146MAX_SUPPORTED_SIGNED_VALUE_VERSION = 2
147"""The newest signed value version supported by this version of Tornado.
148
149Signed values newer than this version cannot be decoded.
150
151.. versionadded:: 3.2.1
152"""
153
154DEFAULT_SIGNED_VALUE_VERSION = 2
155"""The signed value version produced by `.RequestHandler.create_signed_value`.
156
157May be overridden by passing a ``version`` keyword argument.
158
159.. versionadded:: 3.2.1
160"""
161
162DEFAULT_SIGNED_VALUE_MIN_VERSION = 1
163"""The oldest signed value accepted by `.RequestHandler.get_secure_cookie`.
164
165May be overridden by passing a ``min_version`` keyword argument.
166
167.. versionadded:: 3.2.1
168"""
169
170
171class _ArgDefaultMarker:
172    pass
173
174
175_ARG_DEFAULT = _ArgDefaultMarker()
176
177
178class RequestHandler(object):
179    """Base class for HTTP request handlers.
180
181    Subclasses must define at least one of the methods defined in the
182    "Entry points" section below.
183
184    Applications should not construct `RequestHandler` objects
185    directly and subclasses should not override ``__init__`` (override
186    `~RequestHandler.initialize` instead).
187
188    """
189
190    SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT", "OPTIONS")
191
192    _template_loaders = {}  # type: Dict[str, template.BaseLoader]
193    _template_loader_lock = threading.Lock()
194    _remove_control_chars_regex = re.compile(r"[\x00-\x08\x0e-\x1f]")
195
196    _stream_request_body = False
197
198    # Will be set in _execute.
199    _transforms = None  # type: List[OutputTransform]
200    path_args = None  # type: List[str]
201    path_kwargs = None  # type: Dict[str, str]
202
203    def __init__(
204        self,
205        application: "Application",
206        request: httputil.HTTPServerRequest,
207        **kwargs: Any
208    ) -> None:
209        super().__init__()
210
211        self.application = application
212        self.request = request
213        self._headers_written = False
214        self._finished = False
215        self._auto_finish = True
216        self._prepared_future = None
217        self.ui = ObjectDict(
218            (n, self._ui_method(m)) for n, m in application.ui_methods.items()
219        )
220        # UIModules are available as both `modules` and `_tt_modules` in the
221        # template namespace.  Historically only `modules` was available
222        # but could be clobbered by user additions to the namespace.
223        # The template {% module %} directive looks in `_tt_modules` to avoid
224        # possible conflicts.
225        self.ui["_tt_modules"] = _UIModuleNamespace(self, application.ui_modules)
226        self.ui["modules"] = self.ui["_tt_modules"]
227        self.clear()
228        assert self.request.connection is not None
229        # TODO: need to add set_close_callback to HTTPConnection interface
230        self.request.connection.set_close_callback(  # type: ignore
231            self.on_connection_close
232        )
233        self.initialize(**kwargs)  # type: ignore
234
235    def _initialize(self) -> None:
236        pass
237
238    initialize = _initialize  # type: Callable[..., None]
239    """Hook for subclass initialization. Called for each request.
240
241    A dictionary passed as the third argument of a ``URLSpec`` will be
242    supplied as keyword arguments to ``initialize()``.
243
244    Example::
245
246        class ProfileHandler(RequestHandler):
247            def initialize(self, database):
248                self.database = database
249
250            def get(self, username):
251                ...
252
253        app = Application([
254            (r'/user/(.*)', ProfileHandler, dict(database=database)),
255            ])
256    """
257
258    @property
259    def settings(self) -> Dict[str, Any]:
260        """An alias for `self.application.settings <Application.settings>`."""
261        return self.application.settings
262
263    def _unimplemented_method(self, *args: str, **kwargs: str) -> None:
264        raise HTTPError(405)
265
266    head = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
267    get = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
268    post = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
269    delete = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
270    patch = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
271    put = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
272    options = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
273
274    def prepare(self) -> Optional[Awaitable[None]]:
275        """Called at the beginning of a request before  `get`/`post`/etc.
276
277        Override this method to perform common initialization regardless
278        of the request method.
279
280        Asynchronous support: Use ``async def`` or decorate this method with
281        `.gen.coroutine` to make it asynchronous.
282        If this method returns an  ``Awaitable`` execution will not proceed
283        until the ``Awaitable`` is done.
284
285        .. versionadded:: 3.1
286           Asynchronous support.
287        """
288        pass
289
290    def on_finish(self) -> None:
291        """Called after the end of a request.
292
293        Override this method to perform cleanup, logging, etc.
294        This method is a counterpart to `prepare`.  ``on_finish`` may
295        not produce any output, as it is called after the response
296        has been sent to the client.
297        """
298        pass
299
300    def on_connection_close(self) -> None:
301        """Called in async handlers if the client closed the connection.
302
303        Override this to clean up resources associated with
304        long-lived connections.  Note that this method is called only if
305        the connection was closed during asynchronous processing; if you
306        need to do cleanup after every request override `on_finish`
307        instead.
308
309        Proxies may keep a connection open for a time (perhaps
310        indefinitely) after the client has gone away, so this method
311        may not be called promptly after the end user closes their
312        connection.
313        """
314        if _has_stream_request_body(self.__class__):
315            if not self.request._body_future.done():
316                self.request._body_future.set_exception(iostream.StreamClosedError())
317                self.request._body_future.exception()
318
319    def clear(self) -> None:
320        """Resets all headers and content for this response."""
321        self._headers = httputil.HTTPHeaders(
322            {
323                "Server": "TornadoServer/%s" % tornado.version,
324                "Content-Type": "text/html; charset=UTF-8",
325                "Date": httputil.format_timestamp(time.time()),
326            }
327        )
328        self.set_default_headers()
329        self._write_buffer = []  # type: List[bytes]
330        self._status_code = 200
331        self._reason = httputil.responses[200]
332
333    def set_default_headers(self) -> None:
334        """Override this to set HTTP headers at the beginning of the request.
335
336        For example, this is the place to set a custom ``Server`` header.
337        Note that setting such headers in the normal flow of request
338        processing may not do what you want, since headers may be reset
339        during error handling.
340        """
341        pass
342
343    def set_status(self, status_code: int, reason: Optional[str] = None) -> None:
344        """Sets the status code for our response.
345
346        :arg int status_code: Response status code.
347        :arg str reason: Human-readable reason phrase describing the status
348            code. If ``None``, it will be filled in from
349            `http.client.responses` or "Unknown".
350
351        .. versionchanged:: 5.0
352
353           No longer validates that the response code is in
354           `http.client.responses`.
355        """
356        self._status_code = status_code
357        if reason is not None:
358            self._reason = escape.native_str(reason)
359        else:
360            self._reason = httputil.responses.get(status_code, "Unknown")
361
362    def get_status(self) -> int:
363        """Returns the status code for our response."""
364        return self._status_code
365
366    def set_header(self, name: str, value: _HeaderTypes) -> None:
367        """Sets the given response header name and value.
368
369        All header values are converted to strings (`datetime` objects
370        are formatted according to the HTTP specification for the
371        ``Date`` header).
372
373        """
374        self._headers[name] = self._convert_header_value(value)
375
376    def add_header(self, name: str, value: _HeaderTypes) -> None:
377        """Adds the given response header and value.
378
379        Unlike `set_header`, `add_header` may be called multiple times
380        to return multiple values for the same header.
381        """
382        self._headers.add(name, self._convert_header_value(value))
383
384    def clear_header(self, name: str) -> None:
385        """Clears an outgoing header, undoing a previous `set_header` call.
386
387        Note that this method does not apply to multi-valued headers
388        set by `add_header`.
389        """
390        if name in self._headers:
391            del self._headers[name]
392
393    _INVALID_HEADER_CHAR_RE = re.compile(r"[\x00-\x1f]")
394
395    def _convert_header_value(self, value: _HeaderTypes) -> str:
396        # Convert the input value to a str. This type check is a bit
397        # subtle: The bytes case only executes on python 3, and the
398        # unicode case only executes on python 2, because the other
399        # cases are covered by the first match for str.
400        if isinstance(value, str):
401            retval = value
402        elif isinstance(value, bytes):  # py3
403            # Non-ascii characters in headers are not well supported,
404            # but if you pass bytes, use latin1 so they pass through as-is.
405            retval = value.decode("latin1")
406        elif isinstance(value, unicode_type):  # py2
407            # TODO: This is inconsistent with the use of latin1 above,
408            # but it's been that way for a long time. Should it change?
409            retval = escape.utf8(value)
410        elif isinstance(value, numbers.Integral):
411            # return immediately since we know the converted value will be safe
412            return str(value)
413        elif isinstance(value, datetime.datetime):
414            return httputil.format_timestamp(value)
415        else:
416            raise TypeError("Unsupported header value %r" % value)
417        # If \n is allowed into the header, it is possible to inject
418        # additional headers or split the request.
419        if RequestHandler._INVALID_HEADER_CHAR_RE.search(retval):
420            raise ValueError("Unsafe header value %r", retval)
421        return retval
422
423    @overload
424    def get_argument(self, name: str, default: str, strip: bool = True) -> str:
425        pass
426
427    @overload
428    def get_argument(  # noqa: F811
429        self, name: str, default: _ArgDefaultMarker = _ARG_DEFAULT, strip: bool = True
430    ) -> str:
431        pass
432
433    @overload
434    def get_argument(  # noqa: F811
435        self, name: str, default: None, strip: bool = True
436    ) -> Optional[str]:
437        pass
438
439    def get_argument(  # noqa: F811
440        self,
441        name: str,
442        default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT,
443        strip: bool = True,
444    ) -> Optional[str]:
445        """Returns the value of the argument with the given name.
446
447        If default is not provided, the argument is considered to be
448        required, and we raise a `MissingArgumentError` if it is missing.
449
450        If the argument appears in the request more than once, we return the
451        last value.
452
453        This method searches both the query and body arguments.
454        """
455        return self._get_argument(name, default, self.request.arguments, strip)
456
457    def get_arguments(self, name: str, strip: bool = True) -> List[str]:
458        """Returns a list of the arguments with the given name.
459
460        If the argument is not present, returns an empty list.
461
462        This method searches both the query and body arguments.
463        """
464
465        # Make sure `get_arguments` isn't accidentally being called with a
466        # positional argument that's assumed to be a default (like in
467        # `get_argument`.)
468        assert isinstance(strip, bool)
469
470        return self._get_arguments(name, self.request.arguments, strip)
471
472    def get_body_argument(
473        self,
474        name: str,
475        default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT,
476        strip: bool = True,
477    ) -> Optional[str]:
478        """Returns the value of the argument with the given name
479        from the request body.
480
481        If default is not provided, the argument is considered to be
482        required, and we raise a `MissingArgumentError` if it is missing.
483
484        If the argument appears in the url more than once, we return the
485        last value.
486
487        .. versionadded:: 3.2
488        """
489        return self._get_argument(name, default, self.request.body_arguments, strip)
490
491    def get_body_arguments(self, name: str, strip: bool = True) -> List[str]:
492        """Returns a list of the body arguments with the given name.
493
494        If the argument is not present, returns an empty list.
495
496        .. versionadded:: 3.2
497        """
498        return self._get_arguments(name, self.request.body_arguments, strip)
499
500    def get_query_argument(
501        self,
502        name: str,
503        default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT,
504        strip: bool = True,
505    ) -> Optional[str]:
506        """Returns the value of the argument with the given name
507        from the request query string.
508
509        If default is not provided, the argument is considered to be
510        required, and we raise a `MissingArgumentError` if it is missing.
511
512        If the argument appears in the url more than once, we return the
513        last value.
514
515        .. versionadded:: 3.2
516        """
517        return self._get_argument(name, default, self.request.query_arguments, strip)
518
519    def get_query_arguments(self, name: str, strip: bool = True) -> List[str]:
520        """Returns a list of the query arguments with the given name.
521
522        If the argument is not present, returns an empty list.
523
524        .. versionadded:: 3.2
525        """
526        return self._get_arguments(name, self.request.query_arguments, strip)
527
528    def _get_argument(
529        self,
530        name: str,
531        default: Union[None, str, _ArgDefaultMarker],
532        source: Dict[str, List[bytes]],
533        strip: bool = True,
534    ) -> Optional[str]:
535        args = self._get_arguments(name, source, strip=strip)
536        if not args:
537            if isinstance(default, _ArgDefaultMarker):
538                raise MissingArgumentError(name)
539            return default
540        return args[-1]
541
542    def _get_arguments(
543        self, name: str, source: Dict[str, List[bytes]], strip: bool = True
544    ) -> List[str]:
545        values = []
546        for v in source.get(name, []):
547            s = self.decode_argument(v, name=name)
548            if isinstance(s, unicode_type):
549                # Get rid of any weird control chars (unless decoding gave
550                # us bytes, in which case leave it alone)
551                s = RequestHandler._remove_control_chars_regex.sub(" ", s)
552            if strip:
553                s = s.strip()
554            values.append(s)
555        return values
556
557    def decode_argument(self, value: bytes, name: Optional[str] = None) -> str:
558        """Decodes an argument from the request.
559
560        The argument has been percent-decoded and is now a byte string.
561        By default, this method decodes the argument as utf-8 and returns
562        a unicode string, but this may be overridden in subclasses.
563
564        This method is used as a filter for both `get_argument()` and for
565        values extracted from the url and passed to `get()`/`post()`/etc.
566
567        The name of the argument is provided if known, but may be None
568        (e.g. for unnamed groups in the url regex).
569        """
570        try:
571            return _unicode(value)
572        except UnicodeDecodeError:
573            raise HTTPError(
574                400, "Invalid unicode in %s: %r" % (name or "url", value[:40])
575            )
576
577    @property
578    def cookies(self) -> Dict[str, http.cookies.Morsel]:
579        """An alias for
580        `self.request.cookies <.httputil.HTTPServerRequest.cookies>`."""
581        return self.request.cookies
582
583    def get_cookie(self, name: str, default: Optional[str] = None) -> Optional[str]:
584        """Returns the value of the request cookie with the given name.
585
586        If the named cookie is not present, returns ``default``.
587
588        This method only returns cookies that were present in the request.
589        It does not see the outgoing cookies set by `set_cookie` in this
590        handler.
591        """
592        if self.request.cookies is not None and name in self.request.cookies:
593            return self.request.cookies[name].value
594        return default
595
596    def set_cookie(
597        self,
598        name: str,
599        value: Union[str, bytes],
600        domain: Optional[str] = None,
601        expires: Optional[Union[float, Tuple, datetime.datetime]] = None,
602        path: str = "/",
603        expires_days: Optional[float] = None,
604        **kwargs: Any
605    ) -> None:
606        """Sets an outgoing cookie name/value with the given options.
607
608        Newly-set cookies are not immediately visible via `get_cookie`;
609        they are not present until the next request.
610
611        expires may be a numeric timestamp as returned by `time.time`,
612        a time tuple as returned by `time.gmtime`, or a
613        `datetime.datetime` object.
614
615        Additional keyword arguments are set on the cookies.Morsel
616        directly.
617        See https://docs.python.org/3/library/http.cookies.html#http.cookies.Morsel
618        for available attributes.
619        """
620        # The cookie library only accepts type str, in both python 2 and 3
621        name = escape.native_str(name)
622        value = escape.native_str(value)
623        if re.search(r"[\x00-\x20]", name + value):
624            # Don't let us accidentally inject bad stuff
625            raise ValueError("Invalid cookie %r: %r" % (name, value))
626        if not hasattr(self, "_new_cookie"):
627            self._new_cookie = (
628                http.cookies.SimpleCookie()
629            )  # type: http.cookies.SimpleCookie
630        if name in self._new_cookie:
631            del self._new_cookie[name]
632        self._new_cookie[name] = value
633        morsel = self._new_cookie[name]
634        if domain:
635            morsel["domain"] = domain
636        if expires_days is not None and not expires:
637            expires = datetime.datetime.utcnow() + datetime.timedelta(days=expires_days)
638        if expires:
639            morsel["expires"] = httputil.format_timestamp(expires)
640        if path:
641            morsel["path"] = path
642        for k, v in kwargs.items():
643            if k == "max_age":
644                k = "max-age"
645
646            # skip falsy values for httponly and secure flags because
647            # SimpleCookie sets them regardless
648            if k in ["httponly", "secure"] and not v:
649                continue
650
651            morsel[k] = v
652
653    def clear_cookie(
654        self, name: str, path: str = "/", domain: Optional[str] = None
655    ) -> None:
656        """Deletes the cookie with the given name.
657
658        Due to limitations of the cookie protocol, you must pass the same
659        path and domain to clear a cookie as were used when that cookie
660        was set (but there is no way to find out on the server side
661        which values were used for a given cookie).
662
663        Similar to `set_cookie`, the effect of this method will not be
664        seen until the following request.
665        """
666        expires = datetime.datetime.utcnow() - datetime.timedelta(days=365)
667        self.set_cookie(name, value="", path=path, expires=expires, domain=domain)
668
669    def clear_all_cookies(self, path: str = "/", domain: Optional[str] = None) -> None:
670        """Deletes all the cookies the user sent with this request.
671
672        See `clear_cookie` for more information on the path and domain
673        parameters.
674
675        Similar to `set_cookie`, the effect of this method will not be
676        seen until the following request.
677
678        .. versionchanged:: 3.2
679
680           Added the ``path`` and ``domain`` parameters.
681        """
682        for name in self.request.cookies:
683            self.clear_cookie(name, path=path, domain=domain)
684
685    def set_secure_cookie(
686        self,
687        name: str,
688        value: Union[str, bytes],
689        expires_days: Optional[float] = 30,
690        version: Optional[int] = None,
691        **kwargs: Any
692    ) -> None:
693        """Signs and timestamps a cookie so it cannot be forged.
694
695        You must specify the ``cookie_secret`` setting in your Application
696        to use this method. It should be a long, random sequence of bytes
697        to be used as the HMAC secret for the signature.
698
699        To read a cookie set with this method, use `get_secure_cookie()`.
700
701        Note that the ``expires_days`` parameter sets the lifetime of the
702        cookie in the browser, but is independent of the ``max_age_days``
703        parameter to `get_secure_cookie`.
704        A value of None limits the lifetime to the current browser session.
705
706        Secure cookies may contain arbitrary byte values, not just unicode
707        strings (unlike regular cookies)
708
709        Similar to `set_cookie`, the effect of this method will not be
710        seen until the following request.
711
712        .. versionchanged:: 3.2.1
713
714           Added the ``version`` argument.  Introduced cookie version 2
715           and made it the default.
716        """
717        self.set_cookie(
718            name,
719            self.create_signed_value(name, value, version=version),
720            expires_days=expires_days,
721            **kwargs
722        )
723
724    def create_signed_value(
725        self, name: str, value: Union[str, bytes], version: Optional[int] = None
726    ) -> bytes:
727        """Signs and timestamps a string so it cannot be forged.
728
729        Normally used via set_secure_cookie, but provided as a separate
730        method for non-cookie uses.  To decode a value not stored
731        as a cookie use the optional value argument to get_secure_cookie.
732
733        .. versionchanged:: 3.2.1
734
735           Added the ``version`` argument.  Introduced cookie version 2
736           and made it the default.
737        """
738        self.require_setting("cookie_secret", "secure cookies")
739        secret = self.application.settings["cookie_secret"]
740        key_version = None
741        if isinstance(secret, dict):
742            if self.application.settings.get("key_version") is None:
743                raise Exception("key_version setting must be used for secret_key dicts")
744            key_version = self.application.settings["key_version"]
745
746        return create_signed_value(
747            secret, name, value, version=version, key_version=key_version
748        )
749
750    def get_secure_cookie(
751        self,
752        name: str,
753        value: Optional[str] = None,
754        max_age_days: float = 31,
755        min_version: Optional[int] = None,
756    ) -> Optional[bytes]:
757        """Returns the given signed cookie if it validates, or None.
758
759        The decoded cookie value is returned as a byte string (unlike
760        `get_cookie`).
761
762        Similar to `get_cookie`, this method only returns cookies that
763        were present in the request. It does not see outgoing cookies set by
764        `set_secure_cookie` in this handler.
765
766        .. versionchanged:: 3.2.1
767
768           Added the ``min_version`` argument.  Introduced cookie version 2;
769           both versions 1 and 2 are accepted by default.
770        """
771        self.require_setting("cookie_secret", "secure cookies")
772        if value is None:
773            value = self.get_cookie(name)
774        return decode_signed_value(
775            self.application.settings["cookie_secret"],
776            name,
777            value,
778            max_age_days=max_age_days,
779            min_version=min_version,
780        )
781
782    def get_secure_cookie_key_version(
783        self, name: str, value: Optional[str] = None
784    ) -> Optional[int]:
785        """Returns the signing key version of the secure cookie.
786
787        The version is returned as int.
788        """
789        self.require_setting("cookie_secret", "secure cookies")
790        if value is None:
791            value = self.get_cookie(name)
792        if value is None:
793            return None
794        return get_signature_key_version(value)
795
796    def redirect(
797        self, url: str, permanent: bool = False, status: Optional[int] = None
798    ) -> None:
799        """Sends a redirect to the given (optionally relative) URL.
800
801        If the ``status`` argument is specified, that value is used as the
802        HTTP status code; otherwise either 301 (permanent) or 302
803        (temporary) is chosen based on the ``permanent`` argument.
804        The default is 302 (temporary).
805        """
806        if self._headers_written:
807            raise Exception("Cannot redirect after headers have been written")
808        if status is None:
809            status = 301 if permanent else 302
810        else:
811            assert isinstance(status, int) and 300 <= status <= 399
812        self.set_status(status)
813        self.set_header("Location", utf8(url))
814        self.finish()
815
816    def write(self, chunk: Union[str, bytes, dict]) -> None:
817        """Writes the given chunk to the output buffer.
818
819        To write the output to the network, use the `flush()` method below.
820
821        If the given chunk is a dictionary, we write it as JSON and set
822        the Content-Type of the response to be ``application/json``.
823        (if you want to send JSON as a different ``Content-Type``, call
824        ``set_header`` *after* calling ``write()``).
825
826        Note that lists are not converted to JSON because of a potential
827        cross-site security vulnerability.  All JSON output should be
828        wrapped in a dictionary.  More details at
829        http://haacked.com/archive/2009/06/25/json-hijacking.aspx/ and
830        https://github.com/facebook/tornado/issues/1009
831        """
832        if self._finished:
833            raise RuntimeError("Cannot write() after finish()")
834        if not isinstance(chunk, (bytes, unicode_type, dict)):
835            message = "write() only accepts bytes, unicode, and dict objects"
836            if isinstance(chunk, list):
837                message += (
838                    ". Lists not accepted for security reasons; see "
839                    + "http://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.write"  # noqa: E501
840                )
841            raise TypeError(message)
842        if isinstance(chunk, dict):
843            chunk = escape.json_encode(chunk)
844            self.set_header("Content-Type", "application/json; charset=UTF-8")
845        chunk = utf8(chunk)
846        self._write_buffer.append(chunk)
847
848    def render(self, template_name: str, **kwargs: Any) -> "Future[None]":
849        """Renders the template with the given arguments as the response.
850
851        ``render()`` calls ``finish()``, so no other output methods can be called
852        after it.
853
854        Returns a `.Future` with the same semantics as the one returned by `finish`.
855        Awaiting this `.Future` is optional.
856
857        .. versionchanged:: 5.1
858
859           Now returns a `.Future` instead of ``None``.
860        """
861        if self._finished:
862            raise RuntimeError("Cannot render() after finish()")
863        html = self.render_string(template_name, **kwargs)
864
865        # Insert the additional JS and CSS added by the modules on the page
866        js_embed = []
867        js_files = []
868        css_embed = []
869        css_files = []
870        html_heads = []
871        html_bodies = []
872        for module in getattr(self, "_active_modules", {}).values():
873            embed_part = module.embedded_javascript()
874            if embed_part:
875                js_embed.append(utf8(embed_part))
876            file_part = module.javascript_files()
877            if file_part:
878                if isinstance(file_part, (unicode_type, bytes)):
879                    js_files.append(_unicode(file_part))
880                else:
881                    js_files.extend(file_part)
882            embed_part = module.embedded_css()
883            if embed_part:
884                css_embed.append(utf8(embed_part))
885            file_part = module.css_files()
886            if file_part:
887                if isinstance(file_part, (unicode_type, bytes)):
888                    css_files.append(_unicode(file_part))
889                else:
890                    css_files.extend(file_part)
891            head_part = module.html_head()
892            if head_part:
893                html_heads.append(utf8(head_part))
894            body_part = module.html_body()
895            if body_part:
896                html_bodies.append(utf8(body_part))
897
898        if js_files:
899            # Maintain order of JavaScript files given by modules
900            js = self.render_linked_js(js_files)
901            sloc = html.rindex(b"</body>")
902            html = html[:sloc] + utf8(js) + b"\n" + html[sloc:]
903        if js_embed:
904            js_bytes = self.render_embed_js(js_embed)
905            sloc = html.rindex(b"</body>")
906            html = html[:sloc] + js_bytes + b"\n" + html[sloc:]
907        if css_files:
908            css = self.render_linked_css(css_files)
909            hloc = html.index(b"</head>")
910            html = html[:hloc] + utf8(css) + b"\n" + html[hloc:]
911        if css_embed:
912            css_bytes = self.render_embed_css(css_embed)
913            hloc = html.index(b"</head>")
914            html = html[:hloc] + css_bytes + b"\n" + html[hloc:]
915        if html_heads:
916            hloc = html.index(b"</head>")
917            html = html[:hloc] + b"".join(html_heads) + b"\n" + html[hloc:]
918        if html_bodies:
919            hloc = html.index(b"</body>")
920            html = html[:hloc] + b"".join(html_bodies) + b"\n" + html[hloc:]
921        return self.finish(html)
922
923    def render_linked_js(self, js_files: Iterable[str]) -> str:
924        """Default method used to render the final js links for the
925        rendered webpage.
926
927        Override this method in a sub-classed controller to change the output.
928        """
929        paths = []
930        unique_paths = set()  # type: Set[str]
931
932        for path in js_files:
933            if not is_absolute(path):
934                path = self.static_url(path)
935            if path not in unique_paths:
936                paths.append(path)
937                unique_paths.add(path)
938
939        return "".join(
940            '<script src="'
941            + escape.xhtml_escape(p)
942            + '" type="text/javascript"></script>'
943            for p in paths
944        )
945
946    def render_embed_js(self, js_embed: Iterable[bytes]) -> bytes:
947        """Default method used to render the final embedded js for the
948        rendered webpage.
949
950        Override this method in a sub-classed controller to change the output.
951        """
952        return (
953            b'<script type="text/javascript">\n//<![CDATA[\n'
954            + b"\n".join(js_embed)
955            + b"\n//]]>\n</script>"
956        )
957
958    def render_linked_css(self, css_files: Iterable[str]) -> str:
959        """Default method used to render the final css links for the
960        rendered webpage.
961
962        Override this method in a sub-classed controller to change the output.
963        """
964        paths = []
965        unique_paths = set()  # type: Set[str]
966
967        for path in css_files:
968            if not is_absolute(path):
969                path = self.static_url(path)
970            if path not in unique_paths:
971                paths.append(path)
972                unique_paths.add(path)
973
974        return "".join(
975            '<link href="' + escape.xhtml_escape(p) + '" '
976            'type="text/css" rel="stylesheet"/>'
977            for p in paths
978        )
979
980    def render_embed_css(self, css_embed: Iterable[bytes]) -> bytes:
981        """Default method used to render the final embedded css for the
982        rendered webpage.
983
984        Override this method in a sub-classed controller to change the output.
985        """
986        return b'<style type="text/css">\n' + b"\n".join(css_embed) + b"\n</style>"
987
988    def render_string(self, template_name: str, **kwargs: Any) -> bytes:
989        """Generate the given template with the given arguments.
990
991        We return the generated byte string (in utf8). To generate and
992        write a template as a response, use render() above.
993        """
994        # If no template_path is specified, use the path of the calling file
995        template_path = self.get_template_path()
996        if not template_path:
997            frame = sys._getframe(0)
998            web_file = frame.f_code.co_filename
999            while frame.f_code.co_filename == web_file:
1000                frame = frame.f_back
1001            assert frame.f_code.co_filename is not None
1002            template_path = os.path.dirname(frame.f_code.co_filename)
1003        with RequestHandler._template_loader_lock:
1004            if template_path not in RequestHandler._template_loaders:
1005                loader = self.create_template_loader(template_path)
1006                RequestHandler._template_loaders[template_path] = loader
1007            else:
1008                loader = RequestHandler._template_loaders[template_path]
1009        t = loader.load(template_name)
1010        namespace = self.get_template_namespace()
1011        namespace.update(kwargs)
1012        return t.generate(**namespace)
1013
1014    def get_template_namespace(self) -> Dict[str, Any]:
1015        """Returns a dictionary to be used as the default template namespace.
1016
1017        May be overridden by subclasses to add or modify values.
1018
1019        The results of this method will be combined with additional
1020        defaults in the `tornado.template` module and keyword arguments
1021        to `render` or `render_string`.
1022        """
1023        namespace = dict(
1024            handler=self,
1025            request=self.request,
1026            current_user=self.current_user,
1027            locale=self.locale,
1028            _=self.locale.translate,
1029            pgettext=self.locale.pgettext,
1030            static_url=self.static_url,
1031            xsrf_form_html=self.xsrf_form_html,
1032            reverse_url=self.reverse_url,
1033        )
1034        namespace.update(self.ui)
1035        return namespace
1036
1037    def create_template_loader(self, template_path: str) -> template.BaseLoader:
1038        """Returns a new template loader for the given path.
1039
1040        May be overridden by subclasses.  By default returns a
1041        directory-based loader on the given path, using the
1042        ``autoescape`` and ``template_whitespace`` application
1043        settings.  If a ``template_loader`` application setting is
1044        supplied, uses that instead.
1045        """
1046        settings = self.application.settings
1047        if "template_loader" in settings:
1048            return settings["template_loader"]
1049        kwargs = {}
1050        if "autoescape" in settings:
1051            # autoescape=None means "no escaping", so we have to be sure
1052            # to only pass this kwarg if the user asked for it.
1053            kwargs["autoescape"] = settings["autoescape"]
1054        if "template_whitespace" in settings:
1055            kwargs["whitespace"] = settings["template_whitespace"]
1056        return template.Loader(template_path, **kwargs)
1057
1058    def flush(self, include_footers: bool = False) -> "Future[None]":
1059        """Flushes the current output buffer to the network.
1060
1061        .. versionchanged:: 4.0
1062           Now returns a `.Future` if no callback is given.
1063
1064        .. versionchanged:: 6.0
1065
1066           The ``callback`` argument was removed.
1067        """
1068        assert self.request.connection is not None
1069        chunk = b"".join(self._write_buffer)
1070        self._write_buffer = []
1071        if not self._headers_written:
1072            self._headers_written = True
1073            for transform in self._transforms:
1074                assert chunk is not None
1075                (
1076                    self._status_code,
1077                    self._headers,
1078                    chunk,
1079                ) = transform.transform_first_chunk(
1080                    self._status_code, self._headers, chunk, include_footers
1081                )
1082            # Ignore the chunk and only write the headers for HEAD requests
1083            if self.request.method == "HEAD":
1084                chunk = b""
1085
1086            # Finalize the cookie headers (which have been stored in a side
1087            # object so an outgoing cookie could be overwritten before it
1088            # is sent).
1089            if hasattr(self, "_new_cookie"):
1090                for cookie in self._new_cookie.values():
1091                    self.add_header("Set-Cookie", cookie.OutputString(None))
1092
1093            start_line = httputil.ResponseStartLine("", self._status_code, self._reason)
1094            return self.request.connection.write_headers(
1095                start_line, self._headers, chunk
1096            )
1097        else:
1098            for transform in self._transforms:
1099                chunk = transform.transform_chunk(chunk, include_footers)
1100            # Ignore the chunk and only write the headers for HEAD requests
1101            if self.request.method != "HEAD":
1102                return self.request.connection.write(chunk)
1103            else:
1104                future = Future()  # type: Future[None]
1105                future.set_result(None)
1106                return future
1107
1108    def finish(self, chunk: Optional[Union[str, bytes, dict]] = None) -> "Future[None]":
1109        """Finishes this response, ending the HTTP request.
1110
1111        Passing a ``chunk`` to ``finish()`` is equivalent to passing that
1112        chunk to ``write()`` and then calling ``finish()`` with no arguments.
1113
1114        Returns a `.Future` which may optionally be awaited to track the sending
1115        of the response to the client. This `.Future` resolves when all the response
1116        data has been sent, and raises an error if the connection is closed before all
1117        data can be sent.
1118
1119        .. versionchanged:: 5.1
1120
1121           Now returns a `.Future` instead of ``None``.
1122        """
1123        if self._finished:
1124            raise RuntimeError("finish() called twice")
1125
1126        if chunk is not None:
1127            self.write(chunk)
1128
1129        # Automatically support ETags and add the Content-Length header if
1130        # we have not flushed any content yet.
1131        if not self._headers_written:
1132            if (
1133                self._status_code == 200
1134                and self.request.method in ("GET", "HEAD")
1135                and "Etag" not in self._headers
1136            ):
1137                self.set_etag_header()
1138                if self.check_etag_header():
1139                    self._write_buffer = []
1140                    self.set_status(304)
1141            if self._status_code in (204, 304) or (100 <= self._status_code < 200):
1142                assert not self._write_buffer, (
1143                    "Cannot send body with %s" % self._status_code
1144                )
1145                self._clear_representation_headers()
1146            elif "Content-Length" not in self._headers:
1147                content_length = sum(len(part) for part in self._write_buffer)
1148                self.set_header("Content-Length", content_length)
1149
1150        assert self.request.connection is not None
1151        # Now that the request is finished, clear the callback we
1152        # set on the HTTPConnection (which would otherwise prevent the
1153        # garbage collection of the RequestHandler when there
1154        # are keepalive connections)
1155        self.request.connection.set_close_callback(None)  # type: ignore
1156
1157        future = self.flush(include_footers=True)
1158        self.request.connection.finish()
1159        self._log()
1160        self._finished = True
1161        self.on_finish()
1162        self._break_cycles()
1163        return future
1164
1165    def detach(self) -> iostream.IOStream:
1166        """Take control of the underlying stream.
1167
1168        Returns the underlying `.IOStream` object and stops all
1169        further HTTP processing. Intended for implementing protocols
1170        like websockets that tunnel over an HTTP handshake.
1171
1172        This method is only supported when HTTP/1.1 is used.
1173
1174        .. versionadded:: 5.1
1175        """
1176        self._finished = True
1177        # TODO: add detach to HTTPConnection?
1178        return self.request.connection.detach()  # type: ignore
1179
1180    def _break_cycles(self) -> None:
1181        # Break up a reference cycle between this handler and the
1182        # _ui_module closures to allow for faster GC on CPython.
1183        self.ui = None  # type: ignore
1184
1185    def send_error(self, status_code: int = 500, **kwargs: Any) -> None:
1186        """Sends the given HTTP error code to the browser.
1187
1188        If `flush()` has already been called, it is not possible to send
1189        an error, so this method will simply terminate the response.
1190        If output has been written but not yet flushed, it will be discarded
1191        and replaced with the error page.
1192
1193        Override `write_error()` to customize the error page that is returned.
1194        Additional keyword arguments are passed through to `write_error`.
1195        """
1196        if self._headers_written:
1197            gen_log.error("Cannot send error response after headers written")
1198            if not self._finished:
1199                # If we get an error between writing headers and finishing,
1200                # we are unlikely to be able to finish due to a
1201                # Content-Length mismatch. Try anyway to release the
1202                # socket.
1203                try:
1204                    self.finish()
1205                except Exception:
1206                    gen_log.error("Failed to flush partial response", exc_info=True)
1207            return
1208        self.clear()
1209
1210        reason = kwargs.get("reason")
1211        if "exc_info" in kwargs:
1212            exception = kwargs["exc_info"][1]
1213            if isinstance(exception, HTTPError) and exception.reason:
1214                reason = exception.reason
1215        self.set_status(status_code, reason=reason)
1216        try:
1217            self.write_error(status_code, **kwargs)
1218        except Exception:
1219            app_log.error("Uncaught exception in write_error", exc_info=True)
1220        if not self._finished:
1221            self.finish()
1222
1223    def write_error(self, status_code: int, **kwargs: Any) -> None:
1224        """Override to implement custom error pages.
1225
1226        ``write_error`` may call `write`, `render`, `set_header`, etc
1227        to produce output as usual.
1228
1229        If this error was caused by an uncaught exception (including
1230        HTTPError), an ``exc_info`` triple will be available as
1231        ``kwargs["exc_info"]``.  Note that this exception may not be
1232        the "current" exception for purposes of methods like
1233        ``sys.exc_info()`` or ``traceback.format_exc``.
1234        """
1235        if self.settings.get("serve_traceback") and "exc_info" in kwargs:
1236            # in debug mode, try to send a traceback
1237            self.set_header("Content-Type", "text/plain")
1238            for line in traceback.format_exception(*kwargs["exc_info"]):
1239                self.write(line)
1240            self.finish()
1241        else:
1242            self.finish(
1243                "<html><title>%(code)d: %(message)s</title>"
1244                "<body>%(code)d: %(message)s</body></html>"
1245                % {"code": status_code, "message": self._reason}
1246            )
1247
1248    @property
1249    def locale(self) -> tornado.locale.Locale:
1250        """The locale for the current session.
1251
1252        Determined by either `get_user_locale`, which you can override to
1253        set the locale based on, e.g., a user preference stored in a
1254        database, or `get_browser_locale`, which uses the ``Accept-Language``
1255        header.
1256
1257        .. versionchanged: 4.1
1258           Added a property setter.
1259        """
1260        if not hasattr(self, "_locale"):
1261            loc = self.get_user_locale()
1262            if loc is not None:
1263                self._locale = loc
1264            else:
1265                self._locale = self.get_browser_locale()
1266                assert self._locale
1267        return self._locale
1268
1269    @locale.setter
1270    def locale(self, value: tornado.locale.Locale) -> None:
1271        self._locale = value
1272
1273    def get_user_locale(self) -> Optional[tornado.locale.Locale]:
1274        """Override to determine the locale from the authenticated user.
1275
1276        If None is returned, we fall back to `get_browser_locale()`.
1277
1278        This method should return a `tornado.locale.Locale` object,
1279        most likely obtained via a call like ``tornado.locale.get("en")``
1280        """
1281        return None
1282
1283    def get_browser_locale(self, default: str = "en_US") -> tornado.locale.Locale:
1284        """Determines the user's locale from ``Accept-Language`` header.
1285
1286        See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
1287        """
1288        if "Accept-Language" in self.request.headers:
1289            languages = self.request.headers["Accept-Language"].split(",")
1290            locales = []
1291            for language in languages:
1292                parts = language.strip().split(";")
1293                if len(parts) > 1 and parts[1].startswith("q="):
1294                    try:
1295                        score = float(parts[1][2:])
1296                    except (ValueError, TypeError):
1297                        score = 0.0
1298                else:
1299                    score = 1.0
1300                locales.append((parts[0], score))
1301            if locales:
1302                locales.sort(key=lambda pair: pair[1], reverse=True)
1303                codes = [loc[0] for loc in locales]
1304                return locale.get(*codes)
1305        return locale.get(default)
1306
1307    @property
1308    def current_user(self) -> Any:
1309        """The authenticated user for this request.
1310
1311        This is set in one of two ways:
1312
1313        * A subclass may override `get_current_user()`, which will be called
1314          automatically the first time ``self.current_user`` is accessed.
1315          `get_current_user()` will only be called once per request,
1316          and is cached for future access::
1317
1318              def get_current_user(self):
1319                  user_cookie = self.get_secure_cookie("user")
1320                  if user_cookie:
1321                      return json.loads(user_cookie)
1322                  return None
1323
1324        * It may be set as a normal variable, typically from an overridden
1325          `prepare()`::
1326
1327              @gen.coroutine
1328              def prepare(self):
1329                  user_id_cookie = self.get_secure_cookie("user_id")
1330                  if user_id_cookie:
1331                      self.current_user = yield load_user(user_id_cookie)
1332
1333        Note that `prepare()` may be a coroutine while `get_current_user()`
1334        may not, so the latter form is necessary if loading the user requires
1335        asynchronous operations.
1336
1337        The user object may be any type of the application's choosing.
1338        """
1339        if not hasattr(self, "_current_user"):
1340            self._current_user = self.get_current_user()
1341        return self._current_user
1342
1343    @current_user.setter
1344    def current_user(self, value: Any) -> None:
1345        self._current_user = value
1346
1347    def get_current_user(self) -> Any:
1348        """Override to determine the current user from, e.g., a cookie.
1349
1350        This method may not be a coroutine.
1351        """
1352        return None
1353
1354    def get_login_url(self) -> str:
1355        """Override to customize the login URL based on the request.
1356
1357        By default, we use the ``login_url`` application setting.
1358        """
1359        self.require_setting("login_url", "@tornado.web.authenticated")
1360        return self.application.settings["login_url"]
1361
1362    def get_template_path(self) -> Optional[str]:
1363        """Override to customize template path for each handler.
1364
1365        By default, we use the ``template_path`` application setting.
1366        Return None to load templates relative to the calling file.
1367        """
1368        return self.application.settings.get("template_path")
1369
1370    @property
1371    def xsrf_token(self) -> bytes:
1372        """The XSRF-prevention token for the current user/session.
1373
1374        To prevent cross-site request forgery, we set an '_xsrf' cookie
1375        and include the same '_xsrf' value as an argument with all POST
1376        requests. If the two do not match, we reject the form submission
1377        as a potential forgery.
1378
1379        See http://en.wikipedia.org/wiki/Cross-site_request_forgery
1380
1381        This property is of type `bytes`, but it contains only ASCII
1382        characters. If a character string is required, there is no
1383        need to base64-encode it; just decode the byte string as
1384        UTF-8.
1385
1386        .. versionchanged:: 3.2.2
1387           The xsrf token will now be have a random mask applied in every
1388           request, which makes it safe to include the token in pages
1389           that are compressed.  See http://breachattack.com for more
1390           information on the issue fixed by this change.  Old (version 1)
1391           cookies will be converted to version 2 when this method is called
1392           unless the ``xsrf_cookie_version`` `Application` setting is
1393           set to 1.
1394
1395        .. versionchanged:: 4.3
1396           The ``xsrf_cookie_kwargs`` `Application` setting may be
1397           used to supply additional cookie options (which will be
1398           passed directly to `set_cookie`). For example,
1399           ``xsrf_cookie_kwargs=dict(httponly=True, secure=True)``
1400           will set the ``secure`` and ``httponly`` flags on the
1401           ``_xsrf`` cookie.
1402        """
1403        if not hasattr(self, "_xsrf_token"):
1404            version, token, timestamp = self._get_raw_xsrf_token()
1405            output_version = self.settings.get("xsrf_cookie_version", 2)
1406            cookie_kwargs = self.settings.get("xsrf_cookie_kwargs", {})
1407            if output_version == 1:
1408                self._xsrf_token = binascii.b2a_hex(token)
1409            elif output_version == 2:
1410                mask = os.urandom(4)
1411                self._xsrf_token = b"|".join(
1412                    [
1413                        b"2",
1414                        binascii.b2a_hex(mask),
1415                        binascii.b2a_hex(_websocket_mask(mask, token)),
1416                        utf8(str(int(timestamp))),
1417                    ]
1418                )
1419            else:
1420                raise ValueError("unknown xsrf cookie version %d", output_version)
1421            if version is None:
1422                if self.current_user and "expires_days" not in cookie_kwargs:
1423                    cookie_kwargs["expires_days"] = 30
1424                self.set_cookie("_xsrf", self._xsrf_token, **cookie_kwargs)
1425        return self._xsrf_token
1426
1427    def _get_raw_xsrf_token(self) -> Tuple[Optional[int], bytes, float]:
1428        """Read or generate the xsrf token in its raw form.
1429
1430        The raw_xsrf_token is a tuple containing:
1431
1432        * version: the version of the cookie from which this token was read,
1433          or None if we generated a new token in this request.
1434        * token: the raw token data; random (non-ascii) bytes.
1435        * timestamp: the time this token was generated (will not be accurate
1436          for version 1 cookies)
1437        """
1438        if not hasattr(self, "_raw_xsrf_token"):
1439            cookie = self.get_cookie("_xsrf")
1440            if cookie:
1441                version, token, timestamp = self._decode_xsrf_token(cookie)
1442            else:
1443                version, token, timestamp = None, None, None
1444            if token is None:
1445                version = None
1446                token = os.urandom(16)
1447                timestamp = time.time()
1448            assert token is not None
1449            assert timestamp is not None
1450            self._raw_xsrf_token = (version, token, timestamp)
1451        return self._raw_xsrf_token
1452
1453    def _decode_xsrf_token(
1454        self, cookie: str
1455    ) -> Tuple[Optional[int], Optional[bytes], Optional[float]]:
1456        """Convert a cookie string into a the tuple form returned by
1457        _get_raw_xsrf_token.
1458        """
1459
1460        try:
1461            m = _signed_value_version_re.match(utf8(cookie))
1462
1463            if m:
1464                version = int(m.group(1))
1465                if version == 2:
1466                    _, mask_str, masked_token, timestamp_str = cookie.split("|")
1467
1468                    mask = binascii.a2b_hex(utf8(mask_str))
1469                    token = _websocket_mask(mask, binascii.a2b_hex(utf8(masked_token)))
1470                    timestamp = int(timestamp_str)
1471                    return version, token, timestamp
1472                else:
1473                    # Treat unknown versions as not present instead of failing.
1474                    raise Exception("Unknown xsrf cookie version")
1475            else:
1476                version = 1
1477                try:
1478                    token = binascii.a2b_hex(utf8(cookie))
1479                except (binascii.Error, TypeError):
1480                    token = utf8(cookie)
1481                # We don't have a usable timestamp in older versions.
1482                timestamp = int(time.time())
1483                return (version, token, timestamp)
1484        except Exception:
1485            # Catch exceptions and return nothing instead of failing.
1486            gen_log.debug("Uncaught exception in _decode_xsrf_token", exc_info=True)
1487            return None, None, None
1488
1489    def check_xsrf_cookie(self) -> None:
1490        """Verifies that the ``_xsrf`` cookie matches the ``_xsrf`` argument.
1491
1492        To prevent cross-site request forgery, we set an ``_xsrf``
1493        cookie and include the same value as a non-cookie
1494        field with all ``POST`` requests. If the two do not match, we
1495        reject the form submission as a potential forgery.
1496
1497        The ``_xsrf`` value may be set as either a form field named ``_xsrf``
1498        or in a custom HTTP header named ``X-XSRFToken`` or ``X-CSRFToken``
1499        (the latter is accepted for compatibility with Django).
1500
1501        See http://en.wikipedia.org/wiki/Cross-site_request_forgery
1502
1503        .. versionchanged:: 3.2.2
1504           Added support for cookie version 2.  Both versions 1 and 2 are
1505           supported.
1506        """
1507        # Prior to release 1.1.1, this check was ignored if the HTTP header
1508        # ``X-Requested-With: XMLHTTPRequest`` was present.  This exception
1509        # has been shown to be insecure and has been removed.  For more
1510        # information please see
1511        # http://www.djangoproject.com/weblog/2011/feb/08/security/
1512        # http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails
1513        token = (
1514            self.get_argument("_xsrf", None)
1515            or self.request.headers.get("X-Xsrftoken")
1516            or self.request.headers.get("X-Csrftoken")
1517        )
1518        if not token:
1519            raise HTTPError(403, "'_xsrf' argument missing from POST")
1520        _, token, _ = self._decode_xsrf_token(token)
1521        _, expected_token, _ = self._get_raw_xsrf_token()
1522        if not token:
1523            raise HTTPError(403, "'_xsrf' argument has invalid format")
1524        if not hmac.compare_digest(utf8(token), utf8(expected_token)):
1525            raise HTTPError(403, "XSRF cookie does not match POST argument")
1526
1527    def xsrf_form_html(self) -> str:
1528        """An HTML ``<input/>`` element to be included with all POST forms.
1529
1530        It defines the ``_xsrf`` input value, which we check on all POST
1531        requests to prevent cross-site request forgery. If you have set
1532        the ``xsrf_cookies`` application setting, you must include this
1533        HTML within all of your HTML forms.
1534
1535        In a template, this method should be called with ``{% module
1536        xsrf_form_html() %}``
1537
1538        See `check_xsrf_cookie()` above for more information.
1539        """
1540        return (
1541            '<input type="hidden" name="_xsrf" value="'
1542            + escape.xhtml_escape(self.xsrf_token)
1543            + '"/>'
1544        )
1545
1546    def static_url(
1547        self, path: str, include_host: Optional[bool] = None, **kwargs: Any
1548    ) -> str:
1549        """Returns a static URL for the given relative static file path.
1550
1551        This method requires you set the ``static_path`` setting in your
1552        application (which specifies the root directory of your static
1553        files).
1554
1555        This method returns a versioned url (by default appending
1556        ``?v=<signature>``), which allows the static files to be
1557        cached indefinitely.  This can be disabled by passing
1558        ``include_version=False`` (in the default implementation;
1559        other static file implementations are not required to support
1560        this, but they may support other options).
1561
1562        By default this method returns URLs relative to the current
1563        host, but if ``include_host`` is true the URL returned will be
1564        absolute.  If this handler has an ``include_host`` attribute,
1565        that value will be used as the default for all `static_url`
1566        calls that do not pass ``include_host`` as a keyword argument.
1567
1568        """
1569        self.require_setting("static_path", "static_url")
1570        get_url = self.settings.get(
1571            "static_handler_class", StaticFileHandler
1572        ).make_static_url
1573
1574        if include_host is None:
1575            include_host = getattr(self, "include_host", False)
1576
1577        if include_host:
1578            base = self.request.protocol + "://" + self.request.host
1579        else:
1580            base = ""
1581
1582        return base + get_url(self.settings, path, **kwargs)
1583
1584    def require_setting(self, name: str, feature: str = "this feature") -> None:
1585        """Raises an exception if the given app setting is not defined."""
1586        if not self.application.settings.get(name):
1587            raise Exception(
1588                "You must define the '%s' setting in your "
1589                "application to use %s" % (name, feature)
1590            )
1591
1592    def reverse_url(self, name: str, *args: Any) -> str:
1593        """Alias for `Application.reverse_url`."""
1594        return self.application.reverse_url(name, *args)
1595
1596    def compute_etag(self) -> Optional[str]:
1597        """Computes the etag header to be used for this request.
1598
1599        By default uses a hash of the content written so far.
1600
1601        May be overridden to provide custom etag implementations,
1602        or may return None to disable tornado's default etag support.
1603        """
1604        hasher = hashlib.sha1()
1605        for part in self._write_buffer:
1606            hasher.update(part)
1607        return '"%s"' % hasher.hexdigest()
1608
1609    def set_etag_header(self) -> None:
1610        """Sets the response's Etag header using ``self.compute_etag()``.
1611
1612        Note: no header will be set if ``compute_etag()`` returns ``None``.
1613
1614        This method is called automatically when the request is finished.
1615        """
1616        etag = self.compute_etag()
1617        if etag is not None:
1618            self.set_header("Etag", etag)
1619
1620    def check_etag_header(self) -> bool:
1621        """Checks the ``Etag`` header against requests's ``If-None-Match``.
1622
1623        Returns ``True`` if the request's Etag matches and a 304 should be
1624        returned. For example::
1625
1626            self.set_etag_header()
1627            if self.check_etag_header():
1628                self.set_status(304)
1629                return
1630
1631        This method is called automatically when the request is finished,
1632        but may be called earlier for applications that override
1633        `compute_etag` and want to do an early check for ``If-None-Match``
1634        before completing the request.  The ``Etag`` header should be set
1635        (perhaps with `set_etag_header`) before calling this method.
1636        """
1637        computed_etag = utf8(self._headers.get("Etag", ""))
1638        # Find all weak and strong etag values from If-None-Match header
1639        # because RFC 7232 allows multiple etag values in a single header.
1640        etags = re.findall(
1641            br'\*|(?:W/)?"[^"]*"', utf8(self.request.headers.get("If-None-Match", ""))
1642        )
1643        if not computed_etag or not etags:
1644            return False
1645
1646        match = False
1647        if etags[0] == b"*":
1648            match = True
1649        else:
1650            # Use a weak comparison when comparing entity-tags.
1651            def val(x: bytes) -> bytes:
1652                return x[2:] if x.startswith(b"W/") else x
1653
1654            for etag in etags:
1655                if val(etag) == val(computed_etag):
1656                    match = True
1657                    break
1658        return match
1659
1660    async def _execute(
1661        self, transforms: List["OutputTransform"], *args: bytes, **kwargs: bytes
1662    ) -> None:
1663        """Executes this request with the given output transforms."""
1664        self._transforms = transforms
1665        try:
1666            if self.request.method not in self.SUPPORTED_METHODS:
1667                raise HTTPError(405)
1668            self.path_args = [self.decode_argument(arg) for arg in args]
1669            self.path_kwargs = dict(
1670                (k, self.decode_argument(v, name=k)) for (k, v) in kwargs.items()
1671            )
1672            # If XSRF cookies are turned on, reject form submissions without
1673            # the proper cookie
1674            if self.request.method not in (
1675                "GET",
1676                "HEAD",
1677                "OPTIONS",
1678            ) and self.application.settings.get("xsrf_cookies"):
1679                self.check_xsrf_cookie()
1680
1681            result = self.prepare()
1682            if result is not None:
1683                result = await result
1684            if self._prepared_future is not None:
1685                # Tell the Application we've finished with prepare()
1686                # and are ready for the body to arrive.
1687                future_set_result_unless_cancelled(self._prepared_future, None)
1688            if self._finished:
1689                return
1690
1691            if _has_stream_request_body(self.__class__):
1692                # In streaming mode request.body is a Future that signals
1693                # the body has been completely received.  The Future has no
1694                # result; the data has been passed to self.data_received
1695                # instead.
1696                try:
1697                    await self.request._body_future
1698                except iostream.StreamClosedError:
1699                    return
1700
1701            method = getattr(self, self.request.method.lower())
1702            result = method(*self.path_args, **self.path_kwargs)
1703            if result is not None:
1704                result = await result
1705            if self._auto_finish and not self._finished:
1706                self.finish()
1707        except Exception as e:
1708            try:
1709                self._handle_request_exception(e)
1710            except Exception:
1711                app_log.error("Exception in exception handler", exc_info=True)
1712            finally:
1713                # Unset result to avoid circular references
1714                result = None
1715            if self._prepared_future is not None and not self._prepared_future.done():
1716                # In case we failed before setting _prepared_future, do it
1717                # now (to unblock the HTTP server).  Note that this is not
1718                # in a finally block to avoid GC issues prior to Python 3.4.
1719                self._prepared_future.set_result(None)
1720
1721    def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:
1722        """Implement this method to handle streamed request data.
1723
1724        Requires the `.stream_request_body` decorator.
1725
1726        May be a coroutine for flow control.
1727        """
1728        raise NotImplementedError()
1729
1730    def _log(self) -> None:
1731        """Logs the current request.
1732
1733        Sort of deprecated since this functionality was moved to the
1734        Application, but left in place for the benefit of existing apps
1735        that have overridden this method.
1736        """
1737        self.application.log_request(self)
1738
1739    def _request_summary(self) -> str:
1740        return "%s %s (%s)" % (
1741            self.request.method,
1742            self.request.uri,
1743            self.request.remote_ip,
1744        )
1745
1746    def _handle_request_exception(self, e: BaseException) -> None:
1747        if isinstance(e, Finish):
1748            # Not an error; just finish the request without logging.
1749            if not self._finished:
1750                self.finish(*e.args)
1751            return
1752        try:
1753            self.log_exception(*sys.exc_info())
1754        except Exception:
1755            # An error here should still get a best-effort send_error()
1756            # to avoid leaking the connection.
1757            app_log.error("Error in exception logger", exc_info=True)
1758        if self._finished:
1759            # Extra errors after the request has been finished should
1760            # be logged, but there is no reason to continue to try and
1761            # send a response.
1762            return
1763        if isinstance(e, HTTPError):
1764            self.send_error(e.status_code, exc_info=sys.exc_info())
1765        else:
1766            self.send_error(500, exc_info=sys.exc_info())
1767
1768    def log_exception(
1769        self,
1770        typ: "Optional[Type[BaseException]]",
1771        value: Optional[BaseException],
1772        tb: Optional[TracebackType],
1773    ) -> None:
1774        """Override to customize logging of uncaught exceptions.
1775
1776        By default logs instances of `HTTPError` as warnings without
1777        stack traces (on the ``tornado.general`` logger), and all
1778        other exceptions as errors with stack traces (on the
1779        ``tornado.application`` logger).
1780
1781        .. versionadded:: 3.1
1782        """
1783        if isinstance(value, HTTPError):
1784            if value.log_message:
1785                format = "%d %s: " + value.log_message
1786                args = [value.status_code, self._request_summary()] + list(value.args)
1787                gen_log.warning(format, *args)
1788        else:
1789            app_log.error(
1790                "Uncaught exception %s\n%r",
1791                self._request_summary(),
1792                self.request,
1793                exc_info=(typ, value, tb),  # type: ignore
1794            )
1795
1796    def _ui_module(self, name: str, module: Type["UIModule"]) -> Callable[..., str]:
1797        def render(*args, **kwargs) -> str:  # type: ignore
1798            if not hasattr(self, "_active_modules"):
1799                self._active_modules = {}  # type: Dict[str, UIModule]
1800            if name not in self._active_modules:
1801                self._active_modules[name] = module(self)
1802            rendered = self._active_modules[name].render(*args, **kwargs)
1803            return rendered
1804
1805        return render
1806
1807    def _ui_method(self, method: Callable[..., str]) -> Callable[..., str]:
1808        return lambda *args, **kwargs: method(self, *args, **kwargs)
1809
1810    def _clear_representation_headers(self) -> None:
1811        # 304 responses should not contain representation metadata
1812        # headers (defined in
1813        # https://tools.ietf.org/html/rfc7231#section-3.1)
1814        # not explicitly allowed by
1815        # https://tools.ietf.org/html/rfc7232#section-4.1
1816        headers = ["Content-Encoding", "Content-Language", "Content-Type"]
1817        for h in headers:
1818            self.clear_header(h)
1819
1820
1821def stream_request_body(cls: Type[RequestHandler]) -> Type[RequestHandler]:
1822    """Apply to `RequestHandler` subclasses to enable streaming body support.
1823
1824    This decorator implies the following changes:
1825
1826    * `.HTTPServerRequest.body` is undefined, and body arguments will not
1827      be included in `RequestHandler.get_argument`.
1828    * `RequestHandler.prepare` is called when the request headers have been
1829      read instead of after the entire body has been read.
1830    * The subclass must define a method ``data_received(self, data):``, which
1831      will be called zero or more times as data is available.  Note that
1832      if the request has an empty body, ``data_received`` may not be called.
1833    * ``prepare`` and ``data_received`` may return Futures (such as via
1834      ``@gen.coroutine``, in which case the next method will not be called
1835      until those futures have completed.
1836    * The regular HTTP method (``post``, ``put``, etc) will be called after
1837      the entire body has been read.
1838
1839    See the `file receiver demo <https://github.com/tornadoweb/tornado/tree/master/demos/file_upload/>`_
1840    for example usage.
1841    """  # noqa: E501
1842    if not issubclass(cls, RequestHandler):
1843        raise TypeError("expected subclass of RequestHandler, got %r", cls)
1844    cls._stream_request_body = True
1845    return cls
1846
1847
1848def _has_stream_request_body(cls: Type[RequestHandler]) -> bool:
1849    if not issubclass(cls, RequestHandler):
1850        raise TypeError("expected subclass of RequestHandler, got %r", cls)
1851    return cls._stream_request_body
1852
1853
1854def removeslash(
1855    method: Callable[..., Optional[Awaitable[None]]]
1856) -> Callable[..., Optional[Awaitable[None]]]:
1857    """Use this decorator to remove trailing slashes from the request path.
1858
1859    For example, a request to ``/foo/`` would redirect to ``/foo`` with this
1860    decorator. Your request handler mapping should use a regular expression
1861    like ``r'/foo/*'`` in conjunction with using the decorator.
1862    """
1863
1864    @functools.wraps(method)
1865    def wrapper(  # type: ignore
1866        self: RequestHandler, *args, **kwargs
1867    ) -> Optional[Awaitable[None]]:
1868        if self.request.path.endswith("/"):
1869            if self.request.method in ("GET", "HEAD"):
1870                uri = self.request.path.rstrip("/")
1871                if uri:  # don't try to redirect '/' to ''
1872                    if self.request.query:
1873                        uri += "?" + self.request.query
1874                    self.redirect(uri, permanent=True)
1875                    return None
1876            else:
1877                raise HTTPError(404)
1878        return method(self, *args, **kwargs)
1879
1880    return wrapper
1881
1882
1883def addslash(
1884    method: Callable[..., Optional[Awaitable[None]]]
1885) -> Callable[..., Optional[Awaitable[None]]]:
1886    """Use this decorator to add a missing trailing slash to the request path.
1887
1888    For example, a request to ``/foo`` would redirect to ``/foo/`` with this
1889    decorator. Your request handler mapping should use a regular expression
1890    like ``r'/foo/?'`` in conjunction with using the decorator.
1891    """
1892
1893    @functools.wraps(method)
1894    def wrapper(  # type: ignore
1895        self: RequestHandler, *args, **kwargs
1896    ) -> Optional[Awaitable[None]]:
1897        if not self.request.path.endswith("/"):
1898            if self.request.method in ("GET", "HEAD"):
1899                uri = self.request.path + "/"
1900                if self.request.query:
1901                    uri += "?" + self.request.query
1902                self.redirect(uri, permanent=True)
1903                return None
1904            raise HTTPError(404)
1905        return method(self, *args, **kwargs)
1906
1907    return wrapper
1908
1909
1910class _ApplicationRouter(ReversibleRuleRouter):
1911    """Routing implementation used internally by `Application`.
1912
1913    Provides a binding between `Application` and `RequestHandler`.
1914    This implementation extends `~.routing.ReversibleRuleRouter` in a couple of ways:
1915        * it allows to use `RequestHandler` subclasses as `~.routing.Rule` target and
1916        * it allows to use a list/tuple of rules as `~.routing.Rule` target.
1917        ``process_rule`` implementation will substitute this list with an appropriate
1918        `_ApplicationRouter` instance.
1919    """
1920
1921    def __init__(
1922        self, application: "Application", rules: Optional[_RuleList] = None
1923    ) -> None:
1924        assert isinstance(application, Application)
1925        self.application = application
1926        super().__init__(rules)
1927
1928    def process_rule(self, rule: Rule) -> Rule:
1929        rule = super().process_rule(rule)
1930
1931        if isinstance(rule.target, (list, tuple)):
1932            rule.target = _ApplicationRouter(
1933                self.application, rule.target  # type: ignore
1934            )
1935
1936        return rule
1937
1938    def get_target_delegate(
1939        self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any
1940    ) -> Optional[httputil.HTTPMessageDelegate]:
1941        if isclass(target) and issubclass(target, RequestHandler):
1942            return self.application.get_handler_delegate(
1943                request, target, **target_params
1944            )
1945
1946        return super().get_target_delegate(target, request, **target_params)
1947
1948
1949class Application(ReversibleRouter):
1950    r"""A collection of request handlers that make up a web application.
1951
1952    Instances of this class are callable and can be passed directly to
1953    HTTPServer to serve the application::
1954
1955        application = web.Application([
1956            (r"/", MainPageHandler),
1957        ])
1958        http_server = httpserver.HTTPServer(application)
1959        http_server.listen(8080)
1960        ioloop.IOLoop.current().start()
1961
1962    The constructor for this class takes in a list of `~.routing.Rule`
1963    objects or tuples of values corresponding to the arguments of
1964    `~.routing.Rule` constructor: ``(matcher, target, [target_kwargs], [name])``,
1965    the values in square brackets being optional. The default matcher is
1966    `~.routing.PathMatches`, so ``(regexp, target)`` tuples can also be used
1967    instead of ``(PathMatches(regexp), target)``.
1968
1969    A common routing target is a `RequestHandler` subclass, but you can also
1970    use lists of rules as a target, which create a nested routing configuration::
1971
1972        application = web.Application([
1973            (HostMatches("example.com"), [
1974                (r"/", MainPageHandler),
1975                (r"/feed", FeedHandler),
1976            ]),
1977        ])
1978
1979    In addition to this you can use nested `~.routing.Router` instances,
1980    `~.httputil.HTTPMessageDelegate` subclasses and callables as routing targets
1981    (see `~.routing` module docs for more information).
1982
1983    When we receive requests, we iterate over the list in order and
1984    instantiate an instance of the first request class whose regexp
1985    matches the request path. The request class can be specified as
1986    either a class object or a (fully-qualified) name.
1987
1988    A dictionary may be passed as the third element (``target_kwargs``)
1989    of the tuple, which will be used as keyword arguments to the handler's
1990    constructor and `~RequestHandler.initialize` method. This pattern
1991    is used for the `StaticFileHandler` in this example (note that a
1992    `StaticFileHandler` can be installed automatically with the
1993    static_path setting described below)::
1994
1995        application = web.Application([
1996            (r"/static/(.*)", web.StaticFileHandler, {"path": "/var/www"}),
1997        ])
1998
1999    We support virtual hosts with the `add_handlers` method, which takes in
2000    a host regular expression as the first argument::
2001
2002        application.add_handlers(r"www\.myhost\.com", [
2003            (r"/article/([0-9]+)", ArticleHandler),
2004        ])
2005
2006    If there's no match for the current request's host, then ``default_host``
2007    parameter value is matched against host regular expressions.
2008
2009
2010    .. warning::
2011
2012       Applications that do not use TLS may be vulnerable to :ref:`DNS
2013       rebinding <dnsrebinding>` attacks. This attack is especially
2014       relevant to applications that only listen on ``127.0.0.1`` or
2015       other private networks. Appropriate host patterns must be used
2016       (instead of the default of ``r'.*'``) to prevent this risk. The
2017       ``default_host`` argument must not be used in applications that
2018       may be vulnerable to DNS rebinding.
2019
2020    You can serve static files by sending the ``static_path`` setting
2021    as a keyword argument. We will serve those files from the
2022    ``/static/`` URI (this is configurable with the
2023    ``static_url_prefix`` setting), and we will serve ``/favicon.ico``
2024    and ``/robots.txt`` from the same directory.  A custom subclass of
2025    `StaticFileHandler` can be specified with the
2026    ``static_handler_class`` setting.
2027
2028    .. versionchanged:: 4.5
2029       Integration with the new `tornado.routing` module.
2030
2031    """
2032
2033    def __init__(
2034        self,
2035        handlers: Optional[_RuleList] = None,
2036        default_host: Optional[str] = None,
2037        transforms: Optional[List[Type["OutputTransform"]]] = None,
2038        **settings: Any
2039    ) -> None:
2040        if transforms is None:
2041            self.transforms = []  # type: List[Type[OutputTransform]]
2042            if settings.get("compress_response") or settings.get("gzip"):
2043                self.transforms.append(GZipContentEncoding)
2044        else:
2045            self.transforms = transforms
2046        self.default_host = default_host
2047        self.settings = settings
2048        self.ui_modules = {
2049            "linkify": _linkify,
2050            "xsrf_form_html": _xsrf_form_html,
2051            "Template": TemplateModule,
2052        }
2053        self.ui_methods = {}  # type: Dict[str, Callable[..., str]]
2054        self._load_ui_modules(settings.get("ui_modules", {}))
2055        self._load_ui_methods(settings.get("ui_methods", {}))
2056        if self.settings.get("static_path"):
2057            path = self.settings["static_path"]
2058            handlers = list(handlers or [])
2059            static_url_prefix = settings.get("static_url_prefix", "/static/")
2060            static_handler_class = settings.get(
2061                "static_handler_class", StaticFileHandler
2062            )
2063            static_handler_args = settings.get("static_handler_args", {})
2064            static_handler_args["path"] = path
2065            for pattern in [
2066                re.escape(static_url_prefix) + r"(.*)",
2067                r"/(favicon\.ico)",
2068                r"/(robots\.txt)",
2069            ]:
2070                handlers.insert(0, (pattern, static_handler_class, static_handler_args))
2071
2072        if self.settings.get("debug"):
2073            self.settings.setdefault("autoreload", True)
2074            self.settings.setdefault("compiled_template_cache", False)
2075            self.settings.setdefault("static_hash_cache", False)
2076            self.settings.setdefault("serve_traceback", True)
2077
2078        self.wildcard_router = _ApplicationRouter(self, handlers)
2079        self.default_router = _ApplicationRouter(
2080            self, [Rule(AnyMatches(), self.wildcard_router)]
2081        )
2082
2083        # Automatically reload modified modules
2084        if self.settings.get("autoreload"):
2085            from tornado import autoreload
2086
2087            autoreload.start()
2088
2089    def listen(self, port: int, address: str = "", **kwargs: Any) -> HTTPServer:
2090        """Starts an HTTP server for this application on the given port.
2091
2092        This is a convenience alias for creating an `.HTTPServer`
2093        object and calling its listen method.  Keyword arguments not
2094        supported by `HTTPServer.listen <.TCPServer.listen>` are passed to the
2095        `.HTTPServer` constructor.  For advanced uses
2096        (e.g. multi-process mode), do not use this method; create an
2097        `.HTTPServer` and call its
2098        `.TCPServer.bind`/`.TCPServer.start` methods directly.
2099
2100        Note that after calling this method you still need to call
2101        ``IOLoop.current().start()`` to start the server.
2102
2103        Returns the `.HTTPServer` object.
2104
2105        .. versionchanged:: 4.3
2106           Now returns the `.HTTPServer` object.
2107        """
2108        server = HTTPServer(self, **kwargs)
2109        server.listen(port, address)
2110        return server
2111
2112    def add_handlers(self, host_pattern: str, host_handlers: _RuleList) -> None:
2113        """Appends the given handlers to our handler list.
2114
2115        Host patterns are processed sequentially in the order they were
2116        added. All matching patterns will be considered.
2117        """
2118        host_matcher = HostMatches(host_pattern)
2119        rule = Rule(host_matcher, _ApplicationRouter(self, host_handlers))
2120
2121        self.default_router.rules.insert(-1, rule)
2122
2123        if self.default_host is not None:
2124            self.wildcard_router.add_rules(
2125                [(DefaultHostMatches(self, host_matcher.host_pattern), host_handlers)]
2126            )
2127
2128    def add_transform(self, transform_class: Type["OutputTransform"]) -> None:
2129        self.transforms.append(transform_class)
2130
2131    def _load_ui_methods(self, methods: Any) -> None:
2132        if isinstance(methods, types.ModuleType):
2133            self._load_ui_methods(dict((n, getattr(methods, n)) for n in dir(methods)))
2134        elif isinstance(methods, list):
2135            for m in methods:
2136                self._load_ui_methods(m)
2137        else:
2138            for name, fn in methods.items():
2139                if (
2140                    not name.startswith("_")
2141                    and hasattr(fn, "__call__")
2142                    and name[0].lower() == name[0]
2143                ):
2144                    self.ui_methods[name] = fn
2145
2146    def _load_ui_modules(self, modules: Any) -> None:
2147        if isinstance(modules, types.ModuleType):
2148            self._load_ui_modules(dict((n, getattr(modules, n)) for n in dir(modules)))
2149        elif isinstance(modules, list):
2150            for m in modules:
2151                self._load_ui_modules(m)
2152        else:
2153            assert isinstance(modules, dict)
2154            for name, cls in modules.items():
2155                try:
2156                    if issubclass(cls, UIModule):
2157                        self.ui_modules[name] = cls
2158                except TypeError:
2159                    pass
2160
2161    def __call__(
2162        self, request: httputil.HTTPServerRequest
2163    ) -> Optional[Awaitable[None]]:
2164        # Legacy HTTPServer interface
2165        dispatcher = self.find_handler(request)
2166        return dispatcher.execute()
2167
2168    def find_handler(
2169        self, request: httputil.HTTPServerRequest, **kwargs: Any
2170    ) -> "_HandlerDelegate":
2171        route = self.default_router.find_handler(request)
2172        if route is not None:
2173            return cast("_HandlerDelegate", route)
2174
2175        if self.settings.get("default_handler_class"):
2176            return self.get_handler_delegate(
2177                request,
2178                self.settings["default_handler_class"],
2179                self.settings.get("default_handler_args", {}),
2180            )
2181
2182        return self.get_handler_delegate(request, ErrorHandler, {"status_code": 404})
2183
2184    def get_handler_delegate(
2185        self,
2186        request: httputil.HTTPServerRequest,
2187        target_class: Type[RequestHandler],
2188        target_kwargs: Optional[Dict[str, Any]] = None,
2189        path_args: Optional[List[bytes]] = None,
2190        path_kwargs: Optional[Dict[str, bytes]] = None,
2191    ) -> "_HandlerDelegate":
2192        """Returns `~.httputil.HTTPMessageDelegate` that can serve a request
2193        for application and `RequestHandler` subclass.
2194
2195        :arg httputil.HTTPServerRequest request: current HTTP request.
2196        :arg RequestHandler target_class: a `RequestHandler` class.
2197        :arg dict target_kwargs: keyword arguments for ``target_class`` constructor.
2198        :arg list path_args: positional arguments for ``target_class`` HTTP method that
2199            will be executed while handling a request (``get``, ``post`` or any other).
2200        :arg dict path_kwargs: keyword arguments for ``target_class`` HTTP method.
2201        """
2202        return _HandlerDelegate(
2203            self, request, target_class, target_kwargs, path_args, path_kwargs
2204        )
2205
2206    def reverse_url(self, name: str, *args: Any) -> str:
2207        """Returns a URL path for handler named ``name``
2208
2209        The handler must be added to the application as a named `URLSpec`.
2210
2211        Args will be substituted for capturing groups in the `URLSpec` regex.
2212        They will be converted to strings if necessary, encoded as utf8,
2213        and url-escaped.
2214        """
2215        reversed_url = self.default_router.reverse_url(name, *args)
2216        if reversed_url is not None:
2217            return reversed_url
2218
2219        raise KeyError("%s not found in named urls" % name)
2220
2221    def log_request(self, handler: RequestHandler) -> None:
2222        """Writes a completed HTTP request to the logs.
2223
2224        By default writes to the python root logger.  To change
2225        this behavior either subclass Application and override this method,
2226        or pass a function in the application settings dictionary as
2227        ``log_function``.
2228        """
2229        if "log_function" in self.settings:
2230            self.settings["log_function"](handler)
2231            return
2232        if handler.get_status() < 400:
2233            log_method = access_log.info
2234        elif handler.get_status() < 500:
2235            log_method = access_log.warning
2236        else:
2237            log_method = access_log.error
2238        request_time = 1000.0 * handler.request.request_time()
2239        log_method(
2240            "%d %s %.2fms",
2241            handler.get_status(),
2242            handler._request_summary(),
2243            request_time,
2244        )
2245
2246
2247class _HandlerDelegate(httputil.HTTPMessageDelegate):
2248    def __init__(
2249        self,
2250        application: Application,
2251        request: httputil.HTTPServerRequest,
2252        handler_class: Type[RequestHandler],
2253        handler_kwargs: Optional[Dict[str, Any]],
2254        path_args: Optional[List[bytes]],
2255        path_kwargs: Optional[Dict[str, bytes]],
2256    ) -> None:
2257        self.application = application
2258        self.connection = request.connection
2259        self.request = request
2260        self.handler_class = handler_class
2261        self.handler_kwargs = handler_kwargs or {}
2262        self.path_args = path_args or []
2263        self.path_kwargs = path_kwargs or {}
2264        self.chunks = []  # type: List[bytes]
2265        self.stream_request_body = _has_stream_request_body(self.handler_class)
2266
2267    def headers_received(
2268        self,
2269        start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine],
2270        headers: httputil.HTTPHeaders,
2271    ) -> Optional[Awaitable[None]]:
2272        if self.stream_request_body:
2273            self.request._body_future = Future()
2274            return self.execute()
2275        return None
2276
2277    def data_received(self, data: bytes) -> Optional[Awaitable[None]]:
2278        if self.stream_request_body:
2279            return self.handler.data_received(data)
2280        else:
2281            self.chunks.append(data)
2282            return None
2283
2284    def finish(self) -> None:
2285        if self.stream_request_body:
2286            future_set_result_unless_cancelled(self.request._body_future, None)
2287        else:
2288            self.request.body = b"".join(self.chunks)
2289            self.request._parse_body()
2290            self.execute()
2291
2292    def on_connection_close(self) -> None:
2293        if self.stream_request_body:
2294            self.handler.on_connection_close()
2295        else:
2296            self.chunks = None  # type: ignore
2297
2298    def execute(self) -> Optional[Awaitable[None]]:
2299        # If template cache is disabled (usually in the debug mode),
2300        # re-compile templates and reload static files on every
2301        # request so you don't need to restart to see changes
2302        if not self.application.settings.get("compiled_template_cache", True):
2303            with RequestHandler._template_loader_lock:
2304                for loader in RequestHandler._template_loaders.values():
2305                    loader.reset()
2306        if not self.application.settings.get("static_hash_cache", True):
2307            StaticFileHandler.reset()
2308
2309        self.handler = self.handler_class(
2310            self.application, self.request, **self.handler_kwargs
2311        )
2312        transforms = [t(self.request) for t in self.application.transforms]
2313
2314        if self.stream_request_body:
2315            self.handler._prepared_future = Future()
2316        # Note that if an exception escapes handler._execute it will be
2317        # trapped in the Future it returns (which we are ignoring here,
2318        # leaving it to be logged when the Future is GC'd).
2319        # However, that shouldn't happen because _execute has a blanket
2320        # except handler, and we cannot easily access the IOLoop here to
2321        # call add_future (because of the requirement to remain compatible
2322        # with WSGI)
2323        fut = gen.convert_yielded(
2324            self.handler._execute(transforms, *self.path_args, **self.path_kwargs)
2325        )
2326        fut.add_done_callback(lambda f: f.result())
2327        # If we are streaming the request body, then execute() is finished
2328        # when the handler has prepared to receive the body.  If not,
2329        # it doesn't matter when execute() finishes (so we return None)
2330        return self.handler._prepared_future
2331
2332
2333class HTTPError(Exception):
2334    """An exception that will turn into an HTTP error response.
2335
2336    Raising an `HTTPError` is a convenient alternative to calling
2337    `RequestHandler.send_error` since it automatically ends the
2338    current function.
2339
2340    To customize the response sent with an `HTTPError`, override
2341    `RequestHandler.write_error`.
2342
2343    :arg int status_code: HTTP status code.  Must be listed in
2344        `httplib.responses <http.client.responses>` unless the ``reason``
2345        keyword argument is given.
2346    :arg str log_message: Message to be written to the log for this error
2347        (will not be shown to the user unless the `Application` is in debug
2348        mode).  May contain ``%s``-style placeholders, which will be filled
2349        in with remaining positional parameters.
2350    :arg str reason: Keyword-only argument.  The HTTP "reason" phrase
2351        to pass in the status line along with ``status_code``.  Normally
2352        determined automatically from ``status_code``, but can be used
2353        to use a non-standard numeric code.
2354    """
2355
2356    def __init__(
2357        self,
2358        status_code: int = 500,
2359        log_message: Optional[str] = None,
2360        *args: Any,
2361        **kwargs: Any
2362    ) -> None:
2363        self.status_code = status_code
2364        self.log_message = log_message
2365        self.args = args
2366        self.reason = kwargs.get("reason", None)
2367        if log_message and not args:
2368            self.log_message = log_message.replace("%", "%%")
2369
2370    def __str__(self) -> str:
2371        message = "HTTP %d: %s" % (
2372            self.status_code,
2373            self.reason or httputil.responses.get(self.status_code, "Unknown"),
2374        )
2375        if self.log_message:
2376            return message + " (" + (self.log_message % self.args) + ")"
2377        else:
2378            return message
2379
2380
2381class Finish(Exception):
2382    """An exception that ends the request without producing an error response.
2383
2384    When `Finish` is raised in a `RequestHandler`, the request will
2385    end (calling `RequestHandler.finish` if it hasn't already been
2386    called), but the error-handling methods (including
2387    `RequestHandler.write_error`) will not be called.
2388
2389    If `Finish()` was created with no arguments, the pending response
2390    will be sent as-is. If `Finish()` was given an argument, that
2391    argument will be passed to `RequestHandler.finish()`.
2392
2393    This can be a more convenient way to implement custom error pages
2394    than overriding ``write_error`` (especially in library code)::
2395
2396        if self.current_user is None:
2397            self.set_status(401)
2398            self.set_header('WWW-Authenticate', 'Basic realm="something"')
2399            raise Finish()
2400
2401    .. versionchanged:: 4.3
2402       Arguments passed to ``Finish()`` will be passed on to
2403       `RequestHandler.finish`.
2404    """
2405
2406    pass
2407
2408
2409class MissingArgumentError(HTTPError):
2410    """Exception raised by `RequestHandler.get_argument`.
2411
2412    This is a subclass of `HTTPError`, so if it is uncaught a 400 response
2413    code will be used instead of 500 (and a stack trace will not be logged).
2414
2415    .. versionadded:: 3.1
2416    """
2417
2418    def __init__(self, arg_name: str) -> None:
2419        super().__init__(400, "Missing argument %s" % arg_name)
2420        self.arg_name = arg_name
2421
2422
2423class ErrorHandler(RequestHandler):
2424    """Generates an error response with ``status_code`` for all requests."""
2425
2426    def initialize(self, status_code: int) -> None:
2427        self.set_status(status_code)
2428
2429    def prepare(self) -> None:
2430        raise HTTPError(self._status_code)
2431
2432    def check_xsrf_cookie(self) -> None:
2433        # POSTs to an ErrorHandler don't actually have side effects,
2434        # so we don't need to check the xsrf token.  This allows POSTs
2435        # to the wrong url to return a 404 instead of 403.
2436        pass
2437
2438
2439class RedirectHandler(RequestHandler):
2440    """Redirects the client to the given URL for all GET requests.
2441
2442    You should provide the keyword argument ``url`` to the handler, e.g.::
2443
2444        application = web.Application([
2445            (r"/oldpath", web.RedirectHandler, {"url": "/newpath"}),
2446        ])
2447
2448    `RedirectHandler` supports regular expression substitutions. E.g., to
2449    swap the first and second parts of a path while preserving the remainder::
2450
2451        application = web.Application([
2452            (r"/(.*?)/(.*?)/(.*)", web.RedirectHandler, {"url": "/{1}/{0}/{2}"}),
2453        ])
2454
2455    The final URL is formatted with `str.format` and the substrings that match
2456    the capturing groups. In the above example, a request to "/a/b/c" would be
2457    formatted like::
2458
2459        str.format("/{1}/{0}/{2}", "a", "b", "c")  # -> "/b/a/c"
2460
2461    Use Python's :ref:`format string syntax <formatstrings>` to customize how
2462    values are substituted.
2463
2464    .. versionchanged:: 4.5
2465       Added support for substitutions into the destination URL.
2466
2467    .. versionchanged:: 5.0
2468       If any query arguments are present, they will be copied to the
2469       destination URL.
2470    """
2471
2472    def initialize(self, url: str, permanent: bool = True) -> None:
2473        self._url = url
2474        self._permanent = permanent
2475
2476    def get(self, *args: Any, **kwargs: Any) -> None:
2477        to_url = self._url.format(*args, **kwargs)
2478        if self.request.query_arguments:
2479            # TODO: figure out typing for the next line.
2480            to_url = httputil.url_concat(
2481                to_url,
2482                list(httputil.qs_to_qsl(self.request.query_arguments)),  # type: ignore
2483            )
2484        self.redirect(to_url, permanent=self._permanent)
2485
2486
2487class StaticFileHandler(RequestHandler):
2488    """A simple handler that can serve static content from a directory.
2489
2490    A `StaticFileHandler` is configured automatically if you pass the
2491    ``static_path`` keyword argument to `Application`.  This handler
2492    can be customized with the ``static_url_prefix``, ``static_handler_class``,
2493    and ``static_handler_args`` settings.
2494
2495    To map an additional path to this handler for a static data directory
2496    you would add a line to your application like::
2497
2498        application = web.Application([
2499            (r"/content/(.*)", web.StaticFileHandler, {"path": "/var/www"}),
2500        ])
2501
2502    The handler constructor requires a ``path`` argument, which specifies the
2503    local root directory of the content to be served.
2504
2505    Note that a capture group in the regex is required to parse the value for
2506    the ``path`` argument to the get() method (different than the constructor
2507    argument above); see `URLSpec` for details.
2508
2509    To serve a file like ``index.html`` automatically when a directory is
2510    requested, set ``static_handler_args=dict(default_filename="index.html")``
2511    in your application settings, or add ``default_filename`` as an initializer
2512    argument for your ``StaticFileHandler``.
2513
2514    To maximize the effectiveness of browser caching, this class supports
2515    versioned urls (by default using the argument ``?v=``).  If a version
2516    is given, we instruct the browser to cache this file indefinitely.
2517    `make_static_url` (also available as `RequestHandler.static_url`) can
2518    be used to construct a versioned url.
2519
2520    This handler is intended primarily for use in development and light-duty
2521    file serving; for heavy traffic it will be more efficient to use
2522    a dedicated static file server (such as nginx or Apache).  We support
2523    the HTTP ``Accept-Ranges`` mechanism to return partial content (because
2524    some browsers require this functionality to be present to seek in
2525    HTML5 audio or video).
2526
2527    **Subclassing notes**
2528
2529    This class is designed to be extensible by subclassing, but because
2530    of the way static urls are generated with class methods rather than
2531    instance methods, the inheritance patterns are somewhat unusual.
2532    Be sure to use the ``@classmethod`` decorator when overriding a
2533    class method.  Instance methods may use the attributes ``self.path``
2534    ``self.absolute_path``, and ``self.modified``.
2535
2536    Subclasses should only override methods discussed in this section;
2537    overriding other methods is error-prone.  Overriding
2538    ``StaticFileHandler.get`` is particularly problematic due to the
2539    tight coupling with ``compute_etag`` and other methods.
2540
2541    To change the way static urls are generated (e.g. to match the behavior
2542    of another server or CDN), override `make_static_url`, `parse_url_path`,
2543    `get_cache_time`, and/or `get_version`.
2544
2545    To replace all interaction with the filesystem (e.g. to serve
2546    static content from a database), override `get_content`,
2547    `get_content_size`, `get_modified_time`, `get_absolute_path`, and
2548    `validate_absolute_path`.
2549
2550    .. versionchanged:: 3.1
2551       Many of the methods for subclasses were added in Tornado 3.1.
2552    """
2553
2554    CACHE_MAX_AGE = 86400 * 365 * 10  # 10 years
2555
2556    _static_hashes = {}  # type: Dict[str, Optional[str]]
2557    _lock = threading.Lock()  # protects _static_hashes
2558
2559    def initialize(self, path: str, default_filename: Optional[str] = None) -> None:
2560        self.root = path
2561        self.default_filename = default_filename
2562
2563    @classmethod
2564    def reset(cls) -> None:
2565        with cls._lock:
2566            cls._static_hashes = {}
2567
2568    def head(self, path: str) -> Awaitable[None]:
2569        return self.get(path, include_body=False)
2570
2571    async def get(self, path: str, include_body: bool = True) -> None:
2572        # Set up our path instance variables.
2573        self.path = self.parse_url_path(path)
2574        del path  # make sure we don't refer to path instead of self.path again
2575        absolute_path = self.get_absolute_path(self.root, self.path)
2576        self.absolute_path = self.validate_absolute_path(self.root, absolute_path)
2577        if self.absolute_path is None:
2578            return
2579
2580        self.modified = self.get_modified_time()
2581        self.set_headers()
2582
2583        if self.should_return_304():
2584            self.set_status(304)
2585            return
2586
2587        request_range = None
2588        range_header = self.request.headers.get("Range")
2589        if range_header:
2590            # As per RFC 2616 14.16, if an invalid Range header is specified,
2591            # the request will be treated as if the header didn't exist.
2592            request_range = httputil._parse_request_range(range_header)
2593
2594        size = self.get_content_size()
2595        if request_range:
2596            start, end = request_range
2597            if start is not None and start < 0:
2598                start += size
2599                if start < 0:
2600                    start = 0
2601            if (
2602                start is not None
2603                and (start >= size or (end is not None and start >= end))
2604            ) or end == 0:
2605                # As per RFC 2616 14.35.1, a range is not satisfiable only: if
2606                # the first requested byte is equal to or greater than the
2607                # content, or when a suffix with length 0 is specified.
2608                # https://tools.ietf.org/html/rfc7233#section-2.1
2609                # A byte-range-spec is invalid if the last-byte-pos value is present
2610                # and less than the first-byte-pos.
2611                self.set_status(416)  # Range Not Satisfiable
2612                self.set_header("Content-Type", "text/plain")
2613                self.set_header("Content-Range", "bytes */%s" % (size,))
2614                return
2615            if end is not None and end > size:
2616                # Clients sometimes blindly use a large range to limit their
2617                # download size; cap the endpoint at the actual file size.
2618                end = size
2619            # Note: only return HTTP 206 if less than the entire range has been
2620            # requested. Not only is this semantically correct, but Chrome
2621            # refuses to play audio if it gets an HTTP 206 in response to
2622            # ``Range: bytes=0-``.
2623            if size != (end or size) - (start or 0):
2624                self.set_status(206)  # Partial Content
2625                self.set_header(
2626                    "Content-Range", httputil._get_content_range(start, end, size)
2627                )
2628        else:
2629            start = end = None
2630
2631        if start is not None and end is not None:
2632            content_length = end - start
2633        elif end is not None:
2634            content_length = end
2635        elif start is not None:
2636            content_length = size - start
2637        else:
2638            content_length = size
2639        self.set_header("Content-Length", content_length)
2640
2641        if include_body:
2642            content = self.get_content(self.absolute_path, start, end)
2643            if isinstance(content, bytes):
2644                content = [content]
2645            for chunk in content:
2646                try:
2647                    self.write(chunk)
2648                    await self.flush()
2649                except iostream.StreamClosedError:
2650                    return
2651        else:
2652            assert self.request.method == "HEAD"
2653
2654    def compute_etag(self) -> Optional[str]:
2655        """Sets the ``Etag`` header based on static url version.
2656
2657        This allows efficient ``If-None-Match`` checks against cached
2658        versions, and sends the correct ``Etag`` for a partial response
2659        (i.e. the same ``Etag`` as the full file).
2660
2661        .. versionadded:: 3.1
2662        """
2663        assert self.absolute_path is not None
2664        version_hash = self._get_cached_version(self.absolute_path)
2665        if not version_hash:
2666            return None
2667        return '"%s"' % (version_hash,)
2668
2669    def set_headers(self) -> None:
2670        """Sets the content and caching headers on the response.
2671
2672        .. versionadded:: 3.1
2673        """
2674        self.set_header("Accept-Ranges", "bytes")
2675        self.set_etag_header()
2676
2677        if self.modified is not None:
2678            self.set_header("Last-Modified", self.modified)
2679
2680        content_type = self.get_content_type()
2681        if content_type:
2682            self.set_header("Content-Type", content_type)
2683
2684        cache_time = self.get_cache_time(self.path, self.modified, content_type)
2685        if cache_time > 0:
2686            self.set_header(
2687                "Expires",
2688                datetime.datetime.utcnow() + datetime.timedelta(seconds=cache_time),
2689            )
2690            self.set_header("Cache-Control", "max-age=" + str(cache_time))
2691
2692        self.set_extra_headers(self.path)
2693
2694    def should_return_304(self) -> bool:
2695        """Returns True if the headers indicate that we should return 304.
2696
2697        .. versionadded:: 3.1
2698        """
2699        # If client sent If-None-Match, use it, ignore If-Modified-Since
2700        if self.request.headers.get("If-None-Match"):
2701            return self.check_etag_header()
2702
2703        # Check the If-Modified-Since, and don't send the result if the
2704        # content has not been modified
2705        ims_value = self.request.headers.get("If-Modified-Since")
2706        if ims_value is not None:
2707            date_tuple = email.utils.parsedate(ims_value)
2708            if date_tuple is not None:
2709                if_since = datetime.datetime(*date_tuple[:6])
2710                assert self.modified is not None
2711                if if_since >= self.modified:
2712                    return True
2713
2714        return False
2715
2716    @classmethod
2717    def get_absolute_path(cls, root: str, path: str) -> str:
2718        """Returns the absolute location of ``path`` relative to ``root``.
2719
2720        ``root`` is the path configured for this `StaticFileHandler`
2721        (in most cases the ``static_path`` `Application` setting).
2722
2723        This class method may be overridden in subclasses.  By default
2724        it returns a filesystem path, but other strings may be used
2725        as long as they are unique and understood by the subclass's
2726        overridden `get_content`.
2727
2728        .. versionadded:: 3.1
2729        """
2730        abspath = os.path.abspath(os.path.join(root, path))
2731        return abspath
2732
2733    def validate_absolute_path(self, root: str, absolute_path: str) -> Optional[str]:
2734        """Validate and return the absolute path.
2735
2736        ``root`` is the configured path for the `StaticFileHandler`,
2737        and ``path`` is the result of `get_absolute_path`
2738
2739        This is an instance method called during request processing,
2740        so it may raise `HTTPError` or use methods like
2741        `RequestHandler.redirect` (return None after redirecting to
2742        halt further processing).  This is where 404 errors for missing files
2743        are generated.
2744
2745        This method may modify the path before returning it, but note that
2746        any such modifications will not be understood by `make_static_url`.
2747
2748        In instance methods, this method's result is available as
2749        ``self.absolute_path``.
2750
2751        .. versionadded:: 3.1
2752        """
2753        # os.path.abspath strips a trailing /.
2754        # We must add it back to `root` so that we only match files
2755        # in a directory named `root` instead of files starting with
2756        # that prefix.
2757        root = os.path.abspath(root)
2758        if not root.endswith(os.path.sep):
2759            # abspath always removes a trailing slash, except when
2760            # root is '/'. This is an unusual case, but several projects
2761            # have independently discovered this technique to disable
2762            # Tornado's path validation and (hopefully) do their own,
2763            # so we need to support it.
2764            root += os.path.sep
2765        # The trailing slash also needs to be temporarily added back
2766        # the requested path so a request to root/ will match.
2767        if not (absolute_path + os.path.sep).startswith(root):
2768            raise HTTPError(403, "%s is not in root static directory", self.path)
2769        if os.path.isdir(absolute_path) and self.default_filename is not None:
2770            # need to look at the request.path here for when path is empty
2771            # but there is some prefix to the path that was already
2772            # trimmed by the routing
2773            if not self.request.path.endswith("/"):
2774                self.redirect(self.request.path + "/", permanent=True)
2775                return None
2776            absolute_path = os.path.join(absolute_path, self.default_filename)
2777        if not os.path.exists(absolute_path):
2778            raise HTTPError(404)
2779        if not os.path.isfile(absolute_path):
2780            raise HTTPError(403, "%s is not a file", self.path)
2781        return absolute_path
2782
2783    @classmethod
2784    def get_content(
2785        cls, abspath: str, start: Optional[int] = None, end: Optional[int] = None
2786    ) -> Generator[bytes, None, None]:
2787        """Retrieve the content of the requested resource which is located
2788        at the given absolute path.
2789
2790        This class method may be overridden by subclasses.  Note that its
2791        signature is different from other overridable class methods
2792        (no ``settings`` argument); this is deliberate to ensure that
2793        ``abspath`` is able to stand on its own as a cache key.
2794
2795        This method should either return a byte string or an iterator
2796        of byte strings.  The latter is preferred for large files
2797        as it helps reduce memory fragmentation.
2798
2799        .. versionadded:: 3.1
2800        """
2801        with open(abspath, "rb") as file:
2802            if start is not None:
2803                file.seek(start)
2804            if end is not None:
2805                remaining = end - (start or 0)  # type: Optional[int]
2806            else:
2807                remaining = None
2808            while True:
2809                chunk_size = 64 * 1024
2810                if remaining is not None and remaining < chunk_size:
2811                    chunk_size = remaining
2812                chunk = file.read(chunk_size)
2813                if chunk:
2814                    if remaining is not None:
2815                        remaining -= len(chunk)
2816                    yield chunk
2817                else:
2818                    if remaining is not None:
2819                        assert remaining == 0
2820                    return
2821
2822    @classmethod
2823    def get_content_version(cls, abspath: str) -> str:
2824        """Returns a version string for the resource at the given path.
2825
2826        This class method may be overridden by subclasses.  The
2827        default implementation is a SHA-512 hash of the file's contents.
2828
2829        .. versionadded:: 3.1
2830        """
2831        data = cls.get_content(abspath)
2832        hasher = hashlib.sha512()
2833        if isinstance(data, bytes):
2834            hasher.update(data)
2835        else:
2836            for chunk in data:
2837                hasher.update(chunk)
2838        return hasher.hexdigest()
2839
2840    def _stat(self) -> os.stat_result:
2841        assert self.absolute_path is not None
2842        if not hasattr(self, "_stat_result"):
2843            self._stat_result = os.stat(self.absolute_path)
2844        return self._stat_result
2845
2846    def get_content_size(self) -> int:
2847        """Retrieve the total size of the resource at the given path.
2848
2849        This method may be overridden by subclasses.
2850
2851        .. versionadded:: 3.1
2852
2853        .. versionchanged:: 4.0
2854           This method is now always called, instead of only when
2855           partial results are requested.
2856        """
2857        stat_result = self._stat()
2858        return stat_result.st_size
2859
2860    def get_modified_time(self) -> Optional[datetime.datetime]:
2861        """Returns the time that ``self.absolute_path`` was last modified.
2862
2863        May be overridden in subclasses.  Should return a `~datetime.datetime`
2864        object or None.
2865
2866        .. versionadded:: 3.1
2867        """
2868        stat_result = self._stat()
2869        # NOTE: Historically, this used stat_result[stat.ST_MTIME],
2870        # which truncates the fractional portion of the timestamp. It
2871        # was changed from that form to stat_result.st_mtime to
2872        # satisfy mypy (which disallows the bracket operator), but the
2873        # latter form returns a float instead of an int. For
2874        # consistency with the past (and because we have a unit test
2875        # that relies on this), we truncate the float here, although
2876        # I'm not sure that's the right thing to do.
2877        modified = datetime.datetime.utcfromtimestamp(int(stat_result.st_mtime))
2878        return modified
2879
2880    def get_content_type(self) -> str:
2881        """Returns the ``Content-Type`` header to be used for this request.
2882
2883        .. versionadded:: 3.1
2884        """
2885        assert self.absolute_path is not None
2886        mime_type, encoding = mimetypes.guess_type(self.absolute_path)
2887        # per RFC 6713, use the appropriate type for a gzip compressed file
2888        if encoding == "gzip":
2889            return "application/gzip"
2890        # As of 2015-07-21 there is no bzip2 encoding defined at
2891        # http://www.iana.org/assignments/media-types/media-types.xhtml
2892        # So for that (and any other encoding), use octet-stream.
2893        elif encoding is not None:
2894            return "application/octet-stream"
2895        elif mime_type is not None:
2896            return mime_type
2897        # if mime_type not detected, use application/octet-stream
2898        else:
2899            return "application/octet-stream"
2900
2901    def set_extra_headers(self, path: str) -> None:
2902        """For subclass to add extra headers to the response"""
2903        pass
2904
2905    def get_cache_time(
2906        self, path: str, modified: Optional[datetime.datetime], mime_type: str
2907    ) -> int:
2908        """Override to customize cache control behavior.
2909
2910        Return a positive number of seconds to make the result
2911        cacheable for that amount of time or 0 to mark resource as
2912        cacheable for an unspecified amount of time (subject to
2913        browser heuristics).
2914
2915        By default returns cache expiry of 10 years for resources requested
2916        with ``v`` argument.
2917        """
2918        return self.CACHE_MAX_AGE if "v" in self.request.arguments else 0
2919
2920    @classmethod
2921    def make_static_url(
2922        cls, settings: Dict[str, Any], path: str, include_version: bool = True
2923    ) -> str:
2924        """Constructs a versioned url for the given path.
2925
2926        This method may be overridden in subclasses (but note that it
2927        is a class method rather than an instance method).  Subclasses
2928        are only required to implement the signature
2929        ``make_static_url(cls, settings, path)``; other keyword
2930        arguments may be passed through `~RequestHandler.static_url`
2931        but are not standard.
2932
2933        ``settings`` is the `Application.settings` dictionary.  ``path``
2934        is the static path being requested.  The url returned should be
2935        relative to the current host.
2936
2937        ``include_version`` determines whether the generated URL should
2938        include the query string containing the version hash of the
2939        file corresponding to the given ``path``.
2940
2941        """
2942        url = settings.get("static_url_prefix", "/static/") + path
2943        if not include_version:
2944            return url
2945
2946        version_hash = cls.get_version(settings, path)
2947        if not version_hash:
2948            return url
2949
2950        return "%s?v=%s" % (url, version_hash)
2951
2952    def parse_url_path(self, url_path: str) -> str:
2953        """Converts a static URL path into a filesystem path.
2954
2955        ``url_path`` is the path component of the URL with
2956        ``static_url_prefix`` removed.  The return value should be
2957        filesystem path relative to ``static_path``.
2958
2959        This is the inverse of `make_static_url`.
2960        """
2961        if os.path.sep != "/":
2962            url_path = url_path.replace("/", os.path.sep)
2963        return url_path
2964
2965    @classmethod
2966    def get_version(cls, settings: Dict[str, Any], path: str) -> Optional[str]:
2967        """Generate the version string to be used in static URLs.
2968
2969        ``settings`` is the `Application.settings` dictionary and ``path``
2970        is the relative location of the requested asset on the filesystem.
2971        The returned value should be a string, or ``None`` if no version
2972        could be determined.
2973
2974        .. versionchanged:: 3.1
2975           This method was previously recommended for subclasses to override;
2976           `get_content_version` is now preferred as it allows the base
2977           class to handle caching of the result.
2978        """
2979        abs_path = cls.get_absolute_path(settings["static_path"], path)
2980        return cls._get_cached_version(abs_path)
2981
2982    @classmethod
2983    def _get_cached_version(cls, abs_path: str) -> Optional[str]:
2984        with cls._lock:
2985            hashes = cls._static_hashes
2986            if abs_path not in hashes:
2987                try:
2988                    hashes[abs_path] = cls.get_content_version(abs_path)
2989                except Exception:
2990                    gen_log.error("Could not open static file %r", abs_path)
2991                    hashes[abs_path] = None
2992            hsh = hashes.get(abs_path)
2993            if hsh:
2994                return hsh
2995        return None
2996
2997
2998class FallbackHandler(RequestHandler):
2999    """A `RequestHandler` that wraps another HTTP server callback.
3000
3001    The fallback is a callable object that accepts an
3002    `~.httputil.HTTPServerRequest`, such as an `Application` or
3003    `tornado.wsgi.WSGIContainer`.  This is most useful to use both
3004    Tornado ``RequestHandlers`` and WSGI in the same server.  Typical
3005    usage::
3006
3007        wsgi_app = tornado.wsgi.WSGIContainer(
3008            django.core.handlers.wsgi.WSGIHandler())
3009        application = tornado.web.Application([
3010            (r"/foo", FooHandler),
3011            (r".*", FallbackHandler, dict(fallback=wsgi_app),
3012        ])
3013    """
3014
3015    def initialize(
3016        self, fallback: Callable[[httputil.HTTPServerRequest], None]
3017    ) -> None:
3018        self.fallback = fallback
3019
3020    def prepare(self) -> None:
3021        self.fallback(self.request)
3022        self._finished = True
3023        self.on_finish()
3024
3025
3026class OutputTransform(object):
3027    """A transform modifies the result of an HTTP request (e.g., GZip encoding)
3028
3029    Applications are not expected to create their own OutputTransforms
3030    or interact with them directly; the framework chooses which transforms
3031    (if any) to apply.
3032    """
3033
3034    def __init__(self, request: httputil.HTTPServerRequest) -> None:
3035        pass
3036
3037    def transform_first_chunk(
3038        self,
3039        status_code: int,
3040        headers: httputil.HTTPHeaders,
3041        chunk: bytes,
3042        finishing: bool,
3043    ) -> Tuple[int, httputil.HTTPHeaders, bytes]:
3044        return status_code, headers, chunk
3045
3046    def transform_chunk(self, chunk: bytes, finishing: bool) -> bytes:
3047        return chunk
3048
3049
3050class GZipContentEncoding(OutputTransform):
3051    """Applies the gzip content encoding to the response.
3052
3053    See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
3054
3055    .. versionchanged:: 4.0
3056        Now compresses all mime types beginning with ``text/``, instead
3057        of just a whitelist. (the whitelist is still used for certain
3058        non-text mime types).
3059    """
3060
3061    # Whitelist of compressible mime types (in addition to any types
3062    # beginning with "text/").
3063    CONTENT_TYPES = set(
3064        [
3065            "application/javascript",
3066            "application/x-javascript",
3067            "application/xml",
3068            "application/atom+xml",
3069            "application/json",
3070            "application/xhtml+xml",
3071            "image/svg+xml",
3072        ]
3073    )
3074    # Python's GzipFile defaults to level 9, while most other gzip
3075    # tools (including gzip itself) default to 6, which is probably a
3076    # better CPU/size tradeoff.
3077    GZIP_LEVEL = 6
3078    # Responses that are too short are unlikely to benefit from gzipping
3079    # after considering the "Content-Encoding: gzip" header and the header
3080    # inside the gzip encoding.
3081    # Note that responses written in multiple chunks will be compressed
3082    # regardless of size.
3083    MIN_LENGTH = 1024
3084
3085    def __init__(self, request: httputil.HTTPServerRequest) -> None:
3086        self._gzipping = "gzip" in request.headers.get("Accept-Encoding", "")
3087
3088    def _compressible_type(self, ctype: str) -> bool:
3089        return ctype.startswith("text/") or ctype in self.CONTENT_TYPES
3090
3091    def transform_first_chunk(
3092        self,
3093        status_code: int,
3094        headers: httputil.HTTPHeaders,
3095        chunk: bytes,
3096        finishing: bool,
3097    ) -> Tuple[int, httputil.HTTPHeaders, bytes]:
3098        # TODO: can/should this type be inherited from the superclass?
3099        if "Vary" in headers:
3100            headers["Vary"] += ", Accept-Encoding"
3101        else:
3102            headers["Vary"] = "Accept-Encoding"
3103        if self._gzipping:
3104            ctype = _unicode(headers.get("Content-Type", "")).split(";")[0]
3105            self._gzipping = (
3106                self._compressible_type(ctype)
3107                and (not finishing or len(chunk) >= self.MIN_LENGTH)
3108                and ("Content-Encoding" not in headers)
3109            )
3110        if self._gzipping:
3111            headers["Content-Encoding"] = "gzip"
3112            self._gzip_value = BytesIO()
3113            self._gzip_file = gzip.GzipFile(
3114                mode="w", fileobj=self._gzip_value, compresslevel=self.GZIP_LEVEL
3115            )
3116            chunk = self.transform_chunk(chunk, finishing)
3117            if "Content-Length" in headers:
3118                # The original content length is no longer correct.
3119                # If this is the last (and only) chunk, we can set the new
3120                # content-length; otherwise we remove it and fall back to
3121                # chunked encoding.
3122                if finishing:
3123                    headers["Content-Length"] = str(len(chunk))
3124                else:
3125                    del headers["Content-Length"]
3126        return status_code, headers, chunk
3127
3128    def transform_chunk(self, chunk: bytes, finishing: bool) -> bytes:
3129        if self._gzipping:
3130            self._gzip_file.write(chunk)
3131            if finishing:
3132                self._gzip_file.close()
3133            else:
3134                self._gzip_file.flush()
3135            chunk = self._gzip_value.getvalue()
3136            self._gzip_value.truncate(0)
3137            self._gzip_value.seek(0)
3138        return chunk
3139
3140
3141def authenticated(
3142    method: Callable[..., Optional[Awaitable[None]]]
3143) -> Callable[..., Optional[Awaitable[None]]]:
3144    """Decorate methods with this to require that the user be logged in.
3145
3146    If the user is not logged in, they will be redirected to the configured
3147    `login url <RequestHandler.get_login_url>`.
3148
3149    If you configure a login url with a query parameter, Tornado will
3150    assume you know what you're doing and use it as-is.  If not, it
3151    will add a `next` parameter so the login page knows where to send
3152    you once you're logged in.
3153    """
3154
3155    @functools.wraps(method)
3156    def wrapper(  # type: ignore
3157        self: RequestHandler, *args, **kwargs
3158    ) -> Optional[Awaitable[None]]:
3159        if not self.current_user:
3160            if self.request.method in ("GET", "HEAD"):
3161                url = self.get_login_url()
3162                if "?" not in url:
3163                    if urllib.parse.urlsplit(url).scheme:
3164                        # if login url is absolute, make next absolute too
3165                        next_url = self.request.full_url()
3166                    else:
3167                        assert self.request.uri is not None
3168                        next_url = self.request.uri
3169                    url += "?" + urlencode(dict(next=next_url))
3170                self.redirect(url)
3171                return None
3172            raise HTTPError(403)
3173        return method(self, *args, **kwargs)
3174
3175    return wrapper
3176
3177
3178class UIModule(object):
3179    """A re-usable, modular UI unit on a page.
3180
3181    UI modules often execute additional queries, and they can include
3182    additional CSS and JavaScript that will be included in the output
3183    page, which is automatically inserted on page render.
3184
3185    Subclasses of UIModule must override the `render` method.
3186    """
3187
3188    def __init__(self, handler: RequestHandler) -> None:
3189        self.handler = handler
3190        self.request = handler.request
3191        self.ui = handler.ui
3192        self.locale = handler.locale
3193
3194    @property
3195    def current_user(self) -> Any:
3196        return self.handler.current_user
3197
3198    def render(self, *args: Any, **kwargs: Any) -> str:
3199        """Override in subclasses to return this module's output."""
3200        raise NotImplementedError()
3201
3202    def embedded_javascript(self) -> Optional[str]:
3203        """Override to return a JavaScript string
3204        to be embedded in the page."""
3205        return None
3206
3207    def javascript_files(self) -> Optional[Iterable[str]]:
3208        """Override to return a list of JavaScript files needed by this module.
3209
3210        If the return values are relative paths, they will be passed to
3211        `RequestHandler.static_url`; otherwise they will be used as-is.
3212        """
3213        return None
3214
3215    def embedded_css(self) -> Optional[str]:
3216        """Override to return a CSS string
3217        that will be embedded in the page."""
3218        return None
3219
3220    def css_files(self) -> Optional[Iterable[str]]:
3221        """Override to returns a list of CSS files required by this module.
3222
3223        If the return values are relative paths, they will be passed to
3224        `RequestHandler.static_url`; otherwise they will be used as-is.
3225        """
3226        return None
3227
3228    def html_head(self) -> Optional[str]:
3229        """Override to return an HTML string that will be put in the <head/>
3230        element.
3231        """
3232        return None
3233
3234    def html_body(self) -> Optional[str]:
3235        """Override to return an HTML string that will be put at the end of
3236        the <body/> element.
3237        """
3238        return None
3239
3240    def render_string(self, path: str, **kwargs: Any) -> bytes:
3241        """Renders a template and returns it as a string."""
3242        return self.handler.render_string(path, **kwargs)
3243
3244
3245class _linkify(UIModule):
3246    def render(self, text: str, **kwargs: Any) -> str:  # type: ignore
3247        return escape.linkify(text, **kwargs)
3248
3249
3250class _xsrf_form_html(UIModule):
3251    def render(self) -> str:  # type: ignore
3252        return self.handler.xsrf_form_html()
3253
3254
3255class TemplateModule(UIModule):
3256    """UIModule that simply renders the given template.
3257
3258    {% module Template("foo.html") %} is similar to {% include "foo.html" %},
3259    but the module version gets its own namespace (with kwargs passed to
3260    Template()) instead of inheriting the outer template's namespace.
3261
3262    Templates rendered through this module also get access to UIModule's
3263    automatic JavaScript/CSS features.  Simply call set_resources
3264    inside the template and give it keyword arguments corresponding to
3265    the methods on UIModule: {{ set_resources(js_files=static_url("my.js")) }}
3266    Note that these resources are output once per template file, not once
3267    per instantiation of the template, so they must not depend on
3268    any arguments to the template.
3269    """
3270
3271    def __init__(self, handler: RequestHandler) -> None:
3272        super().__init__(handler)
3273        # keep resources in both a list and a dict to preserve order
3274        self._resource_list = []  # type: List[Dict[str, Any]]
3275        self._resource_dict = {}  # type: Dict[str, Dict[str, Any]]
3276
3277    def render(self, path: str, **kwargs: Any) -> bytes:  # type: ignore
3278        def set_resources(**kwargs) -> str:  # type: ignore
3279            if path not in self._resource_dict:
3280                self._resource_list.append(kwargs)
3281                self._resource_dict[path] = kwargs
3282            else:
3283                if self._resource_dict[path] != kwargs:
3284                    raise ValueError(
3285                        "set_resources called with different "
3286                        "resources for the same template"
3287                    )
3288            return ""
3289
3290        return self.render_string(path, set_resources=set_resources, **kwargs)
3291
3292    def _get_resources(self, key: str) -> Iterable[str]:
3293        return (r[key] for r in self._resource_list if key in r)
3294
3295    def embedded_javascript(self) -> str:
3296        return "\n".join(self._get_resources("embedded_javascript"))
3297
3298    def javascript_files(self) -> Iterable[str]:
3299        result = []
3300        for f in self._get_resources("javascript_files"):
3301            if isinstance(f, (unicode_type, bytes)):
3302                result.append(f)
3303            else:
3304                result.extend(f)
3305        return result
3306
3307    def embedded_css(self) -> str:
3308        return "\n".join(self._get_resources("embedded_css"))
3309
3310    def css_files(self) -> Iterable[str]:
3311        result = []
3312        for f in self._get_resources("css_files"):
3313            if isinstance(f, (unicode_type, bytes)):
3314                result.append(f)
3315            else:
3316                result.extend(f)
3317        return result
3318
3319    def html_head(self) -> str:
3320        return "".join(self._get_resources("html_head"))
3321
3322    def html_body(self) -> str:
3323        return "".join(self._get_resources("html_body"))
3324
3325
3326class _UIModuleNamespace(object):
3327    """Lazy namespace which creates UIModule proxies bound to a handler."""
3328
3329    def __init__(
3330        self, handler: RequestHandler, ui_modules: Dict[str, Type[UIModule]]
3331    ) -> None:
3332        self.handler = handler
3333        self.ui_modules = ui_modules
3334
3335    def __getitem__(self, key: str) -> Callable[..., str]:
3336        return self.handler._ui_module(key, self.ui_modules[key])
3337
3338    def __getattr__(self, key: str) -> Callable[..., str]:
3339        try:
3340            return self[key]
3341        except KeyError as e:
3342            raise AttributeError(str(e))
3343
3344
3345def create_signed_value(
3346    secret: _CookieSecretTypes,
3347    name: str,
3348    value: Union[str, bytes],
3349    version: Optional[int] = None,
3350    clock: Optional[Callable[[], float]] = None,
3351    key_version: Optional[int] = None,
3352) -> bytes:
3353    if version is None:
3354        version = DEFAULT_SIGNED_VALUE_VERSION
3355    if clock is None:
3356        clock = time.time
3357
3358    timestamp = utf8(str(int(clock())))
3359    value = base64.b64encode(utf8(value))
3360    if version == 1:
3361        assert not isinstance(secret, dict)
3362        signature = _create_signature_v1(secret, name, value, timestamp)
3363        value = b"|".join([value, timestamp, signature])
3364        return value
3365    elif version == 2:
3366        # The v2 format consists of a version number and a series of
3367        # length-prefixed fields "%d:%s", the last of which is a
3368        # signature, all separated by pipes.  All numbers are in
3369        # decimal format with no leading zeros.  The signature is an
3370        # HMAC-SHA256 of the whole string up to that point, including
3371        # the final pipe.
3372        #
3373        # The fields are:
3374        # - format version (i.e. 2; no length prefix)
3375        # - key version (integer, default is 0)
3376        # - timestamp (integer seconds since epoch)
3377        # - name (not encoded; assumed to be ~alphanumeric)
3378        # - value (base64-encoded)
3379        # - signature (hex-encoded; no length prefix)
3380        def format_field(s: Union[str, bytes]) -> bytes:
3381            return utf8("%d:" % len(s)) + utf8(s)
3382
3383        to_sign = b"|".join(
3384            [
3385                b"2",
3386                format_field(str(key_version or 0)),
3387                format_field(timestamp),
3388                format_field(name),
3389                format_field(value),
3390                b"",
3391            ]
3392        )
3393
3394        if isinstance(secret, dict):
3395            assert (
3396                key_version is not None
3397            ), "Key version must be set when sign key dict is used"
3398            assert version >= 2, "Version must be at least 2 for key version support"
3399            secret = secret[key_version]
3400
3401        signature = _create_signature_v2(secret, to_sign)
3402        return to_sign + signature
3403    else:
3404        raise ValueError("Unsupported version %d" % version)
3405
3406
3407# A leading version number in decimal
3408# with no leading zeros, followed by a pipe.
3409_signed_value_version_re = re.compile(br"^([1-9][0-9]*)\|(.*)$")
3410
3411
3412def _get_version(value: bytes) -> int:
3413    # Figures out what version value is.  Version 1 did not include an
3414    # explicit version field and started with arbitrary base64 data,
3415    # which makes this tricky.
3416    m = _signed_value_version_re.match(value)
3417    if m is None:
3418        version = 1
3419    else:
3420        try:
3421            version = int(m.group(1))
3422            if version > 999:
3423                # Certain payloads from the version-less v1 format may
3424                # be parsed as valid integers.  Due to base64 padding
3425                # restrictions, this can only happen for numbers whose
3426                # length is a multiple of 4, so we can treat all
3427                # numbers up to 999 as versions, and for the rest we
3428                # fall back to v1 format.
3429                version = 1
3430        except ValueError:
3431            version = 1
3432    return version
3433
3434
3435def decode_signed_value(
3436    secret: _CookieSecretTypes,
3437    name: str,
3438    value: Union[None, str, bytes],
3439    max_age_days: float = 31,
3440    clock: Optional[Callable[[], float]] = None,
3441    min_version: Optional[int] = None,
3442) -> Optional[bytes]:
3443    if clock is None:
3444        clock = time.time
3445    if min_version is None:
3446        min_version = DEFAULT_SIGNED_VALUE_MIN_VERSION
3447    if min_version > 2:
3448        raise ValueError("Unsupported min_version %d" % min_version)
3449    if not value:
3450        return None
3451
3452    value = utf8(value)
3453    version = _get_version(value)
3454
3455    if version < min_version:
3456        return None
3457    if version == 1:
3458        assert not isinstance(secret, dict)
3459        return _decode_signed_value_v1(secret, name, value, max_age_days, clock)
3460    elif version == 2:
3461        return _decode_signed_value_v2(secret, name, value, max_age_days, clock)
3462    else:
3463        return None
3464
3465
3466def _decode_signed_value_v1(
3467    secret: Union[str, bytes],
3468    name: str,
3469    value: bytes,
3470    max_age_days: float,
3471    clock: Callable[[], float],
3472) -> Optional[bytes]:
3473    parts = utf8(value).split(b"|")
3474    if len(parts) != 3:
3475        return None
3476    signature = _create_signature_v1(secret, name, parts[0], parts[1])
3477    if not hmac.compare_digest(parts[2], signature):
3478        gen_log.warning("Invalid cookie signature %r", value)
3479        return None
3480    timestamp = int(parts[1])
3481    if timestamp < clock() - max_age_days * 86400:
3482        gen_log.warning("Expired cookie %r", value)
3483        return None
3484    if timestamp > clock() + 31 * 86400:
3485        # _cookie_signature does not hash a delimiter between the
3486        # parts of the cookie, so an attacker could transfer trailing
3487        # digits from the payload to the timestamp without altering the
3488        # signature.  For backwards compatibility, sanity-check timestamp
3489        # here instead of modifying _cookie_signature.
3490        gen_log.warning("Cookie timestamp in future; possible tampering %r", value)
3491        return None
3492    if parts[1].startswith(b"0"):
3493        gen_log.warning("Tampered cookie %r", value)
3494        return None
3495    try:
3496        return base64.b64decode(parts[0])
3497    except Exception:
3498        return None
3499
3500
3501def _decode_fields_v2(value: bytes) -> Tuple[int, bytes, bytes, bytes, bytes]:
3502    def _consume_field(s: bytes) -> Tuple[bytes, bytes]:
3503        length, _, rest = s.partition(b":")
3504        n = int(length)
3505        field_value = rest[:n]
3506        # In python 3, indexing bytes returns small integers; we must
3507        # use a slice to get a byte string as in python 2.
3508        if rest[n : n + 1] != b"|":
3509            raise ValueError("malformed v2 signed value field")
3510        rest = rest[n + 1 :]
3511        return field_value, rest
3512
3513    rest = value[2:]  # remove version number
3514    key_version, rest = _consume_field(rest)
3515    timestamp, rest = _consume_field(rest)
3516    name_field, rest = _consume_field(rest)
3517    value_field, passed_sig = _consume_field(rest)
3518    return int(key_version), timestamp, name_field, value_field, passed_sig
3519
3520
3521def _decode_signed_value_v2(
3522    secret: _CookieSecretTypes,
3523    name: str,
3524    value: bytes,
3525    max_age_days: float,
3526    clock: Callable[[], float],
3527) -> Optional[bytes]:
3528    try:
3529        (
3530            key_version,
3531            timestamp_bytes,
3532            name_field,
3533            value_field,
3534            passed_sig,
3535        ) = _decode_fields_v2(value)
3536    except ValueError:
3537        return None
3538    signed_string = value[: -len(passed_sig)]
3539
3540    if isinstance(secret, dict):
3541        try:
3542            secret = secret[key_version]
3543        except KeyError:
3544            return None
3545
3546    expected_sig = _create_signature_v2(secret, signed_string)
3547    if not hmac.compare_digest(passed_sig, expected_sig):
3548        return None
3549    if name_field != utf8(name):
3550        return None
3551    timestamp = int(timestamp_bytes)
3552    if timestamp < clock() - max_age_days * 86400:
3553        # The signature has expired.
3554        return None
3555    try:
3556        return base64.b64decode(value_field)
3557    except Exception:
3558        return None
3559
3560
3561def get_signature_key_version(value: Union[str, bytes]) -> Optional[int]:
3562    value = utf8(value)
3563    version = _get_version(value)
3564    if version < 2:
3565        return None
3566    try:
3567        key_version, _, _, _, _ = _decode_fields_v2(value)
3568    except ValueError:
3569        return None
3570
3571    return key_version
3572
3573
3574def _create_signature_v1(secret: Union[str, bytes], *parts: Union[str, bytes]) -> bytes:
3575    hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
3576    for part in parts:
3577        hash.update(utf8(part))
3578    return utf8(hash.hexdigest())
3579
3580
3581def _create_signature_v2(secret: Union[str, bytes], s: bytes) -> bytes:
3582    hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
3583    hash.update(utf8(s))
3584    return utf8(hash.hexdigest())
3585
3586
3587def is_absolute(path: str) -> bool:
3588    return any(path.startswith(x) for x in ["/", "http:", "https:"])
3589