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

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

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

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

    import tornado.ioloop
    import tornado.web

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

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

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


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

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

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

B
Bret Taylor 已提交
58 59
"""

60
from __future__ import absolute_import, division, print_function
61

B
Bret Taylor 已提交
62 63 64 65 66
import base64
import binascii
import datetime
import email.utils
import functools
67
import gzip
B
Bret Taylor 已提交
68 69 70
import hashlib
import hmac
import mimetypes
71
import numbers
B
Bret Taylor 已提交
72 73 74 75
import os.path
import re
import stat
import sys
76
import threading
B
Bret Taylor 已提交
77
import time
78
import tornado
79
import traceback
B
Bret Taylor 已提交
80
import types
A
Andrey Sumin 已提交
81
from inspect import isclass
M
Mikhail Korobov 已提交
82
from io import BytesIO
B
Bret Taylor 已提交
83

84
from tornado.concurrent import Future, future_set_result_unless_cancelled
85
from tornado import escape
86
from tornado import gen
87
from tornado import httputil
88
from tornado import iostream
89
from tornado import locale
90
from tornado.log import access_log, app_log, gen_log
91 92
from tornado import stack_context
from tornado import template
93
from tornado.escape import utf8, _unicode
A
Andrey Sumin 已提交
94 95 96 97 98 99 100
from tornado.routing import (AnyMatches, DefaultHostMatches, HostMatches,
                             ReversibleRouter, Rule, ReversibleRuleRouter,
                             URLSpec)
from tornado.util import (ObjectDict, raise_exc_info,
                          unicode_type, _websocket_mask, PY3)

url = URLSpec
101

102 103 104 105 106 107 108 109
if PY3:
    import http.cookies as Cookie
    import urllib.parse as urlparse
    from urllib.parse import urlencode
else:
    import Cookie
    import urlparse
    from urllib import urlencode
110

111 112
try:
    import typing  # noqa
113 114 115 116 117

    # The following types are accepted by RequestHandler.set_header
    # and related methods.
    _HeaderTypes = typing.Union[bytes, unicode_type,
                                numbers.Integral, datetime.datetime]
118 119 120
except ImportError:
    pass

121

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
MIN_SUPPORTED_SIGNED_VALUE_VERSION = 1
"""The oldest signed value version supported by this version of Tornado.

Signed values older than this version cannot be decoded.

.. versionadded:: 3.2.1
"""

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

Signed values newer than this version cannot be decoded.

.. versionadded:: 3.2.1
"""

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

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

.. versionadded:: 3.2.1
"""

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

149
May be overridden by passing a ``min_version`` keyword argument.
150 151 152 153

.. versionadded:: 3.2.1
"""

B
Ben Darnell 已提交
154

B
Bret Taylor 已提交
155
class RequestHandler(object):
B
Ben Darnell 已提交
156
    """Base class for HTTP request handlers.
B
Bret Taylor 已提交
157

B
Ben Darnell 已提交
158 159
    Subclasses must define at least one of the methods defined in the
    "Entry points" section below.
B
Bret Taylor 已提交
160
    """
161 162
    SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT",
                         "OPTIONS")
B
Bret Taylor 已提交
163

164
    _template_loaders = {}  # type: typing.Dict[str, template.BaseLoader]
165
    _template_loader_lock = threading.Lock()
166
    _remove_control_chars_regex = re.compile(r"[\x00-\x08\x0e-\x1f]")
167

168
    def __init__(self, application, request, **kwargs):
169
        super(RequestHandler, self).__init__()
170

B
Bret Taylor 已提交
171 172 173 174 175
        self.application = application
        self.request = request
        self._headers_written = False
        self._finished = False
        self._auto_finish = True
176
        self._transforms = None  # will be set in _execute
177
        self._prepared_future = None
178
        self._headers = None  # type: httputil.HTTPHeaders
179 180
        self.path_args = None
        self.path_kwargs = None
A
Alek Storm 已提交
181
        self.ui = ObjectDict((n, self._ui_method(m)) for n, m in
182
                             application.ui_methods.items())
183
        # UIModules are available as both `modules` and `_tt_modules` in the
B
Ben Darnell 已提交
184 185
        # template namespace.  Historically only `modules` was available
        # but could be clobbered by user additions to the namespace.
186
        # The template {% module %} directive looks in `_tt_modules` to avoid
B
Ben Darnell 已提交
187
        # possible conflicts.
188 189
        self.ui["_tt_modules"] = _UIModuleNamespace(self,
                                                    application.ui_modules)
190
        self.ui["modules"] = self.ui["_tt_modules"]
B
Bret Taylor 已提交
191
        self.clear()
192
        self.request.connection.set_close_callback(self.on_connection_close)
193 194 195
        self.initialize(**kwargs)

    def initialize(self):
H
Hermann Schachner 已提交
196
        """Hook for subclass initialization. Called for each request.
A
afg 已提交
197

198 199 200
        A dictionary passed as the third argument of a url spec will be
        supplied as keyword arguments to initialize().

B
Ben Darnell 已提交
201
        Example::
B
Ben Darnell 已提交
202

203 204 205 206 207 208 209 210 211 212 213 214
            class ProfileHandler(RequestHandler):
                def initialize(self, database):
                    self.database = database

                def get(self, username):
                    ...

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

    @property
    def settings(self):
218
        """An alias for `self.application.settings <Application.settings>`."""
B
Bret Taylor 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232
        return self.application.settings

    def head(self, *args, **kwargs):
        raise HTTPError(405)

    def get(self, *args, **kwargs):
        raise HTTPError(405)

    def post(self, *args, **kwargs):
        raise HTTPError(405)

    def delete(self, *args, **kwargs):
        raise HTTPError(405)

233 234 235
    def patch(self, *args, **kwargs):
        raise HTTPError(405)

B
Bret Taylor 已提交
236 237 238
    def put(self, *args, **kwargs):
        raise HTTPError(405)

239 240 241
    def options(self, *args, **kwargs):
        raise HTTPError(405)

B
Bret Taylor 已提交
242
    def prepare(self):
243
        """Called at the beginning of a request before  `get`/`post`/etc.
B
Bret Taylor 已提交
244

245 246
        Override this method to perform common initialization regardless
        of the request method.
247 248

        Asynchronous support: Decorate this method with `.gen.coroutine`
249
        or use ``async def`` to make it asynchronous (the
250 251 252
        `asynchronous` decorator cannot be used on `prepare`).
        If this method returns a `.Future` execution will not proceed
        until the `.Future` is done.
253 254 255

        .. versionadded:: 3.1
           Asynchronous support.
256 257 258 259 260 261 262 263 264 265
        """
        pass

    def on_finish(self):
        """Called after the end of a request.

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

269 270 271
    def on_connection_close(self):
        """Called in async handlers if the client closed the connection.

272 273 274 275 276
        Override this to clean up resources associated with
        long-lived connections.  Note that this method is called only if
        the connection was closed during asynchronous processing; if you
        need to do cleanup after every request override `on_finish`
        instead.
277

278 279 280 281
        Proxies may keep a connection open for a time (perhaps
        indefinitely) after the client has gone away, so this method
        may not be called promptly after the end user closes their
        connection.
282
        """
283 284 285
        if _has_stream_request_body(self.__class__):
            if not self.request.body.done():
                self.request.body.set_exception(iostream.StreamClosedError())
286
                self.request.body.exception()
287

B
Bret Taylor 已提交
288 289
    def clear(self):
        """Resets all headers and content for this response."""
290
        self._headers = httputil.HTTPHeaders({
B
Ben Darnell 已提交
291 292
            "Server": "TornadoServer/%s" % tornado.version,
            "Content-Type": "text/html; charset=UTF-8",
293
            "Date": httputil.format_timestamp(time.time()),
B
Ben Darnell 已提交
294
        })
295
        self.set_default_headers()
B
Bret Taylor 已提交
296 297
        self._write_buffer = []
        self._status_code = 200
298
        self._reason = httputil.responses[200]
B
Bret Taylor 已提交
299

300 301 302 303 304 305 306 307 308 309
    def set_default_headers(self):
        """Override this to set HTTP headers at the beginning of the request.

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

310 311 312
    def set_status(self, status_code, reason=None):
        """Sets the status code for our response.

B
Ben Darnell 已提交
313
        :arg int status_code: Response status code.
B
Ben Darnell 已提交
314
        :arg str reason: Human-readable reason phrase describing the status
315
            code. If ``None``, it will be filled in from
B
Ben Darnell 已提交
316 317 318 319 320 321
            `http.client.responses` or "Unknown".

        .. versionchanged:: 5.0

           No longer validates that the response code is in
           `http.client.responses`.
322
        """
B
Bret Taylor 已提交
323
        self._status_code = status_code
324 325 326
        if reason is not None:
            self._reason = escape.native_str(reason)
        else:
327
            self._reason = httputil.responses.get(status_code, "Unknown")
B
Bret Taylor 已提交
328

329 330 331 332
    def get_status(self):
        """Returns the status code for our response."""
        return self._status_code

B
Bret Taylor 已提交
333
    def set_header(self, name, value):
334
        # type: (str, _HeaderTypes) -> None
B
Bret Taylor 已提交
335 336 337 338 339 340
        """Sets the given response header name and value.

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

    def add_header(self, name, value):
344
        # type: (str, _HeaderTypes) -> None
345 346 347 348 349
        """Adds the given response header and value.

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

352 353 354 355 356 357 358 359 360
    def clear_header(self, name):
        """Clears an outgoing header, undoing a previous `set_header` call.

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

361
    _INVALID_HEADER_CHAR_RE = re.compile(r"[\x00-\x1f]")
362

363
    def _convert_header_value(self, value):
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
        # type: (_HeaderTypes) -> str

        # Convert the input value to a str. This type check is a bit
        # subtle: The bytes case only executes on python 3, and the
        # unicode case only executes on python 2, because the other
        # cases are covered by the first match for str.
        if isinstance(value, str):
            retval = value
        elif isinstance(value, bytes):  # py3
            # Non-ascii characters in headers are not well supported,
            # but if you pass bytes, use latin1 so they pass through as-is.
            retval = value.decode('latin1')
        elif isinstance(value, unicode_type):  # py2
            # TODO: This is inconsistent with the use of latin1 above,
            # but it's been that way for a long time. Should it change?
            retval = escape.utf8(value)
380
        elif isinstance(value, numbers.Integral):
B
Ben Darnell 已提交
381 382
            # return immediately since we know the converted value will be safe
            return str(value)
B
Ben Darnell 已提交
383
        elif isinstance(value, datetime.datetime):
384
            return httputil.format_timestamp(value)
B
Ben Darnell 已提交
385 386
        else:
            raise TypeError("Unsupported header value %r" % value)
B
Ben Darnell 已提交
387
        # If \n is allowed into the header, it is possible to inject
388
        # additional headers or split the request.
389 390 391
        if RequestHandler._INVALID_HEADER_CHAR_RE.search(retval):
            raise ValueError("Unsafe header value %r", retval)
        return retval
392

393
    _ARG_DEFAULT = object()
394

B
Bret Taylor 已提交
395 396 397 398
    def get_argument(self, name, default=_ARG_DEFAULT, strip=True):
        """Returns the value of the argument with the given name.

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

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

B
Bret Taylor 已提交
404 405
        The returned value is always unicode.
        """
T
Travis Beauvais 已提交
406 407 408 409 410 411 412 413 414
        return self._get_argument(name, default, self.request.arguments, strip)

    def get_arguments(self, name, strip=True):
        """Returns a list of the arguments with the given name.

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

        The returned values are always unicode.
        """
415 416 417 418 419 420

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

T
Travis Beauvais 已提交
421
        return self._get_arguments(name, self.request.arguments, strip)
T
Travis Beauvais 已提交
422 423 424 425 426 427 428 429 430 431 432 433

    def get_body_argument(self, name, default=_ARG_DEFAULT, strip=True):
        """Returns the value of the argument with the given name
        from the request body.

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

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

        The returned value is always unicode.
B
Ben Darnell 已提交
434 435

        .. versionadded:: 3.2
T
Travis Beauvais 已提交
436
        """
G
Gonzalo Rafuls 已提交
437 438
        return self._get_argument(name, default, self.request.body_arguments,
                                  strip)
T
Travis Beauvais 已提交
439 440 441 442 443 444 445

    def get_body_arguments(self, name, strip=True):
        """Returns a list of the body arguments with the given name.

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

        The returned values are always unicode.
B
Ben Darnell 已提交
446 447

        .. versionadded:: 3.2
T
Travis Beauvais 已提交
448 449
        """
        return self._get_arguments(name, self.request.body_arguments, strip)
T
Travis Beauvais 已提交
450 451 452 453 454 455 456 457 458 459 460 461

    def get_query_argument(self, name, default=_ARG_DEFAULT, strip=True):
        """Returns the value of the argument with the given name
        from the request query string.

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

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

        The returned value is always unicode.
B
Ben Darnell 已提交
462 463

        .. versionadded:: 3.2
T
Travis Beauvais 已提交
464
        """
G
Gonzalo Rafuls 已提交
465 466
        return self._get_argument(name, default,
                                  self.request.query_arguments, strip)
T
Travis Beauvais 已提交
467

T
Travis Beauvais 已提交
468 469
    def get_query_arguments(self, name, strip=True):
        """Returns a list of the query arguments with the given name.
470 471 472 473

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

        The returned values are always unicode.
B
Ben Darnell 已提交
474 475

        .. versionadded:: 3.2
476
        """
T
Travis Beauvais 已提交
477 478 479 480 481 482 483 484 485
        return self._get_arguments(name, self.request.query_arguments, strip)

    def _get_argument(self, name, default, source, strip=True):
        args = self._get_arguments(name, source, strip=strip)
        if not args:
            if default is self._ARG_DEFAULT:
                raise MissingArgumentError(name)
            return default
        return args[-1]
486

T
Travis Beauvais 已提交
487
    def _get_arguments(self, name, source, strip=True):
488
        values = []
T
Travis Beauvais 已提交
489
        for v in source.get(name, []):
490
            v = self.decode_argument(v, name=name)
491
            if isinstance(v, unicode_type):
492 493
                # Get rid of any weird control chars (unless decoding gave
                # us bytes, in which case leave it alone)
494
                v = RequestHandler._remove_control_chars_regex.sub(" ", v)
495 496 497
            if strip:
                v = v.strip()
            values.append(v)
498 499
        return values

500 501 502 503 504 505 506
    def decode_argument(self, value, name=None):
        """Decodes an argument from the request.

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

507 508
        This method is used as a filter for both `get_argument()` and for
        values extracted from the url and passed to `get()`/`post()`/etc.
509 510 511 512

        The name of the argument is provided if known, but may be None
        (e.g. for unnamed groups in the url regex).
        """
513 514 515
        try:
            return _unicode(value)
        except UnicodeDecodeError:
516 517
            raise HTTPError(400, "Invalid unicode in %s: %r" %
                            (name or "url", value[:40]))
B
Bret Taylor 已提交
518

519 520
    @property
    def cookies(self):
G
Gonzalo Rafuls 已提交
521 522
        """An alias for
        `self.request.cookies <.httputil.HTTPServerRequest.cookies>`."""
523 524
        return self.request.cookies

B
Bret Taylor 已提交
525
    def get_cookie(self, name, default=None):
B
Ben Darnell 已提交
526 527 528 529 530 531 532 533
        """Returns the value of the request cookie with the given name.

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

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

    def set_cookie(self, name, value, domain=None, expires=None, path="/",
539
                   expires_days=None, **kwargs):
B
Ben Darnell 已提交
540 541 542 543
        """Sets an outgoing cookie name/value with the given options.

        Newly-set cookies are not immediately visible via `get_cookie`;
        they are not present until the next request.
544

B
Ben Darnell 已提交
545
        Additional keyword arguments are set on the cookies.Morsel
546
        directly.
B
Ben Darnell 已提交
547
        See https://docs.python.org/3/library/http.cookies.html#http.cookies.Morsel
548 549
        for available attributes.
        """
550 551 552
        # The cookie library only accepts type str, in both python 2 and 3
        name = escape.native_str(name)
        value = escape.native_str(value)
B
Bret Taylor 已提交
553 554 555
        if re.search(r"[\x00-\x20]", name + value):
            # Don't let us accidentally inject bad stuff
            raise ValueError("Invalid cookie %r: %r" % (name, value))
556 557 558 559 560 561
        if not hasattr(self, "_new_cookie"):
            self._new_cookie = Cookie.SimpleCookie()
        if name in self._new_cookie:
            del self._new_cookie[name]
        self._new_cookie[name] = value
        morsel = self._new_cookie[name]
B
Bret Taylor 已提交
562
        if domain:
563
            morsel["domain"] = domain
B
Bret Taylor 已提交
564 565 566 567
        if expires_days is not None and not expires:
            expires = datetime.datetime.utcnow() + datetime.timedelta(
                days=expires_days)
        if expires:
568
            morsel["expires"] = httputil.format_timestamp(expires)
B
Bret Taylor 已提交
569
        if path:
570
            morsel["path"] = path
571
        for k, v in kwargs.items():
572 573
            if k == 'max_age':
                k = 'max-age'
574 575 576 577 578 579

            # skip falsy values for httponly and secure flags because
            # SimpleCookie sets them regardless
            if k in ['httponly', 'secure'] and not v:
                continue

580
            morsel[k] = v
B
Bret Taylor 已提交
581 582

    def clear_cookie(self, name, path="/", domain=None):
583 584 585 586 587 588
        """Deletes the cookie with the given name.

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

        Similar to `set_cookie`, the effect of this method will not be
        seen until the following request.
592
        """
B
Bret Taylor 已提交
593 594 595 596
        expires = datetime.datetime.utcnow() - datetime.timedelta(days=365)
        self.set_cookie(name, value="", path=path, expires=expires,
                        domain=domain)

597
    def clear_all_cookies(self, path="/", domain=None):
598 599 600 601
        """Deletes all the cookies the user sent with this request.

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

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

B
Ben Darnell 已提交
606 607 608
        .. versionchanged:: 3.2

           Added the ``path`` and ``domain`` parameters.
609
        """
610
        for name in self.request.cookies:
611
            self.clear_cookie(name, path=path, domain=domain)
B
Bret Taylor 已提交
612

613 614
    def set_secure_cookie(self, name, value, expires_days=30, version=None,
                          **kwargs):
B
Bret Taylor 已提交
615 616
        """Signs and timestamps a cookie so it cannot be forged.

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

621 622 623 624 625
        To read a cookie set with this method, use `get_secure_cookie()`.

        Note that the ``expires_days`` parameter sets the lifetime of the
        cookie in the browser, but is independent of the ``max_age_days``
        parameter to `get_secure_cookie`.
626 627 628

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

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

633 634 635 636
        .. versionchanged:: 3.2.1

           Added the ``version`` argument.  Introduced cookie version 2
           and made it the default.
B
Bret Taylor 已提交
637
        """
638 639
        self.set_cookie(name, self.create_signed_value(name, value,
                                                       version=version),
640 641
                        expires_days=expires_days, **kwargs)

642
    def create_signed_value(self, name, value, version=None):
643 644 645 646 647
        """Signs and timestamps a string so it cannot be forged.

        Normally used via set_secure_cookie, but provided as a separate
        method for non-cookie uses.  To decode a value not stored
        as a cookie use the optional value argument to get_secure_cookie.
648 649 650 651 652

        .. versionchanged:: 3.2.1

           Added the ``version`` argument.  Introduced cookie version 2
           and made it the default.
653
        """
654
        self.require_setting("cookie_secret", "secure cookies")
655 656 657 658 659 660 661 662 663
        secret = self.application.settings["cookie_secret"]
        key_version = None
        if isinstance(secret, dict):
            if self.application.settings.get("key_version") is None:
                raise Exception("key_version setting must be used for secret_key dicts")
            key_version = self.application.settings["key_version"]

        return create_signed_value(secret, name, value, version=version,
                                   key_version=key_version)
B
Bret Taylor 已提交
664

665 666
    def get_secure_cookie(self, name, value=None, max_age_days=31,
                          min_version=None):
667 668 669 670
        """Returns the given signed cookie if it validates, or None.

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

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

676 677 678 679
        .. versionchanged:: 3.2.1

           Added the ``min_version`` argument.  Introduced cookie version 2;
           both versions 1 and 2 are accepted by default.
680
        """
B
Bret Taylor 已提交
681
        self.require_setting("cookie_secret", "secure cookies")
682 683
        if value is None:
            value = self.get_cookie(name)
684
        return decode_signed_value(self.application.settings["cookie_secret"],
685 686
                                   name, value, max_age_days=max_age_days,
                                   min_version=min_version)
B
Bret Taylor 已提交
687

688 689 690 691 692 693 694 695 696 697
    def get_secure_cookie_key_version(self, name, value=None):
        """Returns the signing key version of the secure cookie.

        The version is returned as int.
        """
        self.require_setting("cookie_secret", "secure cookies")
        if value is None:
            value = self.get_cookie(name)
        return get_signature_key_version(value)

698 699 700 701 702 703 704 705
    def redirect(self, url, permanent=False, status=None):
        """Sends a redirect to the given (optionally relative) URL.

        If the ``status`` argument is specified, that value is used as the
        HTTP status code; otherwise either 301 (permanent) or 302
        (temporary) is chosen based on the ``permanent`` argument.
        The default is 302 (temporary).
        """
B
Bret Taylor 已提交
706 707
        if self._headers_written:
            raise Exception("Cannot redirect after headers have been written")
708 709 710 711 712
        if status is None:
            status = 301 if permanent else 302
        else:
            assert isinstance(status, int) and 300 <= status <= 399
        self.set_status(status)
713
        self.set_header("Location", utf8(url))
B
Bret Taylor 已提交
714 715 716 717 718 719 720 721
        self.finish()

    def write(self, chunk):
        """Writes the given chunk to the output buffer.

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

        If the given chunk is a dictionary, we write it as JSON and set
722 723
        the Content-Type of the response to be ``application/json``.
        (if you want to send JSON as a different ``Content-Type``, call
724
        set_header *after* calling write()).
725 726 727 728

        Note that lists are not converted to JSON because of a potential
        cross-site security vulnerability.  All JSON output should be
        wrapped in a dictionary.  More details at
729 730
        http://haacked.com/archive/2009/06/25/json-hijacking.aspx/ and
        https://github.com/facebook/tornado/issues/1009
B
Bret Taylor 已提交
731
        """
732
        if self._finished:
733
            raise RuntimeError("Cannot write() after finish()")
734
        if not isinstance(chunk, (bytes, unicode_type, dict)):
735 736
            message = "write() only accepts bytes, unicode, and dict objects"
            if isinstance(chunk, list):
737 738
                message += ". Lists not accepted for security reasons; see " + \
                    "http://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.write"
739
            raise TypeError(message)
B
Bret Taylor 已提交
740 741
        if isinstance(chunk, dict):
            chunk = escape.json_encode(chunk)
742
            self.set_header("Content-Type", "application/json; charset=UTF-8")
743
        chunk = utf8(chunk)
B
Bret Taylor 已提交
744 745 746 747
        self._write_buffer.append(chunk)

    def render(self, template_name, **kwargs):
        """Renders the template with the given arguments as the response."""
748 749
        if self._finished:
            raise RuntimeError("Cannot render() after finish()")
B
Bret Taylor 已提交
750 751 752 753 754 755 756 757
        html = self.render_string(template_name, **kwargs)

        # Insert the additional JS and CSS added by the modules on the page
        js_embed = []
        js_files = []
        css_embed = []
        css_files = []
        html_heads = []
758
        html_bodies = []
759
        for module in getattr(self, "_active_modules", {}).values():
B
Bret Taylor 已提交
760
            embed_part = module.embedded_javascript()
761 762
            if embed_part:
                js_embed.append(utf8(embed_part))
B
Bret Taylor 已提交
763 764
            file_part = module.javascript_files()
            if file_part:
765
                if isinstance(file_part, (unicode_type, bytes)):
B
Bret Taylor 已提交
766 767 768 769
                    js_files.append(file_part)
                else:
                    js_files.extend(file_part)
            embed_part = module.embedded_css()
770 771
            if embed_part:
                css_embed.append(utf8(embed_part))
B
Bret Taylor 已提交
772 773
            file_part = module.css_files()
            if file_part:
774
                if isinstance(file_part, (unicode_type, bytes)):
B
Bret Taylor 已提交
775 776 777 778
                    css_files.append(file_part)
                else:
                    css_files.extend(file_part)
            head_part = module.html_head()
779 780
            if head_part:
                html_heads.append(utf8(head_part))
781
            body_part = module.html_body()
782 783 784
            if body_part:
                html_bodies.append(utf8(body_part))

B
Bret Taylor 已提交
785
        if js_files:
786
            # Maintain order of JavaScript files given by modules
787
            js = self.render_linked_js(js_files)
788 789
            sloc = html.rindex(b'</body>')
            html = html[:sloc] + utf8(js) + b'\n' + html[sloc:]
B
Bret Taylor 已提交
790
        if js_embed:
791
            js = self.render_embed_js(js_embed)
792 793
            sloc = html.rindex(b'</body>')
            html = html[:sloc] + js + b'\n' + html[sloc:]
B
Bret Taylor 已提交
794
        if css_files:
795
            css = self.render_linked_css(css_files)
796 797
            hloc = html.index(b'</head>')
            html = html[:hloc] + utf8(css) + b'\n' + html[hloc:]
B
Bret Taylor 已提交
798
        if css_embed:
799
            css = self.render_embed_css(css_embed)
800 801
            hloc = html.index(b'</head>')
            html = html[:hloc] + css + b'\n' + html[hloc:]
B
Bret Taylor 已提交
802
        if html_heads:
803 804
            hloc = html.index(b'</head>')
            html = html[:hloc] + b''.join(html_heads) + b'\n' + html[hloc:]
805
        if html_bodies:
806 807
            hloc = html.index(b'</body>')
            html = html[:hloc] + b''.join(html_bodies) + b'\n' + html[hloc:]
B
Bret Taylor 已提交
808 809
        self.finish(html)

810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
    def render_linked_js(self, js_files):
        """Default method used to render the final js links for the
        rendered webpage.

        Override this method in a sub-classed controller to change the output.
        """
        paths = []
        unique_paths = set()

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

        return ''.join('<script src="' + escape.xhtml_escape(p) +
                       '" type="text/javascript"></script>'
                       for p in paths)

    def render_embed_js(self, js_embed):
        """Default method used to render the final embedded js for the
        rendered webpage.

        Override this method in a sub-classed controller to change the output.
        """
        return b'<script type="text/javascript">\n//<![CDATA[\n' + \
               b'\n'.join(js_embed) + b'\n//]]>\n</script>'

    def render_linked_css(self, css_files):
        """Default method used to render the final css links for the
        rendered webpage.

        Override this method in a sub-classed controller to change the output.
        """
        paths = []
        unique_paths = set()

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

        return ''.join('<link href="' + escape.xhtml_escape(p) + '" '
                       'type="text/css" rel="stylesheet"/>'
                       for p in paths)

    def render_embed_css(self, css_embed):
        """Default method used to render the final embedded css for the
        rendered webpage.

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

B
Bret Taylor 已提交
868 869 870
    def render_string(self, template_name, **kwargs):
        """Generate the given template with the given arguments.

871 872
        We return the generated byte string (in utf8). To generate and
        write a template as a response, use render() above.
B
Bret Taylor 已提交
873 874
        """
        # If no template_path is specified, use the path of the calling file
875
        template_path = self.get_template_path()
B
Bret Taylor 已提交
876 877 878 879 880 881
        if not template_path:
            frame = sys._getframe(0)
            web_file = frame.f_code.co_filename
            while frame.f_code.co_filename == web_file:
                frame = frame.f_back
            template_path = os.path.dirname(frame.f_code.co_filename)
882 883 884 885 886 887 888
        with RequestHandler._template_loader_lock:
            if template_path not in RequestHandler._template_loaders:
                loader = self.create_template_loader(template_path)
                RequestHandler._template_loaders[template_path] = loader
            else:
                loader = RequestHandler._template_loaders[template_path]
        t = loader.load(template_name)
889 890 891 892 893 894 895 896 897 898 899 900 901 902
        namespace = self.get_template_namespace()
        namespace.update(kwargs)
        return t.generate(**namespace)

    def get_template_namespace(self):
        """Returns a dictionary to be used as the default template namespace.

        May be overridden by subclasses to add or modify values.

        The results of this method will be combined with additional
        defaults in the `tornado.template` module and keyword arguments
        to `render` or `render_string`.
        """
        namespace = dict(
B
Bret Taylor 已提交
903 904 905 906 907
            handler=self,
            request=self.request,
            current_user=self.current_user,
            locale=self.locale,
            _=self.locale.translate,
908
            pgettext=self.locale.pgettext,
B
Bret Taylor 已提交
909 910
            static_url=self.static_url,
            xsrf_form_html=self.xsrf_form_html,
911
            reverse_url=self.reverse_url
B
Bret Taylor 已提交
912
        )
913 914
        namespace.update(self.ui)
        return namespace
B
Bret Taylor 已提交
915

916
    def create_template_loader(self, template_path):
917 918 919 920
        """Returns a new template loader for the given path.

        May be overridden by subclasses.  By default returns a
        directory-based loader on the given path, using the
921 922 923
        ``autoescape`` and ``template_whitespace`` application
        settings.  If a ``template_loader`` application setting is
        supplied, uses that instead.
924
        """
925 926 927 928 929 930 931 932
        settings = self.application.settings
        if "template_loader" in settings:
            return settings["template_loader"]
        kwargs = {}
        if "autoescape" in settings:
            # autoescape=None means "no escaping", so we have to be sure
            # to only pass this kwarg if the user asked for it.
            kwargs["autoescape"] = settings["autoescape"]
933 934
        if "template_whitespace" in settings:
            kwargs["whitespace"] = settings["template_whitespace"]
935 936
        return template.Loader(template_path, **kwargs)

937 938
    def flush(self, include_footers=False, callback=None):
        """Flushes the current output buffer to the network.
939

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

946
        .. versionchanged:: 4.0
B
Ben Darnell 已提交
947
           Now returns a `.Future` if no callback is given.
948
        """
949
        chunk = b"".join(self._write_buffer)
950
        self._write_buffer = []
B
Bret Taylor 已提交
951 952
        if not self._headers_written:
            self._headers_written = True
953
            for transform in self._transforms:
954 955
                self._status_code, self._headers, chunk = \
                    transform.transform_first_chunk(
G
Gonzalo Rafuls 已提交
956 957
                        self._status_code, self._headers,
                        chunk, include_footers)
958 959 960 961
            # Ignore the chunk and only write the headers for HEAD requests
            if self.request.method == "HEAD":
                chunk = None

962 963 964 965 966 967 968
            # Finalize the cookie headers (which have been stored in a side
            # object so an outgoing cookie could be overwritten before it
            # is sent).
            if hasattr(self, "_new_cookie"):
                for cookie in self._new_cookie.values():
                    self.add_header("Set-Cookie", cookie.OutputString(None))

969
            start_line = httputil.ResponseStartLine('',
970 971
                                                    self._status_code,
                                                    self._reason)
972 973
            return self.request.connection.write_headers(
                start_line, self._headers, chunk, callback=callback)
B
Bret Taylor 已提交
974
        else:
975 976
            for transform in self._transforms:
                chunk = transform.transform_chunk(chunk, include_footers)
977 978
            # Ignore the chunk and only write the headers for HEAD requests
            if self.request.method != "HEAD":
979 980 981 982 983
                return self.request.connection.write(chunk, callback=callback)
            else:
                future = Future()
                future.set_result(None)
                return future
B
Bret Taylor 已提交
984 985 986

    def finish(self, chunk=None):
        """Finishes this response, ending the HTTP request."""
987
        if self._finished:
988
            raise RuntimeError("finish() called twice")
989

990 991
        if chunk is not None:
            self.write(chunk)
B
Bret Taylor 已提交
992 993 994 995

        # Automatically support ETags and add the Content-Length header if
        # we have not flushed any content yet.
        if not self._headers_written:
996 997
            if (self._status_code == 200 and
                self.request.method in ("GET", "HEAD") and
998
                    "Etag" not in self._headers):
999 1000 1001 1002
                self.set_etag_header()
                if self.check_etag_header():
                    self._write_buffer = []
                    self.set_status(304)
S
Sebastien Boving 已提交
1003
            if (self._status_code in (204, 304) or
1004
                    (self._status_code >= 100 and self._status_code < 200)):
1005
                assert not self._write_buffer, "Cannot send body with %s" % self._status_code
1006
                self._clear_headers_for_304()
S
Sebastien Boving 已提交
1007
            elif "Content-Length" not in self._headers:
B
Bret Taylor 已提交
1008 1009 1010
                content_length = sum(len(part) for part in self._write_buffer)
                self.set_header("Content-Length", content_length)

1011 1012
        if hasattr(self.request, "connection"):
            # Now that the request is finished, clear the callback we
1013
            # set on the HTTPConnection (which would otherwise prevent the
1014 1015
            # garbage collection of the RequestHandler when there
            # are keepalive connections)
1016
            self.request.connection.set_close_callback(None)
1017

1018 1019 1020
        self.flush(include_footers=True)
        self.request.finish()
        self._log()
B
Bret Taylor 已提交
1021
        self._finished = True
1022
        self.on_finish()
1023 1024 1025
        self._break_cycles()

    def _break_cycles(self):
1026 1027 1028
        # Break up a reference cycle between this handler and the
        # _ui_module closures to allow for faster GC on CPython.
        self.ui = None
B
Bret Taylor 已提交
1029

1030
    def send_error(self, status_code=500, **kwargs):
B
Bret Taylor 已提交
1031 1032
        """Sends the given HTTP error code to the browser.

1033 1034 1035 1036 1037 1038 1039
        If `flush()` has already been called, it is not possible to send
        an error, so this method will simply terminate the response.
        If output has been written but not yet flushed, it will be discarded
        and replaced with the error page.

        Override `write_error()` to customize the error page that is returned.
        Additional keyword arguments are passed through to `write_error`.
B
Bret Taylor 已提交
1040 1041
        """
        if self._headers_written:
1042
            gen_log.error("Cannot send error response after headers written")
B
Bret Taylor 已提交
1043
            if not self._finished:
1044 1045 1046 1047 1048 1049 1050 1051 1052
                # If we get an error between writing headers and finishing,
                # we are unlikely to be able to finish due to a
                # Content-Length mismatch. Try anyway to release the
                # socket.
                try:
                    self.finish()
                except Exception:
                    gen_log.error("Failed to flush partial response",
                                  exc_info=True)
B
Bret Taylor 已提交
1053 1054
            return
        self.clear()
1055

1056
        reason = kwargs.get('reason')
1057 1058 1059 1060 1061
        if 'exc_info' in kwargs:
            exception = kwargs['exc_info'][1]
            if isinstance(exception, HTTPError) and exception.reason:
                reason = exception.reason
        self.set_status(status_code, reason=reason)
1062 1063 1064
        try:
            self.write_error(status_code, **kwargs)
        except Exception:
1065
            app_log.error("Uncaught exception in write_error", exc_info=True)
1066 1067
        if not self._finished:
            self.finish()
B
Bret Taylor 已提交
1068

1069
    def write_error(self, status_code, **kwargs):
1070 1071
        """Override to implement custom error pages.

1072 1073 1074
        ``write_error`` may call `write`, `render`, `set_header`, etc
        to produce output as usual.

1075 1076 1077 1078 1079
        If this error was caused by an uncaught exception (including
        HTTPError), an ``exc_info`` triple will be available as
        ``kwargs["exc_info"]``.  Note that this exception may not be
        the "current" exception for purposes of methods like
        ``sys.exc_info()`` or ``traceback.format_exc``.
1080
        """
1081
        if self.settings.get("serve_traceback") and "exc_info" in kwargs:
1082 1083
            # in debug mode, try to send a traceback
            self.set_header('Content-Type', 'text/plain')
1084 1085 1086
            for line in traceback.format_exception(*kwargs["exc_info"]):
                self.write(line)
            self.finish()
1087
        else:
1088
            self.finish("<html><title>%(code)d: %(message)s</title>"
1089
                        "<body>%(code)d: %(message)s</body></html>" % {
1090 1091 1092
                            "code": status_code,
                            "message": self._reason,
                        })
B
Bret Taylor 已提交
1093 1094 1095

    @property
    def locale(self):
1096
        """The locale for the current session.
B
Bret Taylor 已提交
1097

1098
        Determined by either `get_user_locale`, which you can override to
B
Bret Taylor 已提交
1099
        set the locale based on, e.g., a user preference stored in a
1100
        database, or `get_browser_locale`, which uses the ``Accept-Language``
B
Bret Taylor 已提交
1101
        header.
1102 1103 1104

        .. versionchanged: 4.1
           Added a property setter.
B
Bret Taylor 已提交
1105 1106 1107 1108 1109 1110 1111
        """
        if not hasattr(self, "_locale"):
            self._locale = self.get_user_locale()
            if not self._locale:
                self._locale = self.get_browser_locale()
                assert self._locale
        return self._locale
1112

1113 1114 1115 1116
    @locale.setter
    def locale(self, value):
        self._locale = value

B
Bret Taylor 已提交
1117 1118 1119
    def get_user_locale(self):
        """Override to determine the locale from the authenticated user.

1120
        If None is returned, we fall back to `get_browser_locale()`.
1121

1122 1123
        This method should return a `tornado.locale.Locale` object,
        most likely obtained via a call like ``tornado.locale.get("en")``
B
Bret Taylor 已提交
1124 1125 1126 1127
        """
        return None

    def get_browser_locale(self, default="en_US"):
1128
        """Determines the user's locale from ``Accept-Language`` header.
B
Bret Taylor 已提交
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145

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

    @property
    def current_user(self):
        """The authenticated user for this request.

1155
        This is set in one of two ways:
B
Bret Taylor 已提交
1156

1157 1158 1159 1160 1161 1162 1163
        * A subclass may override `get_current_user()`, which will be called
          automatically the first time ``self.current_user`` is accessed.
          `get_current_user()` will only be called once per request,
          and is cached for future access::

              def get_current_user(self):
                  user_cookie = self.get_secure_cookie("user")
1164 1165
                  if user_cookie:
                      return json.loads(user_cookie)
1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
                  return None

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

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

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

A
Andrew Rabert 已提交
1181
        The user object may be any type of the application's choosing.
B
Bret Taylor 已提交
1182 1183 1184 1185 1186
        """
        if not hasattr(self, "_current_user"):
            self._current_user = self.get_current_user()
        return self._current_user

1187 1188 1189 1190
    @current_user.setter
    def current_user(self, value):
        self._current_user = value

B
Bret Taylor 已提交
1191
    def get_current_user(self):
1192 1193 1194 1195
        """Override to determine the current user from, e.g., a cookie.

        This method may not be a coroutine.
        """
B
Bret Taylor 已提交
1196 1197 1198 1199 1200
        return None

    def get_login_url(self):
        """Override to customize the login URL based on the request.

1201
        By default, we use the ``login_url`` application setting.
B
Bret Taylor 已提交
1202 1203 1204 1205
        """
        self.require_setting("login_url", "@tornado.web.authenticated")
        return self.application.settings["login_url"]

1206 1207 1208
    def get_template_path(self):
        """Override to customize template path for each handler.

1209
        By default, we use the ``template_path`` application setting.
1210 1211 1212 1213
        Return None to load templates relative to the calling file.
        """
        return self.application.settings.get("template_path")

B
Bret Taylor 已提交
1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
    @property
    def xsrf_token(self):
        """The XSRF-prevention token for the current user/session.

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

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

1225 1226 1227 1228 1229
        This property is of type `bytes`, but it contains only ASCII
        characters. If a character string is required, there is no
        need to base64-encode it; just decode the byte string as
        UTF-8.

1230 1231 1232 1233 1234 1235 1236 1237
        .. versionchanged:: 3.2.2
           The xsrf token will now be have a random mask applied in every
           request, which makes it safe to include the token in pages
           that are compressed.  See http://breachattack.com for more
           information on the issue fixed by this change.  Old (version 1)
           cookies will be converted to version 2 when this method is called
           unless the ``xsrf_cookie_version`` `Application` setting is
           set to 1.
1238 1239 1240 1241 1242 1243 1244 1245

        .. versionchanged:: 4.3
           The ``xsrf_cookie_kwargs`` `Application` setting may be
           used to supply additional cookie options (which will be
           passed directly to `set_cookie`). For example,
           ``xsrf_cookie_kwargs=dict(httponly=True, secure=True)``
           will set the ``secure`` and ``httponly`` flags on the
           ``_xsrf`` cookie.
B
Bret Taylor 已提交
1246 1247
        """
        if not hasattr(self, "_xsrf_token"):
1248
            version, token, timestamp = self._get_raw_xsrf_token()
1249
            output_version = self.settings.get("xsrf_cookie_version", 2)
1250
            cookie_kwargs = self.settings.get("xsrf_cookie_kwargs", {})
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
            if output_version == 1:
                self._xsrf_token = binascii.b2a_hex(token)
            elif output_version == 2:
                mask = os.urandom(4)
                self._xsrf_token = b"|".join([
                    b"2",
                    binascii.b2a_hex(mask),
                    binascii.b2a_hex(_websocket_mask(mask, token)),
                    utf8(str(int(timestamp)))])
            else:
                raise ValueError("unknown xsrf cookie version %d",
                                 output_version)
            if version is None:
B
Bret Taylor 已提交
1264
                expires_days = 30 if self.current_user else None
1265
                self.set_cookie("_xsrf", self._xsrf_token,
1266 1267
                                expires_days=expires_days,
                                **cookie_kwargs)
B
Bret Taylor 已提交
1268 1269
        return self._xsrf_token

1270
    def _get_raw_xsrf_token(self):
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280
        """Read or generate the xsrf token in its raw form.

        The raw_xsrf_token is a tuple containing:

        * version: the version of the cookie from which this token was read,
          or None if we generated a new token in this request.
        * token: the raw token data; random (non-ascii) bytes.
        * timestamp: the time this token was generated (will not be accurate
          for version 1 cookies)
        """
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
        if not hasattr(self, '_raw_xsrf_token'):
            cookie = self.get_cookie("_xsrf")
            if cookie:
                version, token, timestamp = self._decode_xsrf_token(cookie)
            else:
                version, token, timestamp = None, None, None
            if token is None:
                version = None
                token = os.urandom(16)
                timestamp = time.time()
            self._raw_xsrf_token = (version, token, timestamp)
        return self._raw_xsrf_token

    def _decode_xsrf_token(self, cookie):
1295 1296 1297
        """Convert a cookie string into a the tuple form returned by
        _get_raw_xsrf_token.
        """
1298 1299 1300 1301 1302 1303 1304

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

            if m:
                version = int(m.group(1))
                if version == 2:
1305
                    _, mask, masked_token, timestamp = cookie.split("|")
1306 1307 1308 1309 1310 1311 1312 1313 1314

                    mask = binascii.a2b_hex(utf8(mask))
                    token = _websocket_mask(
                        mask, binascii.a2b_hex(utf8(masked_token)))
                    timestamp = int(timestamp)
                    return version, token, timestamp
                else:
                    # Treat unknown versions as not present instead of failing.
                    raise Exception("Unknown xsrf cookie version")
1315
            else:
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
                version = 1
                try:
                    token = binascii.a2b_hex(utf8(cookie))
                except (binascii.Error, TypeError):
                    token = utf8(cookie)
                # We don't have a usable timestamp in older versions.
                timestamp = int(time.time())
                return (version, token, timestamp)
        except Exception:
            # Catch exceptions and return nothing instead of failing.
G
Gonzalo Rafuls 已提交
1326 1327
            gen_log.debug("Uncaught exception in _decode_xsrf_token",
                          exc_info=True)
1328
            return None, None, None
1329

B
Bret Taylor 已提交
1330
    def check_xsrf_cookie(self):
1331
        """Verifies that the ``_xsrf`` cookie matches the ``_xsrf`` argument.
B
Bret Taylor 已提交
1332

1333
        To prevent cross-site request forgery, we set an ``_xsrf``
1334
        cookie and include the same value as a non-cookie
1335
        field with all ``POST`` requests. If the two do not match, we
1336 1337
        reject the form submission as a potential forgery.

1338 1339
        The ``_xsrf`` value may be set as either a form field named ``_xsrf``
        or in a custom HTTP header named ``X-XSRFToken`` or ``X-CSRFToken``
1340
        (the latter is accepted for compatibility with Django).
B
Bret Taylor 已提交
1341 1342

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

        Prior to release 1.1.1, this check was ignored if the HTTP header
1345
        ``X-Requested-With: XMLHTTPRequest`` was present.  This exception
1346 1347 1348 1349
        has been shown to be insecure and has been removed.  For more
        information please see
        http://www.djangoproject.com/weblog/2011/feb/08/security/
        http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails
1350 1351 1352 1353

        .. versionchanged:: 3.2.2
           Added support for cookie version 2.  Both versions 1 and 2 are
           supported.
B
Bret Taylor 已提交
1354
        """
1355 1356 1357
        token = (self.get_argument("_xsrf", None) or
                 self.request.headers.get("X-Xsrftoken") or
                 self.request.headers.get("X-Csrftoken"))
B
Bret Taylor 已提交
1358 1359
        if not token:
            raise HTTPError(403, "'_xsrf' argument missing from POST")
1360 1361
        _, token, _ = self._decode_xsrf_token(token)
        _, expected_token, _ = self._get_raw_xsrf_token()
1362 1363
        if not token:
            raise HTTPError(403, "'_xsrf' argument has invalid format")
1364
        if not _time_independent_equals(utf8(token), utf8(expected_token)):
B
Bret Taylor 已提交
1365 1366 1367
            raise HTTPError(403, "XSRF cookie does not match POST argument")

    def xsrf_form_html(self):
1368
        """An HTML ``<input/>`` element to be included with all POST forms.
B
Bret Taylor 已提交
1369

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

1375 1376 1377 1378
        In a template, this method should be called with ``{% module
        xsrf_form_html() %}``

        See `check_xsrf_cookie()` above for more information.
B
Bret Taylor 已提交
1379 1380 1381 1382
        """
        return '<input type="hidden" name="_xsrf" value="' + \
            escape.xhtml_escape(self.xsrf_token) + '"/>'

1383
    def static_url(self, path, include_host=None, **kwargs):
B
Bret Taylor 已提交
1384 1385
        """Returns a static URL for the given relative static file path.

1386
        This method requires you set the ``static_path`` setting in your
B
Bret Taylor 已提交
1387 1388 1389
        application (which specifies the root directory of your static
        files).

1390 1391 1392 1393 1394 1395
        This method returns a versioned url (by default appending
        ``?v=<signature>``), which allows the static files to be
        cached indefinitely.  This can be disabled by passing
        ``include_version=False`` (in the default implementation;
        other static file implementations are not required to support
        this, but they may support other options).
B
Bret Taylor 已提交
1396

1397 1398 1399 1400 1401
        By default this method returns URLs relative to the current
        host, but if ``include_host`` is true the URL returned will be
        absolute.  If this handler has an ``include_host`` attribute,
        that value will be used as the default for all `static_url`
        calls that do not pass ``include_host`` as a keyword argument.
1402

B
Bret Taylor 已提交
1403 1404
        """
        self.require_setting("static_path", "static_url")
1405 1406
        get_url = self.settings.get("static_handler_class",
                                    StaticFileHandler).make_static_url
1407 1408 1409 1410 1411

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

        if include_host:
1412
            base = self.request.protocol + "://" + self.request.host
B
Bret Taylor 已提交
1413
        else:
1414
            base = ""
1415

1416
        return base + get_url(self.settings, path, **kwargs)
B
Bret Taylor 已提交
1417 1418 1419 1420 1421 1422 1423

    def require_setting(self, name, feature="this feature"):
        """Raises an exception if the given app setting is not defined."""
        if not self.application.settings.get(name):
            raise Exception("You must define the '%s' setting in your "
                            "application to use %s" % (name, feature))

1424
    def reverse_url(self, name, *args):
1425
        """Alias for `Application.reverse_url`."""
1426 1427
        return self.application.reverse_url(name, *args)

1428 1429 1430
    def compute_etag(self):
        """Computes the etag header to be used for this request.

1431 1432
        By default uses a hash of the content written so far.

1433 1434 1435 1436 1437 1438 1439 1440
        May be overridden to provide custom etag implementations,
        or may return None to disable tornado's default etag support.
        """
        hasher = hashlib.sha1()
        for part in self._write_buffer:
            hasher.update(part)
        return '"%s"' % hasher.hexdigest()

1441 1442 1443 1444
    def set_etag_header(self):
        """Sets the response's Etag header using ``self.compute_etag()``.

        Note: no header will be set if ``compute_etag()`` returns ``None``.
1445 1446

        This method is called automatically when the request is finished.
1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461
        """
        etag = self.compute_etag()
        if etag is not None:
            self.set_header("Etag", etag)

    def check_etag_header(self):
        """Checks the ``Etag`` header against requests's ``If-None-Match``.

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

            self.set_etag_header()
            if self.check_etag_header():
                self.set_status(304)
                return
1462 1463 1464 1465 1466 1467

        This method is called automatically when the request is finished,
        but may be called earlier for applications that override
        `compute_etag` and want to do an early check for ``If-None-Match``
        before completing the request.  The ``Etag`` header should be set
        (perhaps with `set_etag_header`) before calling this method.
1468
        """
1469
        computed_etag = utf8(self._headers.get("Etag", ""))
D
daftshady 已提交
1470 1471 1472
        # Find all weak and strong etag values from If-None-Match header
        # because RFC 7232 allows multiple etag values in a single header.
        etags = re.findall(
1473
            br'\*|(?:W/)?"[^"]*"',
D
daftshady 已提交
1474 1475
            utf8(self.request.headers.get("If-None-Match", ""))
        )
1476 1477 1478 1479
        if not computed_etag or not etags:
            return False

        match = False
1480
        if etags[0] == b'*':
1481 1482 1483
            match = True
        else:
            # Use a weak comparison when comparing entity-tags.
1484 1485 1486
            def val(x):
                return x[2:] if x.startswith(b'W/') else x

1487 1488 1489 1490 1491
            for etag in etags:
                if val(etag) == val(computed_etag):
                    match = True
                    break
        return match
1492

1493
    def _stack_context_handle_exception(self, type, value, traceback):
1494 1495 1496 1497 1498
        try:
            # For historical reasons _handle_request_exception only takes
            # the exception value instead of the full triple,
            # so re-raise the exception to ensure that it's in
            # sys.exc_info()
1499
            raise_exc_info((type, value, traceback))
1500
        except Exception:
1501
            self._handle_request_exception(value)
1502
        return True
1503

1504
    @gen.coroutine
B
Bret Taylor 已提交
1505 1506 1507
    def _execute(self, transforms, *args, **kwargs):
        """Executes this request with the given output transforms."""
        self._transforms = transforms
1508
        try:
B
Bret Taylor 已提交
1509 1510
            if self.request.method not in self.SUPPORTED_METHODS:
                raise HTTPError(405)
1511 1512
            self.path_args = [self.decode_argument(arg) for arg in args]
            self.path_kwargs = dict((k, self.decode_argument(v, name=k))
1513
                                    for (k, v) in kwargs.items())
B
Bret Taylor 已提交
1514 1515
            # If XSRF cookies are turned on, reject form submissions without
            # the proper cookie
1516
            if self.request.method not in ("GET", "HEAD", "OPTIONS") and \
1517
                    self.application.settings.get("xsrf_cookies"):
B
Bret Taylor 已提交
1518
                self.check_xsrf_cookie()
1519

1520
            result = self.prepare()
1521
            if result is not None:
1522
                result = yield result
1523 1524 1525
            if self._prepared_future is not None:
                # Tell the Application we've finished with prepare()
                # and are ready for the body to arrive.
1526
                future_set_result_unless_cancelled(self._prepared_future, None)
1527 1528
            if self._finished:
                return
B
Bret Taylor 已提交
1529

1530 1531 1532 1533 1534
            if _has_stream_request_body(self.__class__):
                # In streaming mode request.body is a Future that signals
                # the body has been completely received.  The Future has no
                # result; the data has been passed to self.data_received
                # instead.
1535 1536 1537 1538
                try:
                    yield self.request.body
                except iostream.StreamClosedError:
                    return
1539

1540
            method = getattr(self, self.request.method.lower())
1541
            result = method(*self.path_args, **self.path_kwargs)
1542
            if result is not None:
1543
                result = yield result
1544 1545 1546
            if self._auto_finish and not self._finished:
                self.finish()
        except Exception as e:
1547 1548 1549 1550
            try:
                self._handle_request_exception(e)
            except Exception:
                app_log.error("Exception in exception handler", exc_info=True)
1551 1552 1553
            finally:
                # Unset result to avoid circular references
                result = None
1554
            if (self._prepared_future is not None and
B
Ben Darnell 已提交
1555
                    not self._prepared_future.done()):
1556 1557 1558 1559
                # In case we failed before setting _prepared_future, do it
                # now (to unblock the HTTP server).  Note that this is not
                # in a finally block to avoid GC issues prior to Python 3.4.
                self._prepared_future.set_result(None)
1560

B
Ben Darnell 已提交
1561 1562 1563 1564 1565 1566 1567
    def data_received(self, chunk):
        """Implement this method to handle streamed request data.

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

B
Bret Taylor 已提交
1568
    def _log(self):
1569 1570 1571 1572 1573 1574 1575
        """Logs the current request.

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

    def _request_summary(self):
1578 1579
        return "%s %s (%s)" % (self.request.method, self.request.uri,
                               self.request.remote_ip)
B
Bret Taylor 已提交
1580 1581

    def _handle_request_exception(self, e):
1582 1583 1584
        if isinstance(e, Finish):
            # Not an error; just finish the request without logging.
            if not self._finished:
1585
                self.finish(*e.args)
1586
            return
1587 1588 1589 1590 1591 1592
        try:
            self.log_exception(*sys.exc_info())
        except Exception:
            # An error here should still get a best-effort send_error()
            # to avoid leaking the connection.
            app_log.error("Error in exception logger", exc_info=True)
1593 1594 1595 1596 1597
        if self._finished:
            # Extra errors after the request has been finished should
            # be logged, but there is no reason to continue to try and
            # send a response.
            return
B
Bret Taylor 已提交
1598
        if isinstance(e, HTTPError):
1599
            self.send_error(e.status_code, exc_info=sys.exc_info())
B
Bret Taylor 已提交
1600
        else:
1601
            self.send_error(500, exc_info=sys.exc_info())
B
Bret Taylor 已提交
1602

1603 1604 1605 1606 1607 1608 1609
    def log_exception(self, typ, value, tb):
        """Override to customize logging of uncaught exceptions.

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

        .. versionadded:: 3.1
1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
        """
        if isinstance(value, HTTPError):
            if value.log_message:
                format = "%d %s: " + value.log_message
                args = ([value.status_code, self._request_summary()] +
                        list(value.args))
                gen_log.warning(format, *args)
        else:
            app_log.error("Uncaught exception %s\n%r", self._request_summary(),
                          self.request, exc_info=(typ, value, tb))

B
Bret Taylor 已提交
1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635
    def _ui_module(self, name, module):
        def render(*args, **kwargs):
            if not hasattr(self, "_active_modules"):
                self._active_modules = {}
            if name not in self._active_modules:
                self._active_modules[name] = module(self)
            rendered = self._active_modules[name].render(*args, **kwargs)
            return rendered
        return render

    def _ui_method(self, method):
        return lambda *args, **kwargs: method(self, *args, **kwargs)

1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646
    def _clear_headers_for_304(self):
        # 304 responses should not contain entity headers (defined in
        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.1)
        # not explicitly allowed by
        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
        headers = ["Allow", "Content-Encoding", "Content-Language",
                   "Content-Length", "Content-MD5", "Content-Range",
                   "Content-Type", "Last-Modified"]
        for h in headers:
            self.clear_header(h)

B
Bret Taylor 已提交
1647 1648 1649 1650

def asynchronous(method):
    """Wrap request handler methods with this if they are asynchronous.

1651 1652 1653 1654 1655
    This decorator is for callback-style asynchronous methods; for
    coroutines, use the ``@gen.coroutine`` decorator without
    ``@asynchronous``. (It is legal for legacy reasons to use the two
    decorators together provided ``@asynchronous`` is first, but
    ``@asynchronous`` will be ignored in this case)
1656

1657 1658 1659 1660 1661 1662 1663
    This decorator should only be applied to the :ref:`HTTP verb
    methods <verbs>`; its behavior is undefined for any other method.
    This decorator does not *make* a method asynchronous; it tells
    the framework that the method *is* asynchronous.  For this decorator
    to be useful the method must (at least sometimes) do something
    asynchronous.

B
Bret Taylor 已提交
1664
    If this decorator is given, the response is not finished when the
1665 1666 1667
    method returns. It is up to the request handler to call
    `self.finish() <RequestHandler.finish>` to finish the HTTP
    request. Without this decorator, the request is automatically
1668
    finished when the ``get()`` or ``post()`` method returns. Example:
B
Bret Taylor 已提交
1669

1670 1671 1672 1673
    .. testcode::

       class MyRequestHandler(RequestHandler):
           @asynchronous
B
Bret Taylor 已提交
1674 1675 1676 1677 1678 1679 1680 1681
           def get(self):
              http = httpclient.AsyncHTTPClient()
              http.fetch("http://friendfeed.com/", self._on_download)

           def _on_download(self, response):
              self.write("Downloaded!")
              self.finish()

1682 1683 1684
    .. testoutput::
       :hide:

1685
    .. versionchanged:: 3.1
1686
       The ability to use ``@gen.coroutine`` without ``@asynchronous``.
1687

1688 1689 1690
    .. versionchanged:: 4.3 Returning anything but ``None`` or a
       yieldable object from a method decorated with ``@asynchronous``
       is an error. Such return values were previously ignored silently.
B
Bret Taylor 已提交
1691
    """
1692 1693
    # Delay the IOLoop import because it's not available on app engine.
    from tornado.ioloop import IOLoop
B
Ben Darnell 已提交
1694

B
Bret Taylor 已提交
1695 1696 1697
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        self._auto_finish = False
1698
        with stack_context.ExceptionStackContext(
1699
                self._stack_context_handle_exception):
1700
            result = method(self, *args, **kwargs)
1701 1702
            if result is not None:
                result = gen.convert_yielded(result)
1703

1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714
                # If @asynchronous is used with @gen.coroutine, (but
                # not @gen.engine), we can automatically finish the
                # request when the future resolves.  Additionally,
                # the Future will swallow any exceptions so we need
                # to throw them back out to the stack context to finish
                # the request.
                def future_complete(f):
                    f.result()
                    if not self._finished:
                        self.finish()
                IOLoop.current().add_future(result, future_complete)
1715 1716 1717 1718 1719 1720
                # Once we have done this, hide the Future from our
                # caller (i.e. RequestHandler._when_complete), which
                # would otherwise set up its own callback and
                # exception handler (resulting in exceptions being
                # logged twice).
                return None
1721
            return result
B
Bret Taylor 已提交
1722 1723 1724
    return wrapper


1725 1726 1727 1728
def stream_request_body(cls):
    """Apply to `RequestHandler` subclasses to enable streaming body support.

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

1730 1731 1732 1733 1734 1735 1736
    * `.HTTPServerRequest.body` is undefined, and body arguments will not
      be included in `RequestHandler.get_argument`.
    * `RequestHandler.prepare` is called when the request headers have been
      read instead of after the entire body has been read.
    * The subclass must define a method ``data_received(self, data):``, which
      will be called zero or more times as data is available.  Note that
      if the request has an empty body, ``data_received`` may not be called.
1737 1738 1739
    * ``prepare`` and ``data_received`` may return Futures (such as via
      ``@gen.coroutine``, in which case the next method will not be called
      until those futures have completed.
1740 1741 1742
    * The regular HTTP method (``post``, ``put``, etc) will be called after
      the entire body has been read.

1743 1744
    See the `file receiver demo <https://github.com/tornadoweb/tornado/tree/master/demos/file_upload/>`_
    for example usage.
1745
    """  # noqa: E501
1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757
    if not issubclass(cls, RequestHandler):
        raise TypeError("expected subclass of RequestHandler, got %r", cls)
    cls._stream_request_body = True
    return cls


def _has_stream_request_body(cls):
    if not issubclass(cls, RequestHandler):
        raise TypeError("expected subclass of RequestHandler, got %r", cls)
    return getattr(cls, '_stream_request_body', False)


1758 1759 1760
def removeslash(method):
    """Use this decorator to remove trailing slashes from the request path.

1761
    For example, a request to ``/foo/`` would redirect to ``/foo`` with this
1762
    decorator. Your request handler mapping should use a regular expression
B
Ben Darnell 已提交
1763
    like ``r'/foo/*'`` in conjunction with using the decorator.
1764 1765 1766 1767
    """
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if self.request.path.endswith("/"):
1768
            if self.request.method in ("GET", "HEAD"):
1769
                uri = self.request.path.rstrip("/")
1770
                if uri:  # don't try to redirect '/' to ''
1771 1772
                    if self.request.query:
                        uri += "?" + self.request.query
1773
                    self.redirect(uri, permanent=True)
1774 1775 1776
                    return
            else:
                raise HTTPError(404)
1777 1778 1779 1780 1781 1782 1783
        return method(self, *args, **kwargs)
    return wrapper


def addslash(method):
    """Use this decorator to add a missing trailing slash to the request path.

1784
    For example, a request to ``/foo`` would redirect to ``/foo/`` with this
1785
    decorator. Your request handler mapping should use a regular expression
1786
    like ``r'/foo/?'`` in conjunction with using the decorator.
1787 1788 1789 1790
    """
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if not self.request.path.endswith("/"):
1791
            if self.request.method in ("GET", "HEAD"):
1792
                uri = self.request.path + "/"
1793 1794
                if self.request.query:
                    uri += "?" + self.request.query
1795
                self.redirect(uri, permanent=True)
1796 1797 1798 1799 1800 1801
                return
            raise HTTPError(404)
        return method(self, *args, **kwargs)
    return wrapper


A
Andrey Sumin 已提交
1802
class _ApplicationRouter(ReversibleRuleRouter):
1803
    """Routing implementation used internally by `Application`.
A
Andrey Sumin 已提交
1804

1805
    Provides a binding between `Application` and `RequestHandler`.
A
Andrey Sumin 已提交
1806 1807
    This implementation extends `~.routing.ReversibleRuleRouter` in a couple of ways:
        * it allows to use `RequestHandler` subclasses as `~.routing.Rule` target and
1808 1809 1810
        * it allows to use a list/tuple of rules as `~.routing.Rule` target.
        ``process_rule`` implementation will substitute this list with an appropriate
        `_ApplicationRouter` instance.
A
Andrey Sumin 已提交
1811 1812
    """

A
Andrey Sumin 已提交
1813 1814 1815
    def __init__(self, application, rules=None):
        assert isinstance(application, Application)
        self.application = application
A
Andrey Sumin 已提交
1816
        super(_ApplicationRouter, self).__init__(rules)
A
Andrey Sumin 已提交
1817 1818

    def process_rule(self, rule):
A
Andrey Sumin 已提交
1819
        rule = super(_ApplicationRouter, self).process_rule(rule)
A
Andrey Sumin 已提交
1820 1821

        if isinstance(rule.target, (list, tuple)):
A
Andrey Sumin 已提交
1822
            rule.target = _ApplicationRouter(self.application, rule.target)
A
Andrey Sumin 已提交
1823 1824 1825 1826 1827 1828 1829

        return rule

    def get_target_delegate(self, target, request, **target_params):
        if isclass(target) and issubclass(target, RequestHandler):
            return self.application.get_handler_delegate(request, target, **target_params)

A
Andrey Sumin 已提交
1830
        return super(_ApplicationRouter, self).get_target_delegate(target, request, **target_params)
A
Andrey Sumin 已提交
1831 1832 1833


class Application(ReversibleRouter):
B
Bret Taylor 已提交
1834 1835 1836
    """A collection of request handlers that make up a web application.

    Instances of this class are callable and can be passed directly to
B
Ben Darnell 已提交
1837
    HTTPServer to serve the application::
B
Bret Taylor 已提交
1838 1839 1840 1841 1842 1843

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

A
Andrey Sumin 已提交
1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861
    The constructor for this class takes in a list of `~.routing.Rule`
    objects or tuples of values corresponding to the arguments of
    `~.routing.Rule` constructor: ``(matcher, target, [target_kwargs], [name])``,
    the values in square brackets being optional. The default matcher is
    `~.routing.PathMatches`, so ``(regexp, target)`` tuples can also be used
    instead of ``(PathMatches(regexp), target)``.

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

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

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

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

A
Andrey Sumin 已提交
1872 1873 1874
    A dictionary may be passed as the third element (``target_kwargs``)
    of the tuple, which will be used as keyword arguments to the handler's
    constructor and `~RequestHandler.initialize` method. This pattern
1875 1876 1877
    is used for the `StaticFileHandler` in this example (note that a
    `StaticFileHandler` can be installed automatically with the
    static_path setting described below)::
B
Bret Taylor 已提交
1878 1879 1880 1881 1882

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

1883
    We support virtual hosts with the `add_handlers` method, which takes in
B
Ben Darnell 已提交
1884
    a host regular expression as the first argument::
B
Bret Taylor 已提交
1885 1886 1887 1888 1889

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

1890 1891 1892
    If there's no match for the current request's host, then ``default_host``
    parameter value is matched against host regular expressions.

1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903

    .. warning::

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

1904 1905 1906 1907 1908 1909 1910
    You can serve static files by sending the ``static_path`` setting
    as a keyword argument. We will serve those files from the
    ``/static/`` URI (this is configurable with the
    ``static_url_prefix`` setting), and we will serve ``/favicon.ico``
    and ``/robots.txt`` from the same directory.  A custom subclass of
    `StaticFileHandler` can be specified with the
    ``static_handler_class`` setting.
1911

1912 1913
    .. versionchanged:: 4.5
       Integration with the new `tornado.routing` module.
1914

B
Bret Taylor 已提交
1915
    """
A
Andrey Sumin 已提交
1916
    def __init__(self, handlers=None, default_host=None, transforms=None,
1917
                 **settings):
B
Bret Taylor 已提交
1918
        if transforms is None:
1919
            self.transforms = []
1920
            if settings.get("compress_response") or settings.get("gzip"):
1921
                self.transforms.append(GZipContentEncoding)
B
Bret Taylor 已提交
1922 1923 1924 1925
        else:
            self.transforms = transforms
        self.default_host = default_host
        self.settings = settings
1926
        self.ui_modules = {'linkify': _linkify,
1927 1928 1929
                           'xsrf_form_html': _xsrf_form_html,
                           'Template': TemplateModule,
                           }
B
Bret Taylor 已提交
1930 1931 1932 1933 1934 1935
        self.ui_methods = {}
        self._load_ui_modules(settings.get("ui_modules", {}))
        self._load_ui_methods(settings.get("ui_methods", {}))
        if self.settings.get("static_path"):
            path = self.settings["static_path"]
            handlers = list(handlers or [])
1936 1937
            static_url_prefix = settings.get("static_url_prefix",
                                             "/static/")
1938 1939
            static_handler_class = settings.get("static_handler_class",
                                                StaticFileHandler)
1940 1941 1942 1943 1944 1945
            static_handler_args = settings.get("static_handler_args", {})
            static_handler_args['path'] = path
            for pattern in [re.escape(static_url_prefix) + r"(.*)",
                            r"/(favicon\.ico)", r"/(robots\.txt)"]:
                handlers.insert(0, (pattern, static_handler_class,
                                    static_handler_args))
B
Bret Taylor 已提交
1946

1947 1948
        if self.settings.get('debug'):
            self.settings.setdefault('autoreload', True)
1949 1950 1951
            self.settings.setdefault('compiled_template_cache', False)
            self.settings.setdefault('static_hash_cache', False)
            self.settings.setdefault('serve_traceback', True)
1952

A
Andrey Sumin 已提交
1953 1954
        self.wildcard_router = _ApplicationRouter(self, handlers)
        self.default_router = _ApplicationRouter(self, [
A
Andrey Sumin 已提交
1955 1956 1957
            Rule(AnyMatches(), self.wildcard_router)
        ])

1958
        # Automatically reload modified modules
1959
        if self.settings.get('autoreload'):
B
Ben Darnell 已提交
1960
            from tornado import autoreload
B
Bret Taylor 已提交
1961
            autoreload.start()
1962

1963
    def listen(self, port, address="", **kwargs):
1964 1965
        """Starts an HTTP server for this application on the given port.

1966 1967 1968 1969 1970 1971 1972
        This is a convenience alias for creating an `.HTTPServer`
        object and calling its listen method.  Keyword arguments not
        supported by `HTTPServer.listen <.TCPServer.listen>` are passed to the
        `.HTTPServer` constructor.  For advanced uses
        (e.g. multi-process mode), do not use this method; create an
        `.HTTPServer` and call its
        `.TCPServer.bind`/`.TCPServer.start` methods directly.
1973 1974

        Note that after calling this method you still need to call
1975
        ``IOLoop.current().start()`` to start the server.
1976 1977 1978 1979 1980

        Returns the `.HTTPServer` object.

        .. versionchanged:: 4.3
           Now returns the `.HTTPServer` object.
1981 1982 1983 1984 1985
        """
        # import is here rather than top level because HTTPServer
        # is not importable on appengine
        from tornado.httpserver import HTTPServer
        server = HTTPServer(self, **kwargs)
1986
        server.listen(port, address)
1987
        return server
1988

B
Bret Taylor 已提交
1989
    def add_handlers(self, host_pattern, host_handlers):
1990 1991
        """Appends the given handlers to our handler list.

1992 1993
        Host patterns are processed sequentially in the order they were
        added. All matching patterns will be considered.
1994
        """
A
Andrey Sumin 已提交
1995
        host_matcher = HostMatches(host_pattern)
A
Andrey Sumin 已提交
1996
        rule = Rule(host_matcher, _ApplicationRouter(self, host_handlers))
A
Andrey Sumin 已提交
1997 1998 1999 2000 2001 2002 2003 2004

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

        if self.default_host is not None:
            self.wildcard_router.add_rules([(
                DefaultHostMatches(self, host_matcher.host_pattern),
                host_handlers
            )])
B
Bret Taylor 已提交
2005 2006 2007 2008 2009

    def add_transform(self, transform_class):
        self.transforms.append(transform_class)

    def _load_ui_methods(self, methods):
2010
        if isinstance(methods, types.ModuleType):
B
Bret Taylor 已提交
2011 2012 2013
            self._load_ui_methods(dict((n, getattr(methods, n))
                                       for n in dir(methods)))
        elif isinstance(methods, list):
2014 2015
            for m in methods:
                self._load_ui_methods(m)
B
Bret Taylor 已提交
2016
        else:
2017
            for name, fn in methods.items():
B
Bret Taylor 已提交
2018
                if not name.startswith("_") and hasattr(fn, "__call__") \
2019
                        and name[0].lower() == name[0]:
B
Bret Taylor 已提交
2020 2021 2022
                    self.ui_methods[name] = fn

    def _load_ui_modules(self, modules):
2023
        if isinstance(modules, types.ModuleType):
B
Bret Taylor 已提交
2024 2025 2026
            self._load_ui_modules(dict((n, getattr(modules, n))
                                       for n in dir(modules)))
        elif isinstance(modules, list):
2027 2028
            for m in modules:
                self._load_ui_modules(m)
B
Bret Taylor 已提交
2029 2030
        else:
            assert isinstance(modules, dict)
2031
            for name, cls in modules.items():
B
Bret Taylor 已提交
2032 2033 2034 2035 2036 2037
                try:
                    if issubclass(cls, UIModule):
                        self.ui_modules[name] = cls
                except TypeError:
                    pass

2038 2039
    def __call__(self, request):
        # Legacy HTTPServer interface
A
Andrey Sumin 已提交
2040
        dispatcher = self.find_handler(request)
2041
        return dispatcher.execute()
B
Bret Taylor 已提交
2042

A
Andrey Sumin 已提交
2043
    def find_handler(self, request, **kwargs):
A
Andrey Sumin 已提交
2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058
        route = self.default_router.find_handler(request)
        if route is not None:
            return route

        if self.settings.get('default_handler_class'):
            return self.get_handler_delegate(
                request,
                self.settings['default_handler_class'],
                self.settings.get('default_handler_args', {}))

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

    def get_handler_delegate(self, request, target_class, target_kwargs=None,
                             path_args=None, path_kwargs=None):
2059 2060 2061 2062 2063 2064 2065 2066 2067 2068
        """Returns `~.httputil.HTTPMessageDelegate` that can serve a request
        for application and `RequestHandler` subclass.

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

2072
    def reverse_url(self, name, *args):
2073
        """Returns a URL path for handler named ``name``
2074

2075
        The handler must be added to the application as a named `URLSpec`.
2076

2077
        Args will be substituted for capturing groups in the `URLSpec` regex.
2078 2079
        They will be converted to strings if necessary, encoded as utf8,
        and url-escaped.
2080
        """
A
Andrey Sumin 已提交
2081 2082 2083 2084
        reversed_url = self.default_router.reverse_url(name, *args)
        if reversed_url is not None:
            return reversed_url

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

2087 2088 2089 2090 2091 2092
    def log_request(self, handler):
        """Writes a completed HTTP request to the logs.

        By default writes to the python root logger.  To change
        this behavior either subclass Application and override this method,
        or pass a function in the application settings dictionary as
2093
        ``log_function``.
2094 2095 2096 2097 2098
        """
        if "log_function" in self.settings:
            self.settings["log_function"](handler)
            return
        if handler.get_status() < 400:
2099
            log_method = access_log.info
2100
        elif handler.get_status() < 500:
2101
            log_method = access_log.warning
2102
        else:
2103
            log_method = access_log.error
2104 2105 2106 2107 2108
        request_time = 1000.0 * handler.request.request_time()
        log_method("%d %s %.2fms", handler.get_status(),
                   handler._request_summary(), request_time)


A
Andrey Sumin 已提交
2109 2110 2111
class _HandlerDelegate(httputil.HTTPMessageDelegate):
    def __init__(self, application, request, handler_class, handler_kwargs,
                 path_args, path_kwargs):
2112
        self.application = application
A
Andrey Sumin 已提交
2113 2114 2115 2116 2117 2118
        self.connection = request.connection
        self.request = request
        self.handler_class = handler_class
        self.handler_kwargs = handler_kwargs or {}
        self.path_args = path_args or []
        self.path_kwargs = path_kwargs or {}
2119
        self.chunks = []
A
Andrey Sumin 已提交
2120
        self.stream_request_body = _has_stream_request_body(self.handler_class)
2121 2122

    def headers_received(self, start_line, headers):
2123 2124
        if self.stream_request_body:
            self.request.body = Future()
2125
            return self.execute()
2126 2127

    def data_received(self, data):
2128
        if self.stream_request_body:
2129
            return self.handler.data_received(data)
2130 2131
        else:
            self.chunks.append(data)
2132 2133

    def finish(self):
2134
        if self.stream_request_body:
2135
            future_set_result_unless_cancelled(self.request.body, None)
2136 2137 2138 2139
        else:
            self.request.body = b''.join(self.chunks)
            self.request._parse_body()
            self.execute()
2140

2141 2142 2143 2144 2145 2146
    def on_connection_close(self):
        if self.stream_request_body:
            self.handler.on_connection_close()
        else:
            self.chunks = None

2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157
    def execute(self):
        # If template cache is disabled (usually in the debug mode),
        # re-compile templates and reload static files on every
        # request so you don't need to restart to see changes
        if not self.application.settings.get("compiled_template_cache", True):
            with RequestHandler._template_loader_lock:
                for loader in RequestHandler._template_loaders.values():
                    loader.reset()
        if not self.application.settings.get('static_hash_cache', True):
            StaticFileHandler.reset()

2158
        self.handler = self.handler_class(self.application, self.request,
B
Ben Darnell 已提交
2159
                                          **self.handler_kwargs)
2160
        transforms = [t(self.request) for t in self.application.transforms]
2161 2162 2163

        if self.stream_request_body:
            self.handler._prepared_future = Future()
2164
        # Note that if an exception escapes handler._execute it will be
2165 2166
        # trapped in the Future it returns (which we are ignoring here,
        # leaving it to be logged when the Future is GC'd).
2167 2168
        # However, that shouldn't happen because _execute has a blanket
        # except handler, and we cannot easily access the IOLoop here to
2169 2170
        # call add_future (because of the requirement to remain compatible
        # with WSGI)
2171 2172
        self.handler._execute(transforms, *self.path_args,
                              **self.path_kwargs)
2173 2174 2175 2176 2177
        # If we are streaming the request body, then execute() is finished
        # when the handler has prepared to receive the body.  If not,
        # it doesn't matter when execute() finishes (so we return None)
        return self.handler._prepared_future

2178

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

2182 2183 2184 2185
    Raising an `HTTPError` is a convenient alternative to calling
    `RequestHandler.send_error` since it automatically ends the
    current function.

2186 2187 2188
    To customize the response sent with an `HTTPError`, override
    `RequestHandler.write_error`.

2189
    :arg int status_code: HTTP status code.  Must be listed in
2190 2191
        `httplib.responses <http.client.responses>` unless the ``reason``
        keyword argument is given.
B
Ben Darnell 已提交
2192
    :arg str log_message: Message to be written to the log for this error
2193 2194 2195
        (will not be shown to the user unless the `Application` is in debug
        mode).  May contain ``%s``-style placeholders, which will be filled
        in with remaining positional parameters.
B
Ben Darnell 已提交
2196
    :arg str reason: Keyword-only argument.  The HTTP "reason" phrase
2197 2198 2199 2200
        to pass in the status line along with ``status_code``.  Normally
        determined automatically from ``status_code``, but can be used
        to use a non-standard numeric code.
    """
2201
    def __init__(self, status_code=500, log_message=None, *args, **kwargs):
B
Bret Taylor 已提交
2202 2203 2204
        self.status_code = status_code
        self.log_message = log_message
        self.args = args
2205
        self.reason = kwargs.get('reason', None)
2206 2207
        if log_message and not args:
            self.log_message = log_message.replace('%', '%%')
B
Bret Taylor 已提交
2208 2209 2210

    def __str__(self):
        message = "HTTP %d: %s" % (
2211
            self.status_code,
2212
            self.reason or httputil.responses.get(self.status_code, 'Unknown'))
B
Bret Taylor 已提交
2213 2214 2215 2216 2217 2218
        if self.log_message:
            return message + " (" + (self.log_message % self.args) + ")"
        else:
            return message


2219 2220 2221
class Finish(Exception):
    """An exception that ends the request without producing an error response.

2222 2223 2224 2225 2226 2227 2228 2229
    When `Finish` is raised in a `RequestHandler`, the request will
    end (calling `RequestHandler.finish` if it hasn't already been
    called), but the error-handling methods (including
    `RequestHandler.write_error`) will not be called.

    If `Finish()` was created with no arguments, the pending response
    will be sent as-is. If `Finish()` was given an argument, that
    argument will be passed to `RequestHandler.finish()`.
2230 2231 2232 2233 2234 2235 2236 2237

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

        if self.current_user is None:
            self.set_status(401)
            self.set_header('WWW-Authenticate', 'Basic realm="something"')
            raise Finish()
2238 2239 2240 2241

    .. versionchanged:: 4.3
       Arguments passed to ``Finish()`` will be passed on to
       `RequestHandler.finish`.
2242 2243 2244 2245
    """
    pass


2246 2247 2248 2249 2250
class MissingArgumentError(HTTPError):
    """Exception raised by `RequestHandler.get_argument`.

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

    .. versionadded:: 3.1
2253 2254 2255 2256 2257 2258 2259
    """
    def __init__(self, arg_name):
        super(MissingArgumentError, self).__init__(
            400, 'Missing argument %s' % arg_name)
        self.arg_name = arg_name


B
Bret Taylor 已提交
2260
class ErrorHandler(RequestHandler):
2261
    """Generates an error response with ``status_code`` for all requests."""
2262
    def initialize(self, status_code):
B
Bret Taylor 已提交
2263 2264 2265 2266 2267
        self.set_status(status_code)

    def prepare(self):
        raise HTTPError(self._status_code)

2268 2269 2270 2271 2272 2273
    def check_xsrf_cookie(self):
        # POSTs to an ErrorHandler don't actually have side effects,
        # so we don't need to check the xsrf token.  This allows POSTs
        # to the wrong url to return a 404 instead of 403.
        pass

B
Bret Taylor 已提交
2274 2275 2276 2277

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

2278
    You should provide the keyword argument ``url`` to the handler, e.g.::
B
Bret Taylor 已提交
2279 2280 2281 2282

        application = web.Application([
            (r"/oldpath", web.RedirectHandler, {"url": "/newpath"}),
        ])
2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298

    `RedirectHandler` supports regular expression substitutions. E.g., to
    swap the first and second parts of a path while preserving the remainder::

        application = web.Application([
            (r"/(.*?)/(.*?)/(.*)", web.RedirectHandler, {"url": "/{1}/{0}/{2}"}),
        ])

    The final URL is formatted with `str.format` and the substrings that match
    the capturing groups. In the above example, a request to "/a/b/c" would be
    formatted like::

        str.format("/{1}/{0}/{2}", "a", "b", "c")  # -> "/b/a/c"

    Use Python's :ref:`format string syntax <formatstrings>` to customize how
    values are substituted.
2299 2300 2301

    .. versionchanged:: 4.5
       Added support for substitutions into the destination URL.
B
Ben Darnell 已提交
2302 2303 2304 2305

    .. versionchanged:: 5.0
       If any query arguments are present, they will be copied to the
       destination URL.
B
Bret Taylor 已提交
2306
    """
2307
    def initialize(self, url, permanent=True):
B
Bret Taylor 已提交
2308 2309
        self._url = url
        self._permanent = permanent
2310

2311
    def get(self, *args):
2312 2313 2314 2315 2316
        to_url = self._url.format(*args)
        if self.request.query_arguments:
            to_url = httputil.url_concat(
                to_url, list(httputil.qs_to_qsl(self.request.query_arguments)))
        self.redirect(to_url, permanent=self._permanent)
B
Bret Taylor 已提交
2317 2318 2319 2320 2321


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

2322 2323 2324 2325 2326 2327
    A `StaticFileHandler` is configured automatically if you pass the
    ``static_path`` keyword argument to `Application`.  This handler
    can be customized with the ``static_url_prefix``, ``static_handler_class``,
    and ``static_handler_args`` settings.

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

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

2334 2335 2336 2337
    The handler constructor requires a ``path`` argument, which specifies the
    local root directory of the content to be served.

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

2341 2342 2343 2344 2345
    To serve a file like ``index.html`` automatically when a directory is
    requested, set ``static_handler_args=dict(default_filename="index.html")``
    in your application settings, or add ``default_filename`` as an initializer
    argument for your ``StaticFileHandler``.

2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356
    To maximize the effectiveness of browser caching, this class supports
    versioned urls (by default using the argument ``?v=``).  If a version
    is given, we instruct the browser to cache this file indefinitely.
    `make_static_url` (also available as `RequestHandler.static_url`) can
    be used to construct a versioned url.

    This handler is intended primarily for use in development and light-duty
    file serving; for heavy traffic it will be more efficient to use
    a dedicated static file server (such as nginx or Apache).  We support
    the HTTP ``Accept-Ranges`` mechanism to return partial content (because
    some browsers require this functionality to be present to seek in
2357
    HTML5 audio or video).
2358 2359 2360 2361 2362 2363 2364 2365 2366 2367

    **Subclassing notes**

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

2368 2369 2370 2371 2372
    Subclasses should only override methods discussed in this section;
    overriding other methods is error-prone.  Overriding
    ``StaticFileHandler.get`` is particularly problematic due to the
    tight coupling with ``compute_etag`` and other methods.

2373 2374 2375 2376
    To change the way static urls are generated (e.g. to match the behavior
    of another server or CDN), override `make_static_url`, `parse_url_path`,
    `get_cache_time`, and/or `get_version`.

B
Ben Darnell 已提交
2377 2378 2379 2380
    To replace all interaction with the filesystem (e.g. to serve
    static content from a database), override `get_content`,
    `get_content_size`, `get_modified_time`, `get_absolute_path`, and
    `validate_absolute_path`.
2381 2382 2383

    .. versionchanged:: 3.1
       Many of the methods for subclasses were added in Tornado 3.1.
B
Bret Taylor 已提交
2384
    """
2385
    CACHE_MAX_AGE = 86400 * 365 * 10  # 10 years
2386

2387
    _static_hashes = {}  # type: typing.Dict
2388
    _lock = threading.Lock()  # protects _static_hashes
2389

2390
    def initialize(self, path, default_filename=None):
2391
        self.root = path
2392
        self.default_filename = default_filename
B
Bret Taylor 已提交
2393

2394 2395 2396 2397 2398
    @classmethod
    def reset(cls):
        with cls._lock:
            cls._static_hashes = {}

B
Bret Taylor 已提交
2399
    def head(self, path):
2400
        return self.get(path, include_body=False)
B
Bret Taylor 已提交
2401

2402
    @gen.coroutine
B
Bret Taylor 已提交
2403
    def get(self, path, include_body=True):
2404 2405 2406
        # Set up our path instance variables.
        self.path = self.parse_url_path(path)
        del path  # make sure we don't refer to path instead of self.path again
2407 2408 2409
        absolute_path = self.get_absolute_path(self.root, self.path)
        self.absolute_path = self.validate_absolute_path(
            self.root, absolute_path)
2410 2411
        if self.absolute_path is None:
            return
2412

2413 2414
        self.modified = self.get_modified_time()
        self.set_headers()
2415

2416
        if self.should_return_304():
2417 2418 2419 2420 2421 2422
            self.set_status(304)
            return

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

2427
        size = self.get_content_size()
2428
        if request_range:
2429
            start, end = request_range
2430 2431 2432 2433 2434 2435
            if (start is not None and start >= size) or end == 0:
                # As per RFC 2616 14.35.1, a range is not satisfiable only: if
                # the first requested byte is equal to or greater than the
                # content, or when a suffix with length 0 is specified
                self.set_status(416)  # Range Not Satisfiable
                self.set_header("Content-Type", "text/plain")
B
Ben Darnell 已提交
2436
                self.set_header("Content-Range", "bytes */%s" % (size, ))
2437 2438
                return
            if start is not None and start < 0:
2439
                start += size
2440 2441 2442 2443
            if end is not None and end > size:
                # Clients sometimes blindly use a large range to limit their
                # download size; cap the endpoint at the actual file size.
                end = size
2444 2445 2446 2447
            # Note: only return HTTP 206 if less than the entire range has been
            # requested. Not only is this semantically correct, but Chrome
            # refuses to play audio if it gets an HTTP 206 in response to
            # ``Range: bytes=0-``.
D
David Wolever 已提交
2448 2449
            if size != (end or size) - (start or 0):
                self.set_status(206)  # Partial Content
2450 2451
                self.set_header("Content-Range",
                                httputil._get_content_range(start, end, size))
2452
        else:
2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463
            start = end = None

        if start is not None and end is not None:
            content_length = end - start
        elif end is not None:
            content_length = end
        elif start is not None:
            content_length = size - start
        else:
            content_length = size
        self.set_header("Content-Length", content_length)
2464 2465 2466

        if include_body:
            content = self.get_content(self.absolute_path, start, end)
2467
            if isinstance(content, bytes):
2468 2469
                content = [content]
            for chunk in content:
2470 2471 2472 2473 2474
                try:
                    self.write(chunk)
                    yield self.flush()
                except iostream.StreamClosedError:
                    return
2475
        else:
2476
            assert self.request.method == "HEAD"
2477

2478 2479
    def compute_etag(self):
        """Sets the ``Etag`` header based on static url version.
2480

2481 2482 2483
        This allows efficient ``If-None-Match`` checks against cached
        versions, and sends the correct ``Etag`` for a partial response
        (i.e. the same ``Etag`` as the full file).
2484 2485

        .. versionadded:: 3.1
2486
        """
2487
        version_hash = self._get_cached_version(self.absolute_path)
2488 2489
        if not version_hash:
            return None
B
Ben Darnell 已提交
2490
        return '"%s"' % (version_hash, )
B
Bret Taylor 已提交
2491

2492
    def set_headers(self):
2493 2494 2495 2496
        """Sets the content and caching headers on the response.

        .. versionadded:: 3.1
        """
2497 2498
        self.set_header("Accept-Ranges", "bytes")
        self.set_etag_header()
2499

2500 2501
        if self.modified is not None:
            self.set_header("Last-Modified", self.modified)
B
Bret Taylor 已提交
2502

2503 2504 2505
        content_type = self.get_content_type()
        if content_type:
            self.set_header("Content-Type", content_type)
2506

G
Gonzalo Rafuls 已提交
2507 2508
        cache_time = self.get_cache_time(self.path, self.modified,
                                         content_type)
2509
        if cache_time > 0:
B
Ben Darnell 已提交
2510
            self.set_header("Expires", datetime.datetime.utcnow() +
2511
                            datetime.timedelta(seconds=cache_time))
2512 2513
            self.set_header("Cache-Control", "max-age=" + str(cache_time))

2514 2515 2516
        self.set_extra_headers(self.path)

    def should_return_304(self):
2517 2518 2519 2520
        """Returns True if the headers indicate that we should return 304.

        .. versionadded:: 3.1
        """
2521 2522 2523
        # If client sent If-None-Match, use it, ignore If-Modified-Since
        if self.request.headers.get('If-None-Match'):
            return self.check_etag_header()
2524

2525 2526 2527 2528 2529
        # Check the If-Modified-Since, and don't send the result if the
        # content has not been modified
        ims_value = self.request.headers.get("If-Modified-Since")
        if ims_value is not None:
            date_tuple = email.utils.parsedate(ims_value)
2530 2531 2532 2533
            if date_tuple is not None:
                if_since = datetime.datetime(*date_tuple[:6])
                if if_since >= self.modified:
                    return True
2534

2535
        return False
2536

2537
    @classmethod
2538 2539 2540 2541 2542
    def get_absolute_path(cls, root, path):
        """Returns the absolute location of ``path`` relative to ``root``.

        ``root`` is the path configured for this `StaticFileHandler`
        (in most cases the ``static_path`` `Application` setting).
2543 2544 2545 2546 2547

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

        .. versionadded:: 3.1
2550 2551 2552 2553
        """
        abspath = os.path.abspath(os.path.join(root, path))
        return abspath

2554
    def validate_absolute_path(self, root, absolute_path):
2555 2556
        """Validate and return the absolute path.

2557 2558 2559
        ``root`` is the configured path for the `StaticFileHandler`,
        and ``path`` is the result of `get_absolute_path`

2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570
        This is an instance method called during request processing,
        so it may raise `HTTPError` or use methods like
        `RequestHandler.redirect` (return None after redirecting to
        halt further processing).  This is where 404 errors for missing files
        are generated.

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

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

        .. versionadded:: 3.1
2573
        """
2574 2575 2576 2577
        # os.path.abspath strips a trailing /.
        # We must add it back to `root` so that we only match files
        # in a directory named `root` instead of files starting with
        # that prefix.
B
Ben Darnell 已提交
2578 2579 2580 2581 2582 2583 2584 2585
        root = os.path.abspath(root)
        if not root.endswith(os.path.sep):
            # abspath always removes a trailing slash, except when
            # root is '/'. This is an unusual case, but several projects
            # have independently discovered this technique to disable
            # Tornado's path validation and (hopefully) do their own,
            # so we need to support it.
            root += os.path.sep
2586 2587
        # The trailing slash also needs to be temporarily added back
        # the requested path so a request to root/ will match.
2588 2589 2590 2591
        if not (absolute_path + os.path.sep).startswith(root):
            raise HTTPError(403, "%s is not in root static directory",
                            self.path)
        if (os.path.isdir(absolute_path) and
B
Ben Darnell 已提交
2592
                self.default_filename is not None):
2593 2594 2595 2596
            # need to look at the request.path here for when path is empty
            # but there is some prefix to the path that was already
            # trimmed by the routing
            if not self.request.path.endswith("/"):
2597
                self.redirect(self.request.path + "/", permanent=True)
2598
                return
2599 2600 2601 2602 2603 2604
            absolute_path = os.path.join(absolute_path, self.default_filename)
        if not os.path.exists(absolute_path):
            raise HTTPError(404)
        if not os.path.isfile(absolute_path):
            raise HTTPError(403, "%s is not a file", self.path)
        return absolute_path
2605

2606
    @classmethod
2607
    def get_content(cls, abspath, start=None, end=None):
2608
        """Retrieve the content of the requested resource which is located
2609 2610 2611 2612 2613 2614
        at the given absolute path.

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

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

        .. versionadded:: 3.1
2621
        """
2622
        with open(abspath, "rb") as file:
2623 2624 2625 2626
            if start is not None:
                file.seek(start)
            if end is not None:
                remaining = end - (start or 0)
2627
            else:
B
Ben Darnell 已提交
2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641
                remaining = None
            while True:
                chunk_size = 64 * 1024
                if remaining is not None and remaining < chunk_size:
                    chunk_size = remaining
                chunk = file.read(chunk_size)
                if chunk:
                    if remaining is not None:
                        remaining -= len(chunk)
                    yield chunk
                else:
                    if remaining is not None:
                        assert remaining == 0
                    return
B
Bret Taylor 已提交
2642

2643 2644 2645 2646 2647 2648
    @classmethod
    def get_content_version(cls, abspath):
        """Returns a version string for the resource at the given path.

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

        .. versionadded:: 3.1
2651 2652
        """
        data = cls.get_content(abspath)
B
Ben Darnell 已提交
2653
        hasher = hashlib.md5()
2654
        if isinstance(data, bytes):
B
Ben Darnell 已提交
2655 2656 2657 2658 2659
            hasher.update(data)
        else:
            for chunk in data:
                hasher.update(chunk)
        return hasher.hexdigest()
2660

2661 2662 2663 2664 2665 2666 2667 2668
    def _stat(self):
        if not hasattr(self, '_stat_result'):
            self._stat_result = os.stat(self.absolute_path)
        return self._stat_result

    def get_content_size(self):
        """Retrieve the total size of the resource at the given path.

2669
        This method may be overridden by subclasses.
2670 2671

        .. versionadded:: 3.1
2672

2673
        .. versionchanged:: 4.0
2674 2675
           This method is now always called, instead of only when
           partial results are requested.
2676 2677 2678 2679
        """
        stat_result = self._stat()
        return stat_result[stat.ST_SIZE]

2680 2681 2682 2683 2684
    def get_modified_time(self):
        """Returns the time that ``self.absolute_path`` was last modified.

        May be overridden in subclasses.  Should return a `~datetime.datetime`
        object or None.
2685 2686

        .. versionadded:: 3.1
2687
        """
2688
        stat_result = self._stat()
G
Gonzalo Rafuls 已提交
2689 2690
        modified = datetime.datetime.utcfromtimestamp(
            stat_result[stat.ST_MTIME])
2691 2692 2693
        return modified

    def get_content_type(self):
2694 2695 2696 2697
        """Returns the ``Content-Type`` header to be used for this request.

        .. versionadded:: 3.1
        """
2698
        mime_type, encoding = mimetypes.guess_type(self.absolute_path)
2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711
        # per RFC 6713, use the appropriate type for a gzip compressed file
        if encoding == "gzip":
            return "application/gzip"
        # As of 2015-07-21 there is no bzip2 encoding defined at
        # http://www.iana.org/assignments/media-types/media-types.xhtml
        # So for that (and any other encoding), use octet-stream.
        elif encoding is not None:
            return "application/octet-stream"
        elif mime_type is not None:
            return mime_type
        # if mime_type not detected, use application/octet-stream
        else:
            return "application/octet-stream"
B
Bret Taylor 已提交
2712

2713
    def set_extra_headers(self, path):
B
Ben Darnell 已提交
2714 2715
        """For subclass to add extra headers to the response"""
        pass
2716

2717 2718 2719
    def get_cache_time(self, path, modified, mime_type):
        """Override to customize cache control behavior.

2720 2721 2722 2723
        Return a positive number of seconds to make the result
        cacheable for that amount of time or 0 to mark resource as
        cacheable for an unspecified amount of time (subject to
        browser heuristics).
2724 2725

        By default returns cache expiry of 10 years for resources requested
2726
        with ``v`` argument.
2727 2728
        """
        return self.CACHE_MAX_AGE if "v" in self.request.arguments else 0
2729

2730
    @classmethod
2731
    def make_static_url(cls, settings, path, include_version=True):
2732 2733
        """Constructs a versioned url for the given path.

2734 2735 2736
        This method may be overridden in subclasses (but note that it
        is a class method rather than an instance method).  Subclasses
        are only required to implement the signature
2737 2738 2739
        ``make_static_url(cls, settings, path)``; other keyword
        arguments may be passed through `~RequestHandler.static_url`
        but are not standard.
2740

2741 2742 2743
        ``settings`` is the `Application.settings` dictionary.  ``path``
        is the static path being requested.  The url returned should be
        relative to the current host.
2744 2745 2746 2747

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

2749
        """
2750 2751 2752 2753
        url = settings.get('static_url_prefix', '/static/') + path
        if not include_version:
            return url

2754
        version_hash = cls.get_version(settings, path)
2755 2756 2757 2758
        if not version_hash:
            return url

        return '%s?v=%s' % (url, version_hash)
2759

2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771
    def parse_url_path(self, url_path):
        """Converts a static URL path into a filesystem path.

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

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

    @classmethod
    def get_version(cls, settings, path):
2775 2776 2777
        """Generate the version string to be used in static URLs.

        ``settings`` is the `Application.settings` dictionary and ``path``
2778
        is the relative location of the requested asset on the filesystem.
2779 2780
        The returned value should be a string, or ``None`` if no version
        could be determined.
2781

2782 2783 2784 2785
        .. versionchanged:: 3.1
           This method was previously recommended for subclasses to override;
           `get_content_version` is now preferred as it allows the base
           class to handle caching of the result.
2786
        """
2787
        abs_path = cls.get_absolute_path(settings['static_path'], path)
2788 2789 2790 2791
        return cls._get_cached_version(abs_path)

    @classmethod
    def _get_cached_version(cls, abs_path):
2792 2793 2794 2795
        with cls._lock:
            hashes = cls._static_hashes
            if abs_path not in hashes:
                try:
2796
                    hashes[abs_path] = cls.get_content_version(abs_path)
2797
                except Exception:
2798
                    gen_log.error("Could not open static file %r", abs_path)
2799 2800
                    hashes[abs_path] = None
            hsh = hashes.get(abs_path)
2801
            if hsh:
2802
                return hsh
2803
        return None
2804

B
Bret Taylor 已提交
2805

2806
class FallbackHandler(RequestHandler):
2807
    """A `RequestHandler` that wraps another HTTP server callback.
2808

2809
    The fallback is a callable object that accepts an
B
Ben Darnell 已提交
2810
    `~.httputil.HTTPServerRequest`, such as an `Application` or
2811 2812 2813
    `tornado.wsgi.WSGIContainer`.  This is most useful to use both
    Tornado ``RequestHandlers`` and WSGI in the same server.  Typical
    usage::
B
Ben Darnell 已提交
2814

2815 2816 2817 2818 2819 2820 2821
        wsgi_app = tornado.wsgi.WSGIContainer(
            django.core.handlers.wsgi.WSGIHandler())
        application = tornado.web.Application([
            (r"/foo", FooHandler),
            (r".*", FallbackHandler, dict(fallback=wsgi_app),
        ])
    """
2822
    def initialize(self, fallback):
2823 2824 2825 2826 2827 2828 2829
        self.fallback = fallback

    def prepare(self):
        self.fallback(self.request)
        self._finished = True


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

2833 2834 2835
    Applications are not expected to create their own OutputTransforms
    or interact with them directly; the framework chooses which transforms
    (if any) to apply.
B
Bret Taylor 已提交
2836 2837 2838 2839
    """
    def __init__(self, request):
        pass

2840
    def transform_first_chunk(self, status_code, headers, chunk, finishing):
2841
        # type: (int, httputil.HTTPHeaders, bytes, bool) -> typing.Tuple[int, httputil.HTTPHeaders, bytes] # noqa: E501
2842
        return status_code, headers, chunk
B
Bret Taylor 已提交
2843

2844 2845
    def transform_chunk(self, chunk, finishing):
        return chunk
B
Bret Taylor 已提交
2846

2847 2848 2849 2850 2851

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

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

2853
    .. versionchanged:: 4.0
B
Ben Darnell 已提交
2854 2855 2856
        Now compresses all mime types beginning with ``text/``, instead
        of just a whitelist. (the whitelist is still used for certain
        non-text mime types).
2857
    """
2858 2859 2860 2861
    # Whitelist of compressible mime types (in addition to any types
    # beginning with "text/").
    CONTENT_TYPES = set(["application/javascript", "application/x-javascript",
                         "application/xml", "application/atom+xml",
2862 2863
                         "application/json", "application/xhtml+xml",
                         "image/svg+xml"])
2864 2865 2866 2867 2868 2869 2870 2871 2872 2873
    # Python's GzipFile defaults to level 9, while most other gzip
    # tools (including gzip itself) default to 6, which is probably a
    # better CPU/size tradeoff.
    GZIP_LEVEL = 6
    # Responses that are too short are unlikely to benefit from gzipping
    # after considering the "Content-Encoding: gzip" header and the header
    # inside the gzip encoding.
    # Note that responses written in multiple chunks will be compressed
    # regardless of size.
    MIN_LENGTH = 1024
2874 2875

    def __init__(self, request):
2876
        self._gzipping = "gzip" in request.headers.get("Accept-Encoding", "")
2877

2878 2879 2880
    def _compressible_type(self, ctype):
        return ctype.startswith('text/') or ctype in self.CONTENT_TYPES

2881
    def transform_first_chunk(self, status_code, headers, chunk, finishing):
2882
        # type: (int, httputil.HTTPHeaders, bytes, bool) -> typing.Tuple[int, httputil.HTTPHeaders, bytes] # noqa: E501
2883
        # TODO: can/should this type be inherited from the superclass?
2884
        if 'Vary' in headers:
2885
            headers['Vary'] += ', Accept-Encoding'
2886
        else:
2887
            headers['Vary'] = 'Accept-Encoding'
2888
        if self._gzipping:
2889
            ctype = _unicode(headers.get("Content-Type", "")).split(";")[0]
2890
            self._gzipping = self._compressible_type(ctype) and \
2891 2892 2893 2894
                (not finishing or len(chunk) >= self.MIN_LENGTH) and \
                ("Content-Encoding" not in headers)
        if self._gzipping:
            headers["Content-Encoding"] = "gzip"
2895
            self._gzip_value = BytesIO()
2896 2897
            self._gzip_file = gzip.GzipFile(mode="w", fileobj=self._gzip_value,
                                            compresslevel=self.GZIP_LEVEL)
2898 2899
            chunk = self.transform_chunk(chunk, finishing)
            if "Content-Length" in headers:
2900 2901 2902 2903 2904 2905 2906 2907
                # The original content length is no longer correct.
                # If this is the last (and only) chunk, we can set the new
                # content-length; otherwise we remove it and fall back to
                # chunked encoding.
                if finishing:
                    headers["Content-Length"] = str(len(chunk))
                else:
                    del headers["Content-Length"]
2908
        return status_code, headers, chunk
2909 2910 2911 2912 2913 2914 2915 2916 2917

    def transform_chunk(self, chunk, finishing):
        if self._gzipping:
            self._gzip_file.write(chunk)
            if finishing:
                self._gzip_file.close()
            else:
                self._gzip_file.flush()
            chunk = self._gzip_value.getvalue()
2918 2919
            self._gzip_value.truncate(0)
            self._gzip_value.seek(0)
2920
        return chunk
B
Bret Taylor 已提交
2921 2922 2923


def authenticated(method):
2924 2925 2926 2927
    """Decorate methods with this to require that the user be logged in.

    If the user is not logged in, they will be redirected to the configured
    `login url <RequestHandler.get_login_url>`.
2928 2929 2930 2931 2932

    If you configure a login url with a query parameter, Tornado will
    assume you know what you're doing and use it as-is.  If not, it
    will add a `next` parameter so the login page knows where to send
    you once you're logged in.
2933
    """
B
Bret Taylor 已提交
2934 2935 2936
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if not self.current_user:
2937
            if self.request.method in ("GET", "HEAD"):
B
Bret Taylor 已提交
2938 2939
                url = self.get_login_url()
                if "?" not in url:
2940 2941 2942 2943 2944
                    if urlparse.urlsplit(url).scheme:
                        # if login url is absolute, make next absolute too
                        next_url = self.request.full_url()
                    else:
                        next_url = self.request.uri
2945
                    url += "?" + urlencode(dict(next=next_url))
B
Bret Taylor 已提交
2946 2947 2948 2949 2950 2951 2952 2953
                self.redirect(url)
                return
            raise HTTPError(403)
        return method(self, *args, **kwargs)
    return wrapper


class UIModule(object):
2954
    """A re-usable, modular UI unit on a page.
B
Bret Taylor 已提交
2955 2956 2957 2958

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

    Subclasses of UIModule must override the `render` method.
B
Bret Taylor 已提交
2961 2962 2963 2964 2965 2966 2967
    """
    def __init__(self, handler):
        self.handler = handler
        self.request = handler.request
        self.ui = handler.ui
        self.locale = handler.locale

2968 2969 2970 2971
    @property
    def current_user(self):
        return self.handler.current_user

B
Bret Taylor 已提交
2972
    def render(self, *args, **kwargs):
B
Ben Darnell 已提交
2973
        """Override in subclasses to return this module's output."""
B
Bret Taylor 已提交
2974 2975 2976
        raise NotImplementedError()

    def embedded_javascript(self):
G
Gonzalo Rafuls 已提交
2977 2978
        """Override to return a JavaScript string
        to be embedded in the page."""
B
Bret Taylor 已提交
2979 2980 2981
        return None

    def javascript_files(self):
B
Ben Darnell 已提交
2982 2983 2984 2985 2986
        """Override to return a list of JavaScript files needed by this module.

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

    def embedded_css(self):
G
Gonzalo Rafuls 已提交
2990 2991
        """Override to return a CSS string
        that will be embedded in the page."""
B
Bret Taylor 已提交
2992 2993 2994
        return None

    def css_files(self):
B
Ben Darnell 已提交
2995 2996 2997 2998 2999
        """Override to returns a list of CSS files required by this module.

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

    def html_head(self):
B
Ben Darnell 已提交
3003 3004 3005
        """Override to return an HTML string that will be put in the <head/>
        element.
        """
B
Bret Taylor 已提交
3006 3007
        return None

3008
    def html_body(self):
B
Ben Darnell 已提交
3009 3010 3011
        """Override to return an HTML string that will be put at the end of
        the <body/> element.
        """
3012 3013
        return None

B
Bret Taylor 已提交
3014
    def render_string(self, path, **kwargs):
3015
        """Renders a template and returns it as a string."""
B
Bret Taylor 已提交
3016 3017
        return self.handler.render_string(path, **kwargs)

3018

3019 3020 3021 3022
class _linkify(UIModule):
    def render(self, text, **kwargs):
        return escape.linkify(text, **kwargs)

3023

3024 3025 3026 3027
class _xsrf_form_html(UIModule):
    def render(self):
        return self.handler.xsrf_form_html()

3028

3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040
class TemplateModule(UIModule):
    """UIModule that simply renders the given template.

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

    Templates rendered through this module also get access to UIModule's
    automatic javascript/css features.  Simply call set_resources
    inside the template and give it keyword arguments corresponding to
    the methods on UIModule: {{ set_resources(js_files=static_url("my.js")) }}
    Note that these resources are output once per template file, not once
3041
    per instantiation of the template, so they must not depend on
3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071
    any arguments to the template.
    """
    def __init__(self, handler):
        super(TemplateModule, self).__init__(handler)
        # keep resources in both a list and a dict to preserve order
        self._resource_list = []
        self._resource_dict = {}

    def render(self, path, **kwargs):
        def set_resources(**kwargs):
            if path not in self._resource_dict:
                self._resource_list.append(kwargs)
                self._resource_dict[path] = kwargs
            else:
                if self._resource_dict[path] != kwargs:
                    raise ValueError("set_resources called with different "
                                     "resources for the same template")
            return ""
        return self.render_string(path, set_resources=set_resources,
                                  **kwargs)

    def _get_resources(self, key):
        return (r[key] for r in self._resource_list if key in r)

    def embedded_javascript(self):
        return "\n".join(self._get_resources("embedded_javascript"))

    def javascript_files(self):
        result = []
        for f in self._get_resources("javascript_files"):
3072
            if isinstance(f, (unicode_type, bytes)):
3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083
                result.append(f)
            else:
                result.extend(f)
        return result

    def embedded_css(self):
        return "\n".join(self._get_resources("embedded_css"))

    def css_files(self):
        result = []
        for f in self._get_resources("css_files"):
3084
            if isinstance(f, (unicode_type, bytes)):
3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096
                result.append(f)
            else:
                result.extend(f)
        return result

    def html_head(self):
        return "".join(self._get_resources("html_head"))

    def html_body(self):
        return "".join(self._get_resources("html_body"))


3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112
class _UIModuleNamespace(object):
    """Lazy namespace which creates UIModule proxies bound to a handler."""
    def __init__(self, handler, ui_modules):
        self.handler = handler
        self.ui_modules = ui_modules

    def __getitem__(self, key):
        return self.handler._ui_module(key, self.ui_modules[key])

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError as e:
            raise AttributeError(str(e))


3113 3114 3115 3116 3117 3118 3119
if hasattr(hmac, 'compare_digest'):  # python 3.3
    _time_independent_equals = hmac.compare_digest
else:
    def _time_independent_equals(a, b):
        if len(a) != len(b):
            return False
        result = 0
3120
        if isinstance(a[0], int):  # python3 byte strings
3121 3122 3123 3124 3125 3126
            for x, y in zip(a, b):
                result |= x ^ y
        else:  # python2
            for x, y in zip(a, b):
                result |= ord(x) ^ ord(y)
        return result == 0
3127

3128

3129 3130
def create_signed_value(secret, name, value, version=None, clock=None,
                        key_version=None):
3131
    if version is None:
3132
        version = DEFAULT_SIGNED_VALUE_VERSION
B
Ben Darnell 已提交
3133 3134
    if clock is None:
        clock = time.time
3135

B
Ben Darnell 已提交
3136
    timestamp = utf8(str(int(clock())))
3137
    value = base64.b64encode(utf8(value))
3138
    if version == 1:
B
Ben Darnell 已提交
3139
        signature = _create_signature_v1(secret, name, value, timestamp)
3140 3141
        value = b"|".join([value, timestamp, signature])
        return value
B
Ben Darnell 已提交
3142 3143 3144 3145 3146 3147 3148 3149 3150 3151
    elif version == 2:
        # The v2 format consists of a version number and a series of
        # length-prefixed fields "%d:%s", the last of which is a
        # signature, all separated by pipes.  All numbers are in
        # decimal format with no leading zeros.  The signature is an
        # HMAC-SHA256 of the whole string up to that point, including
        # the final pipe.
        #
        # The fields are:
        # - format version (i.e. 2; no length prefix)
3152
        # - key version (integer, default is 0)
B
Ben Darnell 已提交
3153 3154 3155 3156 3157 3158 3159
        # - timestamp (integer seconds since epoch)
        # - name (not encoded; assumed to be ~alphanumeric)
        # - value (base64-encoded)
        # - signature (hex-encoded; no length prefix)
        def format_field(s):
            return utf8("%d:" % len(s)) + utf8(s)
        to_sign = b"|".join([
3160
            b"2",
3161
            format_field(str(key_version or 0)),
B
Ben Darnell 已提交
3162 3163 3164 3165
            format_field(timestamp),
            format_field(name),
            format_field(value),
            b''])
3166 3167

        if isinstance(secret, dict):
3168 3169
            assert key_version is not None, 'Key version must be set when sign key dict is used'
            assert version >= 2, 'Version must be at least 2 for key version support'
3170 3171
            secret = secret[key_version]

B
Ben Darnell 已提交
3172 3173
        signature = _create_signature_v2(secret, to_sign)
        return to_sign + signature
3174 3175
    else:
        raise ValueError("Unsupported version %d" % version)
3176

B
Ben Darnell 已提交
3177

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

B
Ben Darnell 已提交
3182

3183 3184
def _get_version(value):
    # Figures out what version value is.  Version 1 did not include an
3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202
    # explicit version field and started with arbitrary base64 data,
    # which makes this tricky.
    m = _signed_value_version_re.match(value)
    if m is None:
        version = 1
    else:
        try:
            version = int(m.group(1))
            if version > 999:
                # Certain payloads from the version-less v1 format may
                # be parsed as valid integers.  Due to base64 padding
                # restrictions, this can only happen for numbers whose
                # length is a multiple of 4, so we can treat all
                # numbers up to 999 as versions, and for the rest we
                # fall back to v1 format.
                version = 1
        except ValueError:
            version = 1
3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218
    return version


def decode_signed_value(secret, name, value, max_age_days=31,
                        clock=None, min_version=None):
    if clock is None:
        clock = time.time
    if min_version is None:
        min_version = DEFAULT_SIGNED_VALUE_MIN_VERSION
    if min_version > 2:
        raise ValueError("Unsupported min_version %d" % min_version)
    if not value:
        return None

    value = utf8(value)
    version = _get_version(value)
3219 3220

    if version < min_version:
B
Ben Darnell 已提交
3221
        return None
3222
    if version == 1:
G
Gonzalo Rafuls 已提交
3223 3224
        return _decode_signed_value_v1(secret, name, value,
                                       max_age_days, clock)
B
Ben Darnell 已提交
3225
    elif version == 2:
G
Gonzalo Rafuls 已提交
3226 3227
        return _decode_signed_value_v2(secret, name, value,
                                       max_age_days, clock)
3228
    else:
B
Ben Darnell 已提交
3229
        return None
3230

B
Ben Darnell 已提交
3231

B
Ben Darnell 已提交
3232
def _decode_signed_value_v1(secret, name, value, max_age_days, clock):
3233
    parts = utf8(value).split(b"|")
3234 3235
    if len(parts) != 3:
        return None
B
Ben Darnell 已提交
3236
    signature = _create_signature_v1(secret, name, parts[0], parts[1])
3237
    if not _time_independent_equals(parts[2], signature):
3238
        gen_log.warning("Invalid cookie signature %r", value)
3239 3240
        return None
    timestamp = int(parts[1])
B
Ben Darnell 已提交
3241
    if timestamp < clock() - max_age_days * 86400:
3242
        gen_log.warning("Expired cookie %r", value)
3243
        return None
B
Ben Darnell 已提交
3244
    if timestamp > clock() + 31 * 86400:
3245 3246 3247 3248 3249
        # _cookie_signature does not hash a delimiter between the
        # parts of the cookie, so an attacker could transfer trailing
        # digits from the payload to the timestamp without altering the
        # signature.  For backwards compatibility, sanity-check timestamp
        # here instead of modifying _cookie_signature.
G
Gonzalo Rafuls 已提交
3250 3251
        gen_log.warning("Cookie timestamp in future; possible tampering %r",
                        value)
3252
        return None
3253
    if parts[1].startswith(b"0"):
3254
        gen_log.warning("Tampered cookie %r", value)
3255
        return None
3256 3257 3258 3259 3260
    try:
        return base64.b64decode(parts[0])
    except Exception:
        return None

3261

3262
def _decode_fields_v2(value):
B
Ben Darnell 已提交
3263 3264 3265 3266 3267 3268
    def _consume_field(s):
        length, _, rest = s.partition(b':')
        n = int(length)
        field_value = rest[:n]
        # In python 3, indexing bytes returns small integers; we must
        # use a slice to get a byte string as in python 2.
B
Ben Darnell 已提交
3269
        if rest[n:n + 1] != b'|':
B
Ben Darnell 已提交
3270
            raise ValueError("malformed v2 signed value field")
B
Ben Darnell 已提交
3271
        rest = rest[n + 1:]
B
Ben Darnell 已提交
3272
        return field_value, rest
3273

B
Ben Darnell 已提交
3274
    rest = value[2:]  # remove version number
3275 3276 3277 3278 3279 3280 3281 3282
    key_version, rest = _consume_field(rest)
    timestamp, rest = _consume_field(rest)
    name_field, rest = _consume_field(rest)
    value_field, passed_sig = _consume_field(rest)
    return int(key_version), timestamp, name_field, value_field, passed_sig


def _decode_signed_value_v2(secret, name, value, max_age_days, clock):
B
Ben Darnell 已提交
3283
    try:
3284
        key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value)
B
Ben Darnell 已提交
3285 3286 3287
    except ValueError:
        return None
    signed_string = value[:-len(passed_sig)]
3288 3289 3290 3291 3292 3293 3294

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

B
Ben Darnell 已提交
3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309
    expected_sig = _create_signature_v2(secret, signed_string)
    if not _time_independent_equals(passed_sig, expected_sig):
        return None
    if name_field != utf8(name):
        return None
    timestamp = int(timestamp)
    if timestamp < clock() - max_age_days * 86400:
        # The signature has expired.
        return None
    try:
        return base64.b64decode(value_field)
    except Exception:
        return None


3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322
def get_signature_key_version(value):
    value = utf8(value)
    version = _get_version(value)
    if version < 2:
        return None
    try:
        key_version, _, _, _, _ = _decode_fields_v2(value)
    except ValueError:
        return None

    return key_version


B
Ben Darnell 已提交
3323
def _create_signature_v1(secret, *parts):
3324
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
3325 3326
    for part in parts:
        hash.update(utf8(part))
3327
    return utf8(hash.hexdigest())
3328

B
Ben Darnell 已提交
3329

B
Ben Darnell 已提交
3330 3331 3332 3333
def _create_signature_v2(secret, s):
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
    hash.update(utf8(s))
    return utf8(hash.hexdigest())
B
Ben Darnell 已提交
3334

B
Ben Darnell 已提交
3335

3336 3337
def is_absolute(path):
    return any(path.startswith(x) for x in ["/", "http:", "https:"])