{
    "file_diffs": [
        {
            "old_file_content": "# Copyright 2015 The Tornado Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\n\"\"\"Flexible routing implementation.\n\nTornado routes HTTP requests to appropriate handlers using `Router`\nclass implementations. The `tornado.web.Application` class is a\n`Router` implementation and may be used directly, or the classes in\nthis module may be used for additional flexibility. The `RuleRouter`\nclass can match on more criteria than `.Application`, or the `Router`\ninterface can be subclassed for maximum customization.\n\n`Router` interface extends `~.httputil.HTTPServerConnectionDelegate`\nto provide additional routing capabilities. This also means that any\n`Router` implementation can be used directly as a ``request_callback``\nfor `~.httpserver.HTTPServer` constructor.\n\n`Router` subclass must implement a ``find_handler`` method to provide\na suitable `~.httputil.HTTPMessageDelegate` instance to handle the\nrequest:\n\n.. code-block:: python\n\n    class CustomRouter(Router):\n        def find_handler(self, request, **kwargs):\n            # some routing logic providing a suitable HTTPMessageDelegate instance\n            return MessageDelegate(request.connection)\n\n    class MessageDelegate(HTTPMessageDelegate):\n        def __init__(self, connection):\n            self.connection = connection\n\n        def finish(self):\n            self.connection.write_headers(\n                ResponseStartLine(\"HTTP/1.1\", 200, \"OK\"),\n                HTTPHeaders({\"Content-Length\": \"2\"}),\n                b\"OK\")\n            self.connection.finish()\n\n    router = CustomRouter()\n    server = HTTPServer(router)\n\nThe main responsibility of `Router` implementation is to provide a\nmapping from a request to `~.httputil.HTTPMessageDelegate` instance\nthat will handle this request. In the example above we can see that\nrouting is possible even without instantiating an `~.web.Application`.\n\nFor routing to `~.web.RequestHandler` implementations we need an\n`~.web.Application` instance. `~.web.Application.get_handler_delegate`\nprovides a convenient way to create `~.httputil.HTTPMessageDelegate`\nfor a given request and `~.web.RequestHandler`.\n\nHere is a simple example of how we can we route to\n`~.web.RequestHandler` subclasses by HTTP method:\n\n.. code-block:: python\n\n    resources = {}\n\n    class GetResource(RequestHandler):\n        def get(self, path):\n            if path not in resources:\n                raise HTTPError(404)\n\n            self.finish(resources[path])\n\n    class PostResource(RequestHandler):\n        def post(self, path):\n            resources[path] = self.request.body\n\n    class HTTPMethodRouter(Router):\n        def __init__(self, app):\n            self.app = app\n\n        def find_handler(self, request, **kwargs):\n            handler = GetResource if request.method == \"GET\" else PostResource\n            return self.app.get_handler_delegate(request, handler, path_args=[request.path])\n\n    router = HTTPMethodRouter(Application())\n    server = HTTPServer(router)\n\n`ReversibleRouter` interface adds the ability to distinguish between\nthe routes and reverse them to the original urls using route's name\nand additional arguments. `~.web.Application` is itself an\nimplementation of `ReversibleRouter` class.\n\n`RuleRouter` and `ReversibleRuleRouter` are implementations of\n`Router` and `ReversibleRouter` interfaces and can be used for\ncreating rule-based routing configurations.\n\nRules are instances of `Rule` class. They contain a `Matcher`, which\nprovides the logic for determining whether the rule is a match for a\nparticular request and a target, which can be one of the following.\n\n1) An instance of `~.httputil.HTTPServerConnectionDelegate`:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/handler\"), ConnectionDelegate()),\n        # ... more rules\n    ])\n\n    class ConnectionDelegate(HTTPServerConnectionDelegate):\n        def start_request(self, server_conn, request_conn):\n            return MessageDelegate(request_conn)\n\n2) A callable accepting a single argument of `~.httputil.HTTPServerRequest` type:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/callable\"), request_callable)\n    ])\n\n    def request_callable(request):\n        request.write(b\"HTTP/1.1 200 OK\\\\r\\\\nContent-Length: 2\\\\r\\\\n\\\\r\\\\nOK\")\n        request.finish()\n\n3) Another `Router` instance:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/router.*\"), CustomRouter())\n    ])\n\nOf course a nested `RuleRouter` or a `~.web.Application` is allowed:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(HostMatches(\"example.com\"), RuleRouter([\n            Rule(PathMatches(\"/app1/.*\"), Application([(r\"/app1/handler\", Handler)]))),\n        ]))\n    ])\n\n    server = HTTPServer(router)\n\nIn the example below `RuleRouter` is used to route between applications:\n\n.. code-block:: python\n\n    app1 = Application([\n        (r\"/app1/handler\", Handler1),\n        # other handlers ...\n    ])\n\n    app2 = Application([\n        (r\"/app2/handler\", Handler2),\n        # other handlers ...\n    ])\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/app1.*\"), app1),\n        Rule(PathMatches(\"/app2.*\"), app2)\n    ])\n\n    server = HTTPServer(router)\n\nFor more information on application-level routing see docs for `~.web.Application`.\n\n.. versionadded:: 4.5\n\n\"\"\"\n\nimport re\nfrom functools import partial\n\nfrom tornado import httputil\nfrom tornado.httpserver import _CallableAdapter\nfrom tornado.escape import url_escape, url_unescape, utf8\nfrom tornado.log import app_log\nfrom tornado.util import basestring_type, import_object, re_unescape, unicode_type\n\nfrom typing import Any, Union, Optional, Awaitable, List, Dict, Pattern, Tuple, overload\n\n\nclass Router(httputil.HTTPServerConnectionDelegate):\n    \"\"\"Abstract router interface.\"\"\"\n\n    def find_handler(\n        self, request: httputil.HTTPServerRequest, **kwargs: Any\n    ) -> Optional[httputil.HTTPMessageDelegate]:\n        \"\"\"Must be implemented to return an appropriate instance of `~.httputil.HTTPMessageDelegate`\n        that can serve the request.\n        Routing implementations may pass additional kwargs to extend the routing logic.\n\n        :arg httputil.HTTPServerRequest request: current HTTP request.\n        :arg kwargs: additional keyword arguments passed by routing implementation.\n        :returns: an instance of `~.httputil.HTTPMessageDelegate` that will be used to\n            process the request.\n        \"\"\"\n        raise NotImplementedError()\n\n    def start_request(\n        self, server_conn: object, request_conn: httputil.HTTPConnection\n    ) -> httputil.HTTPMessageDelegate:\n        return _RoutingDelegate(self, server_conn, request_conn)\n\n\nclass ReversibleRouter(Router):\n    \"\"\"Abstract router interface for routers that can handle named routes\n    and support reversing them to original urls.\n    \"\"\"\n\n    def reverse_url(self, name: str, *args: Any) -> Optional[str]:\n        \"\"\"Returns url string for a given route name and arguments\n        or ``None`` if no match is found.\n\n        :arg str name: route name.\n        :arg args: url parameters.\n        :returns: parametrized url string for a given route name (or ``None``).\n        \"\"\"\n        raise NotImplementedError()\n\n\nclass _RoutingDelegate(httputil.HTTPMessageDelegate):\n    def __init__(\n        self, router: Router, server_conn: object, request_conn: httputil.HTTPConnection\n    ) -> None:\n        self.server_conn = server_conn\n        self.request_conn = request_conn\n        self.delegate = None  # type: Optional[httputil.HTTPMessageDelegate]\n        self.router = router  # type: Router\n\n    def headers_received(\n        self,\n        start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine],\n        headers: httputil.HTTPHeaders,\n    ) -> Optional[Awaitable[None]]:\n        assert isinstance(start_line, httputil.RequestStartLine)\n        request = httputil.HTTPServerRequest(\n            connection=self.request_conn,\n            server_connection=self.server_conn,\n            start_line=start_line,\n            headers=headers,\n        )\n\n        self.delegate = self.router.find_handler(request)\n        if self.delegate is None:\n            app_log.debug(\n                \"Delegate for %s %s request not found\",\n                start_line.method,\n                start_line.path,\n            )\n            self.delegate = _DefaultMessageDelegate(self.request_conn)\n\n        return self.delegate.headers_received(start_line, headers)\n\n    def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:\n        assert self.delegate is not None\n        return self.delegate.data_received(chunk)\n\n    def finish(self) -> None:\n        assert self.delegate is not None\n        self.delegate.finish()\n\n    def on_connection_close(self) -> None:\n        assert self.delegate is not None\n        self.delegate.on_connection_close()\n\n\nclass _DefaultMessageDelegate(httputil.HTTPMessageDelegate):\n    def __init__(self, connection: httputil.HTTPConnection) -> None:\n        self.connection = connection\n\n    def finish(self) -> None:\n        self.connection.write_headers(\n            httputil.ResponseStartLine(\"HTTP/1.1\", 404, \"Not Found\"),\n            httputil.HTTPHeaders(),\n        )\n        self.connection.finish()\n\n\n# _RuleList can either contain pre-constructed Rules or a sequence of\n# arguments to be passed to the Rule constructor.\n_RuleList = List[\n    Union[\n        \"Rule\",\n        List[Any],  # Can't do detailed typechecking of lists.\n        Tuple[Union[str, \"Matcher\"], Any],\n        Tuple[Union[str, \"Matcher\"], Any, Dict[str, Any]],\n        Tuple[Union[str, \"Matcher\"], Any, Dict[str, Any], str],\n    ]\n]\n\n\nclass RuleRouter(Router):\n    \"\"\"Rule-based router implementation.\"\"\"\n\n    def __init__(self, rules: Optional[_RuleList] = None) -> None:\n        \"\"\"Constructs a router from an ordered list of rules::\n\n            RuleRouter([\n                Rule(PathMatches(\"/handler\"), Target),\n                # ... more rules\n            ])\n\n        You can also omit explicit `Rule` constructor and use tuples of arguments::\n\n            RuleRouter([\n                (PathMatches(\"/handler\"), Target),\n            ])\n\n        `PathMatches` is a default matcher, so the example above can be simplified::\n\n            RuleRouter([\n                (\"/handler\", Target),\n            ])\n\n        In the examples above, ``Target`` can be a nested `Router` instance, an instance of\n        `~.httputil.HTTPServerConnectionDelegate` or an old-style callable,\n        accepting a request argument.\n\n        :arg rules: a list of `Rule` instances or tuples of `Rule`\n            constructor arguments.\n        \"\"\"\n        self.rules = []  # type: List[Rule]\n        if rules:\n            self.add_rules(rules)\n\n    def add_rules(self, rules: _RuleList) -> None:\n        \"\"\"Appends new rules to the router.\n\n        :arg rules: a list of Rule instances (or tuples of arguments, which are\n            passed to Rule constructor).\n        \"\"\"\n        for rule in rules:\n            if isinstance(rule, (tuple, list)):\n                assert len(rule) in (2, 3, 4)\n                if isinstance(rule[0], basestring_type):\n                    rule = Rule(PathMatches(rule[0]), *rule[1:])\n                else:\n                    rule = Rule(*rule)\n\n            self.rules.append(self.process_rule(rule))\n\n    def process_rule(self, rule: \"Rule\") -> \"Rule\":\n        \"\"\"Override this method for additional preprocessing of each rule.\n\n        :arg Rule rule: a rule to be processed.\n        :returns: the same or modified Rule instance.\n        \"\"\"\n        return rule\n\n    def find_handler(\n        self, request: httputil.HTTPServerRequest, **kwargs: Any\n    ) -> Optional[httputil.HTTPMessageDelegate]:\n        for rule in self.rules:\n            target_params = rule.matcher.match(request)\n            if target_params is not None:\n                if rule.target_kwargs:\n                    target_params[\"target_kwargs\"] = rule.target_kwargs\n\n                delegate = self.get_target_delegate(\n                    rule.target, request, **target_params\n                )\n\n                if delegate is not None:\n                    return delegate\n\n        return None\n\n    def get_target_delegate(\n        self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any\n    ) -> Optional[httputil.HTTPMessageDelegate]:\n        \"\"\"Returns an instance of `~.httputil.HTTPMessageDelegate` for a\n        Rule's target. This method is called by `~.find_handler` and can be\n        extended to provide additional target types.\n\n        :arg target: a Rule's target.\n        :arg httputil.HTTPServerRequest request: current request.\n        :arg target_params: additional parameters that can be useful\n            for `~.httputil.HTTPMessageDelegate` creation.\n        \"\"\"\n        if isinstance(target, Router):\n            return target.find_handler(request, **target_params)\n\n        elif isinstance(target, httputil.HTTPServerConnectionDelegate):\n            assert request.connection is not None\n            return target.start_request(request.server_connection, request.connection)\n\n        elif callable(target):\n            assert request.connection is not None\n            return _CallableAdapter(\n                partial(target, **target_params), request.connection\n            )\n\n        return None\n\n\nclass ReversibleRuleRouter(ReversibleRouter, RuleRouter):\n    \"\"\"A rule-based router that implements ``reverse_url`` method.\n\n    Each rule added to this router may have a ``name`` attribute that can be\n    used to reconstruct an original uri. The actual reconstruction takes place\n    in a rule's matcher (see `Matcher.reverse`).\n    \"\"\"\n\n    def __init__(self, rules: Optional[_RuleList] = None) -> None:\n        self.named_rules = {}  # type: Dict[str, Any]\n        super(ReversibleRuleRouter, self).__init__(rules)\n\n    def process_rule(self, rule: \"Rule\") -> \"Rule\":\n        rule = super(ReversibleRuleRouter, self).process_rule(rule)\n\n        if rule.name:\n            if rule.name in self.named_rules:\n                app_log.warning(\n                    \"Multiple handlers named %s; replacing previous value\", rule.name\n                )\n            self.named_rules[rule.name] = rule\n\n        return rule\n\n    def reverse_url(self, name: str, *args: Any) -> Optional[str]:\n        if name in self.named_rules:\n            return self.named_rules[name].matcher.reverse(*args)\n\n        for rule in self.rules:\n            if isinstance(rule.target, ReversibleRouter):\n                reversed_url = rule.target.reverse_url(name, *args)\n                if reversed_url is not None:\n                    return reversed_url\n\n        return None\n\n\nclass Rule(object):\n    \"\"\"A routing rule.\"\"\"\n\n    def __init__(\n        self,\n        matcher: \"Matcher\",\n        target: Any,\n        target_kwargs: Optional[Dict[str, Any]] = None,\n        name: Optional[str] = None,\n    ) -> None:\n        \"\"\"Constructs a Rule instance.\n\n        :arg Matcher matcher: a `Matcher` instance used for determining\n            whether the rule should be considered a match for a specific\n            request.\n        :arg target: a Rule's target (typically a ``RequestHandler`` or\n            `~.httputil.HTTPServerConnectionDelegate` subclass or even a nested `Router`,\n            depending on routing implementation).\n        :arg dict target_kwargs: a dict of parameters that can be useful\n            at the moment of target instantiation (for example, ``status_code``\n            for a ``RequestHandler`` subclass). They end up in\n            ``target_params['target_kwargs']`` of `RuleRouter.get_target_delegate`\n            method.\n        :arg str name: the name of the rule that can be used to find it\n            in `ReversibleRouter.reverse_url` implementation.\n        \"\"\"\n        if isinstance(target, str):\n            # import the Module and instantiate the class\n            # Must be a fully qualified name (module.ClassName)\n            target = import_object(target)\n\n        self.matcher = matcher  # type: Matcher\n        self.target = target\n        self.target_kwargs = target_kwargs if target_kwargs else {}\n        self.name = name\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        return self.matcher.reverse(*args)\n\n    def __repr__(self) -> str:\n        return \"%s(%r, %s, kwargs=%r, name=%r)\" % (\n            self.__class__.__name__,\n            self.matcher,\n            self.target,\n            self.target_kwargs,\n            self.name,\n        )\n\n\nclass Matcher(object):\n    \"\"\"Represents a matcher for request features.\"\"\"\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        \"\"\"Matches current instance against the request.\n\n        :arg httputil.HTTPServerRequest request: current HTTP request\n        :returns: a dict of parameters to be passed to the target handler\n            (for example, ``handler_kwargs``, ``path_args``, ``path_kwargs``\n            can be passed for proper `~.web.RequestHandler` instantiation).\n            An empty dict is a valid (and common) return value to indicate a match\n            when the argument-passing features are not used.\n            ``None`` must be returned to indicate that there is no match.\"\"\"\n        raise NotImplementedError()\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        \"\"\"Reconstructs full url from matcher instance and additional arguments.\"\"\"\n        return None\n\n\nclass AnyMatches(Matcher):\n    \"\"\"Matches any request.\"\"\"\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        return {}\n\n\nclass HostMatches(Matcher):\n    \"\"\"Matches requests from hosts specified by ``host_pattern`` regex.\"\"\"\n\n    def __init__(self, host_pattern: Union[str, Pattern]) -> None:\n        if isinstance(host_pattern, basestring_type):\n            if not host_pattern.endswith(\"$\"):\n                host_pattern += \"$\"\n            self.host_pattern = re.compile(host_pattern)\n        else:\n            self.host_pattern = host_pattern\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        if self.host_pattern.match(request.host_name):\n            return {}\n\n        return None\n\n\nclass DefaultHostMatches(Matcher):\n    \"\"\"Matches requests from host that is equal to application's default_host.\n    Always returns no match if ``X-Real-Ip`` header is present.\n    \"\"\"\n\n    def __init__(self, application: Any, host_pattern: Pattern) -> None:\n        self.application = application\n        self.host_pattern = host_pattern\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        # Look for default host if not behind load balancer (for debugging)\n        if \"X-Real-Ip\" not in request.headers:\n            if self.host_pattern.match(self.application.default_host):\n                return {}\n        return None\n\n\nclass PathMatches(Matcher):\n    \"\"\"Matches requests with paths specified by ``path_pattern`` regex.\"\"\"\n\n    def __init__(self, path_pattern: Union[str, Pattern]) -> None:\n        if isinstance(path_pattern, basestring_type):\n            if not path_pattern.endswith(\"$\"):\n                path_pattern += \"$\"\n            self.regex = re.compile(path_pattern)\n        else:\n            self.regex = path_pattern\n\n        assert len(self.regex.groupindex) in (0, self.regex.groups), (\n            \"groups in url regexes must either be all named or all \"\n            \"positional: %r\" % self.regex.pattern\n        )\n\n        self._path, self._group_count = self._find_groups()\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        match = self.regex.match(request.path)\n        if match is None:\n            return None\n        if not self.regex.groups:\n            return {}\n\n        path_args = []  # type: List[bytes]\n        path_kwargs = {}  # type: Dict[str, bytes]\n\n        # Pass matched groups to the handler.  Since\n        # match.groups() includes both named and\n        # unnamed groups, we want to use either groups\n        # or groupdict but not both.\n        if self.regex.groupindex:\n            path_kwargs = dict(\n                (str(k), _unquote_or_none(v)) for (k, v) in match.groupdict().items()\n            )\n        else:\n            path_args = [_unquote_or_none(s) for s in match.groups()]\n\n        return dict(path_args=path_args, path_kwargs=path_kwargs)\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        if self._path is None:\n            raise ValueError(\"Cannot reverse url regex \" + self.regex.pattern)\n        assert len(args) == self._group_count, (\n            \"required number of arguments \" \"not found\"\n        )\n        if not len(args):\n            return self._path\n        converted_args = []\n        for a in args:\n            if not isinstance(a, (unicode_type, bytes)):\n                a = str(a)\n            converted_args.append(url_escape(utf8(a), plus=False))\n        return self._path % tuple(converted_args)\n\n    def _find_groups(self) -> Tuple[Optional[str], Optional[int]]:\n        \"\"\"Returns a tuple (reverse string, group count) for a url.\n\n        For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method\n        would return ('/%s/%s/', 2).\n        \"\"\"\n        pattern = self.regex.pattern\n        if pattern.startswith(\"^\"):\n            pattern = pattern[1:]\n        if pattern.endswith(\"$\"):\n            pattern = pattern[:-1]\n\n        if self.regex.groups != pattern.count(\"(\"):\n            # The pattern is too complicated for our simplistic matching,\n            # so we can't support reversing it.\n            return None, None\n\n        pieces = []\n        for fragment in pattern.split(\"(\"):\n            if \")\" in fragment:\n                paren_loc = fragment.index(\")\")\n                if paren_loc >= 0:\n                    pieces.append(\"%s\" + fragment[paren_loc + 1 :])\n            else:\n                try:\n                    unescaped_fragment = re_unescape(fragment)\n                except ValueError:\n                    # If we can't unescape part of it, we can't\n                    # reverse this url.\n                    return (None, None)\n                pieces.append(unescaped_fragment)\n\n        return \"\".join(pieces), self.regex.groups\n\n\nclass URLSpec(Rule):\n    \"\"\"Specifies mappings between URLs and handlers.\n\n    .. versionchanged: 4.5\n       `URLSpec` is now a subclass of a `Rule` with `PathMatches` matcher and is preserved for\n       backwards compatibility.\n    \"\"\"\n\n    def __init__(\n        self,\n        pattern: Union[str, Pattern],\n        handler: Any,\n        kwargs: Optional[Dict[str, Any]] = None,\n        name: Optional[str] = None,\n    ) -> None:\n        \"\"\"Parameters:\n\n        * ``pattern``: Regular expression to be matched. Any capturing\n          groups in the regex will be passed in to the handler's\n          get/post/etc methods as arguments (by keyword if named, by\n          position if unnamed. Named and unnamed capturing groups\n          may not be mixed in the same rule).\n\n        * ``handler``: `~.web.RequestHandler` subclass to be invoked.\n\n        * ``kwargs`` (optional): A dictionary of additional arguments\n          to be passed to the handler's constructor.\n\n        * ``name`` (optional): A name for this handler.  Used by\n          `~.web.Application.reverse_url`.\n\n        \"\"\"\n        matcher = PathMatches(pattern)\n        super(URLSpec, self).__init__(matcher, handler, kwargs, name)\n\n        self.regex = matcher.regex\n        self.handler_class = self.target\n        self.kwargs = kwargs\n\n    def __repr__(self) -> str:\n        return \"%s(%r, %s, kwargs=%r, name=%r)\" % (\n            self.__class__.__name__,\n            self.regex.pattern,\n            self.handler_class,\n            self.kwargs,\n            self.name,\n        )\n\n\n@overload\ndef _unquote_or_none(s: str) -> bytes:\n    pass\n\n\n@overload  # noqa: F811\ndef _unquote_or_none(s: None) -> None:\n    pass\n\n\ndef _unquote_or_none(s: Optional[str]) -> Optional[bytes]:  # noqa: F811\n    \"\"\"None-safe wrapper around url_unescape to handle unmatched optional\n    groups correctly.\n\n    Note that args are passed as bytes so the handler can decide what\n    encoding to use.\n    \"\"\"\n    if s is None:\n        return s\n    return url_unescape(s, encoding=None, plus=False)\n",
            "new_file_content": "# Copyright 2015 The Tornado Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\n\"\"\"Flexible routing implementation.\n\nTornado routes HTTP requests to appropriate handlers using `Router`\nclass implementations. The `tornado.web.Application` class is a\n`Router` implementation and may be used directly, or the classes in\nthis module may be used for additional flexibility. The `RuleRouter`\nclass can match on more criteria than `.Application`, or the `Router`\ninterface can be subclassed for maximum customization.\n\n`Router` interface extends `~.httputil.HTTPServerConnectionDelegate`\nto provide additional routing capabilities. This also means that any\n`Router` implementation can be used directly as a ``request_callback``\nfor `~.httpserver.HTTPServer` constructor.\n\n`Router` subclass must implement a ``find_handler`` method to provide\na suitable `~.httputil.HTTPMessageDelegate` instance to handle the\nrequest:\n\n.. code-block:: python\n\n    class CustomRouter(Router):\n        def find_handler(self, request, **kwargs):\n            # some routing logic providing a suitable HTTPMessageDelegate instance\n            return MessageDelegate(request.connection)\n\n    class MessageDelegate(HTTPMessageDelegate):\n        def __init__(self, connection):\n            self.connection = connection\n\n        def finish(self):\n            self.connection.write_headers(\n                ResponseStartLine(\"HTTP/1.1\", 200, \"OK\"),\n                HTTPHeaders({\"Content-Length\": \"2\"}),\n                b\"OK\")\n            self.connection.finish()\n\n    router = CustomRouter()\n    server = HTTPServer(router)\n\nThe main responsibility of `Router` implementation is to provide a\nmapping from a request to `~.httputil.HTTPMessageDelegate` instance\nthat will handle this request. In the example above we can see that\nrouting is possible even without instantiating an `~.web.Application`.\n\nFor routing to `~.web.RequestHandler` implementations we need an\n`~.web.Application` instance. `~.web.Application.get_handler_delegate`\nprovides a convenient way to create `~.httputil.HTTPMessageDelegate`\nfor a given request and `~.web.RequestHandler`.\n\nHere is a simple example of how we can we route to\n`~.web.RequestHandler` subclasses by HTTP method:\n\n.. code-block:: python\n\n    resources = {}\n\n    class GetResource(RequestHandler):\n        def get(self, path):\n            if path not in resources:\n                raise HTTPError(404)\n\n            self.finish(resources[path])\n\n    class PostResource(RequestHandler):\n        def post(self, path):\n            resources[path] = self.request.body\n\n    class HTTPMethodRouter(Router):\n        def __init__(self, app):\n            self.app = app\n\n        def find_handler(self, request, **kwargs):\n            handler = GetResource if request.method == \"GET\" else PostResource\n            return self.app.get_handler_delegate(request, handler, path_args=[request.path])\n\n    router = HTTPMethodRouter(Application())\n    server = HTTPServer(router)\n\n`ReversibleRouter` interface adds the ability to distinguish between\nthe routes and reverse them to the original urls using route's name\nand additional arguments. `~.web.Application` is itself an\nimplementation of `ReversibleRouter` class.\n\n`RuleRouter` and `ReversibleRuleRouter` are implementations of\n`Router` and `ReversibleRouter` interfaces and can be used for\ncreating rule-based routing configurations.\n\nRules are instances of `Rule` class. They contain a `Matcher`, which\nprovides the logic for determining whether the rule is a match for a\nparticular request and a target, which can be one of the following.\n\n1) An instance of `~.httputil.HTTPServerConnectionDelegate`:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/handler\"), ConnectionDelegate()),\n        # ... more rules\n    ])\n\n    class ConnectionDelegate(HTTPServerConnectionDelegate):\n        def start_request(self, server_conn, request_conn):\n            return MessageDelegate(request_conn)\n\n2) A callable accepting a single argument of `~.httputil.HTTPServerRequest` type:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/callable\"), request_callable)\n    ])\n\n    def request_callable(request):\n        request.write(b\"HTTP/1.1 200 OK\\\\r\\\\nContent-Length: 2\\\\r\\\\n\\\\r\\\\nOK\")\n        request.finish()\n\n3) Another `Router` instance:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/router.*\"), CustomRouter())\n    ])\n\nOf course a nested `RuleRouter` or a `~.web.Application` is allowed:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(HostMatches(\"example.com\"), RuleRouter([\n            Rule(PathMatches(\"/app1/.*\"), Application([(r\"/app1/handler\", Handler)]))),\n        ]))\n    ])\n\n    server = HTTPServer(router)\n\nIn the example below `RuleRouter` is used to route between applications:\n\n.. code-block:: python\n\n    app1 = Application([\n        (r\"/app1/handler\", Handler1),\n        # other handlers ...\n    ])\n\n    app2 = Application([\n        (r\"/app2/handler\", Handler2),\n        # other handlers ...\n    ])\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/app1.*\"), app1),\n        Rule(PathMatches(\"/app2.*\"), app2)\n    ])\n\n    server = HTTPServer(router)\n\nFor more information on application-level routing see docs for `~.web.Application`.\n\n.. versionadded:: 4.5\n\n\"\"\"\n\nimport re\nfrom functools import partial\n\nfrom tornado import httputil\nfrom tornado.httpserver import _CallableAdapter\nfrom tornado.escape import url_escape, url_unescape, utf8\nfrom tornado.log import app_log\nfrom tornado.util import basestring_type, import_object, re_unescape, unicode_type\n\nfrom typing import Any, Union, Optional, Awaitable, List, Dict, Pattern, Tuple, overload\n\n\nclass Router(httputil.HTTPServerConnectionDelegate):\n    \"\"\"Abstract router interface.\"\"\"\n\n    def find_handler(\n        self, request: httputil.HTTPServerRequest, **kwargs: Any\n    ) -> Optional[httputil.HTTPMessageDelegate]:\n        \"\"\"Must be implemented to return an appropriate instance of `~.httputil.HTTPMessageDelegate`\n        that can serve the request.\n        Routing implementations may pass additional kwargs to extend the routing logic.\n\n        :arg httputil.HTTPServerRequest request: current HTTP request.\n        :arg kwargs: additional keyword arguments passed by routing implementation.\n        :returns: an instance of `~.httputil.HTTPMessageDelegate` that will be used to\n            process the request.\n        \"\"\"\n        raise NotImplementedError()\n\n    def start_request(\n        self, server_conn: object, request_conn: httputil.HTTPConnection\n    ) -> httputil.HTTPMessageDelegate:\n        return _RoutingDelegate(self, server_conn, request_conn)\n\n\nclass ReversibleRouter(Router):\n    \"\"\"Abstract router interface for routers that can handle named routes\n    and support reversing them to original urls.\n    \"\"\"\n\n    def reverse_url(self, name: str, *args: Any) -> Optional[str]:\n        \"\"\"Returns url string for a given route name and arguments\n        or ``None`` if no match is found.\n\n        :arg str name: route name.\n        :arg args: url parameters.\n        :returns: parametrized url string for a given route name (or ``None``).\n        \"\"\"\n        raise NotImplementedError()\n\n\nclass _RoutingDelegate(httputil.HTTPMessageDelegate):\n    def __init__(\n        self, router: Router, server_conn: object, request_conn: httputil.HTTPConnection\n    ) -> None:\n        self.server_conn = server_conn\n        self.request_conn = request_conn\n        self.delegate = None  # type: Optional[httputil.HTTPMessageDelegate]\n        self.router = router  # type: Router\n\n    def headers_received(\n        self,\n        start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine],\n        headers: httputil.HTTPHeaders,\n    ) -> Optional[Awaitable[None]]:\n        assert isinstance(start_line, httputil.RequestStartLine)\n        request = httputil.HTTPServerRequest(\n            connection=self.request_conn,\n            server_connection=self.server_conn,\n            start_line=start_line,\n            headers=headers,\n        )\n\n        self.delegate = self.router.find_handler(request)\n        if self.delegate is None:\n            app_log.debug(\n                \"Delegate for %s %s request not found\",\n                start_line.method,\n                start_line.path,\n            )\n            self.delegate = _DefaultMessageDelegate(self.request_conn)\n\n        return self.delegate.headers_received(start_line, headers)\n\n    def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:\n        assert self.delegate is not None\n        return self.delegate.data_received(chunk)\n\n    def finish(self) -> None:\n        assert self.delegate is not None\n        self.delegate.finish()\n\n    def on_connection_close(self) -> None:\n        assert self.delegate is not None\n        self.delegate.on_connection_close()\n\n\nclass _DefaultMessageDelegate(httputil.HTTPMessageDelegate):\n    def __init__(self, connection: httputil.HTTPConnection) -> None:\n        self.connection = connection\n\n    def finish(self) -> None:\n        self.connection.write_headers(\n            httputil.ResponseStartLine(\"HTTP/1.1\", 404, \"Not Found\"),\n            httputil.HTTPHeaders(),\n        )\n        self.connection.finish()\n\n\n# _RuleList can either contain pre-constructed Rules or a sequence of\n# arguments to be passed to the Rule constructor.\n_RuleList = List[\n    Union[\n        \"Rule\",\n        List[Any],  # Can't do detailed typechecking of lists.\n        Tuple[Union[str, \"Matcher\"], Any],\n        Tuple[Union[str, \"Matcher\"], Any, Dict[str, Any]],\n        Tuple[Union[str, \"Matcher\"], Any, Dict[str, Any], str],\n    ]\n]\n\n\nclass RuleRouter(Router):\n    \"\"\"Rule-based router implementation.\"\"\"\n\n    def __init__(self, rules: Optional[_RuleList] = None) -> None:\n        \"\"\"Constructs a router from an ordered list of rules::\n\n            RuleRouter([\n                Rule(PathMatches(\"/handler\"), Target),\n                # ... more rules\n            ])\n\n        You can also omit explicit `Rule` constructor and use tuples of arguments::\n\n            RuleRouter([\n                (PathMatches(\"/handler\"), Target),\n            ])\n\n        `PathMatches` is a default matcher, so the example above can be simplified::\n\n            RuleRouter([\n                (\"/handler\", Target),\n            ])\n\n        In the examples above, ``Target`` can be a nested `Router` instance, an instance of\n        `~.httputil.HTTPServerConnectionDelegate` or an old-style callable,\n        accepting a request argument.\n\n        :arg rules: a list of `Rule` instances or tuples of `Rule`\n            constructor arguments.\n        \"\"\"\n        self.rules = []  # type: List[Rule]\n        if rules:\n            self.add_rules(rules)\n\n    def add_rules(self, rules: _RuleList) -> None:\n        \"\"\"Appends new rules to the router.\n\n        :arg rules: a list of Rule instances (or tuples of arguments, which are\n            passed to Rule constructor).\n        \"\"\"\n        for rule in rules:\n            if isinstance(rule, (tuple, list)):\n                assert len(rule) in (2, 3, 4)\n                if isinstance(rule[0], basestring_type):\n                    rule = Rule(PathMatches(rule[0]), *rule[1:])\n                else:\n                    rule = Rule(*rule)\n\n            self.rules.append(self.process_rule(rule))\n\n    def process_rule(self, rule: \"Rule\") -> \"Rule\":\n        \"\"\"Override this method for additional preprocessing of each rule.\n\n        :arg Rule rule: a rule to be processed.\n        :returns: the same or modified Rule instance.\n        \"\"\"\n        return rule\n\n    def find_handler(\n        self, request: httputil.HTTPServerRequest, **kwargs: Any\n    ) -> Optional[httputil.HTTPMessageDelegate]:\n        for rule in self.rules:\n            target_params = rule.matcher.match(request)\n            if target_params is not None:\n                if rule.target_kwargs:\n                    target_params[\"target_kwargs\"] = rule.target_kwargs\n\n                delegate = self.get_target_delegate(\n                    rule.target, request, **target_params\n                )\n\n                if delegate is not None:\n                    return delegate\n\n        return None\n\n    def get_target_delegate(\n        self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any\n    ) -> Optional[httputil.HTTPMessageDelegate]:\n        \"\"\"Returns an instance of `~.httputil.HTTPMessageDelegate` for a\n        Rule's target. This method is called by `~.find_handler` and can be\n        extended to provide additional target types.\n\n        :arg target: a Rule's target.\n        :arg httputil.HTTPServerRequest request: current request.\n        :arg target_params: additional parameters that can be useful\n            for `~.httputil.HTTPMessageDelegate` creation.\n        \"\"\"\n        if isinstance(target, Router):\n            return target.find_handler(request, **target_params)\n\n        elif isinstance(target, httputil.HTTPServerConnectionDelegate):\n            assert request.connection is not None\n            return target.start_request(request.server_connection, request.connection)\n\n        elif callable(target):\n            assert request.connection is not None\n            return _CallableAdapter(\n                partial(target, **target_params), request.connection\n            )\n\n        return None\n\n\nclass ReversibleRuleRouter(ReversibleRouter, RuleRouter):\n    \"\"\"A rule-based router that implements ``reverse_url`` method.\n\n    Each rule added to this router may have a ``name`` attribute that can be\n    used to reconstruct an original uri. The actual reconstruction takes place\n    in a rule's matcher (see `Matcher.reverse`).\n    \"\"\"\n\n    def __init__(self, rules: Optional[_RuleList] = None) -> None:\n        self.named_rules = {}  # type: Dict[str, Any]\n        super(ReversibleRuleRouter, self).__init__(rules)\n\n    def process_rule(self, rule: \"Rule\") -> \"Rule\":\n        rule = super(ReversibleRuleRouter, self).process_rule(rule)\n\n        if rule.name:\n            if rule.name in self.named_rules:\n                app_log.warning(\n                    \"Multiple handlers named %s; replacing previous value\", rule.name\n                )\n            self.named_rules[rule.name] = rule\n\n        return rule\n\n    def reverse_url(self, name: str, *args: Any) -> Optional[str]:\n        if name in self.named_rules:\n            return self.named_rules[name].matcher.reverse(*args)\n\n        for rule in self.rules:\n            if isinstance(rule.target, ReversibleRouter):\n                reversed_url = rule.target.reverse_url(name, *args)\n                if reversed_url is not None:\n                    return reversed_url\n\n        return None\n\n\nclass Rule(object):\n    \"\"\"A routing rule.\"\"\"\n\n    def __init__(\n        self,\n        matcher: \"Matcher\",\n        target: Any,\n        target_kwargs: Optional[Dict[str, Any]] = None,\n        name: Optional[str] = None,\n    ) -> None:\n        \"\"\"Constructs a Rule instance.\n\n        :arg Matcher matcher: a `Matcher` instance used for determining\n            whether the rule should be considered a match for a specific\n            request.\n        :arg target: a Rule's target (typically a ``RequestHandler`` or\n            `~.httputil.HTTPServerConnectionDelegate` subclass or even a nested `Router`,\n            depending on routing implementation).\n        :arg dict target_kwargs: a dict of parameters that can be useful\n            at the moment of target instantiation (for example, ``status_code``\n            for a ``RequestHandler`` subclass). They end up in\n            ``target_params['target_kwargs']`` of `RuleRouter.get_target_delegate`\n            method.\n        :arg str name: the name of the rule that can be used to find it\n            in `ReversibleRouter.reverse_url` implementation.\n        \"\"\"\n        if isinstance(target, str):\n            # import the Module and instantiate the class\n            # Must be a fully qualified name (module.ClassName)\n            target = import_object(target)\n\n        self.matcher = matcher  # type: Matcher\n        self.target = target\n        self.target_kwargs = target_kwargs if target_kwargs else {}\n        self.name = name\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        return self.matcher.reverse(*args)\n\n    def __repr__(self) -> str:\n        return \"%s(%r, %s, kwargs=%r, name=%r)\" % (\n            self.__class__.__name__,\n            self.matcher,\n            self.target,\n            self.target_kwargs,\n            self.name,\n        )\n\n\nclass Matcher(object):\n    \"\"\"Represents a matcher for request features.\"\"\"\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        \"\"\"Matches current instance against the request.\n\n        :arg httputil.HTTPServerRequest request: current HTTP request\n        :returns: a dict of parameters to be passed to the target handler\n            (for example, ``handler_kwargs``, ``path_args``, ``path_kwargs``\n            can be passed for proper `~.web.RequestHandler` instantiation).\n            An empty dict is a valid (and common) return value to indicate a match\n            when the argument-passing features are not used.\n            ``None`` must be returned to indicate that there is no match.\"\"\"\n        raise NotImplementedError()\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        \"\"\"Reconstructs full url from matcher instance and additional arguments.\"\"\"\n        return None\n\n\nclass AnyMatches(Matcher):\n    \"\"\"Matches any request.\"\"\"\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        return {}\n\n\nclass HostMatches(Matcher):\n    \"\"\"Matches requests from hosts specified by ``host_pattern`` regex.\"\"\"\n\n    def __init__(self, host_pattern: Union[str, Pattern]) -> None:\n        if isinstance(host_pattern, basestring_type):\n            if not host_pattern.endswith(\"$\"):\n                host_pattern += \"$\"\n            self.host_pattern = re.compile(host_pattern)\n        else:\n            self.host_pattern = host_pattern\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        if self.host_pattern.match(request.host_name):\n            return {}\n\n        return None\n\n\nclass DefaultHostMatches(Matcher):\n    \"\"\"Matches requests from host that is equal to application's default_host.\n    Always returns no match if ``X-Real-Ip`` header is present.\n    \"\"\"\n\n    def __init__(self, application: Any, host_pattern: Pattern) -> None:\n        self.application = application\n        self.host_pattern = host_pattern\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        # Look for default host if not behind load balancer (for debugging)\n        if \"X-Real-Ip\" not in request.headers:\n            if self.host_pattern.match(self.application.default_host):\n                return {}\n        return None\n\n\nclass PathMatches(Matcher):\n    \"\"\"Matches requests with paths specified by ``path_pattern`` regex.\"\"\"\n\n    def __init__(self, path_pattern: Union[str, Pattern]) -> None:\n        if isinstance(path_pattern, basestring_type):\n            if not path_pattern.endswith(\"$\"):\n                path_pattern += \"$\"\n            self.regex = re.compile(path_pattern)\n        else:\n            self.regex = path_pattern\n\n        assert len(self.regex.groupindex) in (0, self.regex.groups), (\n            \"groups in url regexes must either be all named or all \"\n            \"positional: %r\" % self.regex.pattern\n        )\n\n        self._path, self._group_count = self._find_groups()\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        match = self.regex.match(request.path)\n        if match is None:\n            return None\n        if not self.regex.groups:\n            return {}\n\n        path_args = []  # type: List[bytes]\n        path_kwargs = {}  # type: Dict[str, bytes]\n\n        # Pass matched groups to the handler.  Since\n        # match.groups() includes both named and\n        # unnamed groups, we want to use either groups\n        # or groupdict but not both.\n        if self.regex.groupindex:\n            path_kwargs = dict(\n                (str(k), _unquote_or_none(v)) for (k, v) in match.groupdict().items()\n            )\n        else:\n            path_args = [_unquote_or_none(s) for s in match.groups()]\n\n        return dict(path_args=path_args, path_kwargs=path_kwargs)\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        if self._path is None:\n            raise ValueError(\"Cannot reverse url regex \" + self.regex.pattern)\n        assert len(args) == self._group_count, (\n            \"required number of arguments \" \"not found\"\n        )\n        if not len(args):\n            return self._path\n        converted_args = []\n        for a in args:\n            if not isinstance(a, (unicode_type, bytes)):\n                a = str(a)\n            converted_args.append(url_escape(utf8(a), plus=False))\n        return self._path % tuple(converted_args)\n\n    def _find_groups(self) -> Tuple[Optional[str], Optional[int]]:\n        \"\"\"Returns a tuple (reverse string, group count) for a url.\n\n        For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method\n        would return ('/%s/%s/', 2).\n        \"\"\"\n        pattern = self.regex.pattern\n        if pattern.startswith(\"^\"):\n            pattern = pattern[1:]\n        if pattern.endswith(\"$\"):\n            pattern = pattern[:-1]\n\n        if self.regex.groups != pattern.count(\"(\"):\n            # The pattern is too complicated for our simplistic matching,\n            # so we can't support reversing it.\n            return None, None\n\n        pieces = []\n        for fragment in pattern.split(\"(\"):\n            if \")\" in fragment:\n                paren_loc = fragment.index(\")\")\n                if paren_loc >= 0:\n                    try:\n                        unescaped_fragment = re_unescape(fragment[paren_loc + 1 :])\n                    except ValueError:\n                        # If we can't unescape part of it, we can't\n                        # reverse this url.\n                        return (None, None)\n                    pieces.append(\"%s\" + unescaped_fragment)\n            else:\n                try:\n                    unescaped_fragment = re_unescape(fragment)\n                except ValueError:\n                    # If we can't unescape part of it, we can't\n                    # reverse this url.\n                    return (None, None)\n                pieces.append(unescaped_fragment)\n\n        return \"\".join(pieces), self.regex.groups\n\n\nclass URLSpec(Rule):\n    \"\"\"Specifies mappings between URLs and handlers.\n\n    .. versionchanged: 4.5\n       `URLSpec` is now a subclass of a `Rule` with `PathMatches` matcher and is preserved for\n       backwards compatibility.\n    \"\"\"\n\n    def __init__(\n        self,\n        pattern: Union[str, Pattern],\n        handler: Any,\n        kwargs: Optional[Dict[str, Any]] = None,\n        name: Optional[str] = None,\n    ) -> None:\n        \"\"\"Parameters:\n\n        * ``pattern``: Regular expression to be matched. Any capturing\n          groups in the regex will be passed in to the handler's\n          get/post/etc methods as arguments (by keyword if named, by\n          position if unnamed. Named and unnamed capturing groups\n          may not be mixed in the same rule).\n\n        * ``handler``: `~.web.RequestHandler` subclass to be invoked.\n\n        * ``kwargs`` (optional): A dictionary of additional arguments\n          to be passed to the handler's constructor.\n\n        * ``name`` (optional): A name for this handler.  Used by\n          `~.web.Application.reverse_url`.\n\n        \"\"\"\n        matcher = PathMatches(pattern)\n        super(URLSpec, self).__init__(matcher, handler, kwargs, name)\n\n        self.regex = matcher.regex\n        self.handler_class = self.target\n        self.kwargs = kwargs\n\n    def __repr__(self) -> str:\n        return \"%s(%r, %s, kwargs=%r, name=%r)\" % (\n            self.__class__.__name__,\n            self.regex.pattern,\n            self.handler_class,\n            self.kwargs,\n            self.name,\n        )\n\n\n@overload\ndef _unquote_or_none(s: str) -> bytes:\n    pass\n\n\n@overload  # noqa: F811\ndef _unquote_or_none(s: None) -> None:\n    pass\n\n\ndef _unquote_or_none(s: Optional[str]) -> Optional[bytes]:  # noqa: F811\n    \"\"\"None-safe wrapper around url_unescape to handle unmatched optional\n    groups correctly.\n\n    Note that args are passed as bytes so the handler can decide what\n    encoding to use.\n    \"\"\"\n    if s is None:\n        return s\n    return url_unescape(s, encoding=None, plus=False)\n",
            "header": {
                "file": {
                    "path": "tornado/routing.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "a35ac348",
                "new_commit_hash": "e137a728",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tornado/routing.py"
            },
            "plus_file": {
                "path": "b/tornado/routing.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 627,
                            "length": 7
                        },
                        "new_range": {
                            "start": 627,
                            "length": 13
                        },
                        "section": "class PathMatches(Matcher):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            if \")\" in fragment:",
                                "type": "context"
                            },
                            {
                                "content": "                paren_loc = fragment.index(\")\")",
                                "type": "context"
                            },
                            {
                                "content": "                if paren_loc >= 0:",
                                "type": "context"
                            },
                            {
                                "content": "                    pieces.append(\"%s\" + fragment[paren_loc + 1 :])",
                                "type": "deleted"
                            },
                            {
                                "content": "                    try:",
                                "type": "added"
                            },
                            {
                                "content": "                        unescaped_fragment = re_unescape(fragment[paren_loc + 1 :])",
                                "type": "added"
                            },
                            {
                                "content": "                    except ValueError:",
                                "type": "added"
                            },
                            {
                                "content": "                        # If we can't unescape part of it, we can't",
                                "type": "added"
                            },
                            {
                                "content": "                        # reverse this url.",
                                "type": "added"
                            },
                            {
                                "content": "                        return (None, None)",
                                "type": "added"
                            },
                            {
                                "content": "                    pieces.append(\"%s\" + unescaped_fragment)",
                                "type": "added"
                            },
                            {
                                "content": "            else:",
                                "type": "context"
                            },
                            {
                                "content": "                try:",
                                "type": "context"
                            },
                            {
                                "content": "                    unescaped_fragment = re_unescape(fragment)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tornado/routing.py",
                            "type": "class",
                            "name": "PathMatches",
                            "content": "class PathMatches(Matcher):\n    \"\"\"Matches requests with paths specified by ``path_pattern`` regex.\"\"\"\n\n    def __init__(self, path_pattern: Union[str, Pattern]) -> None:\n        if isinstance(path_pattern, basestring_type):\n            if not path_pattern.endswith(\"$\"):\n                path_pattern += \"$\"\n            self.regex = re.compile(path_pattern)\n        else:\n            self.regex = path_pattern\n\n        assert len(self.regex.groupindex) in (0, self.regex.groups), (\n            \"groups in url regexes must either be all named or all \"\n            \"positional: %r\" % self.regex.pattern\n        )\n\n        self._path, self._group_count = self._find_groups()\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        match = self.regex.match(request.path)\n        if match is None:\n            return None\n        if not self.regex.groups:\n            return {}\n\n        path_args = []  # type: List[bytes]\n        path_kwargs = {}  # type: Dict[str, bytes]\n\n        # Pass matched groups to the handler.  Since\n        # match.groups() includes both named and\n        # unnamed groups, we want to use either groups\n        # or groupdict but not both.\n        if self.regex.groupindex:\n            path_kwargs = dict(\n                (str(k), _unquote_or_none(v)) for (k, v) in match.groupdict().items()\n            )\n        else:\n            path_args = [_unquote_or_none(s) for s in match.groups()]\n\n        return dict(path_args=path_args, path_kwargs=path_kwargs)\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        if self._path is None:\n            raise ValueError(\"Cannot reverse url regex \" + self.regex.pattern)\n        assert len(args) == self._group_count, (\n            \"required number of arguments \" \"not found\"\n        )\n        if not len(args):\n            return self._path\n        converted_args = []\n        for a in args:\n            if not isinstance(a, (unicode_type, bytes)):\n                a = str(a)\n            converted_args.append(url_escape(utf8(a), plus=False))\n        return self._path % tuple(converted_args)\n\n    def _find_groups(self) -> Tuple[Optional[str], Optional[int]]:\n        \"\"\"Returns a tuple (reverse string, group count) for a url.\n\n        For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method\n        would return ('/%s/%s/', 2).\n        \"\"\"\n        pattern = self.regex.pattern\n        if pattern.startswith(\"^\"):\n            pattern = pattern[1:]\n        if pattern.endswith(\"$\"):\n            pattern = pattern[:-1]\n\n        if self.regex.groups != pattern.count(\"(\"):\n            # The pattern is too complicated for our simplistic matching,\n            # so we can't support reversing it.\n            return None, None\n\n        pieces = []\n        for fragment in pattern.split(\"(\"):\n            if \")\" in fragment:\n                paren_loc = fragment.index(\")\")\n                if paren_loc >= 0:\n                    pieces.append(\"%s\" + fragment[paren_loc + 1 :])\n            else:\n                try:\n                    unescaped_fragment = re_unescape(fragment)\n                except ValueError:\n                    # If we can't unescape part of it, we can't\n                    # reverse this url.\n                    return (None, None)\n                pieces.append(unescaped_fragment)\n\n        return \"\".join(pieces), self.regex.groups",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 552,
                            "end_lineno": 640,
                            "parent": null
                        },
                        {
                            "file_name": "tornado/routing.py",
                            "type": "method",
                            "name": "PathMatches._find_groups",
                            "content": "class PathMatches(Matcher):\n    \"\"\"Matches requests with paths specified by ``path_pattern`` regex.\"\"\"\n\n    def __init__(self, path_pattern: Union[str, Pattern]) -> None:\n        if isinstance(path_pattern, basestring_type):\n            if not path_pattern.endswith(\"$\"):\n                path_pattern += \"$\"\n            self.regex = re.compile(path_pattern)\n        else:\n            self.regex = path_pattern\n\n        assert len(self.regex.groupindex) in (0, self.regex.groups), (\n            \"groups in url regexes must either be all named or all \"\n            \"positional: %r\" % self.regex.pattern\n        )\n\n        self._path, self._group_count = self._find_groups()\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        match = self.regex.match(request.path)\n        if match is None:\n            return None\n        if not self.regex.groups:\n            return {}\n\n        path_args = []  # type: List[bytes]\n        path_kwargs = {}  # type: Dict[str, bytes]\n\n        # Pass matched groups to the handler.  Since\n        # match.groups() includes both named and\n        # unnamed groups, we want to use either groups\n        # or groupdict but not both.\n        if self.regex.groupindex:\n            path_kwargs = dict(\n                (str(k), _unquote_or_none(v)) for (k, v) in match.groupdict().items()\n            )\n        else:\n            path_args = [_unquote_or_none(s) for s in match.groups()]\n\n        return dict(path_args=path_args, path_kwargs=path_kwargs)\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        if self._path is None:\n            raise ValueError(\"Cannot reverse url regex \" + self.regex.pattern)\n        assert len(args) == self._group_count, (\n            \"required number of arguments \" \"not found\"\n        )\n        if not len(args):\n            return self._path\n        converted_args = []\n        for a in args:\n            if not isinstance(a, (unicode_type, bytes)):\n                a = str(a)\n            converted_args.append(url_escape(utf8(a), plus=False))\n        return self._path % tuple(converted_args)\n\n    def _find_groups(self) -> Tuple[Optional[str], Optional[int]]:\n        \"\"\"Returns a tuple (reverse string, group count) for a url.\n\n        For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method\n        would return ('/%s/%s/', 2).\n        \"\"\"\n        pattern = self.regex.pattern\n        if pattern.startswith(\"^\"):\n            pattern = pattern[1:]\n        if pattern.endswith(\"$\"):\n            pattern = pattern[:-1]\n\n        if self.regex.groups != pattern.count(\"(\"):\n            # The pattern is too complicated for our simplistic matching,\n            # so we can't support reversing it.\n            return None, None\n\n        pieces = []\n        for fragment in pattern.split(\"(\"):\n            if \")\" in fragment:\n                paren_loc = fragment.index(\")\")\n                if paren_loc >= 0:\n                    pieces.append(\"%s\" + fragment[paren_loc + 1 :])\n            else:\n                try:\n                    unescaped_fragment = re_unescape(fragment)\n                except ValueError:\n                    # If we can't unescape part of it, we can't\n                    # reverse this url.\n                    return (None, None)\n                pieces.append(unescaped_fragment)\n\n        return \"\".join(pieces), self.regex.groups",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 608,
                            "end_lineno": 640,
                            "parent": {
                                "file_name": "tornado/routing.py",
                                "type": "class",
                                "name": "PathMatches",
                                "content": "class PathMatches(Matcher):\n    \"\"\"Matches requests with paths specified by ``path_pattern`` regex.\"\"\"\n\n    def __init__(self, path_pattern: Union[str, Pattern]) -> None:\n        if isinstance(path_pattern, basestring_type):\n            if not path_pattern.endswith(\"$\"):\n                path_pattern += \"$\"\n            self.regex = re.compile(path_pattern)\n        else:\n            self.regex = path_pattern\n\n        assert len(self.regex.groupindex) in (0, self.regex.groups), (\n            \"groups in url regexes must either be all named or all \"\n            \"positional: %r\" % self.regex.pattern\n        )\n\n        self._path, self._group_count = self._find_groups()\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        match = self.regex.match(request.path)\n        if match is None:\n            return None\n        if not self.regex.groups:\n            return {}\n\n        path_args = []  # type: List[bytes]\n        path_kwargs = {}  # type: Dict[str, bytes]\n\n        # Pass matched groups to the handler.  Since\n        # match.groups() includes both named and\n        # unnamed groups, we want to use either groups\n        # or groupdict but not both.\n        if self.regex.groupindex:\n            path_kwargs = dict(\n                (str(k), _unquote_or_none(v)) for (k, v) in match.groupdict().items()\n            )\n        else:\n            path_args = [_unquote_or_none(s) for s in match.groups()]\n\n        return dict(path_args=path_args, path_kwargs=path_kwargs)\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        if self._path is None:\n            raise ValueError(\"Cannot reverse url regex \" + self.regex.pattern)\n        assert len(args) == self._group_count, (\n            \"required number of arguments \" \"not found\"\n        )\n        if not len(args):\n            return self._path\n        converted_args = []\n        for a in args:\n            if not isinstance(a, (unicode_type, bytes)):\n                a = str(a)\n            converted_args.append(url_escape(utf8(a), plus=False))\n        return self._path % tuple(converted_args)\n\n    def _find_groups(self) -> Tuple[Optional[str], Optional[int]]:\n        \"\"\"Returns a tuple (reverse string, group count) for a url.\n\n        For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method\n        would return ('/%s/%s/', 2).\n        \"\"\"\n        pattern = self.regex.pattern\n        if pattern.startswith(\"^\"):\n            pattern = pattern[1:]\n        if pattern.endswith(\"$\"):\n            pattern = pattern[:-1]\n\n        if self.regex.groups != pattern.count(\"(\"):\n            # The pattern is too complicated for our simplistic matching,\n            # so we can't support reversing it.\n            return None, None\n\n        pieces = []\n        for fragment in pattern.split(\"(\"):\n            if \")\" in fragment:\n                paren_loc = fragment.index(\")\")\n                if paren_loc >= 0:\n                    pieces.append(\"%s\" + fragment[paren_loc + 1 :])\n            else:\n                try:\n                    unescaped_fragment = re_unescape(fragment)\n                except ValueError:\n                    # If we can't unescape part of it, we can't\n                    # reverse this url.\n                    return (None, None)\n                pieces.append(unescaped_fragment)\n\n        return \"\".join(pieces), self.regex.groups",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 552,
                                "end_lineno": 640,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "from tornado.concurrent import Future\nfrom tornado import gen\nfrom tornado.escape import (\n    json_decode,\n    utf8,\n    to_unicode,\n    recursive_unicode,\n    native_str,\n    to_basestring,\n)\nfrom tornado.httpclient import HTTPClientError\nfrom tornado.httputil import format_timestamp\nfrom tornado.iostream import IOStream\nfrom tornado import locale\nfrom tornado.locks import Event\nfrom tornado.log import app_log, gen_log\nfrom tornado.simple_httpclient import SimpleAsyncHTTPClient\nfrom tornado.template import DictLoader\nfrom tornado.testing import AsyncHTTPTestCase, AsyncTestCase, ExpectLog, gen_test\nfrom tornado.util import ObjectDict, unicode_type\nfrom tornado.web import (\n    Application,\n    RequestHandler,\n    StaticFileHandler,\n    RedirectHandler as WebRedirectHandler,\n    HTTPError,\n    MissingArgumentError,\n    ErrorHandler,\n    authenticated,\n    url,\n    _create_signature_v1,\n    create_signed_value,\n    decode_signed_value,\n    get_signature_key_version,\n    UIModule,\n    Finish,\n    stream_request_body,\n    removeslash,\n    addslash,\n    GZipContentEncoding,\n)\n\nimport binascii\nimport contextlib\nimport copy\nimport datetime\nimport email.utils\nimport gzip\nfrom io import BytesIO\nimport itertools\nimport logging\nimport os\nimport re\nimport socket\nimport typing  # noqa: F401\nimport unittest\nimport urllib.parse\n\n\ndef relpath(*a):\n    return os.path.join(os.path.dirname(__file__), *a)\n\n\nclass WebTestCase(AsyncHTTPTestCase):\n    \"\"\"Base class for web tests that also supports WSGI mode.\n\n    Override get_handlers and get_app_kwargs instead of get_app.\n    This class is deprecated since WSGI mode is no longer supported.\n    \"\"\"\n\n    def get_app(self):\n        self.app = Application(self.get_handlers(), **self.get_app_kwargs())\n        return self.app\n\n    def get_handlers(self):\n        raise NotImplementedError()\n\n    def get_app_kwargs(self):\n        return {}\n\n\nclass SimpleHandlerTestCase(WebTestCase):\n    \"\"\"Simplified base class for tests that work with a single handler class.\n\n    To use, define a nested class named ``Handler``.\n    \"\"\"\n\n    def get_handlers(self):\n        return [(\"/\", self.Handler)]\n\n\nclass HelloHandler(RequestHandler):\n    def get(self):\n        self.write(\"hello\")\n\n\nclass CookieTestRequestHandler(RequestHandler):\n    # stub out enough methods to make the secure_cookie functions work\n    def __init__(self, cookie_secret=\"0123456789\", key_version=None):\n        # don't call super.__init__\n        self._cookies = {}  # type: typing.Dict[str, bytes]\n        if key_version is None:\n            self.application = ObjectDict(settings=dict(cookie_secret=cookie_secret))\n        else:\n            self.application = ObjectDict(\n                settings=dict(cookie_secret=cookie_secret, key_version=key_version)\n            )\n\n    def get_cookie(self, name):\n        return self._cookies.get(name)\n\n    def set_cookie(self, name, value, expires_days=None):\n        self._cookies[name] = value\n\n\n# See SignedValueTest below for more.\nclass SecureCookieV1Test(unittest.TestCase):\n    def test_round_trip(self):\n        handler = CookieTestRequestHandler()\n        handler.set_secure_cookie(\"foo\", b\"bar\", version=1)\n        self.assertEqual(handler.get_secure_cookie(\"foo\", min_version=1), b\"bar\")\n\n    def test_cookie_tampering_future_timestamp(self):\n        handler = CookieTestRequestHandler()\n        # this string base64-encodes to '12345678'\n        handler.set_secure_cookie(\"foo\", binascii.a2b_hex(b\"d76df8e7aefc\"), version=1)\n        cookie = handler._cookies[\"foo\"]\n        match = re.match(br\"12345678\\|([0-9]+)\\|([0-9a-f]+)\", cookie)\n        assert match is not None\n        timestamp = match.group(1)\n        sig = match.group(2)\n        self.assertEqual(\n            _create_signature_v1(\n                handler.application.settings[\"cookie_secret\"],\n                \"foo\",\n                \"12345678\",\n                timestamp,\n            ),\n            sig,\n        )\n        # shifting digits from payload to timestamp doesn't alter signature\n        # (this is not desirable behavior, just confirming that that's how it\n        # works)\n        self.assertEqual(\n            _create_signature_v1(\n                handler.application.settings[\"cookie_secret\"],\n                \"foo\",\n                \"1234\",\n                b\"5678\" + timestamp,\n            ),\n            sig,\n        )\n        # tamper with the cookie\n        handler._cookies[\"foo\"] = utf8(\n            \"1234|5678%s|%s\" % (to_basestring(timestamp), to_basestring(sig))\n        )\n        # it gets rejected\n        with ExpectLog(gen_log, \"Cookie timestamp in future\"):\n            self.assertTrue(handler.get_secure_cookie(\"foo\", min_version=1) is None)\n\n    def test_arbitrary_bytes(self):\n        # Secure cookies accept arbitrary data (which is base64 encoded).\n        # Note that normal cookies accept only a subset of ascii.\n        handler = CookieTestRequestHandler()\n        handler.set_secure_cookie(\"foo\", b\"\\xe9\", version=1)\n        self.assertEqual(handler.get_secure_cookie(\"foo\", min_version=1), b\"\\xe9\")\n\n\n# See SignedValueTest below for more.\nclass SecureCookieV2Test(unittest.TestCase):\n    KEY_VERSIONS = {0: \"ajklasdf0ojaisdf\", 1: \"aslkjasaolwkjsdf\"}\n\n    def test_round_trip(self):\n        handler = CookieTestRequestHandler()\n        handler.set_secure_cookie(\"foo\", b\"bar\", version=2)\n        self.assertEqual(handler.get_secure_cookie(\"foo\", min_version=2), b\"bar\")\n\n    def test_key_version_roundtrip(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=0\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        self.assertEqual(handler.get_secure_cookie(\"foo\"), b\"bar\")\n\n    def test_key_version_roundtrip_differing_version(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=1\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        self.assertEqual(handler.get_secure_cookie(\"foo\"), b\"bar\")\n\n    def test_key_version_increment_version(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=0\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        new_handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=1\n        )\n        new_handler._cookies = handler._cookies\n        self.assertEqual(new_handler.get_secure_cookie(\"foo\"), b\"bar\")\n\n    def test_key_version_invalidate_version(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=0\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        new_key_versions = self.KEY_VERSIONS.copy()\n        new_key_versions.pop(0)\n        new_handler = CookieTestRequestHandler(\n            cookie_secret=new_key_versions, key_version=1\n        )\n        new_handler._cookies = handler._cookies\n        self.assertEqual(new_handler.get_secure_cookie(\"foo\"), None)\n\n\nclass FinalReturnTest(WebTestCase):\n    def get_handlers(self):\n        test = self\n\n        class FinishHandler(RequestHandler):\n            @gen.coroutine\n            def get(self):\n                test.final_return = self.finish()\n                yield test.final_return\n\n            @gen.coroutine\n            def post(self):\n                self.write(\"hello,\")\n                yield self.flush()\n                test.final_return = self.finish(\"world\")\n                yield test.final_return\n\n        class RenderHandler(RequestHandler):\n            def create_template_loader(self, path):\n                return DictLoader({\"foo.html\": \"hi\"})\n\n            @gen.coroutine\n            def get(self):\n                test.final_return = self.render(\"foo.html\")\n\n        return [(\"/finish\", FinishHandler), (\"/render\", RenderHandler)]\n\n    def get_app_kwargs(self):\n        return dict(template_path=\"FinalReturnTest\")\n\n    def test_finish_method_return_future(self):\n        response = self.fetch(self.get_url(\"/finish\"))\n        self.assertEqual(response.code, 200)\n        self.assertIsInstance(self.final_return, Future)\n        self.assertTrue(self.final_return.done())\n\n        response = self.fetch(self.get_url(\"/finish\"), method=\"POST\", body=b\"\")\n        self.assertEqual(response.code, 200)\n        self.assertIsInstance(self.final_return, Future)\n        self.assertTrue(self.final_return.done())\n\n    def test_render_method_return_future(self):\n        response = self.fetch(self.get_url(\"/render\"))\n        self.assertEqual(response.code, 200)\n        self.assertIsInstance(self.final_return, Future)\n\n\nclass CookieTest(WebTestCase):\n    def get_handlers(self):\n        class SetCookieHandler(RequestHandler):\n            def get(self):\n                # Try setting cookies with different argument types\n                # to ensure that everything gets encoded correctly\n                self.set_cookie(\"str\", \"asdf\")\n                self.set_cookie(\"unicode\", u\"qwer\")\n                self.set_cookie(\"bytes\", b\"zxcv\")\n\n        class GetCookieHandler(RequestHandler):\n            def get(self):\n                self.write(self.get_cookie(\"foo\", \"default\"))\n\n        class SetCookieDomainHandler(RequestHandler):\n            def get(self):\n                # unicode domain and path arguments shouldn't break things\n                # either (see bug #285)\n                self.set_cookie(\"unicode_args\", \"blah\", domain=u\"foo.com\", path=u\"/foo\")\n\n        class SetCookieSpecialCharHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"equals\", \"a=b\")\n                self.set_cookie(\"semicolon\", \"a;b\")\n                self.set_cookie(\"quote\", 'a\"b')\n\n        class SetCookieOverwriteHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"a\", \"b\", domain=\"example.com\")\n                self.set_cookie(\"c\", \"d\", domain=\"example.com\")\n                # A second call with the same name clobbers the first.\n                # Attributes from the first call are not carried over.\n                self.set_cookie(\"a\", \"e\")\n\n        class SetCookieMaxAgeHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"foo\", \"bar\", max_age=10)\n\n        class SetCookieExpiresDaysHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"foo\", \"bar\", expires_days=10)\n\n        class SetCookieFalsyFlags(RequestHandler):\n            def get(self):\n                self.set_cookie(\"a\", \"1\", secure=True)\n                self.set_cookie(\"b\", \"1\", secure=False)\n                self.set_cookie(\"c\", \"1\", httponly=True)\n                self.set_cookie(\"d\", \"1\", httponly=False)\n\n        return [\n            (\"/set\", SetCookieHandler),\n            (\"/get\", GetCookieHandler),\n            (\"/set_domain\", SetCookieDomainHandler),\n            (\"/special_char\", SetCookieSpecialCharHandler),\n            (\"/set_overwrite\", SetCookieOverwriteHandler),\n            (\"/set_max_age\", SetCookieMaxAgeHandler),\n            (\"/set_expires_days\", SetCookieExpiresDaysHandler),\n            (\"/set_falsy_flags\", SetCookieFalsyFlags),\n        ]\n\n    def test_set_cookie(self):\n        response = self.fetch(\"/set\")\n        self.assertEqual(\n            sorted(response.headers.get_list(\"Set-Cookie\")),\n            [\"bytes=zxcv; Path=/\", \"str=asdf; Path=/\", \"unicode=qwer; Path=/\"],\n        )\n\n    def test_get_cookie(self):\n        response = self.fetch(\"/get\", headers={\"Cookie\": \"foo=bar\"})\n        self.assertEqual(response.body, b\"bar\")\n\n        response = self.fetch(\"/get\", headers={\"Cookie\": 'foo=\"bar\"'})\n        self.assertEqual(response.body, b\"bar\")\n\n        response = self.fetch(\"/get\", headers={\"Cookie\": \"/=exception;\"})\n        self.assertEqual(response.body, b\"default\")\n\n    def test_set_cookie_domain(self):\n        response = self.fetch(\"/set_domain\")\n        self.assertEqual(\n            response.headers.get_list(\"Set-Cookie\"),\n            [\"unicode_args=blah; Domain=foo.com; Path=/foo\"],\n        )\n\n    def test_cookie_special_char(self):\n        response = self.fetch(\"/special_char\")\n        headers = sorted(response.headers.get_list(\"Set-Cookie\"))\n        self.assertEqual(len(headers), 3)\n        self.assertEqual(headers[0], 'equals=\"a=b\"; Path=/')\n        self.assertEqual(headers[1], 'quote=\"a\\\\\"b\"; Path=/')\n        # python 2.7 octal-escapes the semicolon; older versions leave it alone\n        self.assertTrue(\n            headers[2] in ('semicolon=\"a;b\"; Path=/', 'semicolon=\"a\\\\073b\"; Path=/'),\n            headers[2],\n        )\n\n        data = [\n            (\"foo=a=b\", \"a=b\"),\n            ('foo=\"a=b\"', \"a=b\"),\n            ('foo=\"a;b\"', '\"a'),  # even quoted, \";\" is a delimiter\n            (\"foo=a\\\\073b\", \"a\\\\073b\"),  # escapes only decoded in quotes\n            ('foo=\"a\\\\073b\"', \"a;b\"),\n            ('foo=\"a\\\\\"b\"', 'a\"b'),\n        ]\n        for header, expected in data:\n            logging.debug(\"trying %r\", header)\n            response = self.fetch(\"/get\", headers={\"Cookie\": header})\n            self.assertEqual(response.body, utf8(expected))\n\n    def test_set_cookie_overwrite(self):\n        response = self.fetch(\"/set_overwrite\")\n        headers = response.headers.get_list(\"Set-Cookie\")\n        self.assertEqual(\n            sorted(headers), [\"a=e; Path=/\", \"c=d; Domain=example.com; Path=/\"]\n        )\n\n    def test_set_cookie_max_age(self):\n        response = self.fetch(\"/set_max_age\")\n        headers = response.headers.get_list(\"Set-Cookie\")\n        self.assertEqual(sorted(headers), [\"foo=bar; Max-Age=10; Path=/\"])\n\n    def test_set_cookie_expires_days(self):\n        response = self.fetch(\"/set_expires_days\")\n        header = response.headers.get(\"Set-Cookie\")\n        match = re.match(\"foo=bar; expires=(?P<expires>.+); Path=/\", header)\n        assert match is not None\n\n        expires = datetime.datetime.utcnow() + datetime.timedelta(days=10)\n        parsed = email.utils.parsedate(match.groupdict()[\"expires\"])\n        assert parsed is not None\n        header_expires = datetime.datetime(*parsed[:6])\n        self.assertTrue(abs((expires - header_expires).total_seconds()) < 10)\n\n    def test_set_cookie_false_flags(self):\n        response = self.fetch(\"/set_falsy_flags\")\n        headers = sorted(response.headers.get_list(\"Set-Cookie\"))\n        # The secure and httponly headers are capitalized in py35 and\n        # lowercase in older versions.\n        self.assertEqual(headers[0].lower(), \"a=1; path=/; secure\")\n        self.assertEqual(headers[1].lower(), \"b=1; path=/\")\n        self.assertEqual(headers[2].lower(), \"c=1; httponly; path=/\")\n        self.assertEqual(headers[3].lower(), \"d=1; path=/\")\n\n\nclass AuthRedirectRequestHandler(RequestHandler):\n    def initialize(self, login_url):\n        self.login_url = login_url\n\n    def get_login_url(self):\n        return self.login_url\n\n    @authenticated\n    def get(self):\n        # we'll never actually get here because the test doesn't follow redirects\n        self.send_error(500)\n\n\nclass AuthRedirectTest(WebTestCase):\n    def get_handlers(self):\n        return [\n            (\"/relative\", AuthRedirectRequestHandler, dict(login_url=\"/login\")),\n            (\n                \"/absolute\",\n                AuthRedirectRequestHandler,\n                dict(login_url=\"http://example.com/login\"),\n            ),\n        ]\n\n    def test_relative_auth_redirect(self):\n        response = self.fetch(self.get_url(\"/relative\"), follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        self.assertEqual(response.headers[\"Location\"], \"/login?next=%2Frelative\")\n\n    def test_absolute_auth_redirect(self):\n        response = self.fetch(self.get_url(\"/absolute\"), follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        self.assertTrue(\n            re.match(\n                r\"http://example.com/login\\?next=http%3A%2F%2F127.0.0.1%3A[0-9]+%2Fabsolute\",\n                response.headers[\"Location\"],\n            ),\n            response.headers[\"Location\"],\n        )\n\n\nclass ConnectionCloseHandler(RequestHandler):\n    def initialize(self, test):\n        self.test = test\n\n    @gen.coroutine\n    def get(self):\n        self.test.on_handler_waiting()\n        yield self.test.cleanup_event.wait()\n\n    def on_connection_close(self):\n        self.test.on_connection_close()\n\n\nclass ConnectionCloseTest(WebTestCase):\n    def get_handlers(self):\n        self.cleanup_event = Event()\n        return [(\"/\", ConnectionCloseHandler, dict(test=self))]\n\n    def test_connection_close(self):\n        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)\n        s.connect((\"127.0.0.1\", self.get_http_port()))\n        self.stream = IOStream(s)\n        self.stream.write(b\"GET / HTTP/1.0\\r\\n\\r\\n\")\n        self.wait()\n        # Let the hanging coroutine clean up after itself\n        self.cleanup_event.set()\n        self.io_loop.run_sync(lambda: gen.sleep(0))\n\n    def on_handler_waiting(self):\n        logging.debug(\"handler waiting\")\n        self.stream.close()\n\n    def on_connection_close(self):\n        logging.debug(\"connection closed\")\n        self.stop()\n\n\nclass EchoHandler(RequestHandler):\n    def get(self, *path_args):\n        # Type checks: web.py interfaces convert argument values to\n        # unicode strings (by default, but see also decode_argument).\n        # In httpserver.py (i.e. self.request.arguments), they're left\n        # as bytes.  Keys are always native strings.\n        for key in self.request.arguments:\n            if type(key) != str:\n                raise Exception(\"incorrect type for key: %r\" % type(key))\n            for value in self.request.arguments[key]:\n                if type(value) != bytes:\n                    raise Exception(\"incorrect type for value: %r\" % type(value))\n            for value in self.get_arguments(key):\n                if type(value) != unicode_type:\n                    raise Exception(\"incorrect type for value: %r\" % type(value))\n        for arg in path_args:\n            if type(arg) != unicode_type:\n                raise Exception(\"incorrect type for path arg: %r\" % type(arg))\n        self.write(\n            dict(\n                path=self.request.path,\n                path_args=path_args,\n                args=recursive_unicode(self.request.arguments),\n            )\n        )\n\n\nclass RequestEncodingTest(WebTestCase):\n    def get_handlers(self):\n        return [(\"/group/(.*)\", EchoHandler), (\"/slashes/([^/]*)/([^/]*)\", EchoHandler)]\n\n    def fetch_json(self, path):\n        return json_decode(self.fetch(path).body)\n\n    def test_group_question_mark(self):\n        # Ensure that url-encoded question marks are handled properly\n        self.assertEqual(\n            self.fetch_json(\"/group/%3F\"),\n            dict(path=\"/group/%3F\", path_args=[\"?\"], args={}),\n        )\n        self.assertEqual(\n            self.fetch_json(\"/group/%3F?%3F=%3F\"),\n            dict(path=\"/group/%3F\", path_args=[\"?\"], args={\"?\": [\"?\"]}),\n        )\n\n    def test_group_encoding(self):\n        # Path components and query arguments should be decoded the same way\n        self.assertEqual(\n            self.fetch_json(\"/group/%C3%A9?arg=%C3%A9\"),\n            {\n                u\"path\": u\"/group/%C3%A9\",\n                u\"path_args\": [u\"\\u00e9\"],\n                u\"args\": {u\"arg\": [u\"\\u00e9\"]},\n            },\n        )\n\n    def test_slashes(self):\n        # Slashes may be escaped to appear as a single \"directory\" in the path,\n        # but they are then unescaped when passed to the get() method.\n        self.assertEqual(\n            self.fetch_json(\"/slashes/foo/bar\"),\n            dict(path=\"/slashes/foo/bar\", path_args=[\"foo\", \"bar\"], args={}),\n        )\n        self.assertEqual(\n            self.fetch_json(\"/slashes/a%2Fb/c%2Fd\"),\n            dict(path=\"/slashes/a%2Fb/c%2Fd\", path_args=[\"a/b\", \"c/d\"], args={}),\n        )\n\n    def test_error(self):\n        # Percent signs (encoded as %25) should not mess up printf-style\n        # messages in logs\n        with ExpectLog(gen_log, \".*Invalid unicode\"):\n            self.fetch(\"/group/?arg=%25%e9\")\n\n\nclass TypeCheckHandler(RequestHandler):\n    def prepare(self):\n        self.errors = {}  # type: typing.Dict[str, str]\n\n        self.check_type(\"status\", self.get_status(), int)\n\n        # get_argument is an exception from the general rule of using\n        # type str for non-body data mainly for historical reasons.\n        self.check_type(\"argument\", self.get_argument(\"foo\"), unicode_type)\n        self.check_type(\"cookie_key\", list(self.cookies.keys())[0], str)\n        self.check_type(\"cookie_value\", list(self.cookies.values())[0].value, str)\n\n        # Secure cookies return bytes because they can contain arbitrary\n        # data, but regular cookies are native strings.\n        if list(self.cookies.keys()) != [\"asdf\"]:\n            raise Exception(\n                \"unexpected values for cookie keys: %r\" % self.cookies.keys()\n            )\n        self.check_type(\"get_secure_cookie\", self.get_secure_cookie(\"asdf\"), bytes)\n        self.check_type(\"get_cookie\", self.get_cookie(\"asdf\"), str)\n\n        self.check_type(\"xsrf_token\", self.xsrf_token, bytes)\n        self.check_type(\"xsrf_form_html\", self.xsrf_form_html(), str)\n\n        self.check_type(\"reverse_url\", self.reverse_url(\"typecheck\", \"foo\"), str)\n\n        self.check_type(\"request_summary\", self._request_summary(), str)\n\n    def get(self, path_component):\n        # path_component uses type unicode instead of str for consistency\n        # with get_argument()\n        self.check_type(\"path_component\", path_component, unicode_type)\n        self.write(self.errors)\n\n    def post(self, path_component):\n        self.check_type(\"path_component\", path_component, unicode_type)\n        self.write(self.errors)\n\n    def check_type(self, name, obj, expected_type):\n        actual_type = type(obj)\n        if expected_type != actual_type:\n            self.errors[name] = \"expected %s, got %s\" % (expected_type, actual_type)\n\n\nclass DecodeArgHandler(RequestHandler):\n    def decode_argument(self, value, name=None):\n        if type(value) != bytes:\n            raise Exception(\"unexpected type for value: %r\" % type(value))\n        # use self.request.arguments directly to avoid recursion\n        if \"encoding\" in self.request.arguments:\n            return value.decode(to_unicode(self.request.arguments[\"encoding\"][0]))\n        else:\n            return value\n\n    def get(self, arg):\n        def describe(s):\n            if type(s) == bytes:\n                return [\"bytes\", native_str(binascii.b2a_hex(s))]\n            elif type(s) == unicode_type:\n                return [\"unicode\", s]\n            raise Exception(\"unknown type\")\n\n        self.write({\"path\": describe(arg), \"query\": describe(self.get_argument(\"foo\"))})\n\n\nclass LinkifyHandler(RequestHandler):\n    def get(self):\n        self.render(\"linkify.html\", message=\"http://example.com\")\n\n\nclass UIModuleResourceHandler(RequestHandler):\n    def get(self):\n        self.render(\"page.html\", entries=[1, 2])\n\n\nclass OptionalPathHandler(RequestHandler):\n    def get(self, path):\n        self.write({\"path\": path})\n\n\nclass MultiHeaderHandler(RequestHandler):\n    def get(self):\n        self.set_header(\"x-overwrite\", \"1\")\n        self.set_header(\"X-Overwrite\", 2)\n        self.add_header(\"x-multi\", 3)\n        self.add_header(\"X-Multi\", \"4\")\n\n\nclass RedirectHandler(RequestHandler):\n    def get(self):\n        if self.get_argument(\"permanent\", None) is not None:\n            self.redirect(\"/\", permanent=int(self.get_argument(\"permanent\")))\n        elif self.get_argument(\"status\", None) is not None:\n            self.redirect(\"/\", status=int(self.get_argument(\"status\")))\n        else:\n            raise Exception(\"didn't get permanent or status arguments\")\n\n\nclass EmptyFlushCallbackHandler(RequestHandler):\n    @gen.coroutine\n    def get(self):\n        # Ensure that the flush callback is run whether or not there\n        # was any output.  The gen.Task and direct yield forms are\n        # equivalent.\n        yield self.flush()  # \"empty\" flush, but writes headers\n        yield self.flush()  # empty flush\n        self.write(\"o\")\n        yield self.flush()  # flushes the \"o\"\n        yield self.flush()  # empty flush\n        self.finish(\"k\")\n\n\nclass HeaderInjectionHandler(RequestHandler):\n    def get(self):\n        try:\n            self.set_header(\"X-Foo\", \"foo\\r\\nX-Bar: baz\")\n            raise Exception(\"Didn't get expected exception\")\n        except ValueError as e:\n            if \"Unsafe header value\" in str(e):\n                self.finish(b\"ok\")\n            else:\n                raise\n\n\nclass GetArgumentHandler(RequestHandler):\n    def prepare(self):\n        if self.get_argument(\"source\", None) == \"query\":\n            method = self.get_query_argument\n        elif self.get_argument(\"source\", None) == \"body\":\n            method = self.get_body_argument\n        else:\n            method = self.get_argument\n        self.finish(method(\"foo\", \"default\"))\n\n\nclass GetArgumentsHandler(RequestHandler):\n    def prepare(self):\n        self.finish(\n            dict(\n                default=self.get_arguments(\"foo\"),\n                query=self.get_query_arguments(\"foo\"),\n                body=self.get_body_arguments(\"foo\"),\n            )\n        )\n\n\n# This test was shared with wsgi_test.py; now the name is meaningless.\nclass WSGISafeWebTest(WebTestCase):\n    COOKIE_SECRET = \"WebTest.COOKIE_SECRET\"\n\n    def get_app_kwargs(self):\n        loader = DictLoader(\n            {\n                \"linkify.html\": \"{% module linkify(message) %}\",\n                \"page.html\": \"\"\"\\\n<html><head></head><body>\n{% for e in entries %}\n{% module Template(\"entry.html\", entry=e) %}\n{% end %}\n</body></html>\"\"\",\n                \"entry.html\": \"\"\"\\\n{{ set_resources(embedded_css=\".entry { margin-bottom: 1em; }\",\n                 embedded_javascript=\"js_embed()\",\n                 css_files=[\"/base.css\", \"/foo.css\"],\n                 javascript_files=\"/common.js\",\n                 html_head=\"<meta>\",\n                 html_body='<script src=\"/analytics.js\"/>') }}\n<div class=\"entry\">...</div>\"\"\",\n            }\n        )\n        return dict(\n            template_loader=loader,\n            autoescape=\"xhtml_escape\",\n            cookie_secret=self.COOKIE_SECRET,\n        )\n\n    def tearDown(self):\n        super(WSGISafeWebTest, self).tearDown()\n        RequestHandler._template_loaders.clear()\n\n    def get_handlers(self):\n        urls = [\n            url(\"/typecheck/(.*)\", TypeCheckHandler, name=\"typecheck\"),\n            url(\"/decode_arg/(.*)\", DecodeArgHandler, name=\"decode_arg\"),\n            url(\"/decode_arg_kw/(?P<arg>.*)\", DecodeArgHandler),\n            url(\"/linkify\", LinkifyHandler),\n            url(\"/uimodule_resources\", UIModuleResourceHandler),\n            url(\"/optional_path/(.+)?\", OptionalPathHandler),\n            url(\"/multi_header\", MultiHeaderHandler),\n            url(\"/redirect\", RedirectHandler),\n            url(\n                \"/web_redirect_permanent\",\n                WebRedirectHandler,\n                {\"url\": \"/web_redirect_newpath\"},\n            ),\n            url(\n                \"/web_redirect\",\n                WebRedirectHandler,\n                {\"url\": \"/web_redirect_newpath\", \"permanent\": False},\n            ),\n            url(\n                \"//web_redirect_double_slash\",\n                WebRedirectHandler,\n                {\"url\": \"/web_redirect_newpath\"},\n            ),\n            url(\"/header_injection\", HeaderInjectionHandler),\n            url(\"/get_argument\", GetArgumentHandler),\n            url(\"/get_arguments\", GetArgumentsHandler),\n        ]\n        return urls\n\n    def fetch_json(self, *args, **kwargs):\n        response = self.fetch(*args, **kwargs)\n        response.rethrow()\n        return json_decode(response.body)\n\n    def test_types(self):\n        cookie_value = to_unicode(\n            create_signed_value(self.COOKIE_SECRET, \"asdf\", \"qwer\")\n        )\n        response = self.fetch(\n            \"/typecheck/asdf?foo=bar\", headers={\"Cookie\": \"asdf=\" + cookie_value}\n        )\n        data = json_decode(response.body)\n        self.assertEqual(data, {})\n\n        response = self.fetch(\n            \"/typecheck/asdf?foo=bar\",\n            method=\"POST\",\n            headers={\"Cookie\": \"asdf=\" + cookie_value},\n            body=\"foo=bar\",\n        )\n\n    def test_decode_argument(self):\n        # These urls all decode to the same thing\n        urls = [\n            \"/decode_arg/%C3%A9?foo=%C3%A9&encoding=utf-8\",\n            \"/decode_arg/%E9?foo=%E9&encoding=latin1\",\n            \"/decode_arg_kw/%E9?foo=%E9&encoding=latin1\",\n        ]\n        for req_url in urls:\n            response = self.fetch(req_url)\n            response.rethrow()\n            data = json_decode(response.body)\n            self.assertEqual(\n                data,\n                {u\"path\": [u\"unicode\", u\"\\u00e9\"], u\"query\": [u\"unicode\", u\"\\u00e9\"]},\n            )\n\n        response = self.fetch(\"/decode_arg/%C3%A9?foo=%C3%A9\")\n        response.rethrow()\n        data = json_decode(response.body)\n        self.assertEqual(\n            data, {u\"path\": [u\"bytes\", u\"c3a9\"], u\"query\": [u\"bytes\", u\"c3a9\"]}\n        )\n\n    def test_decode_argument_invalid_unicode(self):\n        # test that invalid unicode in URLs causes 400, not 500\n        with ExpectLog(gen_log, \".*Invalid unicode.*\"):\n            response = self.fetch(\"/typecheck/invalid%FF\")\n            self.assertEqual(response.code, 400)\n            response = self.fetch(\"/typecheck/invalid?foo=%FF\")\n            self.assertEqual(response.code, 400)\n\n    def test_decode_argument_plus(self):\n        # These urls are all equivalent.\n        urls = [\n            \"/decode_arg/1%20%2B%201?foo=1%20%2B%201&encoding=utf-8\",\n            \"/decode_arg/1%20+%201?foo=1+%2B+1&encoding=utf-8\",\n        ]\n        for req_url in urls:\n            response = self.fetch(req_url)\n            response.rethrow()\n            data = json_decode(response.body)\n            self.assertEqual(\n                data,\n                {u\"path\": [u\"unicode\", u\"1 + 1\"], u\"query\": [u\"unicode\", u\"1 + 1\"]},\n            )\n\n    def test_reverse_url(self):\n        self.assertEqual(self.app.reverse_url(\"decode_arg\", \"foo\"), \"/decode_arg/foo\")\n        self.assertEqual(self.app.reverse_url(\"decode_arg\", 42), \"/decode_arg/42\")\n        self.assertEqual(self.app.reverse_url(\"decode_arg\", b\"\\xe9\"), \"/decode_arg/%E9\")\n        self.assertEqual(\n            self.app.reverse_url(\"decode_arg\", u\"\\u00e9\"), \"/decode_arg/%C3%A9\"\n        )\n        self.assertEqual(\n            self.app.reverse_url(\"decode_arg\", \"1 + 1\"), \"/decode_arg/1%20%2B%201\"\n        )\n\n    def test_uimodule_unescaped(self):\n        response = self.fetch(\"/linkify\")\n        self.assertEqual(\n            response.body, b'<a href=\"http://example.com\">http://example.com</a>'\n        )\n\n    def test_uimodule_resources(self):\n        response = self.fetch(\"/uimodule_resources\")\n        self.assertEqual(\n            response.body,\n            b\"\"\"\\\n<html><head><link href=\"/base.css\" type=\"text/css\" rel=\"stylesheet\"/><link href=\"/foo.css\" type=\"text/css\" rel=\"stylesheet\"/>\n<style type=\"text/css\">\n.entry { margin-bottom: 1em; }\n</style>\n<meta>\n</head><body>\n\n\n<div class=\"entry\">...</div>\n\n\n<div class=\"entry\">...</div>\n\n<script src=\"/common.js\" type=\"text/javascript\"></script>\n<script type=\"text/javascript\">\n//<![CDATA[\njs_embed()\n//]]>\n</script>\n<script src=\"/analytics.js\"/>\n</body></html>\"\"\",  # noqa: E501\n        )\n\n    def test_optional_path(self):\n        self.assertEqual(self.fetch_json(\"/optional_path/foo\"), {u\"path\": u\"foo\"})\n        self.assertEqual(self.fetch_json(\"/optional_path/\"), {u\"path\": None})\n\n    def test_multi_header(self):\n        response = self.fetch(\"/multi_header\")\n        self.assertEqual(response.headers[\"x-overwrite\"], \"2\")\n        self.assertEqual(response.headers.get_list(\"x-multi\"), [\"3\", \"4\"])\n\n    def test_redirect(self):\n        response = self.fetch(\"/redirect?permanent=1\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        response = self.fetch(\"/redirect?permanent=0\", follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        response = self.fetch(\"/redirect?status=307\", follow_redirects=False)\n        self.assertEqual(response.code, 307)\n\n    def test_web_redirect(self):\n        response = self.fetch(\"/web_redirect_permanent\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/web_redirect_newpath\")\n        response = self.fetch(\"/web_redirect\", follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        self.assertEqual(response.headers[\"Location\"], \"/web_redirect_newpath\")\n\n    def test_web_redirect_double_slash(self):\n        response = self.fetch(\"//web_redirect_double_slash\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/web_redirect_newpath\")\n\n    def test_header_injection(self):\n        response = self.fetch(\"/header_injection\")\n        self.assertEqual(response.body, b\"ok\")\n\n    def test_get_argument(self):\n        response = self.fetch(\"/get_argument?foo=bar\")\n        self.assertEqual(response.body, b\"bar\")\n        response = self.fetch(\"/get_argument?foo=\")\n        self.assertEqual(response.body, b\"\")\n        response = self.fetch(\"/get_argument\")\n        self.assertEqual(response.body, b\"default\")\n\n        # Test merging of query and body arguments.\n        # In singular form, body arguments take precedence over query arguments.\n        body = urllib.parse.urlencode(dict(foo=\"hello\"))\n        response = self.fetch(\"/get_argument?foo=bar\", method=\"POST\", body=body)\n        self.assertEqual(response.body, b\"hello\")\n        # In plural methods they are merged.\n        response = self.fetch(\"/get_arguments?foo=bar\", method=\"POST\", body=body)\n        self.assertEqual(\n            json_decode(response.body),\n            dict(default=[\"bar\", \"hello\"], query=[\"bar\"], body=[\"hello\"]),\n        )\n\n    def test_get_query_arguments(self):\n        # send as a post so we can ensure the separation between query\n        # string and body arguments.\n        body = urllib.parse.urlencode(dict(foo=\"hello\"))\n        response = self.fetch(\n            \"/get_argument?source=query&foo=bar\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"bar\")\n        response = self.fetch(\n            \"/get_argument?source=query&foo=\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"\")\n        response = self.fetch(\"/get_argument?source=query\", method=\"POST\", body=body)\n        self.assertEqual(response.body, b\"default\")\n\n    def test_get_body_arguments(self):\n        body = urllib.parse.urlencode(dict(foo=\"bar\"))\n        response = self.fetch(\n            \"/get_argument?source=body&foo=hello\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"bar\")\n\n        body = urllib.parse.urlencode(dict(foo=\"\"))\n        response = self.fetch(\n            \"/get_argument?source=body&foo=hello\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"\")\n\n        body = urllib.parse.urlencode(dict())\n        response = self.fetch(\n            \"/get_argument?source=body&foo=hello\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"default\")\n\n    def test_no_gzip(self):\n        response = self.fetch(\"/get_argument\")\n        self.assertNotIn(\"Accept-Encoding\", response.headers.get(\"Vary\", \"\"))\n        self.assertNotIn(\"gzip\", response.headers.get(\"Content-Encoding\", \"\"))\n\n\nclass NonWSGIWebTests(WebTestCase):\n    def get_handlers(self):\n        return [(\"/empty_flush\", EmptyFlushCallbackHandler)]\n\n    def test_empty_flush(self):\n        response = self.fetch(\"/empty_flush\")\n        self.assertEqual(response.body, b\"ok\")\n\n\nclass ErrorResponseTest(WebTestCase):\n    def get_handlers(self):\n        class DefaultHandler(RequestHandler):\n            def get(self):\n                if self.get_argument(\"status\", None):\n                    raise HTTPError(int(self.get_argument(\"status\")))\n                1 / 0\n\n        class WriteErrorHandler(RequestHandler):\n            def get(self):\n                if self.get_argument(\"status\", None):\n                    self.send_error(int(self.get_argument(\"status\")))\n                else:\n                    1 / 0\n\n            def write_error(self, status_code, **kwargs):\n                self.set_header(\"Content-Type\", \"text/plain\")\n                if \"exc_info\" in kwargs:\n                    self.write(\"Exception: %s\" % kwargs[\"exc_info\"][0].__name__)\n                else:\n                    self.write(\"Status: %d\" % status_code)\n\n        class FailedWriteErrorHandler(RequestHandler):\n            def get(self):\n                1 / 0\n\n            def write_error(self, status_code, **kwargs):\n                raise Exception(\"exception in write_error\")\n\n        return [\n            url(\"/default\", DefaultHandler),\n            url(\"/write_error\", WriteErrorHandler),\n            url(\"/failed_write_error\", FailedWriteErrorHandler),\n        ]\n\n    def test_default(self):\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/default\")\n            self.assertEqual(response.code, 500)\n            self.assertTrue(b\"500: Internal Server Error\" in response.body)\n\n            response = self.fetch(\"/default?status=503\")\n            self.assertEqual(response.code, 503)\n            self.assertTrue(b\"503: Service Unavailable\" in response.body)\n\n            response = self.fetch(\"/default?status=435\")\n            self.assertEqual(response.code, 435)\n            self.assertTrue(b\"435: Unknown\" in response.body)\n\n    def test_write_error(self):\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/write_error\")\n            self.assertEqual(response.code, 500)\n            self.assertEqual(b\"Exception: ZeroDivisionError\", response.body)\n\n            response = self.fetch(\"/write_error?status=503\")\n            self.assertEqual(response.code, 503)\n            self.assertEqual(b\"Status: 503\", response.body)\n\n    def test_failed_write_error(self):\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/failed_write_error\")\n            self.assertEqual(response.code, 500)\n            self.assertEqual(b\"\", response.body)\n\n\nclass StaticFileTest(WebTestCase):\n    # The expected MD5 hash of robots.txt, used in tests that call\n    # StaticFileHandler.get_version\n    robots_txt_hash = b\"f71d20196d4caf35b6a670db8c70b03d\"\n    static_dir = os.path.join(os.path.dirname(__file__), \"static\")\n\n    def get_handlers(self):\n        class StaticUrlHandler(RequestHandler):\n            def get(self, path):\n                with_v = int(self.get_argument(\"include_version\", 1))\n                self.write(self.static_url(path, include_version=with_v))\n\n        class AbsoluteStaticUrlHandler(StaticUrlHandler):\n            include_host = True\n\n        class OverrideStaticUrlHandler(RequestHandler):\n            def get(self, path):\n                do_include = bool(self.get_argument(\"include_host\"))\n                self.include_host = not do_include\n\n                regular_url = self.static_url(path)\n                override_url = self.static_url(path, include_host=do_include)\n                if override_url == regular_url:\n                    return self.write(str(False))\n\n                protocol = self.request.protocol + \"://\"\n                protocol_length = len(protocol)\n                check_regular = regular_url.find(protocol, 0, protocol_length)\n                check_override = override_url.find(protocol, 0, protocol_length)\n\n                if do_include:\n                    result = check_override == 0 and check_regular == -1\n                else:\n                    result = check_override == -1 and check_regular == 0\n                self.write(str(result))\n\n        return [\n            (\"/static_url/(.*)\", StaticUrlHandler),\n            (\"/abs_static_url/(.*)\", AbsoluteStaticUrlHandler),\n            (\"/override_static_url/(.*)\", OverrideStaticUrlHandler),\n            (\"/root_static/(.*)\", StaticFileHandler, dict(path=\"/\")),\n        ]\n\n    def get_app_kwargs(self):\n        return dict(static_path=relpath(\"static\"))\n\n    def test_static_files(self):\n        response = self.fetch(\"/robots.txt\")\n        self.assertTrue(b\"Disallow: /\" in response.body)\n\n        response = self.fetch(\"/static/robots.txt\")\n        self.assertTrue(b\"Disallow: /\" in response.body)\n        self.assertEqual(response.headers.get(\"Content-Type\"), \"text/plain\")\n\n    def test_static_compressed_files(self):\n        response = self.fetch(\"/static/sample.xml.gz\")\n        self.assertEqual(response.headers.get(\"Content-Type\"), \"application/gzip\")\n        response = self.fetch(\"/static/sample.xml.bz2\")\n        self.assertEqual(\n            response.headers.get(\"Content-Type\"), \"application/octet-stream\"\n        )\n        # make sure the uncompressed file still has the correct type\n        response = self.fetch(\"/static/sample.xml\")\n        self.assertTrue(\n            response.headers.get(\"Content-Type\") in set((\"text/xml\", \"application/xml\"))\n        )\n\n    def test_static_url(self):\n        response = self.fetch(\"/static_url/robots.txt\")\n        self.assertEqual(response.body, b\"/static/robots.txt?v=\" + self.robots_txt_hash)\n\n    def test_absolute_static_url(self):\n        response = self.fetch(\"/abs_static_url/robots.txt\")\n        self.assertEqual(\n            response.body,\n            (utf8(self.get_url(\"/\")) + b\"static/robots.txt?v=\" + self.robots_txt_hash),\n        )\n\n    def test_relative_version_exclusion(self):\n        response = self.fetch(\"/static_url/robots.txt?include_version=0\")\n        self.assertEqual(response.body, b\"/static/robots.txt\")\n\n    def test_absolute_version_exclusion(self):\n        response = self.fetch(\"/abs_static_url/robots.txt?include_version=0\")\n        self.assertEqual(response.body, utf8(self.get_url(\"/\") + \"static/robots.txt\"))\n\n    def test_include_host_override(self):\n        self._trigger_include_host_check(False)\n        self._trigger_include_host_check(True)\n\n    def _trigger_include_host_check(self, include_host):\n        path = \"/override_static_url/robots.txt?include_host=%s\"\n        response = self.fetch(path % int(include_host))\n        self.assertEqual(response.body, utf8(str(True)))\n\n    def get_and_head(self, *args, **kwargs):\n        \"\"\"Performs a GET and HEAD request and returns the GET response.\n\n        Fails if any ``Content-*`` headers returned by the two requests\n        differ.\n        \"\"\"\n        head_response = self.fetch(*args, method=\"HEAD\", **kwargs)\n        get_response = self.fetch(*args, method=\"GET\", **kwargs)\n        content_headers = set()\n        for h in itertools.chain(head_response.headers, get_response.headers):\n            if h.startswith(\"Content-\"):\n                content_headers.add(h)\n        for h in content_headers:\n            self.assertEqual(\n                head_response.headers.get(h),\n                get_response.headers.get(h),\n                \"%s differs between GET (%s) and HEAD (%s)\"\n                % (h, head_response.headers.get(h), get_response.headers.get(h)),\n            )\n        return get_response\n\n    def test_static_304_if_modified_since(self):\n        response1 = self.get_and_head(\"/static/robots.txt\")\n        response2 = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": response1.headers[\"Last-Modified\"]},\n        )\n        self.assertEqual(response2.code, 304)\n        self.assertTrue(\"Content-Length\" not in response2.headers)\n        self.assertTrue(\"Last-Modified\" not in response2.headers)\n\n    def test_static_304_if_none_match(self):\n        response1 = self.get_and_head(\"/static/robots.txt\")\n        response2 = self.get_and_head(\n            \"/static/robots.txt\", headers={\"If-None-Match\": response1.headers[\"Etag\"]}\n        )\n        self.assertEqual(response2.code, 304)\n\n    def test_static_304_etag_modified_bug(self):\n        response1 = self.get_and_head(\"/static/robots.txt\")\n        response2 = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\n                \"If-None-Match\": '\"MISMATCH\"',\n                \"If-Modified-Since\": response1.headers[\"Last-Modified\"],\n            },\n        )\n        self.assertEqual(response2.code, 200)\n\n    def test_static_if_modified_since_pre_epoch(self):\n        # On windows, the functions that work with time_t do not accept\n        # negative values, and at least one client (processing.js) seems\n        # to use if-modified-since 1/1/1960 as a cache-busting technique.\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": \"Fri, 01 Jan 1960 00:00:00 GMT\"},\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_static_if_modified_since_time_zone(self):\n        # Instead of the value from Last-Modified, make requests with times\n        # chosen just before and after the known modification time\n        # of the file to ensure that the right time zone is being used\n        # when parsing If-Modified-Since.\n        stat = os.stat(relpath(\"static/robots.txt\"))\n\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": format_timestamp(stat.st_mtime - 1)},\n        )\n        self.assertEqual(response.code, 200)\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": format_timestamp(stat.st_mtime + 1)},\n        )\n        self.assertEqual(response.code, 304)\n\n    def test_static_etag(self):\n        response = self.get_and_head(\"/static/robots.txt\")\n        self.assertEqual(\n            utf8(response.headers.get(\"Etag\")), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_with_range(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=0-9\"}\n        )\n        self.assertEqual(response.code, 206)\n        self.assertEqual(response.body, b\"User-agent\")\n        self.assertEqual(\n            utf8(response.headers.get(\"Etag\")), b'\"' + self.robots_txt_hash + b'\"'\n        )\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"10\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 0-9/26\")\n\n    def test_static_with_range_full_file(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=0-\"}\n        )\n        # Note: Chrome refuses to play audio if it gets an HTTP 206 in response\n        # to ``Range: bytes=0-`` :(\n        self.assertEqual(response.code, 200)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"26\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), None)\n\n    def test_static_with_range_full_past_end(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=0-10000000\"}\n        )\n        self.assertEqual(response.code, 200)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"26\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), None)\n\n    def test_static_with_range_partial_past_end(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=1-10000000\"}\n        )\n        self.assertEqual(response.code, 206)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()[1:]))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"25\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 1-25/26\")\n\n    def test_static_with_range_end_edge(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=22-\"}\n        )\n        self.assertEqual(response.body, b\": /\\n\")\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"4\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 22-25/26\")\n\n    def test_static_with_range_neg_end(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=-4\"}\n        )\n        self.assertEqual(response.body, b\": /\\n\")\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"4\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 22-25/26\")\n\n    def test_static_with_range_neg_past_start(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=-1000000\"}\n        )\n        self.assertEqual(response.code, 200)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"26\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), None)\n\n    def test_static_invalid_range(self):\n        response = self.get_and_head(\"/static/robots.txt\", headers={\"Range\": \"asdf\"})\n        self.assertEqual(response.code, 200)\n\n    def test_static_unsatisfiable_range_zero_suffix(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=-0\"}\n        )\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\n        self.assertEqual(response.code, 416)\n\n    def test_static_unsatisfiable_range_invalid_start(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=26\"}\n        )\n        self.assertEqual(response.code, 416)\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\n\n    def test_static_unsatisfiable_range_end_less_than_start(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=10-3\"}\n        )\n        self.assertEqual(response.code, 416)\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\n\n    def test_static_head(self):\n        response = self.fetch(\"/static/robots.txt\", method=\"HEAD\")\n        self.assertEqual(response.code, 200)\n        # No body was returned, but we did get the right content length.\n        self.assertEqual(response.body, b\"\")\n        self.assertEqual(response.headers[\"Content-Length\"], \"26\")\n        self.assertEqual(\n            utf8(response.headers[\"Etag\"]), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_head_range(self):\n        response = self.fetch(\n            \"/static/robots.txt\", method=\"HEAD\", headers={\"Range\": \"bytes=1-4\"}\n        )\n        self.assertEqual(response.code, 206)\n        self.assertEqual(response.body, b\"\")\n        self.assertEqual(response.headers[\"Content-Length\"], \"4\")\n        self.assertEqual(\n            utf8(response.headers[\"Etag\"]), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_range_if_none_match(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\n                \"Range\": \"bytes=1-4\",\n                \"If-None-Match\": b'\"' + self.robots_txt_hash + b'\"',\n            },\n        )\n        self.assertEqual(response.code, 304)\n        self.assertEqual(response.body, b\"\")\n        self.assertTrue(\"Content-Length\" not in response.headers)\n        self.assertEqual(\n            utf8(response.headers[\"Etag\"]), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_404(self):\n        response = self.get_and_head(\"/static/blarg\")\n        self.assertEqual(response.code, 404)\n\n    def test_path_traversal_protection(self):\n        # curl_httpclient processes \"..\" on the client side, so we\n        # must test this with simple_httpclient.\n        self.http_client.close()\n        self.http_client = SimpleAsyncHTTPClient()\n        with ExpectLog(gen_log, \".*not in root static directory\"):\n            response = self.get_and_head(\"/static/../static_foo.txt\")\n        # Attempted path traversal should result in 403, not 200\n        # (which means the check failed and the file was served)\n        # or 404 (which means that the file didn't exist and\n        # is probably a packaging error).\n        self.assertEqual(response.code, 403)\n\n    @unittest.skipIf(os.name != \"posix\", \"non-posix OS\")\n    def test_root_static_path(self):\n        # Sometimes people set the StaticFileHandler's path to '/'\n        # to disable Tornado's path validation (in conjunction with\n        # their own validation in get_absolute_path). Make sure\n        # that the stricter validation in 4.2.1 doesn't break them.\n        path = os.path.join(\n            os.path.dirname(os.path.abspath(__file__)), \"static/robots.txt\"\n        )\n        response = self.get_and_head(\"/root_static\" + urllib.parse.quote(path))\n        self.assertEqual(response.code, 200)\n\n\nclass StaticDefaultFilenameTest(WebTestCase):\n    def get_app_kwargs(self):\n        return dict(\n            static_path=relpath(\"static\"),\n            static_handler_args=dict(default_filename=\"index.html\"),\n        )\n\n    def get_handlers(self):\n        return []\n\n    def test_static_default_filename(self):\n        response = self.fetch(\"/static/dir/\", follow_redirects=False)\n        self.assertEqual(response.code, 200)\n        self.assertEqual(b\"this is the index\\n\", response.body)\n\n    def test_static_default_redirect(self):\n        response = self.fetch(\"/static/dir\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertTrue(response.headers[\"Location\"].endswith(\"/static/dir/\"))\n\n\nclass StaticFileWithPathTest(WebTestCase):\n    def get_app_kwargs(self):\n        return dict(\n            static_path=relpath(\"static\"),\n            static_handler_args=dict(default_filename=\"index.html\"),\n        )\n\n    def get_handlers(self):\n        return [(\"/foo/(.*)\", StaticFileHandler, {\"path\": relpath(\"templates/\")})]\n\n    def test_serve(self):\n        response = self.fetch(\"/foo/utf8.html\")\n        self.assertEqual(response.body, b\"H\\xc3\\xa9llo\\n\")\n\n\nclass CustomStaticFileTest(WebTestCase):\n    def get_handlers(self):\n        class MyStaticFileHandler(StaticFileHandler):\n            @classmethod\n            def make_static_url(cls, settings, path):\n                version_hash = cls.get_version(settings, path)\n                extension_index = path.rindex(\".\")\n                before_version = path[:extension_index]\n                after_version = path[(extension_index + 1) :]\n                return \"/static/%s.%s.%s\" % (\n                    before_version,\n                    version_hash,\n                    after_version,\n                )\n\n            def parse_url_path(self, url_path):\n                extension_index = url_path.rindex(\".\")\n                version_index = url_path.rindex(\".\", 0, extension_index)\n                return \"%s%s\" % (url_path[:version_index], url_path[extension_index:])\n\n            @classmethod\n            def get_absolute_path(cls, settings, path):\n                return \"CustomStaticFileTest:\" + path\n\n            def validate_absolute_path(self, root, absolute_path):\n                return absolute_path\n\n            @classmethod\n            def get_content(self, path, start=None, end=None):\n                assert start is None and end is None\n                if path == \"CustomStaticFileTest:foo.txt\":\n                    return b\"bar\"\n                raise Exception(\"unexpected path %r\" % path)\n\n            def get_content_size(self):\n                if self.absolute_path == \"CustomStaticFileTest:foo.txt\":\n                    return 3\n                raise Exception(\"unexpected path %r\" % self.absolute_path)\n\n            def get_modified_time(self):\n                return None\n\n            @classmethod\n            def get_version(cls, settings, path):\n                return \"42\"\n\n        class StaticUrlHandler(RequestHandler):\n            def get(self, path):\n                self.write(self.static_url(path))\n\n        self.static_handler_class = MyStaticFileHandler\n\n        return [(\"/static_url/(.*)\", StaticUrlHandler)]\n\n    def get_app_kwargs(self):\n        return dict(static_path=\"dummy\", static_handler_class=self.static_handler_class)\n\n    def test_serve(self):\n        response = self.fetch(\"/static/foo.42.txt\")\n        self.assertEqual(response.body, b\"bar\")\n\n    def test_static_url(self):\n        with ExpectLog(gen_log, \"Could not open static file\", required=False):\n            response = self.fetch(\"/static_url/foo.txt\")\n            self.assertEqual(response.body, b\"/static/foo.42.txt\")\n\n\nclass HostMatchingTest(WebTestCase):\n    class Handler(RequestHandler):\n        def initialize(self, reply):\n            self.reply = reply\n\n        def get(self):\n            self.write(self.reply)\n\n    def get_handlers(self):\n        return [(\"/foo\", HostMatchingTest.Handler, {\"reply\": \"wildcard\"})]\n\n    def test_host_matching(self):\n        self.app.add_handlers(\n            \"www.example.com\", [(\"/foo\", HostMatchingTest.Handler, {\"reply\": \"[0]\"})]\n        )\n        self.app.add_handlers(\n            r\"www\\.example\\.com\", [(\"/bar\", HostMatchingTest.Handler, {\"reply\": \"[1]\"})]\n        )\n        self.app.add_handlers(\n            \"www.example.com\", [(\"/baz\", HostMatchingTest.Handler, {\"reply\": \"[2]\"})]\n        )\n        self.app.add_handlers(\n            \"www.e.*e.com\", [(\"/baz\", HostMatchingTest.Handler, {\"reply\": \"[3]\"})]\n        )\n\n        response = self.fetch(\"/foo\")\n        self.assertEqual(response.body, b\"wildcard\")\n        response = self.fetch(\"/bar\")\n        self.assertEqual(response.code, 404)\n        response = self.fetch(\"/baz\")\n        self.assertEqual(response.code, 404)\n\n        response = self.fetch(\"/foo\", headers={\"Host\": \"www.example.com\"})\n        self.assertEqual(response.body, b\"[0]\")\n        response = self.fetch(\"/bar\", headers={\"Host\": \"www.example.com\"})\n        self.assertEqual(response.body, b\"[1]\")\n        response = self.fetch(\"/baz\", headers={\"Host\": \"www.example.com\"})\n        self.assertEqual(response.body, b\"[2]\")\n        response = self.fetch(\"/baz\", headers={\"Host\": \"www.exe.com\"})\n        self.assertEqual(response.body, b\"[3]\")\n\n\nclass DefaultHostMatchingTest(WebTestCase):\n    def get_handlers(self):\n        return []\n\n    def get_app_kwargs(self):\n        return {\"default_host\": \"www.example.com\"}\n\n    def test_default_host_matching(self):\n        self.app.add_handlers(\n            \"www.example.com\", [(\"/foo\", HostMatchingTest.Handler, {\"reply\": \"[0]\"})]\n        )\n        self.app.add_handlers(\n            r\"www\\.example\\.com\", [(\"/bar\", HostMatchingTest.Handler, {\"reply\": \"[1]\"})]\n        )\n        self.app.add_handlers(\n            \"www.test.com\", [(\"/baz\", HostMatchingTest.Handler, {\"reply\": \"[2]\"})]\n        )\n\n        response = self.fetch(\"/foo\")\n        self.assertEqual(response.body, b\"[0]\")\n        response = self.fetch(\"/bar\")\n        self.assertEqual(response.body, b\"[1]\")\n        response = self.fetch(\"/baz\")\n        self.assertEqual(response.code, 404)\n\n        response = self.fetch(\"/foo\", headers={\"X-Real-Ip\": \"127.0.0.1\"})\n        self.assertEqual(response.code, 404)\n\n        self.app.default_host = \"www.test.com\"\n\n        response = self.fetch(\"/baz\")\n        self.assertEqual(response.body, b\"[2]\")\n\n\nclass NamedURLSpecGroupsTest(WebTestCase):\n    def get_handlers(self):\n        class EchoHandler(RequestHandler):\n            def get(self, path):\n                self.write(path)\n\n        return [\n            (\"/str/(?P<path>.*)\", EchoHandler),\n            (u\"/unicode/(?P<path>.*)\", EchoHandler),\n        ]\n\n    def test_named_urlspec_groups(self):\n        response = self.fetch(\"/str/foo\")\n        self.assertEqual(response.body, b\"foo\")\n\n        response = self.fetch(\"/unicode/bar\")\n        self.assertEqual(response.body, b\"bar\")\n\n\nclass ClearHeaderTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_header(\"h1\", \"foo\")\n            self.set_header(\"h2\", \"bar\")\n            self.clear_header(\"h1\")\n            self.clear_header(\"nonexistent\")\n\n    def test_clear_header(self):\n        response = self.fetch(\"/\")\n        self.assertTrue(\"h1\" not in response.headers)\n        self.assertEqual(response.headers[\"h2\"], \"bar\")\n\n\nclass Header204Test(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_status(204)\n            self.finish()\n\n    def test_204_headers(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 204)\n        self.assertNotIn(\"Content-Length\", response.headers)\n        self.assertNotIn(\"Transfer-Encoding\", response.headers)\n\n\nclass Header304Test(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_header(\"Content-Language\", \"en_US\")\n            self.write(\"hello\")\n\n    def test_304_headers(self):\n        response1 = self.fetch(\"/\")\n        self.assertEqual(response1.headers[\"Content-Length\"], \"5\")\n        self.assertEqual(response1.headers[\"Content-Language\"], \"en_US\")\n\n        response2 = self.fetch(\n            \"/\", headers={\"If-None-Match\": response1.headers[\"Etag\"]}\n        )\n        self.assertEqual(response2.code, 304)\n        self.assertTrue(\"Content-Length\" not in response2.headers)\n        self.assertTrue(\"Content-Language\" not in response2.headers)\n        # Not an entity header, but should not be added to 304s by chunking\n        self.assertTrue(\"Transfer-Encoding\" not in response2.headers)\n\n\nclass StatusReasonTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            reason = self.request.arguments.get(\"reason\", [])\n            self.set_status(\n                int(self.get_argument(\"code\")), reason=reason[0] if reason else None\n            )\n\n    def get_http_client(self):\n        # simple_httpclient only: curl doesn't expose the reason string\n        return SimpleAsyncHTTPClient()\n\n    def test_status(self):\n        response = self.fetch(\"/?code=304\")\n        self.assertEqual(response.code, 304)\n        self.assertEqual(response.reason, \"Not Modified\")\n        response = self.fetch(\"/?code=304&reason=Foo\")\n        self.assertEqual(response.code, 304)\n        self.assertEqual(response.reason, \"Foo\")\n        response = self.fetch(\"/?code=682&reason=Bar\")\n        self.assertEqual(response.code, 682)\n        self.assertEqual(response.reason, \"Bar\")\n        response = self.fetch(\"/?code=682\")\n        self.assertEqual(response.code, 682)\n        self.assertEqual(response.reason, \"Unknown\")\n\n\nclass DateHeaderTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.write(\"hello\")\n\n    def test_date_header(self):\n        response = self.fetch(\"/\")\n        parsed = email.utils.parsedate(response.headers[\"Date\"])\n        assert parsed is not None\n        header_date = datetime.datetime(*parsed[:6])\n        self.assertTrue(\n            header_date - datetime.datetime.utcnow() < datetime.timedelta(seconds=2)\n        )\n\n\nclass RaiseWithReasonTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            raise HTTPError(682, reason=\"Foo\")\n\n    def get_http_client(self):\n        # simple_httpclient only: curl doesn't expose the reason string\n        return SimpleAsyncHTTPClient()\n\n    def test_raise_with_reason(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 682)\n        self.assertEqual(response.reason, \"Foo\")\n        self.assertIn(b\"682: Foo\", response.body)\n\n    def test_httperror_str(self):\n        self.assertEqual(str(HTTPError(682, reason=\"Foo\")), \"HTTP 682: Foo\")\n\n    def test_httperror_str_from_httputil(self):\n        self.assertEqual(str(HTTPError(682)), \"HTTP 682: Unknown\")\n\n\nclass ErrorHandlerXSRFTest(WebTestCase):\n    def get_handlers(self):\n        # note that if the handlers list is empty we get the default_host\n        # redirect fallback instead of a 404, so test with both an\n        # explicitly defined error handler and an implicit 404.\n        return [(\"/error\", ErrorHandler, dict(status_code=417))]\n\n    def get_app_kwargs(self):\n        return dict(xsrf_cookies=True)\n\n    def test_error_xsrf(self):\n        response = self.fetch(\"/error\", method=\"POST\", body=\"\")\n        self.assertEqual(response.code, 417)\n\n    def test_404_xsrf(self):\n        response = self.fetch(\"/404\", method=\"POST\", body=\"\")\n        self.assertEqual(response.code, 404)\n\n\nclass GzipTestCase(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            for v in self.get_arguments(\"vary\"):\n                self.add_header(\"Vary\", v)\n            # Must write at least MIN_LENGTH bytes to activate compression.\n            self.write(\"hello world\" + (\"!\" * GZipContentEncoding.MIN_LENGTH))\n\n    def get_app_kwargs(self):\n        return dict(\n            gzip=True, static_path=os.path.join(os.path.dirname(__file__), \"static\")\n        )\n\n    def assert_compressed(self, response):\n        # simple_httpclient renames the content-encoding header;\n        # curl_httpclient doesn't.\n        self.assertEqual(\n            response.headers.get(\n                \"Content-Encoding\", response.headers.get(\"X-Consumed-Content-Encoding\")\n            ),\n            \"gzip\",\n        )\n\n    def test_gzip(self):\n        response = self.fetch(\"/\")\n        self.assert_compressed(response)\n        self.assertEqual(response.headers[\"Vary\"], \"Accept-Encoding\")\n\n    def test_gzip_static(self):\n        # The streaming responses in StaticFileHandler have subtle\n        # interactions with the gzip output so test this case separately.\n        response = self.fetch(\"/robots.txt\")\n        self.assert_compressed(response)\n        self.assertEqual(response.headers[\"Vary\"], \"Accept-Encoding\")\n\n    def test_gzip_not_requested(self):\n        response = self.fetch(\"/\", use_gzip=False)\n        self.assertNotIn(\"Content-Encoding\", response.headers)\n        self.assertEqual(response.headers[\"Vary\"], \"Accept-Encoding\")\n\n    def test_vary_already_present(self):\n        response = self.fetch(\"/?vary=Accept-Language\")\n        self.assert_compressed(response)\n        self.assertEqual(\n            [s.strip() for s in response.headers[\"Vary\"].split(\",\")],\n            [\"Accept-Language\", \"Accept-Encoding\"],\n        )\n\n    def test_vary_already_present_multiple(self):\n        # Regression test for https://github.com/tornadoweb/tornado/issues/1670\n        response = self.fetch(\"/?vary=Accept-Language&vary=Cookie\")\n        self.assert_compressed(response)\n        self.assertEqual(\n            [s.strip() for s in response.headers[\"Vary\"].split(\",\")],\n            [\"Accept-Language\", \"Cookie\", \"Accept-Encoding\"],\n        )\n\n\nclass PathArgsInPrepareTest(WebTestCase):\n    class Handler(RequestHandler):\n        def prepare(self):\n            self.write(dict(args=self.path_args, kwargs=self.path_kwargs))\n\n        def get(self, path):\n            assert path == \"foo\"\n            self.finish()\n\n    def get_handlers(self):\n        return [(\"/pos/(.*)\", self.Handler), (\"/kw/(?P<path>.*)\", self.Handler)]\n\n    def test_pos(self):\n        response = self.fetch(\"/pos/foo\")\n        response.rethrow()\n        data = json_decode(response.body)\n        self.assertEqual(data, {\"args\": [\"foo\"], \"kwargs\": {}})\n\n    def test_kw(self):\n        response = self.fetch(\"/kw/foo\")\n        response.rethrow()\n        data = json_decode(response.body)\n        self.assertEqual(data, {\"args\": [], \"kwargs\": {\"path\": \"foo\"}})\n\n\nclass ClearAllCookiesTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.clear_all_cookies()\n            self.write(\"ok\")\n\n    def test_clear_all_cookies(self):\n        response = self.fetch(\"/\", headers={\"Cookie\": \"foo=bar; baz=xyzzy\"})\n        set_cookies = sorted(response.headers.get_list(\"Set-Cookie\"))\n        # Python 3.5 sends 'baz=\"\";'; older versions use 'baz=;'\n        self.assertTrue(\n            set_cookies[0].startswith(\"baz=;\") or set_cookies[0].startswith('baz=\"\";')\n        )\n        self.assertTrue(\n            set_cookies[1].startswith(\"foo=;\") or set_cookies[1].startswith('foo=\"\";')\n        )\n\n\nclass PermissionError(Exception):\n    pass\n\n\nclass ExceptionHandlerTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            exc = self.get_argument(\"exc\")\n            if exc == \"http\":\n                raise HTTPError(410, \"no longer here\")\n            elif exc == \"zero\":\n                1 / 0\n            elif exc == \"permission\":\n                raise PermissionError(\"not allowed\")\n\n        def write_error(self, status_code, **kwargs):\n            if \"exc_info\" in kwargs:\n                typ, value, tb = kwargs[\"exc_info\"]\n                if isinstance(value, PermissionError):\n                    self.set_status(403)\n                    self.write(\"PermissionError\")\n                    return\n            RequestHandler.write_error(self, status_code, **kwargs)\n\n        def log_exception(self, typ, value, tb):\n            if isinstance(value, PermissionError):\n                app_log.warning(\"custom logging for PermissionError: %s\", value.args[0])\n            else:\n                RequestHandler.log_exception(self, typ, value, tb)\n\n    def test_http_error(self):\n        # HTTPErrors are logged as warnings with no stack trace.\n        # TODO: extend ExpectLog to test this more precisely\n        with ExpectLog(gen_log, \".*no longer here\"):\n            response = self.fetch(\"/?exc=http\")\n            self.assertEqual(response.code, 410)\n\n    def test_unknown_error(self):\n        # Unknown errors are logged as errors with a stack trace.\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/?exc=zero\")\n            self.assertEqual(response.code, 500)\n\n    def test_known_error(self):\n        # log_exception can override logging behavior, and write_error\n        # can override the response.\n        with ExpectLog(app_log, \"custom logging for PermissionError: not allowed\"):\n            response = self.fetch(\"/?exc=permission\")\n            self.assertEqual(response.code, 403)\n\n\nclass BuggyLoggingTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            1 / 0\n\n        def log_exception(self, typ, value, tb):\n            1 / 0\n\n    def test_buggy_log_exception(self):\n        # Something gets logged even though the application's\n        # logger is broken.\n        with ExpectLog(app_log, \".*\"):\n            self.fetch(\"/\")\n\n\nclass UIMethodUIModuleTest(SimpleHandlerTestCase):\n    \"\"\"Test that UI methods and modules are created correctly and\n    associated with the handler.\n    \"\"\"\n\n    class Handler(RequestHandler):\n        def get(self):\n            self.render(\"foo.html\")\n\n        def value(self):\n            return self.get_argument(\"value\")\n\n    def get_app_kwargs(self):\n        def my_ui_method(handler, x):\n            return \"In my_ui_method(%s) with handler value %s.\" % (x, handler.value())\n\n        class MyModule(UIModule):\n            def render(self, x):\n                return \"In MyModule(%s) with handler value %s.\" % (\n                    x,\n                    self.handler.value(),\n                )\n\n        loader = DictLoader(\n            {\"foo.html\": \"{{ my_ui_method(42) }} {% module MyModule(123) %}\"}\n        )\n        return dict(\n            template_loader=loader,\n            ui_methods={\"my_ui_method\": my_ui_method},\n            ui_modules={\"MyModule\": MyModule},\n        )\n\n    def tearDown(self):\n        super(UIMethodUIModuleTest, self).tearDown()\n        # TODO: fix template loader caching so this isn't necessary.\n        RequestHandler._template_loaders.clear()\n\n    def test_ui_method(self):\n        response = self.fetch(\"/?value=asdf\")\n        self.assertEqual(\n            response.body,\n            b\"In my_ui_method(42) with handler value asdf. \"\n            b\"In MyModule(123) with handler value asdf.\",\n        )\n\n\nclass GetArgumentErrorTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            try:\n                self.get_argument(\"foo\")\n                self.write({})\n            except MissingArgumentError as e:\n                self.write({\"arg_name\": e.arg_name, \"log_message\": e.log_message})\n\n    def test_catch_error(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(\n            json_decode(response.body),\n            {\"arg_name\": \"foo\", \"log_message\": \"Missing argument foo\"},\n        )\n\n\nclass SetLazyPropertiesTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def prepare(self):\n            self.current_user = \"Ben\"\n            self.locale = locale.get(\"en_US\")\n\n        def get_user_locale(self):\n            raise NotImplementedError()\n\n        def get_current_user(self):\n            raise NotImplementedError()\n\n        def get(self):\n            self.write(\"Hello %s (%s)\" % (self.current_user, self.locale.code))\n\n    def test_set_properties(self):\n        # Ensure that current_user can be assigned to normally for apps\n        # that want to forgo the lazy get_current_user property\n        response = self.fetch(\"/\")\n        self.assertEqual(response.body, b\"Hello Ben (en_US)\")\n\n\nclass GetCurrentUserTest(WebTestCase):\n    def get_app_kwargs(self):\n        class WithoutUserModule(UIModule):\n            def render(self):\n                return \"\"\n\n        class WithUserModule(UIModule):\n            def render(self):\n                return str(self.current_user)\n\n        loader = DictLoader(\n            {\n                \"without_user.html\": \"\",\n                \"with_user.html\": \"{{ current_user }}\",\n                \"without_user_module.html\": \"{% module WithoutUserModule() %}\",\n                \"with_user_module.html\": \"{% module WithUserModule() %}\",\n            }\n        )\n        return dict(\n            template_loader=loader,\n            ui_modules={\n                \"WithUserModule\": WithUserModule,\n                \"WithoutUserModule\": WithoutUserModule,\n            },\n        )\n\n    def tearDown(self):\n        super(GetCurrentUserTest, self).tearDown()\n        RequestHandler._template_loaders.clear()\n\n    def get_handlers(self):\n        class CurrentUserHandler(RequestHandler):\n            def prepare(self):\n                self.has_loaded_current_user = False\n\n            def get_current_user(self):\n                self.has_loaded_current_user = True\n                return \"\"\n\n        class WithoutUserHandler(CurrentUserHandler):\n            def get(self):\n                self.render_string(\"without_user.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        class WithUserHandler(CurrentUserHandler):\n            def get(self):\n                self.render_string(\"with_user.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        class CurrentUserModuleHandler(CurrentUserHandler):\n            def get_template_namespace(self):\n                # If RequestHandler.get_template_namespace is called, then\n                # get_current_user is evaluated. Until #820 is fixed, this\n                # is a small hack to circumvent the issue.\n                return self.ui\n\n        class WithoutUserModuleHandler(CurrentUserModuleHandler):\n            def get(self):\n                self.render_string(\"without_user_module.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        class WithUserModuleHandler(CurrentUserModuleHandler):\n            def get(self):\n                self.render_string(\"with_user_module.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        return [\n            (\"/without_user\", WithoutUserHandler),\n            (\"/with_user\", WithUserHandler),\n            (\"/without_user_module\", WithoutUserModuleHandler),\n            (\"/with_user_module\", WithUserModuleHandler),\n        ]\n\n    @unittest.skip(\"needs fix\")\n    def test_get_current_user_is_lazy(self):\n        # TODO: Make this test pass. See #820.\n        response = self.fetch(\"/without_user\")\n        self.assertEqual(response.body, b\"False\")\n\n    def test_get_current_user_works(self):\n        response = self.fetch(\"/with_user\")\n        self.assertEqual(response.body, b\"True\")\n\n    def test_get_current_user_from_ui_module_is_lazy(self):\n        response = self.fetch(\"/without_user_module\")\n        self.assertEqual(response.body, b\"False\")\n\n    def test_get_current_user_from_ui_module_works(self):\n        response = self.fetch(\"/with_user_module\")\n        self.assertEqual(response.body, b\"True\")\n\n\nclass UnimplementedHTTPMethodsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        pass\n\n    def test_unimplemented_standard_methods(self):\n        for method in [\"HEAD\", \"GET\", \"DELETE\", \"OPTIONS\"]:\n            response = self.fetch(\"/\", method=method)\n            self.assertEqual(response.code, 405)\n        for method in [\"POST\", \"PUT\"]:\n            response = self.fetch(\"/\", method=method, body=b\"\")\n            self.assertEqual(response.code, 405)\n\n\nclass UnimplementedNonStandardMethodsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def other(self):\n            # Even though this method exists, it won't get called automatically\n            # because it is not in SUPPORTED_METHODS.\n            self.write(\"other\")\n\n    def test_unimplemented_patch(self):\n        # PATCH is recently standardized; Tornado supports it by default\n        # but wsgiref.validate doesn't like it.\n        response = self.fetch(\"/\", method=\"PATCH\", body=b\"\")\n        self.assertEqual(response.code, 405)\n\n    def test_unimplemented_other(self):\n        response = self.fetch(\"/\", method=\"OTHER\", allow_nonstandard_methods=True)\n        self.assertEqual(response.code, 405)\n\n\nclass AllHTTPMethodsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def method(self):\n            self.write(self.request.method)\n\n        get = delete = options = post = put = method  # type: ignore\n\n    def test_standard_methods(self):\n        response = self.fetch(\"/\", method=\"HEAD\")\n        self.assertEqual(response.body, b\"\")\n        for method in [\"GET\", \"DELETE\", \"OPTIONS\"]:\n            response = self.fetch(\"/\", method=method)\n            self.assertEqual(response.body, utf8(method))\n        for method in [\"POST\", \"PUT\"]:\n            response = self.fetch(\"/\", method=method, body=b\"\")\n            self.assertEqual(response.body, utf8(method))\n\n\nclass PatchMethodTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        SUPPORTED_METHODS = RequestHandler.SUPPORTED_METHODS + (  # type: ignore\n            \"OTHER\",\n        )\n\n        def patch(self):\n            self.write(\"patch\")\n\n        def other(self):\n            self.write(\"other\")\n\n    def test_patch(self):\n        response = self.fetch(\"/\", method=\"PATCH\", body=b\"\")\n        self.assertEqual(response.body, b\"patch\")\n\n    def test_other(self):\n        response = self.fetch(\"/\", method=\"OTHER\", allow_nonstandard_methods=True)\n        self.assertEqual(response.body, b\"other\")\n\n\nclass FinishInPrepareTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def prepare(self):\n            self.finish(\"done\")\n\n        def get(self):\n            # It's difficult to assert for certain that a method did not\n            # or will not be called in an asynchronous context, but this\n            # will be logged noisily if it is reached.\n            raise Exception(\"should not reach this method\")\n\n    def test_finish_in_prepare(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.body, b\"done\")\n\n\nclass Default404Test(WebTestCase):\n    def get_handlers(self):\n        # If there are no handlers at all a default redirect handler gets added.\n        return [(\"/foo\", RequestHandler)]\n\n    def test_404(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 404)\n        self.assertEqual(\n            response.body,\n            b\"<html><title>404: Not Found</title>\"\n            b\"<body>404: Not Found</body></html>\",\n        )\n\n\nclass Custom404Test(WebTestCase):\n    def get_handlers(self):\n        return [(\"/foo\", RequestHandler)]\n\n    def get_app_kwargs(self):\n        class Custom404Handler(RequestHandler):\n            def get(self):\n                self.set_status(404)\n                self.write(\"custom 404 response\")\n\n        return dict(default_handler_class=Custom404Handler)\n\n    def test_404(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 404)\n        self.assertEqual(response.body, b\"custom 404 response\")\n\n\nclass DefaultHandlerArgumentsTest(WebTestCase):\n    def get_handlers(self):\n        return [(\"/foo\", RequestHandler)]\n\n    def get_app_kwargs(self):\n        return dict(\n            default_handler_class=ErrorHandler,\n            default_handler_args=dict(status_code=403),\n        )\n\n    def test_403(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 403)\n\n\nclass HandlerByNameTest(WebTestCase):\n    def get_handlers(self):\n        # All three are equivalent.\n        return [\n            (\"/hello1\", HelloHandler),\n            (\"/hello2\", \"tornado.test.web_test.HelloHandler\"),\n            url(\"/hello3\", \"tornado.test.web_test.HelloHandler\"),\n        ]\n\n    def test_handler_by_name(self):\n        resp = self.fetch(\"/hello1\")\n        self.assertEqual(resp.body, b\"hello\")\n        resp = self.fetch(\"/hello2\")\n        self.assertEqual(resp.body, b\"hello\")\n        resp = self.fetch(\"/hello3\")\n        self.assertEqual(resp.body, b\"hello\")\n\n\nclass StreamingRequestBodyTest(WebTestCase):\n    def get_handlers(self):\n        @stream_request_body\n        class StreamingBodyHandler(RequestHandler):\n            def initialize(self, test):\n                self.test = test\n\n            def prepare(self):\n                self.test.prepared.set_result(None)\n\n            def data_received(self, data):\n                self.test.data.set_result(data)\n\n            def get(self):\n                self.test.finished.set_result(None)\n                self.write({})\n\n        @stream_request_body\n        class EarlyReturnHandler(RequestHandler):\n            def prepare(self):\n                # If we finish the response in prepare, it won't continue to\n                # the (non-existent) data_received.\n                raise HTTPError(401)\n\n        @stream_request_body\n        class CloseDetectionHandler(RequestHandler):\n            def initialize(self, test):\n                self.test = test\n\n            def on_connection_close(self):\n                super(CloseDetectionHandler, self).on_connection_close()\n                self.test.close_future.set_result(None)\n\n        return [\n            (\"/stream_body\", StreamingBodyHandler, dict(test=self)),\n            (\"/early_return\", EarlyReturnHandler),\n            (\"/close_detection\", CloseDetectionHandler, dict(test=self)),\n        ]\n\n    def connect(self, url, connection_close):\n        # Use a raw connection so we can control the sending of data.\n        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)\n        s.connect((\"127.0.0.1\", self.get_http_port()))\n        stream = IOStream(s)\n        stream.write(b\"GET \" + url + b\" HTTP/1.1\\r\\n\")\n        if connection_close:\n            stream.write(b\"Connection: close\\r\\n\")\n        stream.write(b\"Transfer-Encoding: chunked\\r\\n\\r\\n\")\n        return stream\n\n    @gen_test\n    def test_streaming_body(self):\n        self.prepared = Future()  # type: Future[None]\n        self.data = Future()  # type: Future[bytes]\n        self.finished = Future()  # type: Future[None]\n\n        stream = self.connect(b\"/stream_body\", connection_close=True)\n        yield self.prepared\n        stream.write(b\"4\\r\\nasdf\\r\\n\")\n        # Ensure the first chunk is received before we send the second.\n        data = yield self.data\n        self.assertEqual(data, b\"asdf\")\n        self.data = Future()\n        stream.write(b\"4\\r\\nqwer\\r\\n\")\n        data = yield self.data\n        self.assertEqual(data, b\"qwer\")\n        stream.write(b\"0\\r\\n\\r\\n\")\n        yield self.finished\n        data = yield stream.read_until_close()\n        # This would ideally use an HTTP1Connection to read the response.\n        self.assertTrue(data.endswith(b\"{}\"))\n        stream.close()\n\n    @gen_test\n    def test_early_return(self):\n        stream = self.connect(b\"/early_return\", connection_close=False)\n        data = yield stream.read_until_close()\n        self.assertTrue(data.startswith(b\"HTTP/1.1 401\"))\n\n    @gen_test\n    def test_early_return_with_data(self):\n        stream = self.connect(b\"/early_return\", connection_close=False)\n        stream.write(b\"4\\r\\nasdf\\r\\n\")\n        data = yield stream.read_until_close()\n        self.assertTrue(data.startswith(b\"HTTP/1.1 401\"))\n\n    @gen_test\n    def test_close_during_upload(self):\n        self.close_future = Future()  # type: Future[None]\n        stream = self.connect(b\"/close_detection\", connection_close=False)\n        stream.close()\n        yield self.close_future\n\n\n# Each method in this handler returns a yieldable object and yields to the\n# IOLoop so the future is not immediately ready.  Ensure that the\n# yieldables are respected and no method is called before the previous\n# one has completed.\n@stream_request_body\nclass BaseFlowControlHandler(RequestHandler):\n    def initialize(self, test):\n        self.test = test\n        self.method = None\n        self.methods = []  # type: typing.List[str]\n\n    @contextlib.contextmanager\n    def in_method(self, method):\n        if self.method is not None:\n            self.test.fail(\"entered method %s while in %s\" % (method, self.method))\n        self.method = method\n        self.methods.append(method)\n        try:\n            yield\n        finally:\n            self.method = None\n\n    @gen.coroutine\n    def prepare(self):\n        # Note that asynchronous prepare() does not block data_received,\n        # so we don't use in_method here.\n        self.methods.append(\"prepare\")\n        yield gen.moment\n\n    @gen.coroutine\n    def post(self):\n        with self.in_method(\"post\"):\n            yield gen.moment\n        self.write(dict(methods=self.methods))\n\n\nclass BaseStreamingRequestFlowControlTest(object):\n    def get_httpserver_options(self):\n        # Use a small chunk size so flow control is relevant even though\n        # all the data arrives at once.\n        return dict(chunk_size=10, decompress_request=True)\n\n    def get_http_client(self):\n        # simple_httpclient only: curl doesn't support body_producer.\n        return SimpleAsyncHTTPClient()\n\n    # Test all the slightly different code paths for fixed, chunked, etc bodies.\n    def test_flow_control_fixed_body(self):\n        response = self.fetch(\"/\", body=\"abcdefghijklmnopqrstuvwxyz\", method=\"POST\")\n        response.rethrow()\n        self.assertEqual(\n            json_decode(response.body),\n            dict(\n                methods=[\n                    \"prepare\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"post\",\n                ]\n            ),\n        )\n\n    def test_flow_control_chunked_body(self):\n        chunks = [b\"abcd\", b\"efgh\", b\"ijkl\"]\n\n        @gen.coroutine\n        def body_producer(write):\n            for i in chunks:\n                yield write(i)\n\n        response = self.fetch(\"/\", body_producer=body_producer, method=\"POST\")\n        response.rethrow()\n        self.assertEqual(\n            json_decode(response.body),\n            dict(\n                methods=[\n                    \"prepare\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"post\",\n                ]\n            ),\n        )\n\n    def test_flow_control_compressed_body(self):\n        bytesio = BytesIO()\n        gzip_file = gzip.GzipFile(mode=\"w\", fileobj=bytesio)\n        gzip_file.write(b\"abcdefghijklmnopqrstuvwxyz\")\n        gzip_file.close()\n        compressed_body = bytesio.getvalue()\n        response = self.fetch(\n            \"/\",\n            body=compressed_body,\n            method=\"POST\",\n            headers={\"Content-Encoding\": \"gzip\"},\n        )\n        response.rethrow()\n        self.assertEqual(\n            json_decode(response.body),\n            dict(\n                methods=[\n                    \"prepare\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"post\",\n                ]\n            ),\n        )\n\n\nclass DecoratedStreamingRequestFlowControlTest(\n    BaseStreamingRequestFlowControlTest, WebTestCase\n):\n    def get_handlers(self):\n        class DecoratedFlowControlHandler(BaseFlowControlHandler):\n            @gen.coroutine\n            def data_received(self, data):\n                with self.in_method(\"data_received\"):\n                    yield gen.moment\n\n        return [(\"/\", DecoratedFlowControlHandler, dict(test=self))]\n\n\nclass NativeStreamingRequestFlowControlTest(\n    BaseStreamingRequestFlowControlTest, WebTestCase\n):\n    def get_handlers(self):\n        class NativeFlowControlHandler(BaseFlowControlHandler):\n            async def data_received(self, data):\n                with self.in_method(\"data_received\"):\n                    import asyncio\n\n                    await asyncio.sleep(0)\n\n        return [(\"/\", NativeFlowControlHandler, dict(test=self))]\n\n\nclass IncorrectContentLengthTest(SimpleHandlerTestCase):\n    def get_handlers(self):\n        test = self\n        self.server_error = None\n\n        # Manually set a content-length that doesn't match the actual content.\n        class TooHigh(RequestHandler):\n            def get(self):\n                self.set_header(\"Content-Length\", \"42\")\n                try:\n                    self.finish(\"ok\")\n                except Exception as e:\n                    test.server_error = e\n                    raise\n\n        class TooLow(RequestHandler):\n            def get(self):\n                self.set_header(\"Content-Length\", \"2\")\n                try:\n                    self.finish(\"hello\")\n                except Exception as e:\n                    test.server_error = e\n                    raise\n\n        return [(\"/high\", TooHigh), (\"/low\", TooLow)]\n\n    def test_content_length_too_high(self):\n        # When the content-length is too high, the connection is simply\n        # closed without completing the response.  An error is logged on\n        # the server.\n        with ExpectLog(app_log, \"(Uncaught exception|Exception in callback)\"):\n            with ExpectLog(\n                gen_log,\n                \"(Cannot send error response after headers written\"\n                \"|Failed to flush partial response)\",\n            ):\n                with self.assertRaises(HTTPClientError):\n                    self.fetch(\"/high\", raise_error=True)\n        self.assertEqual(\n            str(self.server_error), \"Tried to write 40 bytes less than Content-Length\"\n        )\n\n    def test_content_length_too_low(self):\n        # When the content-length is too low, the connection is closed\n        # without writing the last chunk, so the client never sees the request\n        # complete (which would be a framing error).\n        with ExpectLog(app_log, \"(Uncaught exception|Exception in callback)\"):\n            with ExpectLog(\n                gen_log,\n                \"(Cannot send error response after headers written\"\n                \"|Failed to flush partial response)\",\n            ):\n                with self.assertRaises(HTTPClientError):\n                    self.fetch(\"/low\", raise_error=True)\n        self.assertEqual(\n            str(self.server_error), \"Tried to write more data than Content-Length\"\n        )\n\n\nclass ClientCloseTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            if self.request.version.startswith(\"HTTP/1\"):\n                # Simulate a connection closed by the client during\n                # request processing.  The client will see an error, but the\n                # server should respond gracefully (without logging errors\n                # because we were unable to write out as many bytes as\n                # Content-Length said we would)\n                self.request.connection.stream.close()\n                self.write(\"hello\")\n            else:\n                # TODO: add a HTTP2-compatible version of this test.\n                self.write(\"requires HTTP/1.x\")\n\n    def test_client_close(self):\n        with self.assertRaises((HTTPClientError, unittest.SkipTest)):\n            response = self.fetch(\"/\", raise_error=True)\n            if response.body == b\"requires HTTP/1.x\":\n                self.skipTest(\"requires HTTP/1.x\")\n            self.assertEqual(response.code, 599)\n\n\nclass SignedValueTest(unittest.TestCase):\n    SECRET = \"It's a secret to everybody\"\n    SECRET_DICT = {0: \"asdfbasdf\", 1: \"12312312\", 2: \"2342342\"}\n\n    def past(self):\n        return self.present() - 86400 * 32\n\n    def present(self):\n        return 1300000000\n\n    def test_known_values(self):\n        signed_v1 = create_signed_value(\n            SignedValueTest.SECRET, \"key\", \"value\", version=1, clock=self.present\n        )\n        self.assertEqual(\n            signed_v1, b\"dmFsdWU=|1300000000|31c934969f53e48164c50768b40cbd7e2daaaa4f\"\n        )\n\n        signed_v2 = create_signed_value(\n            SignedValueTest.SECRET, \"key\", \"value\", version=2, clock=self.present\n        )\n        self.assertEqual(\n            signed_v2,\n            b\"2|1:0|10:1300000000|3:key|8:dmFsdWU=|\"\n            b\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\",\n        )\n\n        signed_default = create_signed_value(\n            SignedValueTest.SECRET, \"key\", \"value\", clock=self.present\n        )\n        self.assertEqual(signed_default, signed_v2)\n\n        decoded_v1 = decode_signed_value(\n            SignedValueTest.SECRET, \"key\", signed_v1, min_version=1, clock=self.present\n        )\n        self.assertEqual(decoded_v1, b\"value\")\n\n        decoded_v2 = decode_signed_value(\n            SignedValueTest.SECRET, \"key\", signed_v2, min_version=2, clock=self.present\n        )\n        self.assertEqual(decoded_v2, b\"value\")\n\n    def test_name_swap(self):\n        signed1 = create_signed_value(\n            SignedValueTest.SECRET, \"key1\", \"value\", clock=self.present\n        )\n        signed2 = create_signed_value(\n            SignedValueTest.SECRET, \"key2\", \"value\", clock=self.present\n        )\n        # Try decoding each string with the other's \"name\"\n        decoded1 = decode_signed_value(\n            SignedValueTest.SECRET, \"key2\", signed1, clock=self.present\n        )\n        self.assertIs(decoded1, None)\n        decoded2 = decode_signed_value(\n            SignedValueTest.SECRET, \"key1\", signed2, clock=self.present\n        )\n        self.assertIs(decoded2, None)\n\n    def test_expired(self):\n        signed = create_signed_value(\n            SignedValueTest.SECRET, \"key1\", \"value\", clock=self.past\n        )\n        decoded_past = decode_signed_value(\n            SignedValueTest.SECRET, \"key1\", signed, clock=self.past\n        )\n        self.assertEqual(decoded_past, b\"value\")\n        decoded_present = decode_signed_value(\n            SignedValueTest.SECRET, \"key1\", signed, clock=self.present\n        )\n        self.assertIs(decoded_present, None)\n\n    def test_payload_tampering(self):\n        # These cookies are variants of the one in test_known_values.\n        sig = \"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\"\n\n        def validate(prefix):\n            return b\"value\" == decode_signed_value(\n                SignedValueTest.SECRET, \"key\", prefix + sig, clock=self.present\n            )\n\n        self.assertTrue(validate(\"2|1:0|10:1300000000|3:key|8:dmFsdWU=|\"))\n        # Change key version\n        self.assertFalse(validate(\"2|1:1|10:1300000000|3:key|8:dmFsdWU=|\"))\n        # length mismatch (field too short)\n        self.assertFalse(validate(\"2|1:0|10:130000000|3:key|8:dmFsdWU=|\"))\n        # length mismatch (field too long)\n        self.assertFalse(validate(\"2|1:0|10:1300000000|3:keey|8:dmFsdWU=|\"))\n\n    def test_signature_tampering(self):\n        prefix = \"2|1:0|10:1300000000|3:key|8:dmFsdWU=|\"\n\n        def validate(sig):\n            return b\"value\" == decode_signed_value(\n                SignedValueTest.SECRET, \"key\", prefix + sig, clock=self.present\n            )\n\n        self.assertTrue(\n            validate(\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\")\n        )\n        # All zeros\n        self.assertFalse(validate(\"0\" * 32))\n        # Change one character\n        self.assertFalse(\n            validate(\"4d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\")\n        )\n        # Change another character\n        self.assertFalse(\n            validate(\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e153\")\n        )\n        # Truncate\n        self.assertFalse(\n            validate(\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e15\")\n        )\n        # Lengthen\n        self.assertFalse(\n            validate(\n                \"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e1538\"\n            )\n        )\n\n    def test_non_ascii(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET, \"key\", value, clock=self.present\n        )\n        decoded = decode_signed_value(\n            SignedValueTest.SECRET, \"key\", signed, clock=self.present\n        )\n        self.assertEqual(value, decoded)\n\n    def test_key_versioning_read_write_default_key(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=0\n        )\n        decoded = decode_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", signed, clock=self.present\n        )\n        self.assertEqual(value, decoded)\n\n    def test_key_versioning_read_write_non_default_key(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=1\n        )\n        decoded = decode_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", signed, clock=self.present\n        )\n        self.assertEqual(value, decoded)\n\n    def test_key_versioning_invalid_key(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=0\n        )\n        newkeys = SignedValueTest.SECRET_DICT.copy()\n        newkeys.pop(0)\n        decoded = decode_signed_value(newkeys, \"key\", signed, clock=self.present)\n        self.assertEqual(None, decoded)\n\n    def test_key_version_retrieval(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=1\n        )\n        key_version = get_signature_key_version(signed)\n        self.assertEqual(1, key_version)\n\n\nclass XSRFTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            version = int(self.get_argument(\"version\", \"2\"))\n            # This would be a bad idea in a real app, but in this test\n            # it's fine.\n            self.settings[\"xsrf_cookie_version\"] = version\n            self.write(self.xsrf_token)\n\n        def post(self):\n            self.write(\"ok\")\n\n    def get_app_kwargs(self):\n        return dict(xsrf_cookies=True)\n\n    def setUp(self):\n        super(XSRFTest, self).setUp()\n        self.xsrf_token = self.get_token()\n\n    def get_token(self, old_token=None, version=None):\n        if old_token is not None:\n            headers = self.cookie_headers(old_token)\n        else:\n            headers = None\n        response = self.fetch(\n            \"/\" if version is None else (\"/?version=%d\" % version), headers=headers\n        )\n        response.rethrow()\n        return native_str(response.body)\n\n    def cookie_headers(self, token=None):\n        if token is None:\n            token = self.xsrf_token\n        return {\"Cookie\": \"_xsrf=\" + token}\n\n    def test_xsrf_fail_no_token(self):\n        with ExpectLog(gen_log, \".*'_xsrf' argument missing\"):\n            response = self.fetch(\"/\", method=\"POST\", body=b\"\")\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_body_no_cookie(self):\n        with ExpectLog(gen_log, \".*XSRF cookie does not match POST\"):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_argument_invalid_format(self):\n        with ExpectLog(gen_log, \".*'_xsrf' argument has invalid format\"):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                headers=self.cookie_headers(),\n                body=urllib.parse.urlencode(dict(_xsrf=\"3|\")),\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_cookie_invalid_format(self):\n        with ExpectLog(gen_log, \".*XSRF cookie does not match POST\"):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                headers=self.cookie_headers(token=\"3|\"),\n                body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_cookie_no_body(self):\n        with ExpectLog(gen_log, \".*'_xsrf' argument missing\"):\n            response = self.fetch(\n                \"/\", method=\"POST\", body=b\"\", headers=self.cookie_headers()\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_success_short_token(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=urllib.parse.urlencode(dict(_xsrf=\"deadbeef\")),\n            headers=self.cookie_headers(token=\"deadbeef\"),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_non_hex_token(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=urllib.parse.urlencode(dict(_xsrf=\"xoxo\")),\n            headers=self.cookie_headers(token=\"xoxo\"),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_post_body(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            headers=self.cookie_headers(),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_query_string(self):\n        response = self.fetch(\n            \"/?\" + urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            method=\"POST\",\n            body=b\"\",\n            headers=self.cookie_headers(),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_header(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=b\"\",\n            headers=dict(\n                {\"X-Xsrftoken\": self.xsrf_token},  # type: ignore\n                **self.cookie_headers()\n            ),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_distinct_tokens(self):\n        # Every request gets a distinct token.\n        NUM_TOKENS = 10\n        tokens = set()\n        for i in range(NUM_TOKENS):\n            tokens.add(self.get_token())\n        self.assertEqual(len(tokens), NUM_TOKENS)\n\n    def test_cross_user(self):\n        token2 = self.get_token()\n        # Each token can be used to authenticate its own request.\n        for token in (self.xsrf_token, token2):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=token)),\n                headers=self.cookie_headers(token),\n            )\n            self.assertEqual(response.code, 200)\n        # Sending one in the cookie and the other in the body is not allowed.\n        for cookie_token, body_token in (\n            (self.xsrf_token, token2),\n            (token2, self.xsrf_token),\n        ):\n            with ExpectLog(gen_log, \".*XSRF cookie does not match POST\"):\n                response = self.fetch(\n                    \"/\",\n                    method=\"POST\",\n                    body=urllib.parse.urlencode(dict(_xsrf=body_token)),\n                    headers=self.cookie_headers(cookie_token),\n                )\n            self.assertEqual(response.code, 403)\n\n    def test_refresh_token(self):\n        token = self.xsrf_token\n        tokens_seen = set([token])\n        # A user's token is stable over time.  Refreshing the page in one tab\n        # might update the cookie while an older tab still has the old cookie\n        # in its DOM.  Simulate this scenario by passing a constant token\n        # in the body and re-querying for the token.\n        for i in range(5):\n            token = self.get_token(token)\n            # Tokens are encoded uniquely each time\n            tokens_seen.add(token)\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n                headers=self.cookie_headers(token),\n            )\n            self.assertEqual(response.code, 200)\n        self.assertEqual(len(tokens_seen), 6)\n\n    def test_versioning(self):\n        # Version 1 still produces distinct tokens per request.\n        self.assertNotEqual(self.get_token(version=1), self.get_token(version=1))\n\n        # Refreshed v1 tokens are all identical.\n        v1_token = self.get_token(version=1)\n        for i in range(5):\n            self.assertEqual(self.get_token(v1_token, version=1), v1_token)\n\n        # Upgrade to a v2 version of the same token\n        v2_token = self.get_token(v1_token)\n        self.assertNotEqual(v1_token, v2_token)\n        # Each v1 token can map to many v2 tokens.\n        self.assertNotEqual(v2_token, self.get_token(v1_token))\n\n        # The tokens are cross-compatible.\n        for cookie_token, body_token in ((v1_token, v2_token), (v2_token, v1_token)):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=body_token)),\n                headers=self.cookie_headers(cookie_token),\n            )\n            self.assertEqual(response.code, 200)\n\n\nclass XSRFCookieKwargsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.write(self.xsrf_token)\n\n    def get_app_kwargs(self):\n        return dict(\n            xsrf_cookies=True, xsrf_cookie_kwargs=dict(httponly=True, expires_days=2)\n        )\n\n    def test_xsrf_httponly(self):\n        response = self.fetch(\"/\")\n        self.assertIn(\"httponly;\", response.headers[\"Set-Cookie\"].lower())\n        self.assertIn(\"expires=\", response.headers[\"Set-Cookie\"].lower())\n        header = response.headers.get(\"Set-Cookie\")\n        match = re.match(\".*; expires=(?P<expires>.+);.*\", header)\n        assert match is not None\n\n        expires = datetime.datetime.utcnow() + datetime.timedelta(days=2)\n        parsed = email.utils.parsedate(match.groupdict()[\"expires\"])\n        assert parsed is not None\n        header_expires = datetime.datetime(*parsed[:6])\n        self.assertTrue(abs((expires - header_expires).total_seconds()) < 10)\n\n\nclass FinishExceptionTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_status(401)\n            self.set_header(\"WWW-Authenticate\", 'Basic realm=\"something\"')\n            if self.get_argument(\"finish_value\", \"\"):\n                raise Finish(\"authentication required\")\n            else:\n                self.write(\"authentication required\")\n                raise Finish()\n\n    def test_finish_exception(self):\n        for u in [\"/\", \"/?finish_value=1\"]:\n            response = self.fetch(u)\n            self.assertEqual(response.code, 401)\n            self.assertEqual(\n                'Basic realm=\"something\"', response.headers.get(\"WWW-Authenticate\")\n            )\n            self.assertEqual(b\"authentication required\", response.body)\n\n\nclass DecoratorTest(WebTestCase):\n    def get_handlers(self):\n        class RemoveSlashHandler(RequestHandler):\n            @removeslash\n            def get(self):\n                pass\n\n        class AddSlashHandler(RequestHandler):\n            @addslash\n            def get(self):\n                pass\n\n        return [(\"/removeslash/\", RemoveSlashHandler), (\"/addslash\", AddSlashHandler)]\n\n    def test_removeslash(self):\n        response = self.fetch(\"/removeslash/\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/removeslash\")\n\n        response = self.fetch(\"/removeslash/?foo=bar\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/removeslash?foo=bar\")\n\n    def test_addslash(self):\n        response = self.fetch(\"/addslash\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/addslash/\")\n\n        response = self.fetch(\"/addslash?foo=bar\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/addslash/?foo=bar\")\n\n\nclass CacheTest(WebTestCase):\n    def get_handlers(self):\n        class EtagHandler(RequestHandler):\n            def get(self, computed_etag):\n                self.write(computed_etag)\n\n            def compute_etag(self):\n                return self._write_buffer[0]\n\n        return [(\"/etag/(.*)\", EtagHandler)]\n\n    def test_wildcard_etag(self):\n        computed_etag = '\"xyzzy\"'\n        etags = \"*\"\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_strong_etag_match(self):\n        computed_etag = '\"xyzzy\"'\n        etags = '\"xyzzy\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_multiple_strong_etag_match(self):\n        computed_etag = '\"xyzzy1\"'\n        etags = '\"xyzzy1\", \"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_strong_etag_not_match(self):\n        computed_etag = '\"xyzzy\"'\n        etags = '\"xyzzy1\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def test_multiple_strong_etag_not_match(self):\n        computed_etag = '\"xyzzy\"'\n        etags = '\"xyzzy1\", \"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def test_weak_etag_match(self):\n        computed_etag = '\"xyzzy1\"'\n        etags = 'W/\"xyzzy1\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_multiple_weak_etag_match(self):\n        computed_etag = '\"xyzzy2\"'\n        etags = 'W/\"xyzzy1\", W/\"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_weak_etag_not_match(self):\n        computed_etag = '\"xyzzy2\"'\n        etags = 'W/\"xyzzy1\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def test_multiple_weak_etag_not_match(self):\n        computed_etag = '\"xyzzy3\"'\n        etags = 'W/\"xyzzy1\", W/\"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def _test_etag(self, computed_etag, etags, status_code):\n        response = self.fetch(\n            \"/etag/\" + computed_etag, headers={\"If-None-Match\": etags}\n        )\n        self.assertEqual(response.code, status_code)\n\n\nclass RequestSummaryTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            # remote_ip is optional, although it's set by\n            # both HTTPServer and WSGIAdapter.\n            # Clobber it to make sure it doesn't break logging.\n            self.request.remote_ip = None\n            self.finish(self._request_summary())\n\n    def test_missing_remote_ip(self):\n        resp = self.fetch(\"/\")\n        self.assertEqual(resp.body, b\"GET / (None)\")\n\n\nclass HTTPErrorTest(unittest.TestCase):\n    def test_copy(self):\n        e = HTTPError(403, reason=\"Go away\")\n        e2 = copy.copy(e)\n        self.assertIsNot(e, e2)\n        self.assertEqual(e.status_code, e2.status_code)\n        self.assertEqual(e.reason, e2.reason)\n\n\nclass ApplicationTest(AsyncTestCase):\n    def test_listen(self):\n        app = Application([])\n        server = app.listen(0, address=\"127.0.0.1\")\n        server.stop()\n\n\nclass URLSpecReverseTest(unittest.TestCase):\n    def test_reverse(self):\n        self.assertEqual(\"/favicon.ico\", url(r\"/favicon\\.ico\", None).reverse())\n        self.assertEqual(\"/favicon.ico\", url(r\"^/favicon\\.ico$\", None).reverse())\n\n    def test_non_reversible(self):\n        # URLSpecs are non-reversible if they include non-constant\n        # regex features outside capturing groups. Currently, this is\n        # only strictly enforced for backslash-escaped character\n        # classes.\n        paths = [r\"^/api/v\\d+/foo/(\\w+)$\"]\n        for path in paths:\n            # A URLSpec can still be created even if it cannot be reversed.\n            url_spec = url(path, None)\n            try:\n                result = url_spec.reverse()\n                self.fail(\n                    \"did not get expected exception when reversing %s. \"\n                    \"result: %s\" % (path, result)\n                )\n            except ValueError:\n                pass\n\n    def test_reverse_arguments(self):\n        self.assertEqual(\n            \"/api/v1/foo/bar\", url(r\"^/api/v1/foo/(\\w+)$\", None).reverse(\"bar\")\n        )\n\n\nclass RedirectHandlerTest(WebTestCase):\n    def get_handlers(self):\n        return [\n            (\"/src\", WebRedirectHandler, {\"url\": \"/dst\"}),\n            (\"/src2\", WebRedirectHandler, {\"url\": \"/dst2?foo=bar\"}),\n            (r\"/(.*?)/(.*?)/(.*)\", WebRedirectHandler, {\"url\": \"/{1}/{0}/{2}\"}),\n        ]\n\n    def test_basic_redirect(self):\n        response = self.fetch(\"/src\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/dst\")\n\n    def test_redirect_with_argument(self):\n        response = self.fetch(\"/src?foo=bar\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/dst?foo=bar\")\n\n    def test_redirect_with_appending_argument(self):\n        response = self.fetch(\"/src2?foo2=bar2\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/dst2?foo=bar&foo2=bar2\")\n\n    def test_redirect_pattern(self):\n        response = self.fetch(\"/a/b/c\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/b/a/c\")\n",
            "new_file_content": "from tornado.concurrent import Future\nfrom tornado import gen\nfrom tornado.escape import (\n    json_decode,\n    utf8,\n    to_unicode,\n    recursive_unicode,\n    native_str,\n    to_basestring,\n)\nfrom tornado.httpclient import HTTPClientError\nfrom tornado.httputil import format_timestamp\nfrom tornado.iostream import IOStream\nfrom tornado import locale\nfrom tornado.locks import Event\nfrom tornado.log import app_log, gen_log\nfrom tornado.simple_httpclient import SimpleAsyncHTTPClient\nfrom tornado.template import DictLoader\nfrom tornado.testing import AsyncHTTPTestCase, AsyncTestCase, ExpectLog, gen_test\nfrom tornado.util import ObjectDict, unicode_type\nfrom tornado.web import (\n    Application,\n    RequestHandler,\n    StaticFileHandler,\n    RedirectHandler as WebRedirectHandler,\n    HTTPError,\n    MissingArgumentError,\n    ErrorHandler,\n    authenticated,\n    url,\n    _create_signature_v1,\n    create_signed_value,\n    decode_signed_value,\n    get_signature_key_version,\n    UIModule,\n    Finish,\n    stream_request_body,\n    removeslash,\n    addslash,\n    GZipContentEncoding,\n)\n\nimport binascii\nimport contextlib\nimport copy\nimport datetime\nimport email.utils\nimport gzip\nfrom io import BytesIO\nimport itertools\nimport logging\nimport os\nimport re\nimport socket\nimport typing  # noqa: F401\nimport unittest\nimport urllib.parse\n\n\ndef relpath(*a):\n    return os.path.join(os.path.dirname(__file__), *a)\n\n\nclass WebTestCase(AsyncHTTPTestCase):\n    \"\"\"Base class for web tests that also supports WSGI mode.\n\n    Override get_handlers and get_app_kwargs instead of get_app.\n    This class is deprecated since WSGI mode is no longer supported.\n    \"\"\"\n\n    def get_app(self):\n        self.app = Application(self.get_handlers(), **self.get_app_kwargs())\n        return self.app\n\n    def get_handlers(self):\n        raise NotImplementedError()\n\n    def get_app_kwargs(self):\n        return {}\n\n\nclass SimpleHandlerTestCase(WebTestCase):\n    \"\"\"Simplified base class for tests that work with a single handler class.\n\n    To use, define a nested class named ``Handler``.\n    \"\"\"\n\n    def get_handlers(self):\n        return [(\"/\", self.Handler)]\n\n\nclass HelloHandler(RequestHandler):\n    def get(self):\n        self.write(\"hello\")\n\n\nclass CookieTestRequestHandler(RequestHandler):\n    # stub out enough methods to make the secure_cookie functions work\n    def __init__(self, cookie_secret=\"0123456789\", key_version=None):\n        # don't call super.__init__\n        self._cookies = {}  # type: typing.Dict[str, bytes]\n        if key_version is None:\n            self.application = ObjectDict(settings=dict(cookie_secret=cookie_secret))\n        else:\n            self.application = ObjectDict(\n                settings=dict(cookie_secret=cookie_secret, key_version=key_version)\n            )\n\n    def get_cookie(self, name):\n        return self._cookies.get(name)\n\n    def set_cookie(self, name, value, expires_days=None):\n        self._cookies[name] = value\n\n\n# See SignedValueTest below for more.\nclass SecureCookieV1Test(unittest.TestCase):\n    def test_round_trip(self):\n        handler = CookieTestRequestHandler()\n        handler.set_secure_cookie(\"foo\", b\"bar\", version=1)\n        self.assertEqual(handler.get_secure_cookie(\"foo\", min_version=1), b\"bar\")\n\n    def test_cookie_tampering_future_timestamp(self):\n        handler = CookieTestRequestHandler()\n        # this string base64-encodes to '12345678'\n        handler.set_secure_cookie(\"foo\", binascii.a2b_hex(b\"d76df8e7aefc\"), version=1)\n        cookie = handler._cookies[\"foo\"]\n        match = re.match(br\"12345678\\|([0-9]+)\\|([0-9a-f]+)\", cookie)\n        assert match is not None\n        timestamp = match.group(1)\n        sig = match.group(2)\n        self.assertEqual(\n            _create_signature_v1(\n                handler.application.settings[\"cookie_secret\"],\n                \"foo\",\n                \"12345678\",\n                timestamp,\n            ),\n            sig,\n        )\n        # shifting digits from payload to timestamp doesn't alter signature\n        # (this is not desirable behavior, just confirming that that's how it\n        # works)\n        self.assertEqual(\n            _create_signature_v1(\n                handler.application.settings[\"cookie_secret\"],\n                \"foo\",\n                \"1234\",\n                b\"5678\" + timestamp,\n            ),\n            sig,\n        )\n        # tamper with the cookie\n        handler._cookies[\"foo\"] = utf8(\n            \"1234|5678%s|%s\" % (to_basestring(timestamp), to_basestring(sig))\n        )\n        # it gets rejected\n        with ExpectLog(gen_log, \"Cookie timestamp in future\"):\n            self.assertTrue(handler.get_secure_cookie(\"foo\", min_version=1) is None)\n\n    def test_arbitrary_bytes(self):\n        # Secure cookies accept arbitrary data (which is base64 encoded).\n        # Note that normal cookies accept only a subset of ascii.\n        handler = CookieTestRequestHandler()\n        handler.set_secure_cookie(\"foo\", b\"\\xe9\", version=1)\n        self.assertEqual(handler.get_secure_cookie(\"foo\", min_version=1), b\"\\xe9\")\n\n\n# See SignedValueTest below for more.\nclass SecureCookieV2Test(unittest.TestCase):\n    KEY_VERSIONS = {0: \"ajklasdf0ojaisdf\", 1: \"aslkjasaolwkjsdf\"}\n\n    def test_round_trip(self):\n        handler = CookieTestRequestHandler()\n        handler.set_secure_cookie(\"foo\", b\"bar\", version=2)\n        self.assertEqual(handler.get_secure_cookie(\"foo\", min_version=2), b\"bar\")\n\n    def test_key_version_roundtrip(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=0\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        self.assertEqual(handler.get_secure_cookie(\"foo\"), b\"bar\")\n\n    def test_key_version_roundtrip_differing_version(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=1\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        self.assertEqual(handler.get_secure_cookie(\"foo\"), b\"bar\")\n\n    def test_key_version_increment_version(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=0\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        new_handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=1\n        )\n        new_handler._cookies = handler._cookies\n        self.assertEqual(new_handler.get_secure_cookie(\"foo\"), b\"bar\")\n\n    def test_key_version_invalidate_version(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=0\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        new_key_versions = self.KEY_VERSIONS.copy()\n        new_key_versions.pop(0)\n        new_handler = CookieTestRequestHandler(\n            cookie_secret=new_key_versions, key_version=1\n        )\n        new_handler._cookies = handler._cookies\n        self.assertEqual(new_handler.get_secure_cookie(\"foo\"), None)\n\n\nclass FinalReturnTest(WebTestCase):\n    def get_handlers(self):\n        test = self\n\n        class FinishHandler(RequestHandler):\n            @gen.coroutine\n            def get(self):\n                test.final_return = self.finish()\n                yield test.final_return\n\n            @gen.coroutine\n            def post(self):\n                self.write(\"hello,\")\n                yield self.flush()\n                test.final_return = self.finish(\"world\")\n                yield test.final_return\n\n        class RenderHandler(RequestHandler):\n            def create_template_loader(self, path):\n                return DictLoader({\"foo.html\": \"hi\"})\n\n            @gen.coroutine\n            def get(self):\n                test.final_return = self.render(\"foo.html\")\n\n        return [(\"/finish\", FinishHandler), (\"/render\", RenderHandler)]\n\n    def get_app_kwargs(self):\n        return dict(template_path=\"FinalReturnTest\")\n\n    def test_finish_method_return_future(self):\n        response = self.fetch(self.get_url(\"/finish\"))\n        self.assertEqual(response.code, 200)\n        self.assertIsInstance(self.final_return, Future)\n        self.assertTrue(self.final_return.done())\n\n        response = self.fetch(self.get_url(\"/finish\"), method=\"POST\", body=b\"\")\n        self.assertEqual(response.code, 200)\n        self.assertIsInstance(self.final_return, Future)\n        self.assertTrue(self.final_return.done())\n\n    def test_render_method_return_future(self):\n        response = self.fetch(self.get_url(\"/render\"))\n        self.assertEqual(response.code, 200)\n        self.assertIsInstance(self.final_return, Future)\n\n\nclass CookieTest(WebTestCase):\n    def get_handlers(self):\n        class SetCookieHandler(RequestHandler):\n            def get(self):\n                # Try setting cookies with different argument types\n                # to ensure that everything gets encoded correctly\n                self.set_cookie(\"str\", \"asdf\")\n                self.set_cookie(\"unicode\", u\"qwer\")\n                self.set_cookie(\"bytes\", b\"zxcv\")\n\n        class GetCookieHandler(RequestHandler):\n            def get(self):\n                self.write(self.get_cookie(\"foo\", \"default\"))\n\n        class SetCookieDomainHandler(RequestHandler):\n            def get(self):\n                # unicode domain and path arguments shouldn't break things\n                # either (see bug #285)\n                self.set_cookie(\"unicode_args\", \"blah\", domain=u\"foo.com\", path=u\"/foo\")\n\n        class SetCookieSpecialCharHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"equals\", \"a=b\")\n                self.set_cookie(\"semicolon\", \"a;b\")\n                self.set_cookie(\"quote\", 'a\"b')\n\n        class SetCookieOverwriteHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"a\", \"b\", domain=\"example.com\")\n                self.set_cookie(\"c\", \"d\", domain=\"example.com\")\n                # A second call with the same name clobbers the first.\n                # Attributes from the first call are not carried over.\n                self.set_cookie(\"a\", \"e\")\n\n        class SetCookieMaxAgeHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"foo\", \"bar\", max_age=10)\n\n        class SetCookieExpiresDaysHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"foo\", \"bar\", expires_days=10)\n\n        class SetCookieFalsyFlags(RequestHandler):\n            def get(self):\n                self.set_cookie(\"a\", \"1\", secure=True)\n                self.set_cookie(\"b\", \"1\", secure=False)\n                self.set_cookie(\"c\", \"1\", httponly=True)\n                self.set_cookie(\"d\", \"1\", httponly=False)\n\n        return [\n            (\"/set\", SetCookieHandler),\n            (\"/get\", GetCookieHandler),\n            (\"/set_domain\", SetCookieDomainHandler),\n            (\"/special_char\", SetCookieSpecialCharHandler),\n            (\"/set_overwrite\", SetCookieOverwriteHandler),\n            (\"/set_max_age\", SetCookieMaxAgeHandler),\n            (\"/set_expires_days\", SetCookieExpiresDaysHandler),\n            (\"/set_falsy_flags\", SetCookieFalsyFlags),\n        ]\n\n    def test_set_cookie(self):\n        response = self.fetch(\"/set\")\n        self.assertEqual(\n            sorted(response.headers.get_list(\"Set-Cookie\")),\n            [\"bytes=zxcv; Path=/\", \"str=asdf; Path=/\", \"unicode=qwer; Path=/\"],\n        )\n\n    def test_get_cookie(self):\n        response = self.fetch(\"/get\", headers={\"Cookie\": \"foo=bar\"})\n        self.assertEqual(response.body, b\"bar\")\n\n        response = self.fetch(\"/get\", headers={\"Cookie\": 'foo=\"bar\"'})\n        self.assertEqual(response.body, b\"bar\")\n\n        response = self.fetch(\"/get\", headers={\"Cookie\": \"/=exception;\"})\n        self.assertEqual(response.body, b\"default\")\n\n    def test_set_cookie_domain(self):\n        response = self.fetch(\"/set_domain\")\n        self.assertEqual(\n            response.headers.get_list(\"Set-Cookie\"),\n            [\"unicode_args=blah; Domain=foo.com; Path=/foo\"],\n        )\n\n    def test_cookie_special_char(self):\n        response = self.fetch(\"/special_char\")\n        headers = sorted(response.headers.get_list(\"Set-Cookie\"))\n        self.assertEqual(len(headers), 3)\n        self.assertEqual(headers[0], 'equals=\"a=b\"; Path=/')\n        self.assertEqual(headers[1], 'quote=\"a\\\\\"b\"; Path=/')\n        # python 2.7 octal-escapes the semicolon; older versions leave it alone\n        self.assertTrue(\n            headers[2] in ('semicolon=\"a;b\"; Path=/', 'semicolon=\"a\\\\073b\"; Path=/'),\n            headers[2],\n        )\n\n        data = [\n            (\"foo=a=b\", \"a=b\"),\n            ('foo=\"a=b\"', \"a=b\"),\n            ('foo=\"a;b\"', '\"a'),  # even quoted, \";\" is a delimiter\n            (\"foo=a\\\\073b\", \"a\\\\073b\"),  # escapes only decoded in quotes\n            ('foo=\"a\\\\073b\"', \"a;b\"),\n            ('foo=\"a\\\\\"b\"', 'a\"b'),\n        ]\n        for header, expected in data:\n            logging.debug(\"trying %r\", header)\n            response = self.fetch(\"/get\", headers={\"Cookie\": header})\n            self.assertEqual(response.body, utf8(expected))\n\n    def test_set_cookie_overwrite(self):\n        response = self.fetch(\"/set_overwrite\")\n        headers = response.headers.get_list(\"Set-Cookie\")\n        self.assertEqual(\n            sorted(headers), [\"a=e; Path=/\", \"c=d; Domain=example.com; Path=/\"]\n        )\n\n    def test_set_cookie_max_age(self):\n        response = self.fetch(\"/set_max_age\")\n        headers = response.headers.get_list(\"Set-Cookie\")\n        self.assertEqual(sorted(headers), [\"foo=bar; Max-Age=10; Path=/\"])\n\n    def test_set_cookie_expires_days(self):\n        response = self.fetch(\"/set_expires_days\")\n        header = response.headers.get(\"Set-Cookie\")\n        match = re.match(\"foo=bar; expires=(?P<expires>.+); Path=/\", header)\n        assert match is not None\n\n        expires = datetime.datetime.utcnow() + datetime.timedelta(days=10)\n        parsed = email.utils.parsedate(match.groupdict()[\"expires\"])\n        assert parsed is not None\n        header_expires = datetime.datetime(*parsed[:6])\n        self.assertTrue(abs((expires - header_expires).total_seconds()) < 10)\n\n    def test_set_cookie_false_flags(self):\n        response = self.fetch(\"/set_falsy_flags\")\n        headers = sorted(response.headers.get_list(\"Set-Cookie\"))\n        # The secure and httponly headers are capitalized in py35 and\n        # lowercase in older versions.\n        self.assertEqual(headers[0].lower(), \"a=1; path=/; secure\")\n        self.assertEqual(headers[1].lower(), \"b=1; path=/\")\n        self.assertEqual(headers[2].lower(), \"c=1; httponly; path=/\")\n        self.assertEqual(headers[3].lower(), \"d=1; path=/\")\n\n\nclass AuthRedirectRequestHandler(RequestHandler):\n    def initialize(self, login_url):\n        self.login_url = login_url\n\n    def get_login_url(self):\n        return self.login_url\n\n    @authenticated\n    def get(self):\n        # we'll never actually get here because the test doesn't follow redirects\n        self.send_error(500)\n\n\nclass AuthRedirectTest(WebTestCase):\n    def get_handlers(self):\n        return [\n            (\"/relative\", AuthRedirectRequestHandler, dict(login_url=\"/login\")),\n            (\n                \"/absolute\",\n                AuthRedirectRequestHandler,\n                dict(login_url=\"http://example.com/login\"),\n            ),\n        ]\n\n    def test_relative_auth_redirect(self):\n        response = self.fetch(self.get_url(\"/relative\"), follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        self.assertEqual(response.headers[\"Location\"], \"/login?next=%2Frelative\")\n\n    def test_absolute_auth_redirect(self):\n        response = self.fetch(self.get_url(\"/absolute\"), follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        self.assertTrue(\n            re.match(\n                r\"http://example.com/login\\?next=http%3A%2F%2F127.0.0.1%3A[0-9]+%2Fabsolute\",\n                response.headers[\"Location\"],\n            ),\n            response.headers[\"Location\"],\n        )\n\n\nclass ConnectionCloseHandler(RequestHandler):\n    def initialize(self, test):\n        self.test = test\n\n    @gen.coroutine\n    def get(self):\n        self.test.on_handler_waiting()\n        yield self.test.cleanup_event.wait()\n\n    def on_connection_close(self):\n        self.test.on_connection_close()\n\n\nclass ConnectionCloseTest(WebTestCase):\n    def get_handlers(self):\n        self.cleanup_event = Event()\n        return [(\"/\", ConnectionCloseHandler, dict(test=self))]\n\n    def test_connection_close(self):\n        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)\n        s.connect((\"127.0.0.1\", self.get_http_port()))\n        self.stream = IOStream(s)\n        self.stream.write(b\"GET / HTTP/1.0\\r\\n\\r\\n\")\n        self.wait()\n        # Let the hanging coroutine clean up after itself\n        self.cleanup_event.set()\n        self.io_loop.run_sync(lambda: gen.sleep(0))\n\n    def on_handler_waiting(self):\n        logging.debug(\"handler waiting\")\n        self.stream.close()\n\n    def on_connection_close(self):\n        logging.debug(\"connection closed\")\n        self.stop()\n\n\nclass EchoHandler(RequestHandler):\n    def get(self, *path_args):\n        # Type checks: web.py interfaces convert argument values to\n        # unicode strings (by default, but see also decode_argument).\n        # In httpserver.py (i.e. self.request.arguments), they're left\n        # as bytes.  Keys are always native strings.\n        for key in self.request.arguments:\n            if type(key) != str:\n                raise Exception(\"incorrect type for key: %r\" % type(key))\n            for value in self.request.arguments[key]:\n                if type(value) != bytes:\n                    raise Exception(\"incorrect type for value: %r\" % type(value))\n            for value in self.get_arguments(key):\n                if type(value) != unicode_type:\n                    raise Exception(\"incorrect type for value: %r\" % type(value))\n        for arg in path_args:\n            if type(arg) != unicode_type:\n                raise Exception(\"incorrect type for path arg: %r\" % type(arg))\n        self.write(\n            dict(\n                path=self.request.path,\n                path_args=path_args,\n                args=recursive_unicode(self.request.arguments),\n            )\n        )\n\n\nclass RequestEncodingTest(WebTestCase):\n    def get_handlers(self):\n        return [(\"/group/(.*)\", EchoHandler), (\"/slashes/([^/]*)/([^/]*)\", EchoHandler)]\n\n    def fetch_json(self, path):\n        return json_decode(self.fetch(path).body)\n\n    def test_group_question_mark(self):\n        # Ensure that url-encoded question marks are handled properly\n        self.assertEqual(\n            self.fetch_json(\"/group/%3F\"),\n            dict(path=\"/group/%3F\", path_args=[\"?\"], args={}),\n        )\n        self.assertEqual(\n            self.fetch_json(\"/group/%3F?%3F=%3F\"),\n            dict(path=\"/group/%3F\", path_args=[\"?\"], args={\"?\": [\"?\"]}),\n        )\n\n    def test_group_encoding(self):\n        # Path components and query arguments should be decoded the same way\n        self.assertEqual(\n            self.fetch_json(\"/group/%C3%A9?arg=%C3%A9\"),\n            {\n                u\"path\": u\"/group/%C3%A9\",\n                u\"path_args\": [u\"\\u00e9\"],\n                u\"args\": {u\"arg\": [u\"\\u00e9\"]},\n            },\n        )\n\n    def test_slashes(self):\n        # Slashes may be escaped to appear as a single \"directory\" in the path,\n        # but they are then unescaped when passed to the get() method.\n        self.assertEqual(\n            self.fetch_json(\"/slashes/foo/bar\"),\n            dict(path=\"/slashes/foo/bar\", path_args=[\"foo\", \"bar\"], args={}),\n        )\n        self.assertEqual(\n            self.fetch_json(\"/slashes/a%2Fb/c%2Fd\"),\n            dict(path=\"/slashes/a%2Fb/c%2Fd\", path_args=[\"a/b\", \"c/d\"], args={}),\n        )\n\n    def test_error(self):\n        # Percent signs (encoded as %25) should not mess up printf-style\n        # messages in logs\n        with ExpectLog(gen_log, \".*Invalid unicode\"):\n            self.fetch(\"/group/?arg=%25%e9\")\n\n\nclass TypeCheckHandler(RequestHandler):\n    def prepare(self):\n        self.errors = {}  # type: typing.Dict[str, str]\n\n        self.check_type(\"status\", self.get_status(), int)\n\n        # get_argument is an exception from the general rule of using\n        # type str for non-body data mainly for historical reasons.\n        self.check_type(\"argument\", self.get_argument(\"foo\"), unicode_type)\n        self.check_type(\"cookie_key\", list(self.cookies.keys())[0], str)\n        self.check_type(\"cookie_value\", list(self.cookies.values())[0].value, str)\n\n        # Secure cookies return bytes because they can contain arbitrary\n        # data, but regular cookies are native strings.\n        if list(self.cookies.keys()) != [\"asdf\"]:\n            raise Exception(\n                \"unexpected values for cookie keys: %r\" % self.cookies.keys()\n            )\n        self.check_type(\"get_secure_cookie\", self.get_secure_cookie(\"asdf\"), bytes)\n        self.check_type(\"get_cookie\", self.get_cookie(\"asdf\"), str)\n\n        self.check_type(\"xsrf_token\", self.xsrf_token, bytes)\n        self.check_type(\"xsrf_form_html\", self.xsrf_form_html(), str)\n\n        self.check_type(\"reverse_url\", self.reverse_url(\"typecheck\", \"foo\"), str)\n\n        self.check_type(\"request_summary\", self._request_summary(), str)\n\n    def get(self, path_component):\n        # path_component uses type unicode instead of str for consistency\n        # with get_argument()\n        self.check_type(\"path_component\", path_component, unicode_type)\n        self.write(self.errors)\n\n    def post(self, path_component):\n        self.check_type(\"path_component\", path_component, unicode_type)\n        self.write(self.errors)\n\n    def check_type(self, name, obj, expected_type):\n        actual_type = type(obj)\n        if expected_type != actual_type:\n            self.errors[name] = \"expected %s, got %s\" % (expected_type, actual_type)\n\n\nclass DecodeArgHandler(RequestHandler):\n    def decode_argument(self, value, name=None):\n        if type(value) != bytes:\n            raise Exception(\"unexpected type for value: %r\" % type(value))\n        # use self.request.arguments directly to avoid recursion\n        if \"encoding\" in self.request.arguments:\n            return value.decode(to_unicode(self.request.arguments[\"encoding\"][0]))\n        else:\n            return value\n\n    def get(self, arg):\n        def describe(s):\n            if type(s) == bytes:\n                return [\"bytes\", native_str(binascii.b2a_hex(s))]\n            elif type(s) == unicode_type:\n                return [\"unicode\", s]\n            raise Exception(\"unknown type\")\n\n        self.write({\"path\": describe(arg), \"query\": describe(self.get_argument(\"foo\"))})\n\n\nclass LinkifyHandler(RequestHandler):\n    def get(self):\n        self.render(\"linkify.html\", message=\"http://example.com\")\n\n\nclass UIModuleResourceHandler(RequestHandler):\n    def get(self):\n        self.render(\"page.html\", entries=[1, 2])\n\n\nclass OptionalPathHandler(RequestHandler):\n    def get(self, path):\n        self.write({\"path\": path})\n\n\nclass MultiHeaderHandler(RequestHandler):\n    def get(self):\n        self.set_header(\"x-overwrite\", \"1\")\n        self.set_header(\"X-Overwrite\", 2)\n        self.add_header(\"x-multi\", 3)\n        self.add_header(\"X-Multi\", \"4\")\n\n\nclass RedirectHandler(RequestHandler):\n    def get(self):\n        if self.get_argument(\"permanent\", None) is not None:\n            self.redirect(\"/\", permanent=int(self.get_argument(\"permanent\")))\n        elif self.get_argument(\"status\", None) is not None:\n            self.redirect(\"/\", status=int(self.get_argument(\"status\")))\n        else:\n            raise Exception(\"didn't get permanent or status arguments\")\n\n\nclass EmptyFlushCallbackHandler(RequestHandler):\n    @gen.coroutine\n    def get(self):\n        # Ensure that the flush callback is run whether or not there\n        # was any output.  The gen.Task and direct yield forms are\n        # equivalent.\n        yield self.flush()  # \"empty\" flush, but writes headers\n        yield self.flush()  # empty flush\n        self.write(\"o\")\n        yield self.flush()  # flushes the \"o\"\n        yield self.flush()  # empty flush\n        self.finish(\"k\")\n\n\nclass HeaderInjectionHandler(RequestHandler):\n    def get(self):\n        try:\n            self.set_header(\"X-Foo\", \"foo\\r\\nX-Bar: baz\")\n            raise Exception(\"Didn't get expected exception\")\n        except ValueError as e:\n            if \"Unsafe header value\" in str(e):\n                self.finish(b\"ok\")\n            else:\n                raise\n\n\nclass GetArgumentHandler(RequestHandler):\n    def prepare(self):\n        if self.get_argument(\"source\", None) == \"query\":\n            method = self.get_query_argument\n        elif self.get_argument(\"source\", None) == \"body\":\n            method = self.get_body_argument\n        else:\n            method = self.get_argument\n        self.finish(method(\"foo\", \"default\"))\n\n\nclass GetArgumentsHandler(RequestHandler):\n    def prepare(self):\n        self.finish(\n            dict(\n                default=self.get_arguments(\"foo\"),\n                query=self.get_query_arguments(\"foo\"),\n                body=self.get_body_arguments(\"foo\"),\n            )\n        )\n\n\n# This test was shared with wsgi_test.py; now the name is meaningless.\nclass WSGISafeWebTest(WebTestCase):\n    COOKIE_SECRET = \"WebTest.COOKIE_SECRET\"\n\n    def get_app_kwargs(self):\n        loader = DictLoader(\n            {\n                \"linkify.html\": \"{% module linkify(message) %}\",\n                \"page.html\": \"\"\"\\\n<html><head></head><body>\n{% for e in entries %}\n{% module Template(\"entry.html\", entry=e) %}\n{% end %}\n</body></html>\"\"\",\n                \"entry.html\": \"\"\"\\\n{{ set_resources(embedded_css=\".entry { margin-bottom: 1em; }\",\n                 embedded_javascript=\"js_embed()\",\n                 css_files=[\"/base.css\", \"/foo.css\"],\n                 javascript_files=\"/common.js\",\n                 html_head=\"<meta>\",\n                 html_body='<script src=\"/analytics.js\"/>') }}\n<div class=\"entry\">...</div>\"\"\",\n            }\n        )\n        return dict(\n            template_loader=loader,\n            autoescape=\"xhtml_escape\",\n            cookie_secret=self.COOKIE_SECRET,\n        )\n\n    def tearDown(self):\n        super(WSGISafeWebTest, self).tearDown()\n        RequestHandler._template_loaders.clear()\n\n    def get_handlers(self):\n        urls = [\n            url(\"/typecheck/(.*)\", TypeCheckHandler, name=\"typecheck\"),\n            url(\"/decode_arg/(.*)\", DecodeArgHandler, name=\"decode_arg\"),\n            url(\"/decode_arg_kw/(?P<arg>.*)\", DecodeArgHandler),\n            url(\"/linkify\", LinkifyHandler),\n            url(\"/uimodule_resources\", UIModuleResourceHandler),\n            url(\"/optional_path/(.+)?\", OptionalPathHandler),\n            url(\"/multi_header\", MultiHeaderHandler),\n            url(\"/redirect\", RedirectHandler),\n            url(\n                \"/web_redirect_permanent\",\n                WebRedirectHandler,\n                {\"url\": \"/web_redirect_newpath\"},\n            ),\n            url(\n                \"/web_redirect\",\n                WebRedirectHandler,\n                {\"url\": \"/web_redirect_newpath\", \"permanent\": False},\n            ),\n            url(\n                \"//web_redirect_double_slash\",\n                WebRedirectHandler,\n                {\"url\": \"/web_redirect_newpath\"},\n            ),\n            url(\"/header_injection\", HeaderInjectionHandler),\n            url(\"/get_argument\", GetArgumentHandler),\n            url(\"/get_arguments\", GetArgumentsHandler),\n        ]\n        return urls\n\n    def fetch_json(self, *args, **kwargs):\n        response = self.fetch(*args, **kwargs)\n        response.rethrow()\n        return json_decode(response.body)\n\n    def test_types(self):\n        cookie_value = to_unicode(\n            create_signed_value(self.COOKIE_SECRET, \"asdf\", \"qwer\")\n        )\n        response = self.fetch(\n            \"/typecheck/asdf?foo=bar\", headers={\"Cookie\": \"asdf=\" + cookie_value}\n        )\n        data = json_decode(response.body)\n        self.assertEqual(data, {})\n\n        response = self.fetch(\n            \"/typecheck/asdf?foo=bar\",\n            method=\"POST\",\n            headers={\"Cookie\": \"asdf=\" + cookie_value},\n            body=\"foo=bar\",\n        )\n\n    def test_decode_argument(self):\n        # These urls all decode to the same thing\n        urls = [\n            \"/decode_arg/%C3%A9?foo=%C3%A9&encoding=utf-8\",\n            \"/decode_arg/%E9?foo=%E9&encoding=latin1\",\n            \"/decode_arg_kw/%E9?foo=%E9&encoding=latin1\",\n        ]\n        for req_url in urls:\n            response = self.fetch(req_url)\n            response.rethrow()\n            data = json_decode(response.body)\n            self.assertEqual(\n                data,\n                {u\"path\": [u\"unicode\", u\"\\u00e9\"], u\"query\": [u\"unicode\", u\"\\u00e9\"]},\n            )\n\n        response = self.fetch(\"/decode_arg/%C3%A9?foo=%C3%A9\")\n        response.rethrow()\n        data = json_decode(response.body)\n        self.assertEqual(\n            data, {u\"path\": [u\"bytes\", u\"c3a9\"], u\"query\": [u\"bytes\", u\"c3a9\"]}\n        )\n\n    def test_decode_argument_invalid_unicode(self):\n        # test that invalid unicode in URLs causes 400, not 500\n        with ExpectLog(gen_log, \".*Invalid unicode.*\"):\n            response = self.fetch(\"/typecheck/invalid%FF\")\n            self.assertEqual(response.code, 400)\n            response = self.fetch(\"/typecheck/invalid?foo=%FF\")\n            self.assertEqual(response.code, 400)\n\n    def test_decode_argument_plus(self):\n        # These urls are all equivalent.\n        urls = [\n            \"/decode_arg/1%20%2B%201?foo=1%20%2B%201&encoding=utf-8\",\n            \"/decode_arg/1%20+%201?foo=1+%2B+1&encoding=utf-8\",\n        ]\n        for req_url in urls:\n            response = self.fetch(req_url)\n            response.rethrow()\n            data = json_decode(response.body)\n            self.assertEqual(\n                data,\n                {u\"path\": [u\"unicode\", u\"1 + 1\"], u\"query\": [u\"unicode\", u\"1 + 1\"]},\n            )\n\n    def test_reverse_url(self):\n        self.assertEqual(self.app.reverse_url(\"decode_arg\", \"foo\"), \"/decode_arg/foo\")\n        self.assertEqual(self.app.reverse_url(\"decode_arg\", 42), \"/decode_arg/42\")\n        self.assertEqual(self.app.reverse_url(\"decode_arg\", b\"\\xe9\"), \"/decode_arg/%E9\")\n        self.assertEqual(\n            self.app.reverse_url(\"decode_arg\", u\"\\u00e9\"), \"/decode_arg/%C3%A9\"\n        )\n        self.assertEqual(\n            self.app.reverse_url(\"decode_arg\", \"1 + 1\"), \"/decode_arg/1%20%2B%201\"\n        )\n\n    def test_uimodule_unescaped(self):\n        response = self.fetch(\"/linkify\")\n        self.assertEqual(\n            response.body, b'<a href=\"http://example.com\">http://example.com</a>'\n        )\n\n    def test_uimodule_resources(self):\n        response = self.fetch(\"/uimodule_resources\")\n        self.assertEqual(\n            response.body,\n            b\"\"\"\\\n<html><head><link href=\"/base.css\" type=\"text/css\" rel=\"stylesheet\"/><link href=\"/foo.css\" type=\"text/css\" rel=\"stylesheet\"/>\n<style type=\"text/css\">\n.entry { margin-bottom: 1em; }\n</style>\n<meta>\n</head><body>\n\n\n<div class=\"entry\">...</div>\n\n\n<div class=\"entry\">...</div>\n\n<script src=\"/common.js\" type=\"text/javascript\"></script>\n<script type=\"text/javascript\">\n//<![CDATA[\njs_embed()\n//]]>\n</script>\n<script src=\"/analytics.js\"/>\n</body></html>\"\"\",  # noqa: E501\n        )\n\n    def test_optional_path(self):\n        self.assertEqual(self.fetch_json(\"/optional_path/foo\"), {u\"path\": u\"foo\"})\n        self.assertEqual(self.fetch_json(\"/optional_path/\"), {u\"path\": None})\n\n    def test_multi_header(self):\n        response = self.fetch(\"/multi_header\")\n        self.assertEqual(response.headers[\"x-overwrite\"], \"2\")\n        self.assertEqual(response.headers.get_list(\"x-multi\"), [\"3\", \"4\"])\n\n    def test_redirect(self):\n        response = self.fetch(\"/redirect?permanent=1\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        response = self.fetch(\"/redirect?permanent=0\", follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        response = self.fetch(\"/redirect?status=307\", follow_redirects=False)\n        self.assertEqual(response.code, 307)\n\n    def test_web_redirect(self):\n        response = self.fetch(\"/web_redirect_permanent\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/web_redirect_newpath\")\n        response = self.fetch(\"/web_redirect\", follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        self.assertEqual(response.headers[\"Location\"], \"/web_redirect_newpath\")\n\n    def test_web_redirect_double_slash(self):\n        response = self.fetch(\"//web_redirect_double_slash\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/web_redirect_newpath\")\n\n    def test_header_injection(self):\n        response = self.fetch(\"/header_injection\")\n        self.assertEqual(response.body, b\"ok\")\n\n    def test_get_argument(self):\n        response = self.fetch(\"/get_argument?foo=bar\")\n        self.assertEqual(response.body, b\"bar\")\n        response = self.fetch(\"/get_argument?foo=\")\n        self.assertEqual(response.body, b\"\")\n        response = self.fetch(\"/get_argument\")\n        self.assertEqual(response.body, b\"default\")\n\n        # Test merging of query and body arguments.\n        # In singular form, body arguments take precedence over query arguments.\n        body = urllib.parse.urlencode(dict(foo=\"hello\"))\n        response = self.fetch(\"/get_argument?foo=bar\", method=\"POST\", body=body)\n        self.assertEqual(response.body, b\"hello\")\n        # In plural methods they are merged.\n        response = self.fetch(\"/get_arguments?foo=bar\", method=\"POST\", body=body)\n        self.assertEqual(\n            json_decode(response.body),\n            dict(default=[\"bar\", \"hello\"], query=[\"bar\"], body=[\"hello\"]),\n        )\n\n    def test_get_query_arguments(self):\n        # send as a post so we can ensure the separation between query\n        # string and body arguments.\n        body = urllib.parse.urlencode(dict(foo=\"hello\"))\n        response = self.fetch(\n            \"/get_argument?source=query&foo=bar\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"bar\")\n        response = self.fetch(\n            \"/get_argument?source=query&foo=\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"\")\n        response = self.fetch(\"/get_argument?source=query\", method=\"POST\", body=body)\n        self.assertEqual(response.body, b\"default\")\n\n    def test_get_body_arguments(self):\n        body = urllib.parse.urlencode(dict(foo=\"bar\"))\n        response = self.fetch(\n            \"/get_argument?source=body&foo=hello\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"bar\")\n\n        body = urllib.parse.urlencode(dict(foo=\"\"))\n        response = self.fetch(\n            \"/get_argument?source=body&foo=hello\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"\")\n\n        body = urllib.parse.urlencode(dict())\n        response = self.fetch(\n            \"/get_argument?source=body&foo=hello\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"default\")\n\n    def test_no_gzip(self):\n        response = self.fetch(\"/get_argument\")\n        self.assertNotIn(\"Accept-Encoding\", response.headers.get(\"Vary\", \"\"))\n        self.assertNotIn(\"gzip\", response.headers.get(\"Content-Encoding\", \"\"))\n\n\nclass NonWSGIWebTests(WebTestCase):\n    def get_handlers(self):\n        return [(\"/empty_flush\", EmptyFlushCallbackHandler)]\n\n    def test_empty_flush(self):\n        response = self.fetch(\"/empty_flush\")\n        self.assertEqual(response.body, b\"ok\")\n\n\nclass ErrorResponseTest(WebTestCase):\n    def get_handlers(self):\n        class DefaultHandler(RequestHandler):\n            def get(self):\n                if self.get_argument(\"status\", None):\n                    raise HTTPError(int(self.get_argument(\"status\")))\n                1 / 0\n\n        class WriteErrorHandler(RequestHandler):\n            def get(self):\n                if self.get_argument(\"status\", None):\n                    self.send_error(int(self.get_argument(\"status\")))\n                else:\n                    1 / 0\n\n            def write_error(self, status_code, **kwargs):\n                self.set_header(\"Content-Type\", \"text/plain\")\n                if \"exc_info\" in kwargs:\n                    self.write(\"Exception: %s\" % kwargs[\"exc_info\"][0].__name__)\n                else:\n                    self.write(\"Status: %d\" % status_code)\n\n        class FailedWriteErrorHandler(RequestHandler):\n            def get(self):\n                1 / 0\n\n            def write_error(self, status_code, **kwargs):\n                raise Exception(\"exception in write_error\")\n\n        return [\n            url(\"/default\", DefaultHandler),\n            url(\"/write_error\", WriteErrorHandler),\n            url(\"/failed_write_error\", FailedWriteErrorHandler),\n        ]\n\n    def test_default(self):\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/default\")\n            self.assertEqual(response.code, 500)\n            self.assertTrue(b\"500: Internal Server Error\" in response.body)\n\n            response = self.fetch(\"/default?status=503\")\n            self.assertEqual(response.code, 503)\n            self.assertTrue(b\"503: Service Unavailable\" in response.body)\n\n            response = self.fetch(\"/default?status=435\")\n            self.assertEqual(response.code, 435)\n            self.assertTrue(b\"435: Unknown\" in response.body)\n\n    def test_write_error(self):\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/write_error\")\n            self.assertEqual(response.code, 500)\n            self.assertEqual(b\"Exception: ZeroDivisionError\", response.body)\n\n            response = self.fetch(\"/write_error?status=503\")\n            self.assertEqual(response.code, 503)\n            self.assertEqual(b\"Status: 503\", response.body)\n\n    def test_failed_write_error(self):\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/failed_write_error\")\n            self.assertEqual(response.code, 500)\n            self.assertEqual(b\"\", response.body)\n\n\nclass StaticFileTest(WebTestCase):\n    # The expected MD5 hash of robots.txt, used in tests that call\n    # StaticFileHandler.get_version\n    robots_txt_hash = b\"f71d20196d4caf35b6a670db8c70b03d\"\n    static_dir = os.path.join(os.path.dirname(__file__), \"static\")\n\n    def get_handlers(self):\n        class StaticUrlHandler(RequestHandler):\n            def get(self, path):\n                with_v = int(self.get_argument(\"include_version\", 1))\n                self.write(self.static_url(path, include_version=with_v))\n\n        class AbsoluteStaticUrlHandler(StaticUrlHandler):\n            include_host = True\n\n        class OverrideStaticUrlHandler(RequestHandler):\n            def get(self, path):\n                do_include = bool(self.get_argument(\"include_host\"))\n                self.include_host = not do_include\n\n                regular_url = self.static_url(path)\n                override_url = self.static_url(path, include_host=do_include)\n                if override_url == regular_url:\n                    return self.write(str(False))\n\n                protocol = self.request.protocol + \"://\"\n                protocol_length = len(protocol)\n                check_regular = regular_url.find(protocol, 0, protocol_length)\n                check_override = override_url.find(protocol, 0, protocol_length)\n\n                if do_include:\n                    result = check_override == 0 and check_regular == -1\n                else:\n                    result = check_override == -1 and check_regular == 0\n                self.write(str(result))\n\n        return [\n            (\"/static_url/(.*)\", StaticUrlHandler),\n            (\"/abs_static_url/(.*)\", AbsoluteStaticUrlHandler),\n            (\"/override_static_url/(.*)\", OverrideStaticUrlHandler),\n            (\"/root_static/(.*)\", StaticFileHandler, dict(path=\"/\")),\n        ]\n\n    def get_app_kwargs(self):\n        return dict(static_path=relpath(\"static\"))\n\n    def test_static_files(self):\n        response = self.fetch(\"/robots.txt\")\n        self.assertTrue(b\"Disallow: /\" in response.body)\n\n        response = self.fetch(\"/static/robots.txt\")\n        self.assertTrue(b\"Disallow: /\" in response.body)\n        self.assertEqual(response.headers.get(\"Content-Type\"), \"text/plain\")\n\n    def test_static_compressed_files(self):\n        response = self.fetch(\"/static/sample.xml.gz\")\n        self.assertEqual(response.headers.get(\"Content-Type\"), \"application/gzip\")\n        response = self.fetch(\"/static/sample.xml.bz2\")\n        self.assertEqual(\n            response.headers.get(\"Content-Type\"), \"application/octet-stream\"\n        )\n        # make sure the uncompressed file still has the correct type\n        response = self.fetch(\"/static/sample.xml\")\n        self.assertTrue(\n            response.headers.get(\"Content-Type\") in set((\"text/xml\", \"application/xml\"))\n        )\n\n    def test_static_url(self):\n        response = self.fetch(\"/static_url/robots.txt\")\n        self.assertEqual(response.body, b\"/static/robots.txt?v=\" + self.robots_txt_hash)\n\n    def test_absolute_static_url(self):\n        response = self.fetch(\"/abs_static_url/robots.txt\")\n        self.assertEqual(\n            response.body,\n            (utf8(self.get_url(\"/\")) + b\"static/robots.txt?v=\" + self.robots_txt_hash),\n        )\n\n    def test_relative_version_exclusion(self):\n        response = self.fetch(\"/static_url/robots.txt?include_version=0\")\n        self.assertEqual(response.body, b\"/static/robots.txt\")\n\n    def test_absolute_version_exclusion(self):\n        response = self.fetch(\"/abs_static_url/robots.txt?include_version=0\")\n        self.assertEqual(response.body, utf8(self.get_url(\"/\") + \"static/robots.txt\"))\n\n    def test_include_host_override(self):\n        self._trigger_include_host_check(False)\n        self._trigger_include_host_check(True)\n\n    def _trigger_include_host_check(self, include_host):\n        path = \"/override_static_url/robots.txt?include_host=%s\"\n        response = self.fetch(path % int(include_host))\n        self.assertEqual(response.body, utf8(str(True)))\n\n    def get_and_head(self, *args, **kwargs):\n        \"\"\"Performs a GET and HEAD request and returns the GET response.\n\n        Fails if any ``Content-*`` headers returned by the two requests\n        differ.\n        \"\"\"\n        head_response = self.fetch(*args, method=\"HEAD\", **kwargs)\n        get_response = self.fetch(*args, method=\"GET\", **kwargs)\n        content_headers = set()\n        for h in itertools.chain(head_response.headers, get_response.headers):\n            if h.startswith(\"Content-\"):\n                content_headers.add(h)\n        for h in content_headers:\n            self.assertEqual(\n                head_response.headers.get(h),\n                get_response.headers.get(h),\n                \"%s differs between GET (%s) and HEAD (%s)\"\n                % (h, head_response.headers.get(h), get_response.headers.get(h)),\n            )\n        return get_response\n\n    def test_static_304_if_modified_since(self):\n        response1 = self.get_and_head(\"/static/robots.txt\")\n        response2 = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": response1.headers[\"Last-Modified\"]},\n        )\n        self.assertEqual(response2.code, 304)\n        self.assertTrue(\"Content-Length\" not in response2.headers)\n        self.assertTrue(\"Last-Modified\" not in response2.headers)\n\n    def test_static_304_if_none_match(self):\n        response1 = self.get_and_head(\"/static/robots.txt\")\n        response2 = self.get_and_head(\n            \"/static/robots.txt\", headers={\"If-None-Match\": response1.headers[\"Etag\"]}\n        )\n        self.assertEqual(response2.code, 304)\n\n    def test_static_304_etag_modified_bug(self):\n        response1 = self.get_and_head(\"/static/robots.txt\")\n        response2 = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\n                \"If-None-Match\": '\"MISMATCH\"',\n                \"If-Modified-Since\": response1.headers[\"Last-Modified\"],\n            },\n        )\n        self.assertEqual(response2.code, 200)\n\n    def test_static_if_modified_since_pre_epoch(self):\n        # On windows, the functions that work with time_t do not accept\n        # negative values, and at least one client (processing.js) seems\n        # to use if-modified-since 1/1/1960 as a cache-busting technique.\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": \"Fri, 01 Jan 1960 00:00:00 GMT\"},\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_static_if_modified_since_time_zone(self):\n        # Instead of the value from Last-Modified, make requests with times\n        # chosen just before and after the known modification time\n        # of the file to ensure that the right time zone is being used\n        # when parsing If-Modified-Since.\n        stat = os.stat(relpath(\"static/robots.txt\"))\n\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": format_timestamp(stat.st_mtime - 1)},\n        )\n        self.assertEqual(response.code, 200)\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": format_timestamp(stat.st_mtime + 1)},\n        )\n        self.assertEqual(response.code, 304)\n\n    def test_static_etag(self):\n        response = self.get_and_head(\"/static/robots.txt\")\n        self.assertEqual(\n            utf8(response.headers.get(\"Etag\")), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_with_range(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=0-9\"}\n        )\n        self.assertEqual(response.code, 206)\n        self.assertEqual(response.body, b\"User-agent\")\n        self.assertEqual(\n            utf8(response.headers.get(\"Etag\")), b'\"' + self.robots_txt_hash + b'\"'\n        )\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"10\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 0-9/26\")\n\n    def test_static_with_range_full_file(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=0-\"}\n        )\n        # Note: Chrome refuses to play audio if it gets an HTTP 206 in response\n        # to ``Range: bytes=0-`` :(\n        self.assertEqual(response.code, 200)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"26\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), None)\n\n    def test_static_with_range_full_past_end(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=0-10000000\"}\n        )\n        self.assertEqual(response.code, 200)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"26\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), None)\n\n    def test_static_with_range_partial_past_end(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=1-10000000\"}\n        )\n        self.assertEqual(response.code, 206)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()[1:]))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"25\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 1-25/26\")\n\n    def test_static_with_range_end_edge(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=22-\"}\n        )\n        self.assertEqual(response.body, b\": /\\n\")\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"4\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 22-25/26\")\n\n    def test_static_with_range_neg_end(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=-4\"}\n        )\n        self.assertEqual(response.body, b\": /\\n\")\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"4\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 22-25/26\")\n\n    def test_static_with_range_neg_past_start(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=-1000000\"}\n        )\n        self.assertEqual(response.code, 200)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"26\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), None)\n\n    def test_static_invalid_range(self):\n        response = self.get_and_head(\"/static/robots.txt\", headers={\"Range\": \"asdf\"})\n        self.assertEqual(response.code, 200)\n\n    def test_static_unsatisfiable_range_zero_suffix(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=-0\"}\n        )\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\n        self.assertEqual(response.code, 416)\n\n    def test_static_unsatisfiable_range_invalid_start(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=26\"}\n        )\n        self.assertEqual(response.code, 416)\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\n\n    def test_static_unsatisfiable_range_end_less_than_start(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=10-3\"}\n        )\n        self.assertEqual(response.code, 416)\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\n\n    def test_static_head(self):\n        response = self.fetch(\"/static/robots.txt\", method=\"HEAD\")\n        self.assertEqual(response.code, 200)\n        # No body was returned, but we did get the right content length.\n        self.assertEqual(response.body, b\"\")\n        self.assertEqual(response.headers[\"Content-Length\"], \"26\")\n        self.assertEqual(\n            utf8(response.headers[\"Etag\"]), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_head_range(self):\n        response = self.fetch(\n            \"/static/robots.txt\", method=\"HEAD\", headers={\"Range\": \"bytes=1-4\"}\n        )\n        self.assertEqual(response.code, 206)\n        self.assertEqual(response.body, b\"\")\n        self.assertEqual(response.headers[\"Content-Length\"], \"4\")\n        self.assertEqual(\n            utf8(response.headers[\"Etag\"]), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_range_if_none_match(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\n                \"Range\": \"bytes=1-4\",\n                \"If-None-Match\": b'\"' + self.robots_txt_hash + b'\"',\n            },\n        )\n        self.assertEqual(response.code, 304)\n        self.assertEqual(response.body, b\"\")\n        self.assertTrue(\"Content-Length\" not in response.headers)\n        self.assertEqual(\n            utf8(response.headers[\"Etag\"]), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_404(self):\n        response = self.get_and_head(\"/static/blarg\")\n        self.assertEqual(response.code, 404)\n\n    def test_path_traversal_protection(self):\n        # curl_httpclient processes \"..\" on the client side, so we\n        # must test this with simple_httpclient.\n        self.http_client.close()\n        self.http_client = SimpleAsyncHTTPClient()\n        with ExpectLog(gen_log, \".*not in root static directory\"):\n            response = self.get_and_head(\"/static/../static_foo.txt\")\n        # Attempted path traversal should result in 403, not 200\n        # (which means the check failed and the file was served)\n        # or 404 (which means that the file didn't exist and\n        # is probably a packaging error).\n        self.assertEqual(response.code, 403)\n\n    @unittest.skipIf(os.name != \"posix\", \"non-posix OS\")\n    def test_root_static_path(self):\n        # Sometimes people set the StaticFileHandler's path to '/'\n        # to disable Tornado's path validation (in conjunction with\n        # their own validation in get_absolute_path). Make sure\n        # that the stricter validation in 4.2.1 doesn't break them.\n        path = os.path.join(\n            os.path.dirname(os.path.abspath(__file__)), \"static/robots.txt\"\n        )\n        response = self.get_and_head(\"/root_static\" + urllib.parse.quote(path))\n        self.assertEqual(response.code, 200)\n\n\nclass StaticDefaultFilenameTest(WebTestCase):\n    def get_app_kwargs(self):\n        return dict(\n            static_path=relpath(\"static\"),\n            static_handler_args=dict(default_filename=\"index.html\"),\n        )\n\n    def get_handlers(self):\n        return []\n\n    def test_static_default_filename(self):\n        response = self.fetch(\"/static/dir/\", follow_redirects=False)\n        self.assertEqual(response.code, 200)\n        self.assertEqual(b\"this is the index\\n\", response.body)\n\n    def test_static_default_redirect(self):\n        response = self.fetch(\"/static/dir\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertTrue(response.headers[\"Location\"].endswith(\"/static/dir/\"))\n\n\nclass StaticFileWithPathTest(WebTestCase):\n    def get_app_kwargs(self):\n        return dict(\n            static_path=relpath(\"static\"),\n            static_handler_args=dict(default_filename=\"index.html\"),\n        )\n\n    def get_handlers(self):\n        return [(\"/foo/(.*)\", StaticFileHandler, {\"path\": relpath(\"templates/\")})]\n\n    def test_serve(self):\n        response = self.fetch(\"/foo/utf8.html\")\n        self.assertEqual(response.body, b\"H\\xc3\\xa9llo\\n\")\n\n\nclass CustomStaticFileTest(WebTestCase):\n    def get_handlers(self):\n        class MyStaticFileHandler(StaticFileHandler):\n            @classmethod\n            def make_static_url(cls, settings, path):\n                version_hash = cls.get_version(settings, path)\n                extension_index = path.rindex(\".\")\n                before_version = path[:extension_index]\n                after_version = path[(extension_index + 1) :]\n                return \"/static/%s.%s.%s\" % (\n                    before_version,\n                    version_hash,\n                    after_version,\n                )\n\n            def parse_url_path(self, url_path):\n                extension_index = url_path.rindex(\".\")\n                version_index = url_path.rindex(\".\", 0, extension_index)\n                return \"%s%s\" % (url_path[:version_index], url_path[extension_index:])\n\n            @classmethod\n            def get_absolute_path(cls, settings, path):\n                return \"CustomStaticFileTest:\" + path\n\n            def validate_absolute_path(self, root, absolute_path):\n                return absolute_path\n\n            @classmethod\n            def get_content(self, path, start=None, end=None):\n                assert start is None and end is None\n                if path == \"CustomStaticFileTest:foo.txt\":\n                    return b\"bar\"\n                raise Exception(\"unexpected path %r\" % path)\n\n            def get_content_size(self):\n                if self.absolute_path == \"CustomStaticFileTest:foo.txt\":\n                    return 3\n                raise Exception(\"unexpected path %r\" % self.absolute_path)\n\n            def get_modified_time(self):\n                return None\n\n            @classmethod\n            def get_version(cls, settings, path):\n                return \"42\"\n\n        class StaticUrlHandler(RequestHandler):\n            def get(self, path):\n                self.write(self.static_url(path))\n\n        self.static_handler_class = MyStaticFileHandler\n\n        return [(\"/static_url/(.*)\", StaticUrlHandler)]\n\n    def get_app_kwargs(self):\n        return dict(static_path=\"dummy\", static_handler_class=self.static_handler_class)\n\n    def test_serve(self):\n        response = self.fetch(\"/static/foo.42.txt\")\n        self.assertEqual(response.body, b\"bar\")\n\n    def test_static_url(self):\n        with ExpectLog(gen_log, \"Could not open static file\", required=False):\n            response = self.fetch(\"/static_url/foo.txt\")\n            self.assertEqual(response.body, b\"/static/foo.42.txt\")\n\n\nclass HostMatchingTest(WebTestCase):\n    class Handler(RequestHandler):\n        def initialize(self, reply):\n            self.reply = reply\n\n        def get(self):\n            self.write(self.reply)\n\n    def get_handlers(self):\n        return [(\"/foo\", HostMatchingTest.Handler, {\"reply\": \"wildcard\"})]\n\n    def test_host_matching(self):\n        self.app.add_handlers(\n            \"www.example.com\", [(\"/foo\", HostMatchingTest.Handler, {\"reply\": \"[0]\"})]\n        )\n        self.app.add_handlers(\n            r\"www\\.example\\.com\", [(\"/bar\", HostMatchingTest.Handler, {\"reply\": \"[1]\"})]\n        )\n        self.app.add_handlers(\n            \"www.example.com\", [(\"/baz\", HostMatchingTest.Handler, {\"reply\": \"[2]\"})]\n        )\n        self.app.add_handlers(\n            \"www.e.*e.com\", [(\"/baz\", HostMatchingTest.Handler, {\"reply\": \"[3]\"})]\n        )\n\n        response = self.fetch(\"/foo\")\n        self.assertEqual(response.body, b\"wildcard\")\n        response = self.fetch(\"/bar\")\n        self.assertEqual(response.code, 404)\n        response = self.fetch(\"/baz\")\n        self.assertEqual(response.code, 404)\n\n        response = self.fetch(\"/foo\", headers={\"Host\": \"www.example.com\"})\n        self.assertEqual(response.body, b\"[0]\")\n        response = self.fetch(\"/bar\", headers={\"Host\": \"www.example.com\"})\n        self.assertEqual(response.body, b\"[1]\")\n        response = self.fetch(\"/baz\", headers={\"Host\": \"www.example.com\"})\n        self.assertEqual(response.body, b\"[2]\")\n        response = self.fetch(\"/baz\", headers={\"Host\": \"www.exe.com\"})\n        self.assertEqual(response.body, b\"[3]\")\n\n\nclass DefaultHostMatchingTest(WebTestCase):\n    def get_handlers(self):\n        return []\n\n    def get_app_kwargs(self):\n        return {\"default_host\": \"www.example.com\"}\n\n    def test_default_host_matching(self):\n        self.app.add_handlers(\n            \"www.example.com\", [(\"/foo\", HostMatchingTest.Handler, {\"reply\": \"[0]\"})]\n        )\n        self.app.add_handlers(\n            r\"www\\.example\\.com\", [(\"/bar\", HostMatchingTest.Handler, {\"reply\": \"[1]\"})]\n        )\n        self.app.add_handlers(\n            \"www.test.com\", [(\"/baz\", HostMatchingTest.Handler, {\"reply\": \"[2]\"})]\n        )\n\n        response = self.fetch(\"/foo\")\n        self.assertEqual(response.body, b\"[0]\")\n        response = self.fetch(\"/bar\")\n        self.assertEqual(response.body, b\"[1]\")\n        response = self.fetch(\"/baz\")\n        self.assertEqual(response.code, 404)\n\n        response = self.fetch(\"/foo\", headers={\"X-Real-Ip\": \"127.0.0.1\"})\n        self.assertEqual(response.code, 404)\n\n        self.app.default_host = \"www.test.com\"\n\n        response = self.fetch(\"/baz\")\n        self.assertEqual(response.body, b\"[2]\")\n\n\nclass NamedURLSpecGroupsTest(WebTestCase):\n    def get_handlers(self):\n        class EchoHandler(RequestHandler):\n            def get(self, path):\n                self.write(path)\n\n        return [\n            (\"/str/(?P<path>.*)\", EchoHandler),\n            (u\"/unicode/(?P<path>.*)\", EchoHandler),\n        ]\n\n    def test_named_urlspec_groups(self):\n        response = self.fetch(\"/str/foo\")\n        self.assertEqual(response.body, b\"foo\")\n\n        response = self.fetch(\"/unicode/bar\")\n        self.assertEqual(response.body, b\"bar\")\n\n\nclass ClearHeaderTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_header(\"h1\", \"foo\")\n            self.set_header(\"h2\", \"bar\")\n            self.clear_header(\"h1\")\n            self.clear_header(\"nonexistent\")\n\n    def test_clear_header(self):\n        response = self.fetch(\"/\")\n        self.assertTrue(\"h1\" not in response.headers)\n        self.assertEqual(response.headers[\"h2\"], \"bar\")\n\n\nclass Header204Test(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_status(204)\n            self.finish()\n\n    def test_204_headers(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 204)\n        self.assertNotIn(\"Content-Length\", response.headers)\n        self.assertNotIn(\"Transfer-Encoding\", response.headers)\n\n\nclass Header304Test(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_header(\"Content-Language\", \"en_US\")\n            self.write(\"hello\")\n\n    def test_304_headers(self):\n        response1 = self.fetch(\"/\")\n        self.assertEqual(response1.headers[\"Content-Length\"], \"5\")\n        self.assertEqual(response1.headers[\"Content-Language\"], \"en_US\")\n\n        response2 = self.fetch(\n            \"/\", headers={\"If-None-Match\": response1.headers[\"Etag\"]}\n        )\n        self.assertEqual(response2.code, 304)\n        self.assertTrue(\"Content-Length\" not in response2.headers)\n        self.assertTrue(\"Content-Language\" not in response2.headers)\n        # Not an entity header, but should not be added to 304s by chunking\n        self.assertTrue(\"Transfer-Encoding\" not in response2.headers)\n\n\nclass StatusReasonTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            reason = self.request.arguments.get(\"reason\", [])\n            self.set_status(\n                int(self.get_argument(\"code\")), reason=reason[0] if reason else None\n            )\n\n    def get_http_client(self):\n        # simple_httpclient only: curl doesn't expose the reason string\n        return SimpleAsyncHTTPClient()\n\n    def test_status(self):\n        response = self.fetch(\"/?code=304\")\n        self.assertEqual(response.code, 304)\n        self.assertEqual(response.reason, \"Not Modified\")\n        response = self.fetch(\"/?code=304&reason=Foo\")\n        self.assertEqual(response.code, 304)\n        self.assertEqual(response.reason, \"Foo\")\n        response = self.fetch(\"/?code=682&reason=Bar\")\n        self.assertEqual(response.code, 682)\n        self.assertEqual(response.reason, \"Bar\")\n        response = self.fetch(\"/?code=682\")\n        self.assertEqual(response.code, 682)\n        self.assertEqual(response.reason, \"Unknown\")\n\n\nclass DateHeaderTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.write(\"hello\")\n\n    def test_date_header(self):\n        response = self.fetch(\"/\")\n        parsed = email.utils.parsedate(response.headers[\"Date\"])\n        assert parsed is not None\n        header_date = datetime.datetime(*parsed[:6])\n        self.assertTrue(\n            header_date - datetime.datetime.utcnow() < datetime.timedelta(seconds=2)\n        )\n\n\nclass RaiseWithReasonTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            raise HTTPError(682, reason=\"Foo\")\n\n    def get_http_client(self):\n        # simple_httpclient only: curl doesn't expose the reason string\n        return SimpleAsyncHTTPClient()\n\n    def test_raise_with_reason(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 682)\n        self.assertEqual(response.reason, \"Foo\")\n        self.assertIn(b\"682: Foo\", response.body)\n\n    def test_httperror_str(self):\n        self.assertEqual(str(HTTPError(682, reason=\"Foo\")), \"HTTP 682: Foo\")\n\n    def test_httperror_str_from_httputil(self):\n        self.assertEqual(str(HTTPError(682)), \"HTTP 682: Unknown\")\n\n\nclass ErrorHandlerXSRFTest(WebTestCase):\n    def get_handlers(self):\n        # note that if the handlers list is empty we get the default_host\n        # redirect fallback instead of a 404, so test with both an\n        # explicitly defined error handler and an implicit 404.\n        return [(\"/error\", ErrorHandler, dict(status_code=417))]\n\n    def get_app_kwargs(self):\n        return dict(xsrf_cookies=True)\n\n    def test_error_xsrf(self):\n        response = self.fetch(\"/error\", method=\"POST\", body=\"\")\n        self.assertEqual(response.code, 417)\n\n    def test_404_xsrf(self):\n        response = self.fetch(\"/404\", method=\"POST\", body=\"\")\n        self.assertEqual(response.code, 404)\n\n\nclass GzipTestCase(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            for v in self.get_arguments(\"vary\"):\n                self.add_header(\"Vary\", v)\n            # Must write at least MIN_LENGTH bytes to activate compression.\n            self.write(\"hello world\" + (\"!\" * GZipContentEncoding.MIN_LENGTH))\n\n    def get_app_kwargs(self):\n        return dict(\n            gzip=True, static_path=os.path.join(os.path.dirname(__file__), \"static\")\n        )\n\n    def assert_compressed(self, response):\n        # simple_httpclient renames the content-encoding header;\n        # curl_httpclient doesn't.\n        self.assertEqual(\n            response.headers.get(\n                \"Content-Encoding\", response.headers.get(\"X-Consumed-Content-Encoding\")\n            ),\n            \"gzip\",\n        )\n\n    def test_gzip(self):\n        response = self.fetch(\"/\")\n        self.assert_compressed(response)\n        self.assertEqual(response.headers[\"Vary\"], \"Accept-Encoding\")\n\n    def test_gzip_static(self):\n        # The streaming responses in StaticFileHandler have subtle\n        # interactions with the gzip output so test this case separately.\n        response = self.fetch(\"/robots.txt\")\n        self.assert_compressed(response)\n        self.assertEqual(response.headers[\"Vary\"], \"Accept-Encoding\")\n\n    def test_gzip_not_requested(self):\n        response = self.fetch(\"/\", use_gzip=False)\n        self.assertNotIn(\"Content-Encoding\", response.headers)\n        self.assertEqual(response.headers[\"Vary\"], \"Accept-Encoding\")\n\n    def test_vary_already_present(self):\n        response = self.fetch(\"/?vary=Accept-Language\")\n        self.assert_compressed(response)\n        self.assertEqual(\n            [s.strip() for s in response.headers[\"Vary\"].split(\",\")],\n            [\"Accept-Language\", \"Accept-Encoding\"],\n        )\n\n    def test_vary_already_present_multiple(self):\n        # Regression test for https://github.com/tornadoweb/tornado/issues/1670\n        response = self.fetch(\"/?vary=Accept-Language&vary=Cookie\")\n        self.assert_compressed(response)\n        self.assertEqual(\n            [s.strip() for s in response.headers[\"Vary\"].split(\",\")],\n            [\"Accept-Language\", \"Cookie\", \"Accept-Encoding\"],\n        )\n\n\nclass PathArgsInPrepareTest(WebTestCase):\n    class Handler(RequestHandler):\n        def prepare(self):\n            self.write(dict(args=self.path_args, kwargs=self.path_kwargs))\n\n        def get(self, path):\n            assert path == \"foo\"\n            self.finish()\n\n    def get_handlers(self):\n        return [(\"/pos/(.*)\", self.Handler), (\"/kw/(?P<path>.*)\", self.Handler)]\n\n    def test_pos(self):\n        response = self.fetch(\"/pos/foo\")\n        response.rethrow()\n        data = json_decode(response.body)\n        self.assertEqual(data, {\"args\": [\"foo\"], \"kwargs\": {}})\n\n    def test_kw(self):\n        response = self.fetch(\"/kw/foo\")\n        response.rethrow()\n        data = json_decode(response.body)\n        self.assertEqual(data, {\"args\": [], \"kwargs\": {\"path\": \"foo\"}})\n\n\nclass ClearAllCookiesTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.clear_all_cookies()\n            self.write(\"ok\")\n\n    def test_clear_all_cookies(self):\n        response = self.fetch(\"/\", headers={\"Cookie\": \"foo=bar; baz=xyzzy\"})\n        set_cookies = sorted(response.headers.get_list(\"Set-Cookie\"))\n        # Python 3.5 sends 'baz=\"\";'; older versions use 'baz=;'\n        self.assertTrue(\n            set_cookies[0].startswith(\"baz=;\") or set_cookies[0].startswith('baz=\"\";')\n        )\n        self.assertTrue(\n            set_cookies[1].startswith(\"foo=;\") or set_cookies[1].startswith('foo=\"\";')\n        )\n\n\nclass PermissionError(Exception):\n    pass\n\n\nclass ExceptionHandlerTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            exc = self.get_argument(\"exc\")\n            if exc == \"http\":\n                raise HTTPError(410, \"no longer here\")\n            elif exc == \"zero\":\n                1 / 0\n            elif exc == \"permission\":\n                raise PermissionError(\"not allowed\")\n\n        def write_error(self, status_code, **kwargs):\n            if \"exc_info\" in kwargs:\n                typ, value, tb = kwargs[\"exc_info\"]\n                if isinstance(value, PermissionError):\n                    self.set_status(403)\n                    self.write(\"PermissionError\")\n                    return\n            RequestHandler.write_error(self, status_code, **kwargs)\n\n        def log_exception(self, typ, value, tb):\n            if isinstance(value, PermissionError):\n                app_log.warning(\"custom logging for PermissionError: %s\", value.args[0])\n            else:\n                RequestHandler.log_exception(self, typ, value, tb)\n\n    def test_http_error(self):\n        # HTTPErrors are logged as warnings with no stack trace.\n        # TODO: extend ExpectLog to test this more precisely\n        with ExpectLog(gen_log, \".*no longer here\"):\n            response = self.fetch(\"/?exc=http\")\n            self.assertEqual(response.code, 410)\n\n    def test_unknown_error(self):\n        # Unknown errors are logged as errors with a stack trace.\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/?exc=zero\")\n            self.assertEqual(response.code, 500)\n\n    def test_known_error(self):\n        # log_exception can override logging behavior, and write_error\n        # can override the response.\n        with ExpectLog(app_log, \"custom logging for PermissionError: not allowed\"):\n            response = self.fetch(\"/?exc=permission\")\n            self.assertEqual(response.code, 403)\n\n\nclass BuggyLoggingTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            1 / 0\n\n        def log_exception(self, typ, value, tb):\n            1 / 0\n\n    def test_buggy_log_exception(self):\n        # Something gets logged even though the application's\n        # logger is broken.\n        with ExpectLog(app_log, \".*\"):\n            self.fetch(\"/\")\n\n\nclass UIMethodUIModuleTest(SimpleHandlerTestCase):\n    \"\"\"Test that UI methods and modules are created correctly and\n    associated with the handler.\n    \"\"\"\n\n    class Handler(RequestHandler):\n        def get(self):\n            self.render(\"foo.html\")\n\n        def value(self):\n            return self.get_argument(\"value\")\n\n    def get_app_kwargs(self):\n        def my_ui_method(handler, x):\n            return \"In my_ui_method(%s) with handler value %s.\" % (x, handler.value())\n\n        class MyModule(UIModule):\n            def render(self, x):\n                return \"In MyModule(%s) with handler value %s.\" % (\n                    x,\n                    self.handler.value(),\n                )\n\n        loader = DictLoader(\n            {\"foo.html\": \"{{ my_ui_method(42) }} {% module MyModule(123) %}\"}\n        )\n        return dict(\n            template_loader=loader,\n            ui_methods={\"my_ui_method\": my_ui_method},\n            ui_modules={\"MyModule\": MyModule},\n        )\n\n    def tearDown(self):\n        super(UIMethodUIModuleTest, self).tearDown()\n        # TODO: fix template loader caching so this isn't necessary.\n        RequestHandler._template_loaders.clear()\n\n    def test_ui_method(self):\n        response = self.fetch(\"/?value=asdf\")\n        self.assertEqual(\n            response.body,\n            b\"In my_ui_method(42) with handler value asdf. \"\n            b\"In MyModule(123) with handler value asdf.\",\n        )\n\n\nclass GetArgumentErrorTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            try:\n                self.get_argument(\"foo\")\n                self.write({})\n            except MissingArgumentError as e:\n                self.write({\"arg_name\": e.arg_name, \"log_message\": e.log_message})\n\n    def test_catch_error(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(\n            json_decode(response.body),\n            {\"arg_name\": \"foo\", \"log_message\": \"Missing argument foo\"},\n        )\n\n\nclass SetLazyPropertiesTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def prepare(self):\n            self.current_user = \"Ben\"\n            self.locale = locale.get(\"en_US\")\n\n        def get_user_locale(self):\n            raise NotImplementedError()\n\n        def get_current_user(self):\n            raise NotImplementedError()\n\n        def get(self):\n            self.write(\"Hello %s (%s)\" % (self.current_user, self.locale.code))\n\n    def test_set_properties(self):\n        # Ensure that current_user can be assigned to normally for apps\n        # that want to forgo the lazy get_current_user property\n        response = self.fetch(\"/\")\n        self.assertEqual(response.body, b\"Hello Ben (en_US)\")\n\n\nclass GetCurrentUserTest(WebTestCase):\n    def get_app_kwargs(self):\n        class WithoutUserModule(UIModule):\n            def render(self):\n                return \"\"\n\n        class WithUserModule(UIModule):\n            def render(self):\n                return str(self.current_user)\n\n        loader = DictLoader(\n            {\n                \"without_user.html\": \"\",\n                \"with_user.html\": \"{{ current_user }}\",\n                \"without_user_module.html\": \"{% module WithoutUserModule() %}\",\n                \"with_user_module.html\": \"{% module WithUserModule() %}\",\n            }\n        )\n        return dict(\n            template_loader=loader,\n            ui_modules={\n                \"WithUserModule\": WithUserModule,\n                \"WithoutUserModule\": WithoutUserModule,\n            },\n        )\n\n    def tearDown(self):\n        super(GetCurrentUserTest, self).tearDown()\n        RequestHandler._template_loaders.clear()\n\n    def get_handlers(self):\n        class CurrentUserHandler(RequestHandler):\n            def prepare(self):\n                self.has_loaded_current_user = False\n\n            def get_current_user(self):\n                self.has_loaded_current_user = True\n                return \"\"\n\n        class WithoutUserHandler(CurrentUserHandler):\n            def get(self):\n                self.render_string(\"without_user.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        class WithUserHandler(CurrentUserHandler):\n            def get(self):\n                self.render_string(\"with_user.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        class CurrentUserModuleHandler(CurrentUserHandler):\n            def get_template_namespace(self):\n                # If RequestHandler.get_template_namespace is called, then\n                # get_current_user is evaluated. Until #820 is fixed, this\n                # is a small hack to circumvent the issue.\n                return self.ui\n\n        class WithoutUserModuleHandler(CurrentUserModuleHandler):\n            def get(self):\n                self.render_string(\"without_user_module.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        class WithUserModuleHandler(CurrentUserModuleHandler):\n            def get(self):\n                self.render_string(\"with_user_module.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        return [\n            (\"/without_user\", WithoutUserHandler),\n            (\"/with_user\", WithUserHandler),\n            (\"/without_user_module\", WithoutUserModuleHandler),\n            (\"/with_user_module\", WithUserModuleHandler),\n        ]\n\n    @unittest.skip(\"needs fix\")\n    def test_get_current_user_is_lazy(self):\n        # TODO: Make this test pass. See #820.\n        response = self.fetch(\"/without_user\")\n        self.assertEqual(response.body, b\"False\")\n\n    def test_get_current_user_works(self):\n        response = self.fetch(\"/with_user\")\n        self.assertEqual(response.body, b\"True\")\n\n    def test_get_current_user_from_ui_module_is_lazy(self):\n        response = self.fetch(\"/without_user_module\")\n        self.assertEqual(response.body, b\"False\")\n\n    def test_get_current_user_from_ui_module_works(self):\n        response = self.fetch(\"/with_user_module\")\n        self.assertEqual(response.body, b\"True\")\n\n\nclass UnimplementedHTTPMethodsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        pass\n\n    def test_unimplemented_standard_methods(self):\n        for method in [\"HEAD\", \"GET\", \"DELETE\", \"OPTIONS\"]:\n            response = self.fetch(\"/\", method=method)\n            self.assertEqual(response.code, 405)\n        for method in [\"POST\", \"PUT\"]:\n            response = self.fetch(\"/\", method=method, body=b\"\")\n            self.assertEqual(response.code, 405)\n\n\nclass UnimplementedNonStandardMethodsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def other(self):\n            # Even though this method exists, it won't get called automatically\n            # because it is not in SUPPORTED_METHODS.\n            self.write(\"other\")\n\n    def test_unimplemented_patch(self):\n        # PATCH is recently standardized; Tornado supports it by default\n        # but wsgiref.validate doesn't like it.\n        response = self.fetch(\"/\", method=\"PATCH\", body=b\"\")\n        self.assertEqual(response.code, 405)\n\n    def test_unimplemented_other(self):\n        response = self.fetch(\"/\", method=\"OTHER\", allow_nonstandard_methods=True)\n        self.assertEqual(response.code, 405)\n\n\nclass AllHTTPMethodsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def method(self):\n            self.write(self.request.method)\n\n        get = delete = options = post = put = method  # type: ignore\n\n    def test_standard_methods(self):\n        response = self.fetch(\"/\", method=\"HEAD\")\n        self.assertEqual(response.body, b\"\")\n        for method in [\"GET\", \"DELETE\", \"OPTIONS\"]:\n            response = self.fetch(\"/\", method=method)\n            self.assertEqual(response.body, utf8(method))\n        for method in [\"POST\", \"PUT\"]:\n            response = self.fetch(\"/\", method=method, body=b\"\")\n            self.assertEqual(response.body, utf8(method))\n\n\nclass PatchMethodTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        SUPPORTED_METHODS = RequestHandler.SUPPORTED_METHODS + (  # type: ignore\n            \"OTHER\",\n        )\n\n        def patch(self):\n            self.write(\"patch\")\n\n        def other(self):\n            self.write(\"other\")\n\n    def test_patch(self):\n        response = self.fetch(\"/\", method=\"PATCH\", body=b\"\")\n        self.assertEqual(response.body, b\"patch\")\n\n    def test_other(self):\n        response = self.fetch(\"/\", method=\"OTHER\", allow_nonstandard_methods=True)\n        self.assertEqual(response.body, b\"other\")\n\n\nclass FinishInPrepareTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def prepare(self):\n            self.finish(\"done\")\n\n        def get(self):\n            # It's difficult to assert for certain that a method did not\n            # or will not be called in an asynchronous context, but this\n            # will be logged noisily if it is reached.\n            raise Exception(\"should not reach this method\")\n\n    def test_finish_in_prepare(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.body, b\"done\")\n\n\nclass Default404Test(WebTestCase):\n    def get_handlers(self):\n        # If there are no handlers at all a default redirect handler gets added.\n        return [(\"/foo\", RequestHandler)]\n\n    def test_404(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 404)\n        self.assertEqual(\n            response.body,\n            b\"<html><title>404: Not Found</title>\"\n            b\"<body>404: Not Found</body></html>\",\n        )\n\n\nclass Custom404Test(WebTestCase):\n    def get_handlers(self):\n        return [(\"/foo\", RequestHandler)]\n\n    def get_app_kwargs(self):\n        class Custom404Handler(RequestHandler):\n            def get(self):\n                self.set_status(404)\n                self.write(\"custom 404 response\")\n\n        return dict(default_handler_class=Custom404Handler)\n\n    def test_404(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 404)\n        self.assertEqual(response.body, b\"custom 404 response\")\n\n\nclass DefaultHandlerArgumentsTest(WebTestCase):\n    def get_handlers(self):\n        return [(\"/foo\", RequestHandler)]\n\n    def get_app_kwargs(self):\n        return dict(\n            default_handler_class=ErrorHandler,\n            default_handler_args=dict(status_code=403),\n        )\n\n    def test_403(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 403)\n\n\nclass HandlerByNameTest(WebTestCase):\n    def get_handlers(self):\n        # All three are equivalent.\n        return [\n            (\"/hello1\", HelloHandler),\n            (\"/hello2\", \"tornado.test.web_test.HelloHandler\"),\n            url(\"/hello3\", \"tornado.test.web_test.HelloHandler\"),\n        ]\n\n    def test_handler_by_name(self):\n        resp = self.fetch(\"/hello1\")\n        self.assertEqual(resp.body, b\"hello\")\n        resp = self.fetch(\"/hello2\")\n        self.assertEqual(resp.body, b\"hello\")\n        resp = self.fetch(\"/hello3\")\n        self.assertEqual(resp.body, b\"hello\")\n\n\nclass StreamingRequestBodyTest(WebTestCase):\n    def get_handlers(self):\n        @stream_request_body\n        class StreamingBodyHandler(RequestHandler):\n            def initialize(self, test):\n                self.test = test\n\n            def prepare(self):\n                self.test.prepared.set_result(None)\n\n            def data_received(self, data):\n                self.test.data.set_result(data)\n\n            def get(self):\n                self.test.finished.set_result(None)\n                self.write({})\n\n        @stream_request_body\n        class EarlyReturnHandler(RequestHandler):\n            def prepare(self):\n                # If we finish the response in prepare, it won't continue to\n                # the (non-existent) data_received.\n                raise HTTPError(401)\n\n        @stream_request_body\n        class CloseDetectionHandler(RequestHandler):\n            def initialize(self, test):\n                self.test = test\n\n            def on_connection_close(self):\n                super(CloseDetectionHandler, self).on_connection_close()\n                self.test.close_future.set_result(None)\n\n        return [\n            (\"/stream_body\", StreamingBodyHandler, dict(test=self)),\n            (\"/early_return\", EarlyReturnHandler),\n            (\"/close_detection\", CloseDetectionHandler, dict(test=self)),\n        ]\n\n    def connect(self, url, connection_close):\n        # Use a raw connection so we can control the sending of data.\n        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)\n        s.connect((\"127.0.0.1\", self.get_http_port()))\n        stream = IOStream(s)\n        stream.write(b\"GET \" + url + b\" HTTP/1.1\\r\\n\")\n        if connection_close:\n            stream.write(b\"Connection: close\\r\\n\")\n        stream.write(b\"Transfer-Encoding: chunked\\r\\n\\r\\n\")\n        return stream\n\n    @gen_test\n    def test_streaming_body(self):\n        self.prepared = Future()  # type: Future[None]\n        self.data = Future()  # type: Future[bytes]\n        self.finished = Future()  # type: Future[None]\n\n        stream = self.connect(b\"/stream_body\", connection_close=True)\n        yield self.prepared\n        stream.write(b\"4\\r\\nasdf\\r\\n\")\n        # Ensure the first chunk is received before we send the second.\n        data = yield self.data\n        self.assertEqual(data, b\"asdf\")\n        self.data = Future()\n        stream.write(b\"4\\r\\nqwer\\r\\n\")\n        data = yield self.data\n        self.assertEqual(data, b\"qwer\")\n        stream.write(b\"0\\r\\n\\r\\n\")\n        yield self.finished\n        data = yield stream.read_until_close()\n        # This would ideally use an HTTP1Connection to read the response.\n        self.assertTrue(data.endswith(b\"{}\"))\n        stream.close()\n\n    @gen_test\n    def test_early_return(self):\n        stream = self.connect(b\"/early_return\", connection_close=False)\n        data = yield stream.read_until_close()\n        self.assertTrue(data.startswith(b\"HTTP/1.1 401\"))\n\n    @gen_test\n    def test_early_return_with_data(self):\n        stream = self.connect(b\"/early_return\", connection_close=False)\n        stream.write(b\"4\\r\\nasdf\\r\\n\")\n        data = yield stream.read_until_close()\n        self.assertTrue(data.startswith(b\"HTTP/1.1 401\"))\n\n    @gen_test\n    def test_close_during_upload(self):\n        self.close_future = Future()  # type: Future[None]\n        stream = self.connect(b\"/close_detection\", connection_close=False)\n        stream.close()\n        yield self.close_future\n\n\n# Each method in this handler returns a yieldable object and yields to the\n# IOLoop so the future is not immediately ready.  Ensure that the\n# yieldables are respected and no method is called before the previous\n# one has completed.\n@stream_request_body\nclass BaseFlowControlHandler(RequestHandler):\n    def initialize(self, test):\n        self.test = test\n        self.method = None\n        self.methods = []  # type: typing.List[str]\n\n    @contextlib.contextmanager\n    def in_method(self, method):\n        if self.method is not None:\n            self.test.fail(\"entered method %s while in %s\" % (method, self.method))\n        self.method = method\n        self.methods.append(method)\n        try:\n            yield\n        finally:\n            self.method = None\n\n    @gen.coroutine\n    def prepare(self):\n        # Note that asynchronous prepare() does not block data_received,\n        # so we don't use in_method here.\n        self.methods.append(\"prepare\")\n        yield gen.moment\n\n    @gen.coroutine\n    def post(self):\n        with self.in_method(\"post\"):\n            yield gen.moment\n        self.write(dict(methods=self.methods))\n\n\nclass BaseStreamingRequestFlowControlTest(object):\n    def get_httpserver_options(self):\n        # Use a small chunk size so flow control is relevant even though\n        # all the data arrives at once.\n        return dict(chunk_size=10, decompress_request=True)\n\n    def get_http_client(self):\n        # simple_httpclient only: curl doesn't support body_producer.\n        return SimpleAsyncHTTPClient()\n\n    # Test all the slightly different code paths for fixed, chunked, etc bodies.\n    def test_flow_control_fixed_body(self):\n        response = self.fetch(\"/\", body=\"abcdefghijklmnopqrstuvwxyz\", method=\"POST\")\n        response.rethrow()\n        self.assertEqual(\n            json_decode(response.body),\n            dict(\n                methods=[\n                    \"prepare\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"post\",\n                ]\n            ),\n        )\n\n    def test_flow_control_chunked_body(self):\n        chunks = [b\"abcd\", b\"efgh\", b\"ijkl\"]\n\n        @gen.coroutine\n        def body_producer(write):\n            for i in chunks:\n                yield write(i)\n\n        response = self.fetch(\"/\", body_producer=body_producer, method=\"POST\")\n        response.rethrow()\n        self.assertEqual(\n            json_decode(response.body),\n            dict(\n                methods=[\n                    \"prepare\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"post\",\n                ]\n            ),\n        )\n\n    def test_flow_control_compressed_body(self):\n        bytesio = BytesIO()\n        gzip_file = gzip.GzipFile(mode=\"w\", fileobj=bytesio)\n        gzip_file.write(b\"abcdefghijklmnopqrstuvwxyz\")\n        gzip_file.close()\n        compressed_body = bytesio.getvalue()\n        response = self.fetch(\n            \"/\",\n            body=compressed_body,\n            method=\"POST\",\n            headers={\"Content-Encoding\": \"gzip\"},\n        )\n        response.rethrow()\n        self.assertEqual(\n            json_decode(response.body),\n            dict(\n                methods=[\n                    \"prepare\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"post\",\n                ]\n            ),\n        )\n\n\nclass DecoratedStreamingRequestFlowControlTest(\n    BaseStreamingRequestFlowControlTest, WebTestCase\n):\n    def get_handlers(self):\n        class DecoratedFlowControlHandler(BaseFlowControlHandler):\n            @gen.coroutine\n            def data_received(self, data):\n                with self.in_method(\"data_received\"):\n                    yield gen.moment\n\n        return [(\"/\", DecoratedFlowControlHandler, dict(test=self))]\n\n\nclass NativeStreamingRequestFlowControlTest(\n    BaseStreamingRequestFlowControlTest, WebTestCase\n):\n    def get_handlers(self):\n        class NativeFlowControlHandler(BaseFlowControlHandler):\n            async def data_received(self, data):\n                with self.in_method(\"data_received\"):\n                    import asyncio\n\n                    await asyncio.sleep(0)\n\n        return [(\"/\", NativeFlowControlHandler, dict(test=self))]\n\n\nclass IncorrectContentLengthTest(SimpleHandlerTestCase):\n    def get_handlers(self):\n        test = self\n        self.server_error = None\n\n        # Manually set a content-length that doesn't match the actual content.\n        class TooHigh(RequestHandler):\n            def get(self):\n                self.set_header(\"Content-Length\", \"42\")\n                try:\n                    self.finish(\"ok\")\n                except Exception as e:\n                    test.server_error = e\n                    raise\n\n        class TooLow(RequestHandler):\n            def get(self):\n                self.set_header(\"Content-Length\", \"2\")\n                try:\n                    self.finish(\"hello\")\n                except Exception as e:\n                    test.server_error = e\n                    raise\n\n        return [(\"/high\", TooHigh), (\"/low\", TooLow)]\n\n    def test_content_length_too_high(self):\n        # When the content-length is too high, the connection is simply\n        # closed without completing the response.  An error is logged on\n        # the server.\n        with ExpectLog(app_log, \"(Uncaught exception|Exception in callback)\"):\n            with ExpectLog(\n                gen_log,\n                \"(Cannot send error response after headers written\"\n                \"|Failed to flush partial response)\",\n            ):\n                with self.assertRaises(HTTPClientError):\n                    self.fetch(\"/high\", raise_error=True)\n        self.assertEqual(\n            str(self.server_error), \"Tried to write 40 bytes less than Content-Length\"\n        )\n\n    def test_content_length_too_low(self):\n        # When the content-length is too low, the connection is closed\n        # without writing the last chunk, so the client never sees the request\n        # complete (which would be a framing error).\n        with ExpectLog(app_log, \"(Uncaught exception|Exception in callback)\"):\n            with ExpectLog(\n                gen_log,\n                \"(Cannot send error response after headers written\"\n                \"|Failed to flush partial response)\",\n            ):\n                with self.assertRaises(HTTPClientError):\n                    self.fetch(\"/low\", raise_error=True)\n        self.assertEqual(\n            str(self.server_error), \"Tried to write more data than Content-Length\"\n        )\n\n\nclass ClientCloseTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            if self.request.version.startswith(\"HTTP/1\"):\n                # Simulate a connection closed by the client during\n                # request processing.  The client will see an error, but the\n                # server should respond gracefully (without logging errors\n                # because we were unable to write out as many bytes as\n                # Content-Length said we would)\n                self.request.connection.stream.close()\n                self.write(\"hello\")\n            else:\n                # TODO: add a HTTP2-compatible version of this test.\n                self.write(\"requires HTTP/1.x\")\n\n    def test_client_close(self):\n        with self.assertRaises((HTTPClientError, unittest.SkipTest)):\n            response = self.fetch(\"/\", raise_error=True)\n            if response.body == b\"requires HTTP/1.x\":\n                self.skipTest(\"requires HTTP/1.x\")\n            self.assertEqual(response.code, 599)\n\n\nclass SignedValueTest(unittest.TestCase):\n    SECRET = \"It's a secret to everybody\"\n    SECRET_DICT = {0: \"asdfbasdf\", 1: \"12312312\", 2: \"2342342\"}\n\n    def past(self):\n        return self.present() - 86400 * 32\n\n    def present(self):\n        return 1300000000\n\n    def test_known_values(self):\n        signed_v1 = create_signed_value(\n            SignedValueTest.SECRET, \"key\", \"value\", version=1, clock=self.present\n        )\n        self.assertEqual(\n            signed_v1, b\"dmFsdWU=|1300000000|31c934969f53e48164c50768b40cbd7e2daaaa4f\"\n        )\n\n        signed_v2 = create_signed_value(\n            SignedValueTest.SECRET, \"key\", \"value\", version=2, clock=self.present\n        )\n        self.assertEqual(\n            signed_v2,\n            b\"2|1:0|10:1300000000|3:key|8:dmFsdWU=|\"\n            b\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\",\n        )\n\n        signed_default = create_signed_value(\n            SignedValueTest.SECRET, \"key\", \"value\", clock=self.present\n        )\n        self.assertEqual(signed_default, signed_v2)\n\n        decoded_v1 = decode_signed_value(\n            SignedValueTest.SECRET, \"key\", signed_v1, min_version=1, clock=self.present\n        )\n        self.assertEqual(decoded_v1, b\"value\")\n\n        decoded_v2 = decode_signed_value(\n            SignedValueTest.SECRET, \"key\", signed_v2, min_version=2, clock=self.present\n        )\n        self.assertEqual(decoded_v2, b\"value\")\n\n    def test_name_swap(self):\n        signed1 = create_signed_value(\n            SignedValueTest.SECRET, \"key1\", \"value\", clock=self.present\n        )\n        signed2 = create_signed_value(\n            SignedValueTest.SECRET, \"key2\", \"value\", clock=self.present\n        )\n        # Try decoding each string with the other's \"name\"\n        decoded1 = decode_signed_value(\n            SignedValueTest.SECRET, \"key2\", signed1, clock=self.present\n        )\n        self.assertIs(decoded1, None)\n        decoded2 = decode_signed_value(\n            SignedValueTest.SECRET, \"key1\", signed2, clock=self.present\n        )\n        self.assertIs(decoded2, None)\n\n    def test_expired(self):\n        signed = create_signed_value(\n            SignedValueTest.SECRET, \"key1\", \"value\", clock=self.past\n        )\n        decoded_past = decode_signed_value(\n            SignedValueTest.SECRET, \"key1\", signed, clock=self.past\n        )\n        self.assertEqual(decoded_past, b\"value\")\n        decoded_present = decode_signed_value(\n            SignedValueTest.SECRET, \"key1\", signed, clock=self.present\n        )\n        self.assertIs(decoded_present, None)\n\n    def test_payload_tampering(self):\n        # These cookies are variants of the one in test_known_values.\n        sig = \"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\"\n\n        def validate(prefix):\n            return b\"value\" == decode_signed_value(\n                SignedValueTest.SECRET, \"key\", prefix + sig, clock=self.present\n            )\n\n        self.assertTrue(validate(\"2|1:0|10:1300000000|3:key|8:dmFsdWU=|\"))\n        # Change key version\n        self.assertFalse(validate(\"2|1:1|10:1300000000|3:key|8:dmFsdWU=|\"))\n        # length mismatch (field too short)\n        self.assertFalse(validate(\"2|1:0|10:130000000|3:key|8:dmFsdWU=|\"))\n        # length mismatch (field too long)\n        self.assertFalse(validate(\"2|1:0|10:1300000000|3:keey|8:dmFsdWU=|\"))\n\n    def test_signature_tampering(self):\n        prefix = \"2|1:0|10:1300000000|3:key|8:dmFsdWU=|\"\n\n        def validate(sig):\n            return b\"value\" == decode_signed_value(\n                SignedValueTest.SECRET, \"key\", prefix + sig, clock=self.present\n            )\n\n        self.assertTrue(\n            validate(\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\")\n        )\n        # All zeros\n        self.assertFalse(validate(\"0\" * 32))\n        # Change one character\n        self.assertFalse(\n            validate(\"4d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\")\n        )\n        # Change another character\n        self.assertFalse(\n            validate(\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e153\")\n        )\n        # Truncate\n        self.assertFalse(\n            validate(\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e15\")\n        )\n        # Lengthen\n        self.assertFalse(\n            validate(\n                \"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e1538\"\n            )\n        )\n\n    def test_non_ascii(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET, \"key\", value, clock=self.present\n        )\n        decoded = decode_signed_value(\n            SignedValueTest.SECRET, \"key\", signed, clock=self.present\n        )\n        self.assertEqual(value, decoded)\n\n    def test_key_versioning_read_write_default_key(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=0\n        )\n        decoded = decode_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", signed, clock=self.present\n        )\n        self.assertEqual(value, decoded)\n\n    def test_key_versioning_read_write_non_default_key(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=1\n        )\n        decoded = decode_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", signed, clock=self.present\n        )\n        self.assertEqual(value, decoded)\n\n    def test_key_versioning_invalid_key(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=0\n        )\n        newkeys = SignedValueTest.SECRET_DICT.copy()\n        newkeys.pop(0)\n        decoded = decode_signed_value(newkeys, \"key\", signed, clock=self.present)\n        self.assertEqual(None, decoded)\n\n    def test_key_version_retrieval(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=1\n        )\n        key_version = get_signature_key_version(signed)\n        self.assertEqual(1, key_version)\n\n\nclass XSRFTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            version = int(self.get_argument(\"version\", \"2\"))\n            # This would be a bad idea in a real app, but in this test\n            # it's fine.\n            self.settings[\"xsrf_cookie_version\"] = version\n            self.write(self.xsrf_token)\n\n        def post(self):\n            self.write(\"ok\")\n\n    def get_app_kwargs(self):\n        return dict(xsrf_cookies=True)\n\n    def setUp(self):\n        super(XSRFTest, self).setUp()\n        self.xsrf_token = self.get_token()\n\n    def get_token(self, old_token=None, version=None):\n        if old_token is not None:\n            headers = self.cookie_headers(old_token)\n        else:\n            headers = None\n        response = self.fetch(\n            \"/\" if version is None else (\"/?version=%d\" % version), headers=headers\n        )\n        response.rethrow()\n        return native_str(response.body)\n\n    def cookie_headers(self, token=None):\n        if token is None:\n            token = self.xsrf_token\n        return {\"Cookie\": \"_xsrf=\" + token}\n\n    def test_xsrf_fail_no_token(self):\n        with ExpectLog(gen_log, \".*'_xsrf' argument missing\"):\n            response = self.fetch(\"/\", method=\"POST\", body=b\"\")\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_body_no_cookie(self):\n        with ExpectLog(gen_log, \".*XSRF cookie does not match POST\"):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_argument_invalid_format(self):\n        with ExpectLog(gen_log, \".*'_xsrf' argument has invalid format\"):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                headers=self.cookie_headers(),\n                body=urllib.parse.urlencode(dict(_xsrf=\"3|\")),\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_cookie_invalid_format(self):\n        with ExpectLog(gen_log, \".*XSRF cookie does not match POST\"):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                headers=self.cookie_headers(token=\"3|\"),\n                body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_cookie_no_body(self):\n        with ExpectLog(gen_log, \".*'_xsrf' argument missing\"):\n            response = self.fetch(\n                \"/\", method=\"POST\", body=b\"\", headers=self.cookie_headers()\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_success_short_token(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=urllib.parse.urlencode(dict(_xsrf=\"deadbeef\")),\n            headers=self.cookie_headers(token=\"deadbeef\"),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_non_hex_token(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=urllib.parse.urlencode(dict(_xsrf=\"xoxo\")),\n            headers=self.cookie_headers(token=\"xoxo\"),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_post_body(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            headers=self.cookie_headers(),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_query_string(self):\n        response = self.fetch(\n            \"/?\" + urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            method=\"POST\",\n            body=b\"\",\n            headers=self.cookie_headers(),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_header(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=b\"\",\n            headers=dict(\n                {\"X-Xsrftoken\": self.xsrf_token},  # type: ignore\n                **self.cookie_headers()\n            ),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_distinct_tokens(self):\n        # Every request gets a distinct token.\n        NUM_TOKENS = 10\n        tokens = set()\n        for i in range(NUM_TOKENS):\n            tokens.add(self.get_token())\n        self.assertEqual(len(tokens), NUM_TOKENS)\n\n    def test_cross_user(self):\n        token2 = self.get_token()\n        # Each token can be used to authenticate its own request.\n        for token in (self.xsrf_token, token2):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=token)),\n                headers=self.cookie_headers(token),\n            )\n            self.assertEqual(response.code, 200)\n        # Sending one in the cookie and the other in the body is not allowed.\n        for cookie_token, body_token in (\n            (self.xsrf_token, token2),\n            (token2, self.xsrf_token),\n        ):\n            with ExpectLog(gen_log, \".*XSRF cookie does not match POST\"):\n                response = self.fetch(\n                    \"/\",\n                    method=\"POST\",\n                    body=urllib.parse.urlencode(dict(_xsrf=body_token)),\n                    headers=self.cookie_headers(cookie_token),\n                )\n            self.assertEqual(response.code, 403)\n\n    def test_refresh_token(self):\n        token = self.xsrf_token\n        tokens_seen = set([token])\n        # A user's token is stable over time.  Refreshing the page in one tab\n        # might update the cookie while an older tab still has the old cookie\n        # in its DOM.  Simulate this scenario by passing a constant token\n        # in the body and re-querying for the token.\n        for i in range(5):\n            token = self.get_token(token)\n            # Tokens are encoded uniquely each time\n            tokens_seen.add(token)\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n                headers=self.cookie_headers(token),\n            )\n            self.assertEqual(response.code, 200)\n        self.assertEqual(len(tokens_seen), 6)\n\n    def test_versioning(self):\n        # Version 1 still produces distinct tokens per request.\n        self.assertNotEqual(self.get_token(version=1), self.get_token(version=1))\n\n        # Refreshed v1 tokens are all identical.\n        v1_token = self.get_token(version=1)\n        for i in range(5):\n            self.assertEqual(self.get_token(v1_token, version=1), v1_token)\n\n        # Upgrade to a v2 version of the same token\n        v2_token = self.get_token(v1_token)\n        self.assertNotEqual(v1_token, v2_token)\n        # Each v1 token can map to many v2 tokens.\n        self.assertNotEqual(v2_token, self.get_token(v1_token))\n\n        # The tokens are cross-compatible.\n        for cookie_token, body_token in ((v1_token, v2_token), (v2_token, v1_token)):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=body_token)),\n                headers=self.cookie_headers(cookie_token),\n            )\n            self.assertEqual(response.code, 200)\n\n\nclass XSRFCookieKwargsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.write(self.xsrf_token)\n\n    def get_app_kwargs(self):\n        return dict(\n            xsrf_cookies=True, xsrf_cookie_kwargs=dict(httponly=True, expires_days=2)\n        )\n\n    def test_xsrf_httponly(self):\n        response = self.fetch(\"/\")\n        self.assertIn(\"httponly;\", response.headers[\"Set-Cookie\"].lower())\n        self.assertIn(\"expires=\", response.headers[\"Set-Cookie\"].lower())\n        header = response.headers.get(\"Set-Cookie\")\n        match = re.match(\".*; expires=(?P<expires>.+);.*\", header)\n        assert match is not None\n\n        expires = datetime.datetime.utcnow() + datetime.timedelta(days=2)\n        parsed = email.utils.parsedate(match.groupdict()[\"expires\"])\n        assert parsed is not None\n        header_expires = datetime.datetime(*parsed[:6])\n        self.assertTrue(abs((expires - header_expires).total_seconds()) < 10)\n\n\nclass FinishExceptionTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_status(401)\n            self.set_header(\"WWW-Authenticate\", 'Basic realm=\"something\"')\n            if self.get_argument(\"finish_value\", \"\"):\n                raise Finish(\"authentication required\")\n            else:\n                self.write(\"authentication required\")\n                raise Finish()\n\n    def test_finish_exception(self):\n        for u in [\"/\", \"/?finish_value=1\"]:\n            response = self.fetch(u)\n            self.assertEqual(response.code, 401)\n            self.assertEqual(\n                'Basic realm=\"something\"', response.headers.get(\"WWW-Authenticate\")\n            )\n            self.assertEqual(b\"authentication required\", response.body)\n\n\nclass DecoratorTest(WebTestCase):\n    def get_handlers(self):\n        class RemoveSlashHandler(RequestHandler):\n            @removeslash\n            def get(self):\n                pass\n\n        class AddSlashHandler(RequestHandler):\n            @addslash\n            def get(self):\n                pass\n\n        return [(\"/removeslash/\", RemoveSlashHandler), (\"/addslash\", AddSlashHandler)]\n\n    def test_removeslash(self):\n        response = self.fetch(\"/removeslash/\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/removeslash\")\n\n        response = self.fetch(\"/removeslash/?foo=bar\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/removeslash?foo=bar\")\n\n    def test_addslash(self):\n        response = self.fetch(\"/addslash\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/addslash/\")\n\n        response = self.fetch(\"/addslash?foo=bar\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/addslash/?foo=bar\")\n\n\nclass CacheTest(WebTestCase):\n    def get_handlers(self):\n        class EtagHandler(RequestHandler):\n            def get(self, computed_etag):\n                self.write(computed_etag)\n\n            def compute_etag(self):\n                return self._write_buffer[0]\n\n        return [(\"/etag/(.*)\", EtagHandler)]\n\n    def test_wildcard_etag(self):\n        computed_etag = '\"xyzzy\"'\n        etags = \"*\"\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_strong_etag_match(self):\n        computed_etag = '\"xyzzy\"'\n        etags = '\"xyzzy\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_multiple_strong_etag_match(self):\n        computed_etag = '\"xyzzy1\"'\n        etags = '\"xyzzy1\", \"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_strong_etag_not_match(self):\n        computed_etag = '\"xyzzy\"'\n        etags = '\"xyzzy1\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def test_multiple_strong_etag_not_match(self):\n        computed_etag = '\"xyzzy\"'\n        etags = '\"xyzzy1\", \"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def test_weak_etag_match(self):\n        computed_etag = '\"xyzzy1\"'\n        etags = 'W/\"xyzzy1\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_multiple_weak_etag_match(self):\n        computed_etag = '\"xyzzy2\"'\n        etags = 'W/\"xyzzy1\", W/\"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_weak_etag_not_match(self):\n        computed_etag = '\"xyzzy2\"'\n        etags = 'W/\"xyzzy1\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def test_multiple_weak_etag_not_match(self):\n        computed_etag = '\"xyzzy3\"'\n        etags = 'W/\"xyzzy1\", W/\"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def _test_etag(self, computed_etag, etags, status_code):\n        response = self.fetch(\n            \"/etag/\" + computed_etag, headers={\"If-None-Match\": etags}\n        )\n        self.assertEqual(response.code, status_code)\n\n\nclass RequestSummaryTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            # remote_ip is optional, although it's set by\n            # both HTTPServer and WSGIAdapter.\n            # Clobber it to make sure it doesn't break logging.\n            self.request.remote_ip = None\n            self.finish(self._request_summary())\n\n    def test_missing_remote_ip(self):\n        resp = self.fetch(\"/\")\n        self.assertEqual(resp.body, b\"GET / (None)\")\n\n\nclass HTTPErrorTest(unittest.TestCase):\n    def test_copy(self):\n        e = HTTPError(403, reason=\"Go away\")\n        e2 = copy.copy(e)\n        self.assertIsNot(e, e2)\n        self.assertEqual(e.status_code, e2.status_code)\n        self.assertEqual(e.reason, e2.reason)\n\n\nclass ApplicationTest(AsyncTestCase):\n    def test_listen(self):\n        app = Application([])\n        server = app.listen(0, address=\"127.0.0.1\")\n        server.stop()\n\n\nclass URLSpecReverseTest(unittest.TestCase):\n    def test_reverse(self):\n        self.assertEqual(\"/favicon.ico\", url(r\"/favicon\\.ico\", None).reverse())\n        self.assertEqual(\"/favicon.ico\", url(r\"^/favicon\\.ico$\", None).reverse())\n\n    def test_non_reversible(self):\n        # URLSpecs are non-reversible if they include non-constant\n        # regex features outside capturing groups. Currently, this is\n        # only strictly enforced for backslash-escaped character\n        # classes.\n        paths = [r\"^/api/v\\d+/foo/(\\w+)$\"]\n        for path in paths:\n            # A URLSpec can still be created even if it cannot be reversed.\n            url_spec = url(path, None)\n            try:\n                result = url_spec.reverse()\n                self.fail(\n                    \"did not get expected exception when reversing %s. \"\n                    \"result: %s\" % (path, result)\n                )\n            except ValueError:\n                pass\n\n    def test_reverse_arguments(self):\n        self.assertEqual(\n            \"/api/v1/foo/bar\", url(r\"^/api/v1/foo/(\\w+)$\", None).reverse(\"bar\")\n        )\n        self.assertEqual(\n            \"/api.v1/foo/5/icon.png\",\n            url(r\"/api\\.v1/foo/([0-9]+)/icon\\.png\", None).reverse(5),\n        )\n\n\nclass RedirectHandlerTest(WebTestCase):\n    def get_handlers(self):\n        return [\n            (\"/src\", WebRedirectHandler, {\"url\": \"/dst\"}),\n            (\"/src2\", WebRedirectHandler, {\"url\": \"/dst2?foo=bar\"}),\n            (r\"/(.*?)/(.*?)/(.*)\", WebRedirectHandler, {\"url\": \"/{1}/{0}/{2}\"}),\n        ]\n\n    def test_basic_redirect(self):\n        response = self.fetch(\"/src\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/dst\")\n\n    def test_redirect_with_argument(self):\n        response = self.fetch(\"/src?foo=bar\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/dst?foo=bar\")\n\n    def test_redirect_with_appending_argument(self):\n        response = self.fetch(\"/src2?foo2=bar2\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/dst2?foo=bar&foo2=bar2\")\n\n    def test_redirect_pattern(self):\n        response = self.fetch(\"/a/b/c\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/b/a/c\")\n",
            "header": {
                "file": {
                    "path": "tornado/test/web_test.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "5908710a",
                "new_commit_hash": "0832f093",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tornado/test/web_test.py"
            },
            "plus_file": {
                "path": "b/tornado/test/web_test.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 3107,
                            "length": 6
                        },
                        "new_range": {
                            "start": 3107,
                            "length": 10
                        },
                        "section": "class URLSpecReverseTest(unittest.TestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.assertEqual(",
                                "type": "context"
                            },
                            {
                                "content": "            \"/api/v1/foo/bar\", url(r\"^/api/v1/foo/(\\w+)$\", None).reverse(\"bar\")",
                                "type": "context"
                            },
                            {
                                "content": "        )",
                                "type": "context"
                            },
                            {
                                "content": "        self.assertEqual(",
                                "type": "added"
                            },
                            {
                                "content": "            \"/api.v1/foo/5/icon.png\",",
                                "type": "added"
                            },
                            {
                                "content": "            url(r\"/api\\.v1/foo/([0-9]+)/icon\\.png\", None).reverse(5),",
                                "type": "added"
                            },
                            {
                                "content": "        )",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "class RedirectHandlerTest(WebTestCase):",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tornado/test/web_test.py",
                            "type": "method",
                            "name": "URLSpecReverseTest.test_reverse_arguments",
                            "content": "class URLSpecReverseTest(unittest.TestCase):\n    def test_reverse(self):\n        self.assertEqual(\"/favicon.ico\", url(r\"/favicon\\.ico\", None).reverse())\n        self.assertEqual(\"/favicon.ico\", url(r\"^/favicon\\.ico$\", None).reverse())\n\n    def test_non_reversible(self):\n        # URLSpecs are non-reversible if they include non-constant\n        # regex features outside capturing groups. Currently, this is\n        # only strictly enforced for backslash-escaped character\n        # classes.\n        paths = [r\"^/api/v\\d+/foo/(\\w+)$\"]\n        for path in paths:\n            # A URLSpec can still be created even if it cannot be reversed.\n            url_spec = url(path, None)\n            try:\n                result = url_spec.reverse()\n                self.fail(\n                    \"did not get expected exception when reversing %s. \"\n                    \"result: %s\" % (path, result)\n                )\n            except ValueError:\n                pass\n\n    def test_reverse_arguments(self):\n        self.assertEqual(\n            \"/api/v1/foo/bar\", url(r\"^/api/v1/foo/(\\w+)$\", None).reverse(\"bar\")\n        )\n        self.assertEqual(\n            \"/api.v1/foo/5/icon.png\",\n            url(r\"/api\\.v1/foo/([0-9]+)/icon\\.png\", None).reverse(5),\n        )",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 3106,
                            "end_lineno": 3113,
                            "parent": {
                                "file_name": "tornado/test/web_test.py",
                                "type": "class",
                                "name": "URLSpecReverseTest",
                                "content": "class URLSpecReverseTest(unittest.TestCase):\n    def test_reverse(self):\n        self.assertEqual(\"/favicon.ico\", url(r\"/favicon\\.ico\", None).reverse())\n        self.assertEqual(\"/favicon.ico\", url(r\"^/favicon\\.ico$\", None).reverse())\n\n    def test_non_reversible(self):\n        # URLSpecs are non-reversible if they include non-constant\n        # regex features outside capturing groups. Currently, this is\n        # only strictly enforced for backslash-escaped character\n        # classes.\n        paths = [r\"^/api/v\\d+/foo/(\\w+)$\"]\n        for path in paths:\n            # A URLSpec can still be created even if it cannot be reversed.\n            url_spec = url(path, None)\n            try:\n                result = url_spec.reverse()\n                self.fail(\n                    \"did not get expected exception when reversing %s. \"\n                    \"result: %s\" % (path, result)\n                )\n            except ValueError:\n                pass\n\n    def test_reverse_arguments(self):\n        self.assertEqual(\n            \"/api/v1/foo/bar\", url(r\"^/api/v1/foo/(\\w+)$\", None).reverse(\"bar\")\n        )\n        self.assertEqual(\n            \"/api.v1/foo/5/icon.png\",\n            url(r\"/api\\.v1/foo/([0-9]+)/icon\\.png\", None).reverse(5),\n        )",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 3083,
                                "end_lineno": 3113,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "tornado/test/web_test.py",
                            "type": "class",
                            "name": "URLSpecReverseTest",
                            "content": "class URLSpecReverseTest(unittest.TestCase):\n    def test_reverse(self):\n        self.assertEqual(\"/favicon.ico\", url(r\"/favicon\\.ico\", None).reverse())\n        self.assertEqual(\"/favicon.ico\", url(r\"^/favicon\\.ico$\", None).reverse())\n\n    def test_non_reversible(self):\n        # URLSpecs are non-reversible if they include non-constant\n        # regex features outside capturing groups. Currently, this is\n        # only strictly enforced for backslash-escaped character\n        # classes.\n        paths = [r\"^/api/v\\d+/foo/(\\w+)$\"]\n        for path in paths:\n            # A URLSpec can still be created even if it cannot be reversed.\n            url_spec = url(path, None)\n            try:\n                result = url_spec.reverse()\n                self.fail(\n                    \"did not get expected exception when reversing %s. \"\n                    \"result: %s\" % (path, result)\n                )\n            except ValueError:\n                pass\n\n    def test_reverse_arguments(self):\n        self.assertEqual(\n            \"/api/v1/foo/bar\", url(r\"^/api/v1/foo/(\\w+)$\", None).reverse(\"bar\")\n        )\n        self.assertEqual(\n            \"/api.v1/foo/5/icon.png\",\n            url(r\"/api\\.v1/foo/([0-9]+)/icon\\.png\", None).reverse(5),\n        )",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 3083,
                            "end_lineno": 3113,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "32377f2c2cdd95ade0e40fabdcc1364e07ea2239^",
    "new_commit_hash": "32377f2c2cdd95ade0e40fabdcc1364e07ea2239",
    "commit_message": "Merge pull request #2745 from remram44/re-unescape\n\nFix unescaping of regex routes",
    "commit_date": "2019-10-20T22:16:10-04:00",
    "metadata": {}
}
{
    "repo_name": "tornado",
    "new_commit_hash": "32377f2c2cdd95ade0e40fabdcc1364e07ea2239",
    "test_file_codes": [
        "from tornado.concurrent import Future\nfrom tornado import gen\nfrom tornado.escape import (\n    json_decode,\n    utf8,\n    to_unicode,\n    recursive_unicode,\n    native_str,\n    to_basestring,\n)\nfrom tornado.httpclient import HTTPClientError\nfrom tornado.httputil import format_timestamp\nfrom tornado.iostream import IOStream\nfrom tornado import locale\nfrom tornado.locks import Event\nfrom tornado.log import app_log, gen_log\nfrom tornado.simple_httpclient import SimpleAsyncHTTPClient\nfrom tornado.template import DictLoader\nfrom tornado.testing import AsyncHTTPTestCase, AsyncTestCase, ExpectLog, gen_test\nfrom tornado.util import ObjectDict, unicode_type\nfrom tornado.web import (\n    Application,\n    RequestHandler,\n    StaticFileHandler,\n    RedirectHandler as WebRedirectHandler,\n    HTTPError,\n    MissingArgumentError,\n    ErrorHandler,\n    authenticated,\n    url,\n    _create_signature_v1,\n    create_signed_value,\n    decode_signed_value,\n    get_signature_key_version,\n    UIModule,\n    Finish,\n    stream_request_body,\n    removeslash,\n    addslash,\n    GZipContentEncoding,\n)\n\nimport binascii\nimport contextlib\nimport copy\nimport datetime\nimport email.utils\nimport gzip\nfrom io import BytesIO\nimport itertools\nimport logging\nimport os\nimport re\nimport socket\nimport typing  # noqa: F401\nimport unittest\nimport urllib.parse\n\n\ndef relpath(*a):\n    return os.path.join(os.path.dirname(__file__), *a)\n\n\nclass WebTestCase(AsyncHTTPTestCase):\n    \"\"\"Base class for web tests that also supports WSGI mode.\n\n    Override get_handlers and get_app_kwargs instead of get_app.\n    This class is deprecated since WSGI mode is no longer supported.\n    \"\"\"\n\n    def get_app(self):\n        self.app = Application(self.get_handlers(), **self.get_app_kwargs())\n        return self.app\n\n    def get_handlers(self):\n        raise NotImplementedError()\n\n    def get_app_kwargs(self):\n        return {}\n\n\nclass SimpleHandlerTestCase(WebTestCase):\n    \"\"\"Simplified base class for tests that work with a single handler class.\n\n    To use, define a nested class named ``Handler``.\n    \"\"\"\n\n    def get_handlers(self):\n        return [(\"/\", self.Handler)]\n\n\nclass HelloHandler(RequestHandler):\n    def get(self):\n        self.write(\"hello\")\n\n\nclass CookieTestRequestHandler(RequestHandler):\n    # stub out enough methods to make the secure_cookie functions work\n    def __init__(self, cookie_secret=\"0123456789\", key_version=None):\n        # don't call super.__init__\n        self._cookies = {}  # type: typing.Dict[str, bytes]\n        if key_version is None:\n            self.application = ObjectDict(settings=dict(cookie_secret=cookie_secret))\n        else:\n            self.application = ObjectDict(\n                settings=dict(cookie_secret=cookie_secret, key_version=key_version)\n            )\n\n    def get_cookie(self, name):\n        return self._cookies.get(name)\n\n    def set_cookie(self, name, value, expires_days=None):\n        self._cookies[name] = value\n\n\n# See SignedValueTest below for more.\nclass SecureCookieV1Test(unittest.TestCase):\n    def test_round_trip(self):\n        handler = CookieTestRequestHandler()\n        handler.set_secure_cookie(\"foo\", b\"bar\", version=1)\n        self.assertEqual(handler.get_secure_cookie(\"foo\", min_version=1), b\"bar\")\n\n    def test_cookie_tampering_future_timestamp(self):\n        handler = CookieTestRequestHandler()\n        # this string base64-encodes to '12345678'\n        handler.set_secure_cookie(\"foo\", binascii.a2b_hex(b\"d76df8e7aefc\"), version=1)\n        cookie = handler._cookies[\"foo\"]\n        match = re.match(br\"12345678\\|([0-9]+)\\|([0-9a-f]+)\", cookie)\n        assert match is not None\n        timestamp = match.group(1)\n        sig = match.group(2)\n        self.assertEqual(\n            _create_signature_v1(\n                handler.application.settings[\"cookie_secret\"],\n                \"foo\",\n                \"12345678\",\n                timestamp,\n            ),\n            sig,\n        )\n        # shifting digits from payload to timestamp doesn't alter signature\n        # (this is not desirable behavior, just confirming that that's how it\n        # works)\n        self.assertEqual(\n            _create_signature_v1(\n                handler.application.settings[\"cookie_secret\"],\n                \"foo\",\n                \"1234\",\n                b\"5678\" + timestamp,\n            ),\n            sig,\n        )\n        # tamper with the cookie\n        handler._cookies[\"foo\"] = utf8(\n            \"1234|5678%s|%s\" % (to_basestring(timestamp), to_basestring(sig))\n        )\n        # it gets rejected\n        with ExpectLog(gen_log, \"Cookie timestamp in future\"):\n            self.assertTrue(handler.get_secure_cookie(\"foo\", min_version=1) is None)\n\n    def test_arbitrary_bytes(self):\n        # Secure cookies accept arbitrary data (which is base64 encoded).\n        # Note that normal cookies accept only a subset of ascii.\n        handler = CookieTestRequestHandler()\n        handler.set_secure_cookie(\"foo\", b\"\\xe9\", version=1)\n        self.assertEqual(handler.get_secure_cookie(\"foo\", min_version=1), b\"\\xe9\")\n\n\n# See SignedValueTest below for more.\nclass SecureCookieV2Test(unittest.TestCase):\n    KEY_VERSIONS = {0: \"ajklasdf0ojaisdf\", 1: \"aslkjasaolwkjsdf\"}\n\n    def test_round_trip(self):\n        handler = CookieTestRequestHandler()\n        handler.set_secure_cookie(\"foo\", b\"bar\", version=2)\n        self.assertEqual(handler.get_secure_cookie(\"foo\", min_version=2), b\"bar\")\n\n    def test_key_version_roundtrip(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=0\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        self.assertEqual(handler.get_secure_cookie(\"foo\"), b\"bar\")\n\n    def test_key_version_roundtrip_differing_version(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=1\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        self.assertEqual(handler.get_secure_cookie(\"foo\"), b\"bar\")\n\n    def test_key_version_increment_version(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=0\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        new_handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=1\n        )\n        new_handler._cookies = handler._cookies\n        self.assertEqual(new_handler.get_secure_cookie(\"foo\"), b\"bar\")\n\n    def test_key_version_invalidate_version(self):\n        handler = CookieTestRequestHandler(\n            cookie_secret=self.KEY_VERSIONS, key_version=0\n        )\n        handler.set_secure_cookie(\"foo\", b\"bar\")\n        new_key_versions = self.KEY_VERSIONS.copy()\n        new_key_versions.pop(0)\n        new_handler = CookieTestRequestHandler(\n            cookie_secret=new_key_versions, key_version=1\n        )\n        new_handler._cookies = handler._cookies\n        self.assertEqual(new_handler.get_secure_cookie(\"foo\"), None)\n\n\nclass FinalReturnTest(WebTestCase):\n    def get_handlers(self):\n        test = self\n\n        class FinishHandler(RequestHandler):\n            @gen.coroutine\n            def get(self):\n                test.final_return = self.finish()\n                yield test.final_return\n\n            @gen.coroutine\n            def post(self):\n                self.write(\"hello,\")\n                yield self.flush()\n                test.final_return = self.finish(\"world\")\n                yield test.final_return\n\n        class RenderHandler(RequestHandler):\n            def create_template_loader(self, path):\n                return DictLoader({\"foo.html\": \"hi\"})\n\n            @gen.coroutine\n            def get(self):\n                test.final_return = self.render(\"foo.html\")\n\n        return [(\"/finish\", FinishHandler), (\"/render\", RenderHandler)]\n\n    def get_app_kwargs(self):\n        return dict(template_path=\"FinalReturnTest\")\n\n    def test_finish_method_return_future(self):\n        response = self.fetch(self.get_url(\"/finish\"))\n        self.assertEqual(response.code, 200)\n        self.assertIsInstance(self.final_return, Future)\n        self.assertTrue(self.final_return.done())\n\n        response = self.fetch(self.get_url(\"/finish\"), method=\"POST\", body=b\"\")\n        self.assertEqual(response.code, 200)\n        self.assertIsInstance(self.final_return, Future)\n        self.assertTrue(self.final_return.done())\n\n    def test_render_method_return_future(self):\n        response = self.fetch(self.get_url(\"/render\"))\n        self.assertEqual(response.code, 200)\n        self.assertIsInstance(self.final_return, Future)\n\n\nclass CookieTest(WebTestCase):\n    def get_handlers(self):\n        class SetCookieHandler(RequestHandler):\n            def get(self):\n                # Try setting cookies with different argument types\n                # to ensure that everything gets encoded correctly\n                self.set_cookie(\"str\", \"asdf\")\n                self.set_cookie(\"unicode\", u\"qwer\")\n                self.set_cookie(\"bytes\", b\"zxcv\")\n\n        class GetCookieHandler(RequestHandler):\n            def get(self):\n                self.write(self.get_cookie(\"foo\", \"default\"))\n\n        class SetCookieDomainHandler(RequestHandler):\n            def get(self):\n                # unicode domain and path arguments shouldn't break things\n                # either (see bug #285)\n                self.set_cookie(\"unicode_args\", \"blah\", domain=u\"foo.com\", path=u\"/foo\")\n\n        class SetCookieSpecialCharHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"equals\", \"a=b\")\n                self.set_cookie(\"semicolon\", \"a;b\")\n                self.set_cookie(\"quote\", 'a\"b')\n\n        class SetCookieOverwriteHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"a\", \"b\", domain=\"example.com\")\n                self.set_cookie(\"c\", \"d\", domain=\"example.com\")\n                # A second call with the same name clobbers the first.\n                # Attributes from the first call are not carried over.\n                self.set_cookie(\"a\", \"e\")\n\n        class SetCookieMaxAgeHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"foo\", \"bar\", max_age=10)\n\n        class SetCookieExpiresDaysHandler(RequestHandler):\n            def get(self):\n                self.set_cookie(\"foo\", \"bar\", expires_days=10)\n\n        class SetCookieFalsyFlags(RequestHandler):\n            def get(self):\n                self.set_cookie(\"a\", \"1\", secure=True)\n                self.set_cookie(\"b\", \"1\", secure=False)\n                self.set_cookie(\"c\", \"1\", httponly=True)\n                self.set_cookie(\"d\", \"1\", httponly=False)\n\n        return [\n            (\"/set\", SetCookieHandler),\n            (\"/get\", GetCookieHandler),\n            (\"/set_domain\", SetCookieDomainHandler),\n            (\"/special_char\", SetCookieSpecialCharHandler),\n            (\"/set_overwrite\", SetCookieOverwriteHandler),\n            (\"/set_max_age\", SetCookieMaxAgeHandler),\n            (\"/set_expires_days\", SetCookieExpiresDaysHandler),\n            (\"/set_falsy_flags\", SetCookieFalsyFlags),\n        ]\n\n    def test_set_cookie(self):\n        response = self.fetch(\"/set\")\n        self.assertEqual(\n            sorted(response.headers.get_list(\"Set-Cookie\")),\n            [\"bytes=zxcv; Path=/\", \"str=asdf; Path=/\", \"unicode=qwer; Path=/\"],\n        )\n\n    def test_get_cookie(self):\n        response = self.fetch(\"/get\", headers={\"Cookie\": \"foo=bar\"})\n        self.assertEqual(response.body, b\"bar\")\n\n        response = self.fetch(\"/get\", headers={\"Cookie\": 'foo=\"bar\"'})\n        self.assertEqual(response.body, b\"bar\")\n\n        response = self.fetch(\"/get\", headers={\"Cookie\": \"/=exception;\"})\n        self.assertEqual(response.body, b\"default\")\n\n    def test_set_cookie_domain(self):\n        response = self.fetch(\"/set_domain\")\n        self.assertEqual(\n            response.headers.get_list(\"Set-Cookie\"),\n            [\"unicode_args=blah; Domain=foo.com; Path=/foo\"],\n        )\n\n    def test_cookie_special_char(self):\n        response = self.fetch(\"/special_char\")\n        headers = sorted(response.headers.get_list(\"Set-Cookie\"))\n        self.assertEqual(len(headers), 3)\n        self.assertEqual(headers[0], 'equals=\"a=b\"; Path=/')\n        self.assertEqual(headers[1], 'quote=\"a\\\\\"b\"; Path=/')\n        # python 2.7 octal-escapes the semicolon; older versions leave it alone\n        self.assertTrue(\n            headers[2] in ('semicolon=\"a;b\"; Path=/', 'semicolon=\"a\\\\073b\"; Path=/'),\n            headers[2],\n        )\n\n        data = [\n            (\"foo=a=b\", \"a=b\"),\n            ('foo=\"a=b\"', \"a=b\"),\n            ('foo=\"a;b\"', '\"a'),  # even quoted, \";\" is a delimiter\n            (\"foo=a\\\\073b\", \"a\\\\073b\"),  # escapes only decoded in quotes\n            ('foo=\"a\\\\073b\"', \"a;b\"),\n            ('foo=\"a\\\\\"b\"', 'a\"b'),\n        ]\n        for header, expected in data:\n            logging.debug(\"trying %r\", header)\n            response = self.fetch(\"/get\", headers={\"Cookie\": header})\n            self.assertEqual(response.body, utf8(expected))\n\n    def test_set_cookie_overwrite(self):\n        response = self.fetch(\"/set_overwrite\")\n        headers = response.headers.get_list(\"Set-Cookie\")\n        self.assertEqual(\n            sorted(headers), [\"a=e; Path=/\", \"c=d; Domain=example.com; Path=/\"]\n        )\n\n    def test_set_cookie_max_age(self):\n        response = self.fetch(\"/set_max_age\")\n        headers = response.headers.get_list(\"Set-Cookie\")\n        self.assertEqual(sorted(headers), [\"foo=bar; Max-Age=10; Path=/\"])\n\n    def test_set_cookie_expires_days(self):\n        response = self.fetch(\"/set_expires_days\")\n        header = response.headers.get(\"Set-Cookie\")\n        match = re.match(\"foo=bar; expires=(?P<expires>.+); Path=/\", header)\n        assert match is not None\n\n        expires = datetime.datetime.utcnow() + datetime.timedelta(days=10)\n        parsed = email.utils.parsedate(match.groupdict()[\"expires\"])\n        assert parsed is not None\n        header_expires = datetime.datetime(*parsed[:6])\n        self.assertTrue(abs((expires - header_expires).total_seconds()) < 10)\n\n    def test_set_cookie_false_flags(self):\n        response = self.fetch(\"/set_falsy_flags\")\n        headers = sorted(response.headers.get_list(\"Set-Cookie\"))\n        # The secure and httponly headers are capitalized in py35 and\n        # lowercase in older versions.\n        self.assertEqual(headers[0].lower(), \"a=1; path=/; secure\")\n        self.assertEqual(headers[1].lower(), \"b=1; path=/\")\n        self.assertEqual(headers[2].lower(), \"c=1; httponly; path=/\")\n        self.assertEqual(headers[3].lower(), \"d=1; path=/\")\n\n\nclass AuthRedirectRequestHandler(RequestHandler):\n    def initialize(self, login_url):\n        self.login_url = login_url\n\n    def get_login_url(self):\n        return self.login_url\n\n    @authenticated\n    def get(self):\n        # we'll never actually get here because the test doesn't follow redirects\n        self.send_error(500)\n\n\nclass AuthRedirectTest(WebTestCase):\n    def get_handlers(self):\n        return [\n            (\"/relative\", AuthRedirectRequestHandler, dict(login_url=\"/login\")),\n            (\n                \"/absolute\",\n                AuthRedirectRequestHandler,\n                dict(login_url=\"http://example.com/login\"),\n            ),\n        ]\n\n    def test_relative_auth_redirect(self):\n        response = self.fetch(self.get_url(\"/relative\"), follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        self.assertEqual(response.headers[\"Location\"], \"/login?next=%2Frelative\")\n\n    def test_absolute_auth_redirect(self):\n        response = self.fetch(self.get_url(\"/absolute\"), follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        self.assertTrue(\n            re.match(\n                r\"http://example.com/login\\?next=http%3A%2F%2F127.0.0.1%3A[0-9]+%2Fabsolute\",\n                response.headers[\"Location\"],\n            ),\n            response.headers[\"Location\"],\n        )\n\n\nclass ConnectionCloseHandler(RequestHandler):\n    def initialize(self, test):\n        self.test = test\n\n    @gen.coroutine\n    def get(self):\n        self.test.on_handler_waiting()\n        yield self.test.cleanup_event.wait()\n\n    def on_connection_close(self):\n        self.test.on_connection_close()\n\n\nclass ConnectionCloseTest(WebTestCase):\n    def get_handlers(self):\n        self.cleanup_event = Event()\n        return [(\"/\", ConnectionCloseHandler, dict(test=self))]\n\n    def test_connection_close(self):\n        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)\n        s.connect((\"127.0.0.1\", self.get_http_port()))\n        self.stream = IOStream(s)\n        self.stream.write(b\"GET / HTTP/1.0\\r\\n\\r\\n\")\n        self.wait()\n        # Let the hanging coroutine clean up after itself\n        self.cleanup_event.set()\n        self.io_loop.run_sync(lambda: gen.sleep(0))\n\n    def on_handler_waiting(self):\n        logging.debug(\"handler waiting\")\n        self.stream.close()\n\n    def on_connection_close(self):\n        logging.debug(\"connection closed\")\n        self.stop()\n\n\nclass EchoHandler(RequestHandler):\n    def get(self, *path_args):\n        # Type checks: web.py interfaces convert argument values to\n        # unicode strings (by default, but see also decode_argument).\n        # In httpserver.py (i.e. self.request.arguments), they're left\n        # as bytes.  Keys are always native strings.\n        for key in self.request.arguments:\n            if type(key) != str:\n                raise Exception(\"incorrect type for key: %r\" % type(key))\n            for value in self.request.arguments[key]:\n                if type(value) != bytes:\n                    raise Exception(\"incorrect type for value: %r\" % type(value))\n            for value in self.get_arguments(key):\n                if type(value) != unicode_type:\n                    raise Exception(\"incorrect type for value: %r\" % type(value))\n        for arg in path_args:\n            if type(arg) != unicode_type:\n                raise Exception(\"incorrect type for path arg: %r\" % type(arg))\n        self.write(\n            dict(\n                path=self.request.path,\n                path_args=path_args,\n                args=recursive_unicode(self.request.arguments),\n            )\n        )\n\n\nclass RequestEncodingTest(WebTestCase):\n    def get_handlers(self):\n        return [(\"/group/(.*)\", EchoHandler), (\"/slashes/([^/]*)/([^/]*)\", EchoHandler)]\n\n    def fetch_json(self, path):\n        return json_decode(self.fetch(path).body)\n\n    def test_group_question_mark(self):\n        # Ensure that url-encoded question marks are handled properly\n        self.assertEqual(\n            self.fetch_json(\"/group/%3F\"),\n            dict(path=\"/group/%3F\", path_args=[\"?\"], args={}),\n        )\n        self.assertEqual(\n            self.fetch_json(\"/group/%3F?%3F=%3F\"),\n            dict(path=\"/group/%3F\", path_args=[\"?\"], args={\"?\": [\"?\"]}),\n        )\n\n    def test_group_encoding(self):\n        # Path components and query arguments should be decoded the same way\n        self.assertEqual(\n            self.fetch_json(\"/group/%C3%A9?arg=%C3%A9\"),\n            {\n                u\"path\": u\"/group/%C3%A9\",\n                u\"path_args\": [u\"\\u00e9\"],\n                u\"args\": {u\"arg\": [u\"\\u00e9\"]},\n            },\n        )\n\n    def test_slashes(self):\n        # Slashes may be escaped to appear as a single \"directory\" in the path,\n        # but they are then unescaped when passed to the get() method.\n        self.assertEqual(\n            self.fetch_json(\"/slashes/foo/bar\"),\n            dict(path=\"/slashes/foo/bar\", path_args=[\"foo\", \"bar\"], args={}),\n        )\n        self.assertEqual(\n            self.fetch_json(\"/slashes/a%2Fb/c%2Fd\"),\n            dict(path=\"/slashes/a%2Fb/c%2Fd\", path_args=[\"a/b\", \"c/d\"], args={}),\n        )\n\n    def test_error(self):\n        # Percent signs (encoded as %25) should not mess up printf-style\n        # messages in logs\n        with ExpectLog(gen_log, \".*Invalid unicode\"):\n            self.fetch(\"/group/?arg=%25%e9\")\n\n\nclass TypeCheckHandler(RequestHandler):\n    def prepare(self):\n        self.errors = {}  # type: typing.Dict[str, str]\n\n        self.check_type(\"status\", self.get_status(), int)\n\n        # get_argument is an exception from the general rule of using\n        # type str for non-body data mainly for historical reasons.\n        self.check_type(\"argument\", self.get_argument(\"foo\"), unicode_type)\n        self.check_type(\"cookie_key\", list(self.cookies.keys())[0], str)\n        self.check_type(\"cookie_value\", list(self.cookies.values())[0].value, str)\n\n        # Secure cookies return bytes because they can contain arbitrary\n        # data, but regular cookies are native strings.\n        if list(self.cookies.keys()) != [\"asdf\"]:\n            raise Exception(\n                \"unexpected values for cookie keys: %r\" % self.cookies.keys()\n            )\n        self.check_type(\"get_secure_cookie\", self.get_secure_cookie(\"asdf\"), bytes)\n        self.check_type(\"get_cookie\", self.get_cookie(\"asdf\"), str)\n\n        self.check_type(\"xsrf_token\", self.xsrf_token, bytes)\n        self.check_type(\"xsrf_form_html\", self.xsrf_form_html(), str)\n\n        self.check_type(\"reverse_url\", self.reverse_url(\"typecheck\", \"foo\"), str)\n\n        self.check_type(\"request_summary\", self._request_summary(), str)\n\n    def get(self, path_component):\n        # path_component uses type unicode instead of str for consistency\n        # with get_argument()\n        self.check_type(\"path_component\", path_component, unicode_type)\n        self.write(self.errors)\n\n    def post(self, path_component):\n        self.check_type(\"path_component\", path_component, unicode_type)\n        self.write(self.errors)\n\n    def check_type(self, name, obj, expected_type):\n        actual_type = type(obj)\n        if expected_type != actual_type:\n            self.errors[name] = \"expected %s, got %s\" % (expected_type, actual_type)\n\n\nclass DecodeArgHandler(RequestHandler):\n    def decode_argument(self, value, name=None):\n        if type(value) != bytes:\n            raise Exception(\"unexpected type for value: %r\" % type(value))\n        # use self.request.arguments directly to avoid recursion\n        if \"encoding\" in self.request.arguments:\n            return value.decode(to_unicode(self.request.arguments[\"encoding\"][0]))\n        else:\n            return value\n\n    def get(self, arg):\n        def describe(s):\n            if type(s) == bytes:\n                return [\"bytes\", native_str(binascii.b2a_hex(s))]\n            elif type(s) == unicode_type:\n                return [\"unicode\", s]\n            raise Exception(\"unknown type\")\n\n        self.write({\"path\": describe(arg), \"query\": describe(self.get_argument(\"foo\"))})\n\n\nclass LinkifyHandler(RequestHandler):\n    def get(self):\n        self.render(\"linkify.html\", message=\"http://example.com\")\n\n\nclass UIModuleResourceHandler(RequestHandler):\n    def get(self):\n        self.render(\"page.html\", entries=[1, 2])\n\n\nclass OptionalPathHandler(RequestHandler):\n    def get(self, path):\n        self.write({\"path\": path})\n\n\nclass MultiHeaderHandler(RequestHandler):\n    def get(self):\n        self.set_header(\"x-overwrite\", \"1\")\n        self.set_header(\"X-Overwrite\", 2)\n        self.add_header(\"x-multi\", 3)\n        self.add_header(\"X-Multi\", \"4\")\n\n\nclass RedirectHandler(RequestHandler):\n    def get(self):\n        if self.get_argument(\"permanent\", None) is not None:\n            self.redirect(\"/\", permanent=int(self.get_argument(\"permanent\")))\n        elif self.get_argument(\"status\", None) is not None:\n            self.redirect(\"/\", status=int(self.get_argument(\"status\")))\n        else:\n            raise Exception(\"didn't get permanent or status arguments\")\n\n\nclass EmptyFlushCallbackHandler(RequestHandler):\n    @gen.coroutine\n    def get(self):\n        # Ensure that the flush callback is run whether or not there\n        # was any output.  The gen.Task and direct yield forms are\n        # equivalent.\n        yield self.flush()  # \"empty\" flush, but writes headers\n        yield self.flush()  # empty flush\n        self.write(\"o\")\n        yield self.flush()  # flushes the \"o\"\n        yield self.flush()  # empty flush\n        self.finish(\"k\")\n\n\nclass HeaderInjectionHandler(RequestHandler):\n    def get(self):\n        try:\n            self.set_header(\"X-Foo\", \"foo\\r\\nX-Bar: baz\")\n            raise Exception(\"Didn't get expected exception\")\n        except ValueError as e:\n            if \"Unsafe header value\" in str(e):\n                self.finish(b\"ok\")\n            else:\n                raise\n\n\nclass GetArgumentHandler(RequestHandler):\n    def prepare(self):\n        if self.get_argument(\"source\", None) == \"query\":\n            method = self.get_query_argument\n        elif self.get_argument(\"source\", None) == \"body\":\n            method = self.get_body_argument\n        else:\n            method = self.get_argument\n        self.finish(method(\"foo\", \"default\"))\n\n\nclass GetArgumentsHandler(RequestHandler):\n    def prepare(self):\n        self.finish(\n            dict(\n                default=self.get_arguments(\"foo\"),\n                query=self.get_query_arguments(\"foo\"),\n                body=self.get_body_arguments(\"foo\"),\n            )\n        )\n\n\n# This test was shared with wsgi_test.py; now the name is meaningless.\nclass WSGISafeWebTest(WebTestCase):\n    COOKIE_SECRET = \"WebTest.COOKIE_SECRET\"\n\n    def get_app_kwargs(self):\n        loader = DictLoader(\n            {\n                \"linkify.html\": \"{% module linkify(message) %}\",\n                \"page.html\": \"\"\"\\\n<html><head></head><body>\n{% for e in entries %}\n{% module Template(\"entry.html\", entry=e) %}\n{% end %}\n</body></html>\"\"\",\n                \"entry.html\": \"\"\"\\\n{{ set_resources(embedded_css=\".entry { margin-bottom: 1em; }\",\n                 embedded_javascript=\"js_embed()\",\n                 css_files=[\"/base.css\", \"/foo.css\"],\n                 javascript_files=\"/common.js\",\n                 html_head=\"<meta>\",\n                 html_body='<script src=\"/analytics.js\"/>') }}\n<div class=\"entry\">...</div>\"\"\",\n            }\n        )\n        return dict(\n            template_loader=loader,\n            autoescape=\"xhtml_escape\",\n            cookie_secret=self.COOKIE_SECRET,\n        )\n\n    def tearDown(self):\n        super(WSGISafeWebTest, self).tearDown()\n        RequestHandler._template_loaders.clear()\n\n    def get_handlers(self):\n        urls = [\n            url(\"/typecheck/(.*)\", TypeCheckHandler, name=\"typecheck\"),\n            url(\"/decode_arg/(.*)\", DecodeArgHandler, name=\"decode_arg\"),\n            url(\"/decode_arg_kw/(?P<arg>.*)\", DecodeArgHandler),\n            url(\"/linkify\", LinkifyHandler),\n            url(\"/uimodule_resources\", UIModuleResourceHandler),\n            url(\"/optional_path/(.+)?\", OptionalPathHandler),\n            url(\"/multi_header\", MultiHeaderHandler),\n            url(\"/redirect\", RedirectHandler),\n            url(\n                \"/web_redirect_permanent\",\n                WebRedirectHandler,\n                {\"url\": \"/web_redirect_newpath\"},\n            ),\n            url(\n                \"/web_redirect\",\n                WebRedirectHandler,\n                {\"url\": \"/web_redirect_newpath\", \"permanent\": False},\n            ),\n            url(\n                \"//web_redirect_double_slash\",\n                WebRedirectHandler,\n                {\"url\": \"/web_redirect_newpath\"},\n            ),\n            url(\"/header_injection\", HeaderInjectionHandler),\n            url(\"/get_argument\", GetArgumentHandler),\n            url(\"/get_arguments\", GetArgumentsHandler),\n        ]\n        return urls\n\n    def fetch_json(self, *args, **kwargs):\n        response = self.fetch(*args, **kwargs)\n        response.rethrow()\n        return json_decode(response.body)\n\n    def test_types(self):\n        cookie_value = to_unicode(\n            create_signed_value(self.COOKIE_SECRET, \"asdf\", \"qwer\")\n        )\n        response = self.fetch(\n            \"/typecheck/asdf?foo=bar\", headers={\"Cookie\": \"asdf=\" + cookie_value}\n        )\n        data = json_decode(response.body)\n        self.assertEqual(data, {})\n\n        response = self.fetch(\n            \"/typecheck/asdf?foo=bar\",\n            method=\"POST\",\n            headers={\"Cookie\": \"asdf=\" + cookie_value},\n            body=\"foo=bar\",\n        )\n\n    def test_decode_argument(self):\n        # These urls all decode to the same thing\n        urls = [\n            \"/decode_arg/%C3%A9?foo=%C3%A9&encoding=utf-8\",\n            \"/decode_arg/%E9?foo=%E9&encoding=latin1\",\n            \"/decode_arg_kw/%E9?foo=%E9&encoding=latin1\",\n        ]\n        for req_url in urls:\n            response = self.fetch(req_url)\n            response.rethrow()\n            data = json_decode(response.body)\n            self.assertEqual(\n                data,\n                {u\"path\": [u\"unicode\", u\"\\u00e9\"], u\"query\": [u\"unicode\", u\"\\u00e9\"]},\n            )\n\n        response = self.fetch(\"/decode_arg/%C3%A9?foo=%C3%A9\")\n        response.rethrow()\n        data = json_decode(response.body)\n        self.assertEqual(\n            data, {u\"path\": [u\"bytes\", u\"c3a9\"], u\"query\": [u\"bytes\", u\"c3a9\"]}\n        )\n\n    def test_decode_argument_invalid_unicode(self):\n        # test that invalid unicode in URLs causes 400, not 500\n        with ExpectLog(gen_log, \".*Invalid unicode.*\"):\n            response = self.fetch(\"/typecheck/invalid%FF\")\n            self.assertEqual(response.code, 400)\n            response = self.fetch(\"/typecheck/invalid?foo=%FF\")\n            self.assertEqual(response.code, 400)\n\n    def test_decode_argument_plus(self):\n        # These urls are all equivalent.\n        urls = [\n            \"/decode_arg/1%20%2B%201?foo=1%20%2B%201&encoding=utf-8\",\n            \"/decode_arg/1%20+%201?foo=1+%2B+1&encoding=utf-8\",\n        ]\n        for req_url in urls:\n            response = self.fetch(req_url)\n            response.rethrow()\n            data = json_decode(response.body)\n            self.assertEqual(\n                data,\n                {u\"path\": [u\"unicode\", u\"1 + 1\"], u\"query\": [u\"unicode\", u\"1 + 1\"]},\n            )\n\n    def test_reverse_url(self):\n        self.assertEqual(self.app.reverse_url(\"decode_arg\", \"foo\"), \"/decode_arg/foo\")\n        self.assertEqual(self.app.reverse_url(\"decode_arg\", 42), \"/decode_arg/42\")\n        self.assertEqual(self.app.reverse_url(\"decode_arg\", b\"\\xe9\"), \"/decode_arg/%E9\")\n        self.assertEqual(\n            self.app.reverse_url(\"decode_arg\", u\"\\u00e9\"), \"/decode_arg/%C3%A9\"\n        )\n        self.assertEqual(\n            self.app.reverse_url(\"decode_arg\", \"1 + 1\"), \"/decode_arg/1%20%2B%201\"\n        )\n\n    def test_uimodule_unescaped(self):\n        response = self.fetch(\"/linkify\")\n        self.assertEqual(\n            response.body, b'<a href=\"http://example.com\">http://example.com</a>'\n        )\n\n    def test_uimodule_resources(self):\n        response = self.fetch(\"/uimodule_resources\")\n        self.assertEqual(\n            response.body,\n            b\"\"\"\\\n<html><head><link href=\"/base.css\" type=\"text/css\" rel=\"stylesheet\"/><link href=\"/foo.css\" type=\"text/css\" rel=\"stylesheet\"/>\n<style type=\"text/css\">\n.entry { margin-bottom: 1em; }\n</style>\n<meta>\n</head><body>\n\n\n<div class=\"entry\">...</div>\n\n\n<div class=\"entry\">...</div>\n\n<script src=\"/common.js\" type=\"text/javascript\"></script>\n<script type=\"text/javascript\">\n//<![CDATA[\njs_embed()\n//]]>\n</script>\n<script src=\"/analytics.js\"/>\n</body></html>\"\"\",  # noqa: E501\n        )\n\n    def test_optional_path(self):\n        self.assertEqual(self.fetch_json(\"/optional_path/foo\"), {u\"path\": u\"foo\"})\n        self.assertEqual(self.fetch_json(\"/optional_path/\"), {u\"path\": None})\n\n    def test_multi_header(self):\n        response = self.fetch(\"/multi_header\")\n        self.assertEqual(response.headers[\"x-overwrite\"], \"2\")\n        self.assertEqual(response.headers.get_list(\"x-multi\"), [\"3\", \"4\"])\n\n    def test_redirect(self):\n        response = self.fetch(\"/redirect?permanent=1\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        response = self.fetch(\"/redirect?permanent=0\", follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        response = self.fetch(\"/redirect?status=307\", follow_redirects=False)\n        self.assertEqual(response.code, 307)\n\n    def test_web_redirect(self):\n        response = self.fetch(\"/web_redirect_permanent\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/web_redirect_newpath\")\n        response = self.fetch(\"/web_redirect\", follow_redirects=False)\n        self.assertEqual(response.code, 302)\n        self.assertEqual(response.headers[\"Location\"], \"/web_redirect_newpath\")\n\n    def test_web_redirect_double_slash(self):\n        response = self.fetch(\"//web_redirect_double_slash\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/web_redirect_newpath\")\n\n    def test_header_injection(self):\n        response = self.fetch(\"/header_injection\")\n        self.assertEqual(response.body, b\"ok\")\n\n    def test_get_argument(self):\n        response = self.fetch(\"/get_argument?foo=bar\")\n        self.assertEqual(response.body, b\"bar\")\n        response = self.fetch(\"/get_argument?foo=\")\n        self.assertEqual(response.body, b\"\")\n        response = self.fetch(\"/get_argument\")\n        self.assertEqual(response.body, b\"default\")\n\n        # Test merging of query and body arguments.\n        # In singular form, body arguments take precedence over query arguments.\n        body = urllib.parse.urlencode(dict(foo=\"hello\"))\n        response = self.fetch(\"/get_argument?foo=bar\", method=\"POST\", body=body)\n        self.assertEqual(response.body, b\"hello\")\n        # In plural methods they are merged.\n        response = self.fetch(\"/get_arguments?foo=bar\", method=\"POST\", body=body)\n        self.assertEqual(\n            json_decode(response.body),\n            dict(default=[\"bar\", \"hello\"], query=[\"bar\"], body=[\"hello\"]),\n        )\n\n    def test_get_query_arguments(self):\n        # send as a post so we can ensure the separation between query\n        # string and body arguments.\n        body = urllib.parse.urlencode(dict(foo=\"hello\"))\n        response = self.fetch(\n            \"/get_argument?source=query&foo=bar\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"bar\")\n        response = self.fetch(\n            \"/get_argument?source=query&foo=\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"\")\n        response = self.fetch(\"/get_argument?source=query\", method=\"POST\", body=body)\n        self.assertEqual(response.body, b\"default\")\n\n    def test_get_body_arguments(self):\n        body = urllib.parse.urlencode(dict(foo=\"bar\"))\n        response = self.fetch(\n            \"/get_argument?source=body&foo=hello\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"bar\")\n\n        body = urllib.parse.urlencode(dict(foo=\"\"))\n        response = self.fetch(\n            \"/get_argument?source=body&foo=hello\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"\")\n\n        body = urllib.parse.urlencode(dict())\n        response = self.fetch(\n            \"/get_argument?source=body&foo=hello\", method=\"POST\", body=body\n        )\n        self.assertEqual(response.body, b\"default\")\n\n    def test_no_gzip(self):\n        response = self.fetch(\"/get_argument\")\n        self.assertNotIn(\"Accept-Encoding\", response.headers.get(\"Vary\", \"\"))\n        self.assertNotIn(\"gzip\", response.headers.get(\"Content-Encoding\", \"\"))\n\n\nclass NonWSGIWebTests(WebTestCase):\n    def get_handlers(self):\n        return [(\"/empty_flush\", EmptyFlushCallbackHandler)]\n\n    def test_empty_flush(self):\n        response = self.fetch(\"/empty_flush\")\n        self.assertEqual(response.body, b\"ok\")\n\n\nclass ErrorResponseTest(WebTestCase):\n    def get_handlers(self):\n        class DefaultHandler(RequestHandler):\n            def get(self):\n                if self.get_argument(\"status\", None):\n                    raise HTTPError(int(self.get_argument(\"status\")))\n                1 / 0\n\n        class WriteErrorHandler(RequestHandler):\n            def get(self):\n                if self.get_argument(\"status\", None):\n                    self.send_error(int(self.get_argument(\"status\")))\n                else:\n                    1 / 0\n\n            def write_error(self, status_code, **kwargs):\n                self.set_header(\"Content-Type\", \"text/plain\")\n                if \"exc_info\" in kwargs:\n                    self.write(\"Exception: %s\" % kwargs[\"exc_info\"][0].__name__)\n                else:\n                    self.write(\"Status: %d\" % status_code)\n\n        class FailedWriteErrorHandler(RequestHandler):\n            def get(self):\n                1 / 0\n\n            def write_error(self, status_code, **kwargs):\n                raise Exception(\"exception in write_error\")\n\n        return [\n            url(\"/default\", DefaultHandler),\n            url(\"/write_error\", WriteErrorHandler),\n            url(\"/failed_write_error\", FailedWriteErrorHandler),\n        ]\n\n    def test_default(self):\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/default\")\n            self.assertEqual(response.code, 500)\n            self.assertTrue(b\"500: Internal Server Error\" in response.body)\n\n            response = self.fetch(\"/default?status=503\")\n            self.assertEqual(response.code, 503)\n            self.assertTrue(b\"503: Service Unavailable\" in response.body)\n\n            response = self.fetch(\"/default?status=435\")\n            self.assertEqual(response.code, 435)\n            self.assertTrue(b\"435: Unknown\" in response.body)\n\n    def test_write_error(self):\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/write_error\")\n            self.assertEqual(response.code, 500)\n            self.assertEqual(b\"Exception: ZeroDivisionError\", response.body)\n\n            response = self.fetch(\"/write_error?status=503\")\n            self.assertEqual(response.code, 503)\n            self.assertEqual(b\"Status: 503\", response.body)\n\n    def test_failed_write_error(self):\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/failed_write_error\")\n            self.assertEqual(response.code, 500)\n            self.assertEqual(b\"\", response.body)\n\n\nclass StaticFileTest(WebTestCase):\n    # The expected MD5 hash of robots.txt, used in tests that call\n    # StaticFileHandler.get_version\n    robots_txt_hash = b\"f71d20196d4caf35b6a670db8c70b03d\"\n    static_dir = os.path.join(os.path.dirname(__file__), \"static\")\n\n    def get_handlers(self):\n        class StaticUrlHandler(RequestHandler):\n            def get(self, path):\n                with_v = int(self.get_argument(\"include_version\", 1))\n                self.write(self.static_url(path, include_version=with_v))\n\n        class AbsoluteStaticUrlHandler(StaticUrlHandler):\n            include_host = True\n\n        class OverrideStaticUrlHandler(RequestHandler):\n            def get(self, path):\n                do_include = bool(self.get_argument(\"include_host\"))\n                self.include_host = not do_include\n\n                regular_url = self.static_url(path)\n                override_url = self.static_url(path, include_host=do_include)\n                if override_url == regular_url:\n                    return self.write(str(False))\n\n                protocol = self.request.protocol + \"://\"\n                protocol_length = len(protocol)\n                check_regular = regular_url.find(protocol, 0, protocol_length)\n                check_override = override_url.find(protocol, 0, protocol_length)\n\n                if do_include:\n                    result = check_override == 0 and check_regular == -1\n                else:\n                    result = check_override == -1 and check_regular == 0\n                self.write(str(result))\n\n        return [\n            (\"/static_url/(.*)\", StaticUrlHandler),\n            (\"/abs_static_url/(.*)\", AbsoluteStaticUrlHandler),\n            (\"/override_static_url/(.*)\", OverrideStaticUrlHandler),\n            (\"/root_static/(.*)\", StaticFileHandler, dict(path=\"/\")),\n        ]\n\n    def get_app_kwargs(self):\n        return dict(static_path=relpath(\"static\"))\n\n    def test_static_files(self):\n        response = self.fetch(\"/robots.txt\")\n        self.assertTrue(b\"Disallow: /\" in response.body)\n\n        response = self.fetch(\"/static/robots.txt\")\n        self.assertTrue(b\"Disallow: /\" in response.body)\n        self.assertEqual(response.headers.get(\"Content-Type\"), \"text/plain\")\n\n    def test_static_compressed_files(self):\n        response = self.fetch(\"/static/sample.xml.gz\")\n        self.assertEqual(response.headers.get(\"Content-Type\"), \"application/gzip\")\n        response = self.fetch(\"/static/sample.xml.bz2\")\n        self.assertEqual(\n            response.headers.get(\"Content-Type\"), \"application/octet-stream\"\n        )\n        # make sure the uncompressed file still has the correct type\n        response = self.fetch(\"/static/sample.xml\")\n        self.assertTrue(\n            response.headers.get(\"Content-Type\") in set((\"text/xml\", \"application/xml\"))\n        )\n\n    def test_static_url(self):\n        response = self.fetch(\"/static_url/robots.txt\")\n        self.assertEqual(response.body, b\"/static/robots.txt?v=\" + self.robots_txt_hash)\n\n    def test_absolute_static_url(self):\n        response = self.fetch(\"/abs_static_url/robots.txt\")\n        self.assertEqual(\n            response.body,\n            (utf8(self.get_url(\"/\")) + b\"static/robots.txt?v=\" + self.robots_txt_hash),\n        )\n\n    def test_relative_version_exclusion(self):\n        response = self.fetch(\"/static_url/robots.txt?include_version=0\")\n        self.assertEqual(response.body, b\"/static/robots.txt\")\n\n    def test_absolute_version_exclusion(self):\n        response = self.fetch(\"/abs_static_url/robots.txt?include_version=0\")\n        self.assertEqual(response.body, utf8(self.get_url(\"/\") + \"static/robots.txt\"))\n\n    def test_include_host_override(self):\n        self._trigger_include_host_check(False)\n        self._trigger_include_host_check(True)\n\n    def _trigger_include_host_check(self, include_host):\n        path = \"/override_static_url/robots.txt?include_host=%s\"\n        response = self.fetch(path % int(include_host))\n        self.assertEqual(response.body, utf8(str(True)))\n\n    def get_and_head(self, *args, **kwargs):\n        \"\"\"Performs a GET and HEAD request and returns the GET response.\n\n        Fails if any ``Content-*`` headers returned by the two requests\n        differ.\n        \"\"\"\n        head_response = self.fetch(*args, method=\"HEAD\", **kwargs)\n        get_response = self.fetch(*args, method=\"GET\", **kwargs)\n        content_headers = set()\n        for h in itertools.chain(head_response.headers, get_response.headers):\n            if h.startswith(\"Content-\"):\n                content_headers.add(h)\n        for h in content_headers:\n            self.assertEqual(\n                head_response.headers.get(h),\n                get_response.headers.get(h),\n                \"%s differs between GET (%s) and HEAD (%s)\"\n                % (h, head_response.headers.get(h), get_response.headers.get(h)),\n            )\n        return get_response\n\n    def test_static_304_if_modified_since(self):\n        response1 = self.get_and_head(\"/static/robots.txt\")\n        response2 = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": response1.headers[\"Last-Modified\"]},\n        )\n        self.assertEqual(response2.code, 304)\n        self.assertTrue(\"Content-Length\" not in response2.headers)\n        self.assertTrue(\"Last-Modified\" not in response2.headers)\n\n    def test_static_304_if_none_match(self):\n        response1 = self.get_and_head(\"/static/robots.txt\")\n        response2 = self.get_and_head(\n            \"/static/robots.txt\", headers={\"If-None-Match\": response1.headers[\"Etag\"]}\n        )\n        self.assertEqual(response2.code, 304)\n\n    def test_static_304_etag_modified_bug(self):\n        response1 = self.get_and_head(\"/static/robots.txt\")\n        response2 = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\n                \"If-None-Match\": '\"MISMATCH\"',\n                \"If-Modified-Since\": response1.headers[\"Last-Modified\"],\n            },\n        )\n        self.assertEqual(response2.code, 200)\n\n    def test_static_if_modified_since_pre_epoch(self):\n        # On windows, the functions that work with time_t do not accept\n        # negative values, and at least one client (processing.js) seems\n        # to use if-modified-since 1/1/1960 as a cache-busting technique.\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": \"Fri, 01 Jan 1960 00:00:00 GMT\"},\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_static_if_modified_since_time_zone(self):\n        # Instead of the value from Last-Modified, make requests with times\n        # chosen just before and after the known modification time\n        # of the file to ensure that the right time zone is being used\n        # when parsing If-Modified-Since.\n        stat = os.stat(relpath(\"static/robots.txt\"))\n\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": format_timestamp(stat.st_mtime - 1)},\n        )\n        self.assertEqual(response.code, 200)\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\"If-Modified-Since\": format_timestamp(stat.st_mtime + 1)},\n        )\n        self.assertEqual(response.code, 304)\n\n    def test_static_etag(self):\n        response = self.get_and_head(\"/static/robots.txt\")\n        self.assertEqual(\n            utf8(response.headers.get(\"Etag\")), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_with_range(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=0-9\"}\n        )\n        self.assertEqual(response.code, 206)\n        self.assertEqual(response.body, b\"User-agent\")\n        self.assertEqual(\n            utf8(response.headers.get(\"Etag\")), b'\"' + self.robots_txt_hash + b'\"'\n        )\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"10\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 0-9/26\")\n\n    def test_static_with_range_full_file(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=0-\"}\n        )\n        # Note: Chrome refuses to play audio if it gets an HTTP 206 in response\n        # to ``Range: bytes=0-`` :(\n        self.assertEqual(response.code, 200)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"26\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), None)\n\n    def test_static_with_range_full_past_end(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=0-10000000\"}\n        )\n        self.assertEqual(response.code, 200)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"26\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), None)\n\n    def test_static_with_range_partial_past_end(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=1-10000000\"}\n        )\n        self.assertEqual(response.code, 206)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()[1:]))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"25\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 1-25/26\")\n\n    def test_static_with_range_end_edge(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=22-\"}\n        )\n        self.assertEqual(response.body, b\": /\\n\")\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"4\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 22-25/26\")\n\n    def test_static_with_range_neg_end(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=-4\"}\n        )\n        self.assertEqual(response.body, b\": /\\n\")\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"4\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes 22-25/26\")\n\n    def test_static_with_range_neg_past_start(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=-1000000\"}\n        )\n        self.assertEqual(response.code, 200)\n        robots_file_path = os.path.join(self.static_dir, \"robots.txt\")\n        with open(robots_file_path) as f:\n            self.assertEqual(response.body, utf8(f.read()))\n        self.assertEqual(response.headers.get(\"Content-Length\"), \"26\")\n        self.assertEqual(response.headers.get(\"Content-Range\"), None)\n\n    def test_static_invalid_range(self):\n        response = self.get_and_head(\"/static/robots.txt\", headers={\"Range\": \"asdf\"})\n        self.assertEqual(response.code, 200)\n\n    def test_static_unsatisfiable_range_zero_suffix(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=-0\"}\n        )\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\n        self.assertEqual(response.code, 416)\n\n    def test_static_unsatisfiable_range_invalid_start(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=26\"}\n        )\n        self.assertEqual(response.code, 416)\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\n\n    def test_static_unsatisfiable_range_end_less_than_start(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\", headers={\"Range\": \"bytes=10-3\"}\n        )\n        self.assertEqual(response.code, 416)\n        self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\n\n    def test_static_head(self):\n        response = self.fetch(\"/static/robots.txt\", method=\"HEAD\")\n        self.assertEqual(response.code, 200)\n        # No body was returned, but we did get the right content length.\n        self.assertEqual(response.body, b\"\")\n        self.assertEqual(response.headers[\"Content-Length\"], \"26\")\n        self.assertEqual(\n            utf8(response.headers[\"Etag\"]), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_head_range(self):\n        response = self.fetch(\n            \"/static/robots.txt\", method=\"HEAD\", headers={\"Range\": \"bytes=1-4\"}\n        )\n        self.assertEqual(response.code, 206)\n        self.assertEqual(response.body, b\"\")\n        self.assertEqual(response.headers[\"Content-Length\"], \"4\")\n        self.assertEqual(\n            utf8(response.headers[\"Etag\"]), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_range_if_none_match(self):\n        response = self.get_and_head(\n            \"/static/robots.txt\",\n            headers={\n                \"Range\": \"bytes=1-4\",\n                \"If-None-Match\": b'\"' + self.robots_txt_hash + b'\"',\n            },\n        )\n        self.assertEqual(response.code, 304)\n        self.assertEqual(response.body, b\"\")\n        self.assertTrue(\"Content-Length\" not in response.headers)\n        self.assertEqual(\n            utf8(response.headers[\"Etag\"]), b'\"' + self.robots_txt_hash + b'\"'\n        )\n\n    def test_static_404(self):\n        response = self.get_and_head(\"/static/blarg\")\n        self.assertEqual(response.code, 404)\n\n    def test_path_traversal_protection(self):\n        # curl_httpclient processes \"..\" on the client side, so we\n        # must test this with simple_httpclient.\n        self.http_client.close()\n        self.http_client = SimpleAsyncHTTPClient()\n        with ExpectLog(gen_log, \".*not in root static directory\"):\n            response = self.get_and_head(\"/static/../static_foo.txt\")\n        # Attempted path traversal should result in 403, not 200\n        # (which means the check failed and the file was served)\n        # or 404 (which means that the file didn't exist and\n        # is probably a packaging error).\n        self.assertEqual(response.code, 403)\n\n    @unittest.skipIf(os.name != \"posix\", \"non-posix OS\")\n    def test_root_static_path(self):\n        # Sometimes people set the StaticFileHandler's path to '/'\n        # to disable Tornado's path validation (in conjunction with\n        # their own validation in get_absolute_path). Make sure\n        # that the stricter validation in 4.2.1 doesn't break them.\n        path = os.path.join(\n            os.path.dirname(os.path.abspath(__file__)), \"static/robots.txt\"\n        )\n        response = self.get_and_head(\"/root_static\" + urllib.parse.quote(path))\n        self.assertEqual(response.code, 200)\n\n\nclass StaticDefaultFilenameTest(WebTestCase):\n    def get_app_kwargs(self):\n        return dict(\n            static_path=relpath(\"static\"),\n            static_handler_args=dict(default_filename=\"index.html\"),\n        )\n\n    def get_handlers(self):\n        return []\n\n    def test_static_default_filename(self):\n        response = self.fetch(\"/static/dir/\", follow_redirects=False)\n        self.assertEqual(response.code, 200)\n        self.assertEqual(b\"this is the index\\n\", response.body)\n\n    def test_static_default_redirect(self):\n        response = self.fetch(\"/static/dir\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertTrue(response.headers[\"Location\"].endswith(\"/static/dir/\"))\n\n\nclass StaticFileWithPathTest(WebTestCase):\n    def get_app_kwargs(self):\n        return dict(\n            static_path=relpath(\"static\"),\n            static_handler_args=dict(default_filename=\"index.html\"),\n        )\n\n    def get_handlers(self):\n        return [(\"/foo/(.*)\", StaticFileHandler, {\"path\": relpath(\"templates/\")})]\n\n    def test_serve(self):\n        response = self.fetch(\"/foo/utf8.html\")\n        self.assertEqual(response.body, b\"H\\xc3\\xa9llo\\n\")\n\n\nclass CustomStaticFileTest(WebTestCase):\n    def get_handlers(self):\n        class MyStaticFileHandler(StaticFileHandler):\n            @classmethod\n            def make_static_url(cls, settings, path):\n                version_hash = cls.get_version(settings, path)\n                extension_index = path.rindex(\".\")\n                before_version = path[:extension_index]\n                after_version = path[(extension_index + 1) :]\n                return \"/static/%s.%s.%s\" % (\n                    before_version,\n                    version_hash,\n                    after_version,\n                )\n\n            def parse_url_path(self, url_path):\n                extension_index = url_path.rindex(\".\")\n                version_index = url_path.rindex(\".\", 0, extension_index)\n                return \"%s%s\" % (url_path[:version_index], url_path[extension_index:])\n\n            @classmethod\n            def get_absolute_path(cls, settings, path):\n                return \"CustomStaticFileTest:\" + path\n\n            def validate_absolute_path(self, root, absolute_path):\n                return absolute_path\n\n            @classmethod\n            def get_content(self, path, start=None, end=None):\n                assert start is None and end is None\n                if path == \"CustomStaticFileTest:foo.txt\":\n                    return b\"bar\"\n                raise Exception(\"unexpected path %r\" % path)\n\n            def get_content_size(self):\n                if self.absolute_path == \"CustomStaticFileTest:foo.txt\":\n                    return 3\n                raise Exception(\"unexpected path %r\" % self.absolute_path)\n\n            def get_modified_time(self):\n                return None\n\n            @classmethod\n            def get_version(cls, settings, path):\n                return \"42\"\n\n        class StaticUrlHandler(RequestHandler):\n            def get(self, path):\n                self.write(self.static_url(path))\n\n        self.static_handler_class = MyStaticFileHandler\n\n        return [(\"/static_url/(.*)\", StaticUrlHandler)]\n\n    def get_app_kwargs(self):\n        return dict(static_path=\"dummy\", static_handler_class=self.static_handler_class)\n\n    def test_serve(self):\n        response = self.fetch(\"/static/foo.42.txt\")\n        self.assertEqual(response.body, b\"bar\")\n\n    def test_static_url(self):\n        with ExpectLog(gen_log, \"Could not open static file\", required=False):\n            response = self.fetch(\"/static_url/foo.txt\")\n            self.assertEqual(response.body, b\"/static/foo.42.txt\")\n\n\nclass HostMatchingTest(WebTestCase):\n    class Handler(RequestHandler):\n        def initialize(self, reply):\n            self.reply = reply\n\n        def get(self):\n            self.write(self.reply)\n\n    def get_handlers(self):\n        return [(\"/foo\", HostMatchingTest.Handler, {\"reply\": \"wildcard\"})]\n\n    def test_host_matching(self):\n        self.app.add_handlers(\n            \"www.example.com\", [(\"/foo\", HostMatchingTest.Handler, {\"reply\": \"[0]\"})]\n        )\n        self.app.add_handlers(\n            r\"www\\.example\\.com\", [(\"/bar\", HostMatchingTest.Handler, {\"reply\": \"[1]\"})]\n        )\n        self.app.add_handlers(\n            \"www.example.com\", [(\"/baz\", HostMatchingTest.Handler, {\"reply\": \"[2]\"})]\n        )\n        self.app.add_handlers(\n            \"www.e.*e.com\", [(\"/baz\", HostMatchingTest.Handler, {\"reply\": \"[3]\"})]\n        )\n\n        response = self.fetch(\"/foo\")\n        self.assertEqual(response.body, b\"wildcard\")\n        response = self.fetch(\"/bar\")\n        self.assertEqual(response.code, 404)\n        response = self.fetch(\"/baz\")\n        self.assertEqual(response.code, 404)\n\n        response = self.fetch(\"/foo\", headers={\"Host\": \"www.example.com\"})\n        self.assertEqual(response.body, b\"[0]\")\n        response = self.fetch(\"/bar\", headers={\"Host\": \"www.example.com\"})\n        self.assertEqual(response.body, b\"[1]\")\n        response = self.fetch(\"/baz\", headers={\"Host\": \"www.example.com\"})\n        self.assertEqual(response.body, b\"[2]\")\n        response = self.fetch(\"/baz\", headers={\"Host\": \"www.exe.com\"})\n        self.assertEqual(response.body, b\"[3]\")\n\n\nclass DefaultHostMatchingTest(WebTestCase):\n    def get_handlers(self):\n        return []\n\n    def get_app_kwargs(self):\n        return {\"default_host\": \"www.example.com\"}\n\n    def test_default_host_matching(self):\n        self.app.add_handlers(\n            \"www.example.com\", [(\"/foo\", HostMatchingTest.Handler, {\"reply\": \"[0]\"})]\n        )\n        self.app.add_handlers(\n            r\"www\\.example\\.com\", [(\"/bar\", HostMatchingTest.Handler, {\"reply\": \"[1]\"})]\n        )\n        self.app.add_handlers(\n            \"www.test.com\", [(\"/baz\", HostMatchingTest.Handler, {\"reply\": \"[2]\"})]\n        )\n\n        response = self.fetch(\"/foo\")\n        self.assertEqual(response.body, b\"[0]\")\n        response = self.fetch(\"/bar\")\n        self.assertEqual(response.body, b\"[1]\")\n        response = self.fetch(\"/baz\")\n        self.assertEqual(response.code, 404)\n\n        response = self.fetch(\"/foo\", headers={\"X-Real-Ip\": \"127.0.0.1\"})\n        self.assertEqual(response.code, 404)\n\n        self.app.default_host = \"www.test.com\"\n\n        response = self.fetch(\"/baz\")\n        self.assertEqual(response.body, b\"[2]\")\n\n\nclass NamedURLSpecGroupsTest(WebTestCase):\n    def get_handlers(self):\n        class EchoHandler(RequestHandler):\n            def get(self, path):\n                self.write(path)\n\n        return [\n            (\"/str/(?P<path>.*)\", EchoHandler),\n            (u\"/unicode/(?P<path>.*)\", EchoHandler),\n        ]\n\n    def test_named_urlspec_groups(self):\n        response = self.fetch(\"/str/foo\")\n        self.assertEqual(response.body, b\"foo\")\n\n        response = self.fetch(\"/unicode/bar\")\n        self.assertEqual(response.body, b\"bar\")\n\n\nclass ClearHeaderTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_header(\"h1\", \"foo\")\n            self.set_header(\"h2\", \"bar\")\n            self.clear_header(\"h1\")\n            self.clear_header(\"nonexistent\")\n\n    def test_clear_header(self):\n        response = self.fetch(\"/\")\n        self.assertTrue(\"h1\" not in response.headers)\n        self.assertEqual(response.headers[\"h2\"], \"bar\")\n\n\nclass Header204Test(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_status(204)\n            self.finish()\n\n    def test_204_headers(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 204)\n        self.assertNotIn(\"Content-Length\", response.headers)\n        self.assertNotIn(\"Transfer-Encoding\", response.headers)\n\n\nclass Header304Test(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_header(\"Content-Language\", \"en_US\")\n            self.write(\"hello\")\n\n    def test_304_headers(self):\n        response1 = self.fetch(\"/\")\n        self.assertEqual(response1.headers[\"Content-Length\"], \"5\")\n        self.assertEqual(response1.headers[\"Content-Language\"], \"en_US\")\n\n        response2 = self.fetch(\n            \"/\", headers={\"If-None-Match\": response1.headers[\"Etag\"]}\n        )\n        self.assertEqual(response2.code, 304)\n        self.assertTrue(\"Content-Length\" not in response2.headers)\n        self.assertTrue(\"Content-Language\" not in response2.headers)\n        # Not an entity header, but should not be added to 304s by chunking\n        self.assertTrue(\"Transfer-Encoding\" not in response2.headers)\n\n\nclass StatusReasonTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            reason = self.request.arguments.get(\"reason\", [])\n            self.set_status(\n                int(self.get_argument(\"code\")), reason=reason[0] if reason else None\n            )\n\n    def get_http_client(self):\n        # simple_httpclient only: curl doesn't expose the reason string\n        return SimpleAsyncHTTPClient()\n\n    def test_status(self):\n        response = self.fetch(\"/?code=304\")\n        self.assertEqual(response.code, 304)\n        self.assertEqual(response.reason, \"Not Modified\")\n        response = self.fetch(\"/?code=304&reason=Foo\")\n        self.assertEqual(response.code, 304)\n        self.assertEqual(response.reason, \"Foo\")\n        response = self.fetch(\"/?code=682&reason=Bar\")\n        self.assertEqual(response.code, 682)\n        self.assertEqual(response.reason, \"Bar\")\n        response = self.fetch(\"/?code=682\")\n        self.assertEqual(response.code, 682)\n        self.assertEqual(response.reason, \"Unknown\")\n\n\nclass DateHeaderTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.write(\"hello\")\n\n    def test_date_header(self):\n        response = self.fetch(\"/\")\n        parsed = email.utils.parsedate(response.headers[\"Date\"])\n        assert parsed is not None\n        header_date = datetime.datetime(*parsed[:6])\n        self.assertTrue(\n            header_date - datetime.datetime.utcnow() < datetime.timedelta(seconds=2)\n        )\n\n\nclass RaiseWithReasonTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            raise HTTPError(682, reason=\"Foo\")\n\n    def get_http_client(self):\n        # simple_httpclient only: curl doesn't expose the reason string\n        return SimpleAsyncHTTPClient()\n\n    def test_raise_with_reason(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 682)\n        self.assertEqual(response.reason, \"Foo\")\n        self.assertIn(b\"682: Foo\", response.body)\n\n    def test_httperror_str(self):\n        self.assertEqual(str(HTTPError(682, reason=\"Foo\")), \"HTTP 682: Foo\")\n\n    def test_httperror_str_from_httputil(self):\n        self.assertEqual(str(HTTPError(682)), \"HTTP 682: Unknown\")\n\n\nclass ErrorHandlerXSRFTest(WebTestCase):\n    def get_handlers(self):\n        # note that if the handlers list is empty we get the default_host\n        # redirect fallback instead of a 404, so test with both an\n        # explicitly defined error handler and an implicit 404.\n        return [(\"/error\", ErrorHandler, dict(status_code=417))]\n\n    def get_app_kwargs(self):\n        return dict(xsrf_cookies=True)\n\n    def test_error_xsrf(self):\n        response = self.fetch(\"/error\", method=\"POST\", body=\"\")\n        self.assertEqual(response.code, 417)\n\n    def test_404_xsrf(self):\n        response = self.fetch(\"/404\", method=\"POST\", body=\"\")\n        self.assertEqual(response.code, 404)\n\n\nclass GzipTestCase(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            for v in self.get_arguments(\"vary\"):\n                self.add_header(\"Vary\", v)\n            # Must write at least MIN_LENGTH bytes to activate compression.\n            self.write(\"hello world\" + (\"!\" * GZipContentEncoding.MIN_LENGTH))\n\n    def get_app_kwargs(self):\n        return dict(\n            gzip=True, static_path=os.path.join(os.path.dirname(__file__), \"static\")\n        )\n\n    def assert_compressed(self, response):\n        # simple_httpclient renames the content-encoding header;\n        # curl_httpclient doesn't.\n        self.assertEqual(\n            response.headers.get(\n                \"Content-Encoding\", response.headers.get(\"X-Consumed-Content-Encoding\")\n            ),\n            \"gzip\",\n        )\n\n    def test_gzip(self):\n        response = self.fetch(\"/\")\n        self.assert_compressed(response)\n        self.assertEqual(response.headers[\"Vary\"], \"Accept-Encoding\")\n\n    def test_gzip_static(self):\n        # The streaming responses in StaticFileHandler have subtle\n        # interactions with the gzip output so test this case separately.\n        response = self.fetch(\"/robots.txt\")\n        self.assert_compressed(response)\n        self.assertEqual(response.headers[\"Vary\"], \"Accept-Encoding\")\n\n    def test_gzip_not_requested(self):\n        response = self.fetch(\"/\", use_gzip=False)\n        self.assertNotIn(\"Content-Encoding\", response.headers)\n        self.assertEqual(response.headers[\"Vary\"], \"Accept-Encoding\")\n\n    def test_vary_already_present(self):\n        response = self.fetch(\"/?vary=Accept-Language\")\n        self.assert_compressed(response)\n        self.assertEqual(\n            [s.strip() for s in response.headers[\"Vary\"].split(\",\")],\n            [\"Accept-Language\", \"Accept-Encoding\"],\n        )\n\n    def test_vary_already_present_multiple(self):\n        # Regression test for https://github.com/tornadoweb/tornado/issues/1670\n        response = self.fetch(\"/?vary=Accept-Language&vary=Cookie\")\n        self.assert_compressed(response)\n        self.assertEqual(\n            [s.strip() for s in response.headers[\"Vary\"].split(\",\")],\n            [\"Accept-Language\", \"Cookie\", \"Accept-Encoding\"],\n        )\n\n\nclass PathArgsInPrepareTest(WebTestCase):\n    class Handler(RequestHandler):\n        def prepare(self):\n            self.write(dict(args=self.path_args, kwargs=self.path_kwargs))\n\n        def get(self, path):\n            assert path == \"foo\"\n            self.finish()\n\n    def get_handlers(self):\n        return [(\"/pos/(.*)\", self.Handler), (\"/kw/(?P<path>.*)\", self.Handler)]\n\n    def test_pos(self):\n        response = self.fetch(\"/pos/foo\")\n        response.rethrow()\n        data = json_decode(response.body)\n        self.assertEqual(data, {\"args\": [\"foo\"], \"kwargs\": {}})\n\n    def test_kw(self):\n        response = self.fetch(\"/kw/foo\")\n        response.rethrow()\n        data = json_decode(response.body)\n        self.assertEqual(data, {\"args\": [], \"kwargs\": {\"path\": \"foo\"}})\n\n\nclass ClearAllCookiesTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.clear_all_cookies()\n            self.write(\"ok\")\n\n    def test_clear_all_cookies(self):\n        response = self.fetch(\"/\", headers={\"Cookie\": \"foo=bar; baz=xyzzy\"})\n        set_cookies = sorted(response.headers.get_list(\"Set-Cookie\"))\n        # Python 3.5 sends 'baz=\"\";'; older versions use 'baz=;'\n        self.assertTrue(\n            set_cookies[0].startswith(\"baz=;\") or set_cookies[0].startswith('baz=\"\";')\n        )\n        self.assertTrue(\n            set_cookies[1].startswith(\"foo=;\") or set_cookies[1].startswith('foo=\"\";')\n        )\n\n\nclass PermissionError(Exception):\n    pass\n\n\nclass ExceptionHandlerTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            exc = self.get_argument(\"exc\")\n            if exc == \"http\":\n                raise HTTPError(410, \"no longer here\")\n            elif exc == \"zero\":\n                1 / 0\n            elif exc == \"permission\":\n                raise PermissionError(\"not allowed\")\n\n        def write_error(self, status_code, **kwargs):\n            if \"exc_info\" in kwargs:\n                typ, value, tb = kwargs[\"exc_info\"]\n                if isinstance(value, PermissionError):\n                    self.set_status(403)\n                    self.write(\"PermissionError\")\n                    return\n            RequestHandler.write_error(self, status_code, **kwargs)\n\n        def log_exception(self, typ, value, tb):\n            if isinstance(value, PermissionError):\n                app_log.warning(\"custom logging for PermissionError: %s\", value.args[0])\n            else:\n                RequestHandler.log_exception(self, typ, value, tb)\n\n    def test_http_error(self):\n        # HTTPErrors are logged as warnings with no stack trace.\n        # TODO: extend ExpectLog to test this more precisely\n        with ExpectLog(gen_log, \".*no longer here\"):\n            response = self.fetch(\"/?exc=http\")\n            self.assertEqual(response.code, 410)\n\n    def test_unknown_error(self):\n        # Unknown errors are logged as errors with a stack trace.\n        with ExpectLog(app_log, \"Uncaught exception\"):\n            response = self.fetch(\"/?exc=zero\")\n            self.assertEqual(response.code, 500)\n\n    def test_known_error(self):\n        # log_exception can override logging behavior, and write_error\n        # can override the response.\n        with ExpectLog(app_log, \"custom logging for PermissionError: not allowed\"):\n            response = self.fetch(\"/?exc=permission\")\n            self.assertEqual(response.code, 403)\n\n\nclass BuggyLoggingTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            1 / 0\n\n        def log_exception(self, typ, value, tb):\n            1 / 0\n\n    def test_buggy_log_exception(self):\n        # Something gets logged even though the application's\n        # logger is broken.\n        with ExpectLog(app_log, \".*\"):\n            self.fetch(\"/\")\n\n\nclass UIMethodUIModuleTest(SimpleHandlerTestCase):\n    \"\"\"Test that UI methods and modules are created correctly and\n    associated with the handler.\n    \"\"\"\n\n    class Handler(RequestHandler):\n        def get(self):\n            self.render(\"foo.html\")\n\n        def value(self):\n            return self.get_argument(\"value\")\n\n    def get_app_kwargs(self):\n        def my_ui_method(handler, x):\n            return \"In my_ui_method(%s) with handler value %s.\" % (x, handler.value())\n\n        class MyModule(UIModule):\n            def render(self, x):\n                return \"In MyModule(%s) with handler value %s.\" % (\n                    x,\n                    self.handler.value(),\n                )\n\n        loader = DictLoader(\n            {\"foo.html\": \"{{ my_ui_method(42) }} {% module MyModule(123) %}\"}\n        )\n        return dict(\n            template_loader=loader,\n            ui_methods={\"my_ui_method\": my_ui_method},\n            ui_modules={\"MyModule\": MyModule},\n        )\n\n    def tearDown(self):\n        super(UIMethodUIModuleTest, self).tearDown()\n        # TODO: fix template loader caching so this isn't necessary.\n        RequestHandler._template_loaders.clear()\n\n    def test_ui_method(self):\n        response = self.fetch(\"/?value=asdf\")\n        self.assertEqual(\n            response.body,\n            b\"In my_ui_method(42) with handler value asdf. \"\n            b\"In MyModule(123) with handler value asdf.\",\n        )\n\n\nclass GetArgumentErrorTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            try:\n                self.get_argument(\"foo\")\n                self.write({})\n            except MissingArgumentError as e:\n                self.write({\"arg_name\": e.arg_name, \"log_message\": e.log_message})\n\n    def test_catch_error(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(\n            json_decode(response.body),\n            {\"arg_name\": \"foo\", \"log_message\": \"Missing argument foo\"},\n        )\n\n\nclass SetLazyPropertiesTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def prepare(self):\n            self.current_user = \"Ben\"\n            self.locale = locale.get(\"en_US\")\n\n        def get_user_locale(self):\n            raise NotImplementedError()\n\n        def get_current_user(self):\n            raise NotImplementedError()\n\n        def get(self):\n            self.write(\"Hello %s (%s)\" % (self.current_user, self.locale.code))\n\n    def test_set_properties(self):\n        # Ensure that current_user can be assigned to normally for apps\n        # that want to forgo the lazy get_current_user property\n        response = self.fetch(\"/\")\n        self.assertEqual(response.body, b\"Hello Ben (en_US)\")\n\n\nclass GetCurrentUserTest(WebTestCase):\n    def get_app_kwargs(self):\n        class WithoutUserModule(UIModule):\n            def render(self):\n                return \"\"\n\n        class WithUserModule(UIModule):\n            def render(self):\n                return str(self.current_user)\n\n        loader = DictLoader(\n            {\n                \"without_user.html\": \"\",\n                \"with_user.html\": \"{{ current_user }}\",\n                \"without_user_module.html\": \"{% module WithoutUserModule() %}\",\n                \"with_user_module.html\": \"{% module WithUserModule() %}\",\n            }\n        )\n        return dict(\n            template_loader=loader,\n            ui_modules={\n                \"WithUserModule\": WithUserModule,\n                \"WithoutUserModule\": WithoutUserModule,\n            },\n        )\n\n    def tearDown(self):\n        super(GetCurrentUserTest, self).tearDown()\n        RequestHandler._template_loaders.clear()\n\n    def get_handlers(self):\n        class CurrentUserHandler(RequestHandler):\n            def prepare(self):\n                self.has_loaded_current_user = False\n\n            def get_current_user(self):\n                self.has_loaded_current_user = True\n                return \"\"\n\n        class WithoutUserHandler(CurrentUserHandler):\n            def get(self):\n                self.render_string(\"without_user.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        class WithUserHandler(CurrentUserHandler):\n            def get(self):\n                self.render_string(\"with_user.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        class CurrentUserModuleHandler(CurrentUserHandler):\n            def get_template_namespace(self):\n                # If RequestHandler.get_template_namespace is called, then\n                # get_current_user is evaluated. Until #820 is fixed, this\n                # is a small hack to circumvent the issue.\n                return self.ui\n\n        class WithoutUserModuleHandler(CurrentUserModuleHandler):\n            def get(self):\n                self.render_string(\"without_user_module.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        class WithUserModuleHandler(CurrentUserModuleHandler):\n            def get(self):\n                self.render_string(\"with_user_module.html\")\n                self.finish(str(self.has_loaded_current_user))\n\n        return [\n            (\"/without_user\", WithoutUserHandler),\n            (\"/with_user\", WithUserHandler),\n            (\"/without_user_module\", WithoutUserModuleHandler),\n            (\"/with_user_module\", WithUserModuleHandler),\n        ]\n\n    @unittest.skip(\"needs fix\")\n    def test_get_current_user_is_lazy(self):\n        # TODO: Make this test pass. See #820.\n        response = self.fetch(\"/without_user\")\n        self.assertEqual(response.body, b\"False\")\n\n    def test_get_current_user_works(self):\n        response = self.fetch(\"/with_user\")\n        self.assertEqual(response.body, b\"True\")\n\n    def test_get_current_user_from_ui_module_is_lazy(self):\n        response = self.fetch(\"/without_user_module\")\n        self.assertEqual(response.body, b\"False\")\n\n    def test_get_current_user_from_ui_module_works(self):\n        response = self.fetch(\"/with_user_module\")\n        self.assertEqual(response.body, b\"True\")\n\n\nclass UnimplementedHTTPMethodsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        pass\n\n    def test_unimplemented_standard_methods(self):\n        for method in [\"HEAD\", \"GET\", \"DELETE\", \"OPTIONS\"]:\n            response = self.fetch(\"/\", method=method)\n            self.assertEqual(response.code, 405)\n        for method in [\"POST\", \"PUT\"]:\n            response = self.fetch(\"/\", method=method, body=b\"\")\n            self.assertEqual(response.code, 405)\n\n\nclass UnimplementedNonStandardMethodsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def other(self):\n            # Even though this method exists, it won't get called automatically\n            # because it is not in SUPPORTED_METHODS.\n            self.write(\"other\")\n\n    def test_unimplemented_patch(self):\n        # PATCH is recently standardized; Tornado supports it by default\n        # but wsgiref.validate doesn't like it.\n        response = self.fetch(\"/\", method=\"PATCH\", body=b\"\")\n        self.assertEqual(response.code, 405)\n\n    def test_unimplemented_other(self):\n        response = self.fetch(\"/\", method=\"OTHER\", allow_nonstandard_methods=True)\n        self.assertEqual(response.code, 405)\n\n\nclass AllHTTPMethodsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def method(self):\n            self.write(self.request.method)\n\n        get = delete = options = post = put = method  # type: ignore\n\n    def test_standard_methods(self):\n        response = self.fetch(\"/\", method=\"HEAD\")\n        self.assertEqual(response.body, b\"\")\n        for method in [\"GET\", \"DELETE\", \"OPTIONS\"]:\n            response = self.fetch(\"/\", method=method)\n            self.assertEqual(response.body, utf8(method))\n        for method in [\"POST\", \"PUT\"]:\n            response = self.fetch(\"/\", method=method, body=b\"\")\n            self.assertEqual(response.body, utf8(method))\n\n\nclass PatchMethodTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        SUPPORTED_METHODS = RequestHandler.SUPPORTED_METHODS + (  # type: ignore\n            \"OTHER\",\n        )\n\n        def patch(self):\n            self.write(\"patch\")\n\n        def other(self):\n            self.write(\"other\")\n\n    def test_patch(self):\n        response = self.fetch(\"/\", method=\"PATCH\", body=b\"\")\n        self.assertEqual(response.body, b\"patch\")\n\n    def test_other(self):\n        response = self.fetch(\"/\", method=\"OTHER\", allow_nonstandard_methods=True)\n        self.assertEqual(response.body, b\"other\")\n\n\nclass FinishInPrepareTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def prepare(self):\n            self.finish(\"done\")\n\n        def get(self):\n            # It's difficult to assert for certain that a method did not\n            # or will not be called in an asynchronous context, but this\n            # will be logged noisily if it is reached.\n            raise Exception(\"should not reach this method\")\n\n    def test_finish_in_prepare(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.body, b\"done\")\n\n\nclass Default404Test(WebTestCase):\n    def get_handlers(self):\n        # If there are no handlers at all a default redirect handler gets added.\n        return [(\"/foo\", RequestHandler)]\n\n    def test_404(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 404)\n        self.assertEqual(\n            response.body,\n            b\"<html><title>404: Not Found</title>\"\n            b\"<body>404: Not Found</body></html>\",\n        )\n\n\nclass Custom404Test(WebTestCase):\n    def get_handlers(self):\n        return [(\"/foo\", RequestHandler)]\n\n    def get_app_kwargs(self):\n        class Custom404Handler(RequestHandler):\n            def get(self):\n                self.set_status(404)\n                self.write(\"custom 404 response\")\n\n        return dict(default_handler_class=Custom404Handler)\n\n    def test_404(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 404)\n        self.assertEqual(response.body, b\"custom 404 response\")\n\n\nclass DefaultHandlerArgumentsTest(WebTestCase):\n    def get_handlers(self):\n        return [(\"/foo\", RequestHandler)]\n\n    def get_app_kwargs(self):\n        return dict(\n            default_handler_class=ErrorHandler,\n            default_handler_args=dict(status_code=403),\n        )\n\n    def test_403(self):\n        response = self.fetch(\"/\")\n        self.assertEqual(response.code, 403)\n\n\nclass HandlerByNameTest(WebTestCase):\n    def get_handlers(self):\n        # All three are equivalent.\n        return [\n            (\"/hello1\", HelloHandler),\n            (\"/hello2\", \"tornado.test.web_test.HelloHandler\"),\n            url(\"/hello3\", \"tornado.test.web_test.HelloHandler\"),\n        ]\n\n    def test_handler_by_name(self):\n        resp = self.fetch(\"/hello1\")\n        self.assertEqual(resp.body, b\"hello\")\n        resp = self.fetch(\"/hello2\")\n        self.assertEqual(resp.body, b\"hello\")\n        resp = self.fetch(\"/hello3\")\n        self.assertEqual(resp.body, b\"hello\")\n\n\nclass StreamingRequestBodyTest(WebTestCase):\n    def get_handlers(self):\n        @stream_request_body\n        class StreamingBodyHandler(RequestHandler):\n            def initialize(self, test):\n                self.test = test\n\n            def prepare(self):\n                self.test.prepared.set_result(None)\n\n            def data_received(self, data):\n                self.test.data.set_result(data)\n\n            def get(self):\n                self.test.finished.set_result(None)\n                self.write({})\n\n        @stream_request_body\n        class EarlyReturnHandler(RequestHandler):\n            def prepare(self):\n                # If we finish the response in prepare, it won't continue to\n                # the (non-existent) data_received.\n                raise HTTPError(401)\n\n        @stream_request_body\n        class CloseDetectionHandler(RequestHandler):\n            def initialize(self, test):\n                self.test = test\n\n            def on_connection_close(self):\n                super(CloseDetectionHandler, self).on_connection_close()\n                self.test.close_future.set_result(None)\n\n        return [\n            (\"/stream_body\", StreamingBodyHandler, dict(test=self)),\n            (\"/early_return\", EarlyReturnHandler),\n            (\"/close_detection\", CloseDetectionHandler, dict(test=self)),\n        ]\n\n    def connect(self, url, connection_close):\n        # Use a raw connection so we can control the sending of data.\n        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)\n        s.connect((\"127.0.0.1\", self.get_http_port()))\n        stream = IOStream(s)\n        stream.write(b\"GET \" + url + b\" HTTP/1.1\\r\\n\")\n        if connection_close:\n            stream.write(b\"Connection: close\\r\\n\")\n        stream.write(b\"Transfer-Encoding: chunked\\r\\n\\r\\n\")\n        return stream\n\n    @gen_test\n    def test_streaming_body(self):\n        self.prepared = Future()  # type: Future[None]\n        self.data = Future()  # type: Future[bytes]\n        self.finished = Future()  # type: Future[None]\n\n        stream = self.connect(b\"/stream_body\", connection_close=True)\n        yield self.prepared\n        stream.write(b\"4\\r\\nasdf\\r\\n\")\n        # Ensure the first chunk is received before we send the second.\n        data = yield self.data\n        self.assertEqual(data, b\"asdf\")\n        self.data = Future()\n        stream.write(b\"4\\r\\nqwer\\r\\n\")\n        data = yield self.data\n        self.assertEqual(data, b\"qwer\")\n        stream.write(b\"0\\r\\n\\r\\n\")\n        yield self.finished\n        data = yield stream.read_until_close()\n        # This would ideally use an HTTP1Connection to read the response.\n        self.assertTrue(data.endswith(b\"{}\"))\n        stream.close()\n\n    @gen_test\n    def test_early_return(self):\n        stream = self.connect(b\"/early_return\", connection_close=False)\n        data = yield stream.read_until_close()\n        self.assertTrue(data.startswith(b\"HTTP/1.1 401\"))\n\n    @gen_test\n    def test_early_return_with_data(self):\n        stream = self.connect(b\"/early_return\", connection_close=False)\n        stream.write(b\"4\\r\\nasdf\\r\\n\")\n        data = yield stream.read_until_close()\n        self.assertTrue(data.startswith(b\"HTTP/1.1 401\"))\n\n    @gen_test\n    def test_close_during_upload(self):\n        self.close_future = Future()  # type: Future[None]\n        stream = self.connect(b\"/close_detection\", connection_close=False)\n        stream.close()\n        yield self.close_future\n\n\n# Each method in this handler returns a yieldable object and yields to the\n# IOLoop so the future is not immediately ready.  Ensure that the\n# yieldables are respected and no method is called before the previous\n# one has completed.\n@stream_request_body\nclass BaseFlowControlHandler(RequestHandler):\n    def initialize(self, test):\n        self.test = test\n        self.method = None\n        self.methods = []  # type: typing.List[str]\n\n    @contextlib.contextmanager\n    def in_method(self, method):\n        if self.method is not None:\n            self.test.fail(\"entered method %s while in %s\" % (method, self.method))\n        self.method = method\n        self.methods.append(method)\n        try:\n            yield\n        finally:\n            self.method = None\n\n    @gen.coroutine\n    def prepare(self):\n        # Note that asynchronous prepare() does not block data_received,\n        # so we don't use in_method here.\n        self.methods.append(\"prepare\")\n        yield gen.moment\n\n    @gen.coroutine\n    def post(self):\n        with self.in_method(\"post\"):\n            yield gen.moment\n        self.write(dict(methods=self.methods))\n\n\nclass BaseStreamingRequestFlowControlTest(object):\n    def get_httpserver_options(self):\n        # Use a small chunk size so flow control is relevant even though\n        # all the data arrives at once.\n        return dict(chunk_size=10, decompress_request=True)\n\n    def get_http_client(self):\n        # simple_httpclient only: curl doesn't support body_producer.\n        return SimpleAsyncHTTPClient()\n\n    # Test all the slightly different code paths for fixed, chunked, etc bodies.\n    def test_flow_control_fixed_body(self):\n        response = self.fetch(\"/\", body=\"abcdefghijklmnopqrstuvwxyz\", method=\"POST\")\n        response.rethrow()\n        self.assertEqual(\n            json_decode(response.body),\n            dict(\n                methods=[\n                    \"prepare\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"post\",\n                ]\n            ),\n        )\n\n    def test_flow_control_chunked_body(self):\n        chunks = [b\"abcd\", b\"efgh\", b\"ijkl\"]\n\n        @gen.coroutine\n        def body_producer(write):\n            for i in chunks:\n                yield write(i)\n\n        response = self.fetch(\"/\", body_producer=body_producer, method=\"POST\")\n        response.rethrow()\n        self.assertEqual(\n            json_decode(response.body),\n            dict(\n                methods=[\n                    \"prepare\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"post\",\n                ]\n            ),\n        )\n\n    def test_flow_control_compressed_body(self):\n        bytesio = BytesIO()\n        gzip_file = gzip.GzipFile(mode=\"w\", fileobj=bytesio)\n        gzip_file.write(b\"abcdefghijklmnopqrstuvwxyz\")\n        gzip_file.close()\n        compressed_body = bytesio.getvalue()\n        response = self.fetch(\n            \"/\",\n            body=compressed_body,\n            method=\"POST\",\n            headers={\"Content-Encoding\": \"gzip\"},\n        )\n        response.rethrow()\n        self.assertEqual(\n            json_decode(response.body),\n            dict(\n                methods=[\n                    \"prepare\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"data_received\",\n                    \"post\",\n                ]\n            ),\n        )\n\n\nclass DecoratedStreamingRequestFlowControlTest(\n    BaseStreamingRequestFlowControlTest, WebTestCase\n):\n    def get_handlers(self):\n        class DecoratedFlowControlHandler(BaseFlowControlHandler):\n            @gen.coroutine\n            def data_received(self, data):\n                with self.in_method(\"data_received\"):\n                    yield gen.moment\n\n        return [(\"/\", DecoratedFlowControlHandler, dict(test=self))]\n\n\nclass NativeStreamingRequestFlowControlTest(\n    BaseStreamingRequestFlowControlTest, WebTestCase\n):\n    def get_handlers(self):\n        class NativeFlowControlHandler(BaseFlowControlHandler):\n            async def data_received(self, data):\n                with self.in_method(\"data_received\"):\n                    import asyncio\n\n                    await asyncio.sleep(0)\n\n        return [(\"/\", NativeFlowControlHandler, dict(test=self))]\n\n\nclass IncorrectContentLengthTest(SimpleHandlerTestCase):\n    def get_handlers(self):\n        test = self\n        self.server_error = None\n\n        # Manually set a content-length that doesn't match the actual content.\n        class TooHigh(RequestHandler):\n            def get(self):\n                self.set_header(\"Content-Length\", \"42\")\n                try:\n                    self.finish(\"ok\")\n                except Exception as e:\n                    test.server_error = e\n                    raise\n\n        class TooLow(RequestHandler):\n            def get(self):\n                self.set_header(\"Content-Length\", \"2\")\n                try:\n                    self.finish(\"hello\")\n                except Exception as e:\n                    test.server_error = e\n                    raise\n\n        return [(\"/high\", TooHigh), (\"/low\", TooLow)]\n\n    def test_content_length_too_high(self):\n        # When the content-length is too high, the connection is simply\n        # closed without completing the response.  An error is logged on\n        # the server.\n        with ExpectLog(app_log, \"(Uncaught exception|Exception in callback)\"):\n            with ExpectLog(\n                gen_log,\n                \"(Cannot send error response after headers written\"\n                \"|Failed to flush partial response)\",\n            ):\n                with self.assertRaises(HTTPClientError):\n                    self.fetch(\"/high\", raise_error=True)\n        self.assertEqual(\n            str(self.server_error), \"Tried to write 40 bytes less than Content-Length\"\n        )\n\n    def test_content_length_too_low(self):\n        # When the content-length is too low, the connection is closed\n        # without writing the last chunk, so the client never sees the request\n        # complete (which would be a framing error).\n        with ExpectLog(app_log, \"(Uncaught exception|Exception in callback)\"):\n            with ExpectLog(\n                gen_log,\n                \"(Cannot send error response after headers written\"\n                \"|Failed to flush partial response)\",\n            ):\n                with self.assertRaises(HTTPClientError):\n                    self.fetch(\"/low\", raise_error=True)\n        self.assertEqual(\n            str(self.server_error), \"Tried to write more data than Content-Length\"\n        )\n\n\nclass ClientCloseTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            if self.request.version.startswith(\"HTTP/1\"):\n                # Simulate a connection closed by the client during\n                # request processing.  The client will see an error, but the\n                # server should respond gracefully (without logging errors\n                # because we were unable to write out as many bytes as\n                # Content-Length said we would)\n                self.request.connection.stream.close()\n                self.write(\"hello\")\n            else:\n                # TODO: add a HTTP2-compatible version of this test.\n                self.write(\"requires HTTP/1.x\")\n\n    def test_client_close(self):\n        with self.assertRaises((HTTPClientError, unittest.SkipTest)):\n            response = self.fetch(\"/\", raise_error=True)\n            if response.body == b\"requires HTTP/1.x\":\n                self.skipTest(\"requires HTTP/1.x\")\n            self.assertEqual(response.code, 599)\n\n\nclass SignedValueTest(unittest.TestCase):\n    SECRET = \"It's a secret to everybody\"\n    SECRET_DICT = {0: \"asdfbasdf\", 1: \"12312312\", 2: \"2342342\"}\n\n    def past(self):\n        return self.present() - 86400 * 32\n\n    def present(self):\n        return 1300000000\n\n    def test_known_values(self):\n        signed_v1 = create_signed_value(\n            SignedValueTest.SECRET, \"key\", \"value\", version=1, clock=self.present\n        )\n        self.assertEqual(\n            signed_v1, b\"dmFsdWU=|1300000000|31c934969f53e48164c50768b40cbd7e2daaaa4f\"\n        )\n\n        signed_v2 = create_signed_value(\n            SignedValueTest.SECRET, \"key\", \"value\", version=2, clock=self.present\n        )\n        self.assertEqual(\n            signed_v2,\n            b\"2|1:0|10:1300000000|3:key|8:dmFsdWU=|\"\n            b\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\",\n        )\n\n        signed_default = create_signed_value(\n            SignedValueTest.SECRET, \"key\", \"value\", clock=self.present\n        )\n        self.assertEqual(signed_default, signed_v2)\n\n        decoded_v1 = decode_signed_value(\n            SignedValueTest.SECRET, \"key\", signed_v1, min_version=1, clock=self.present\n        )\n        self.assertEqual(decoded_v1, b\"value\")\n\n        decoded_v2 = decode_signed_value(\n            SignedValueTest.SECRET, \"key\", signed_v2, min_version=2, clock=self.present\n        )\n        self.assertEqual(decoded_v2, b\"value\")\n\n    def test_name_swap(self):\n        signed1 = create_signed_value(\n            SignedValueTest.SECRET, \"key1\", \"value\", clock=self.present\n        )\n        signed2 = create_signed_value(\n            SignedValueTest.SECRET, \"key2\", \"value\", clock=self.present\n        )\n        # Try decoding each string with the other's \"name\"\n        decoded1 = decode_signed_value(\n            SignedValueTest.SECRET, \"key2\", signed1, clock=self.present\n        )\n        self.assertIs(decoded1, None)\n        decoded2 = decode_signed_value(\n            SignedValueTest.SECRET, \"key1\", signed2, clock=self.present\n        )\n        self.assertIs(decoded2, None)\n\n    def test_expired(self):\n        signed = create_signed_value(\n            SignedValueTest.SECRET, \"key1\", \"value\", clock=self.past\n        )\n        decoded_past = decode_signed_value(\n            SignedValueTest.SECRET, \"key1\", signed, clock=self.past\n        )\n        self.assertEqual(decoded_past, b\"value\")\n        decoded_present = decode_signed_value(\n            SignedValueTest.SECRET, \"key1\", signed, clock=self.present\n        )\n        self.assertIs(decoded_present, None)\n\n    def test_payload_tampering(self):\n        # These cookies are variants of the one in test_known_values.\n        sig = \"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\"\n\n        def validate(prefix):\n            return b\"value\" == decode_signed_value(\n                SignedValueTest.SECRET, \"key\", prefix + sig, clock=self.present\n            )\n\n        self.assertTrue(validate(\"2|1:0|10:1300000000|3:key|8:dmFsdWU=|\"))\n        # Change key version\n        self.assertFalse(validate(\"2|1:1|10:1300000000|3:key|8:dmFsdWU=|\"))\n        # length mismatch (field too short)\n        self.assertFalse(validate(\"2|1:0|10:130000000|3:key|8:dmFsdWU=|\"))\n        # length mismatch (field too long)\n        self.assertFalse(validate(\"2|1:0|10:1300000000|3:keey|8:dmFsdWU=|\"))\n\n    def test_signature_tampering(self):\n        prefix = \"2|1:0|10:1300000000|3:key|8:dmFsdWU=|\"\n\n        def validate(sig):\n            return b\"value\" == decode_signed_value(\n                SignedValueTest.SECRET, \"key\", prefix + sig, clock=self.present\n            )\n\n        self.assertTrue(\n            validate(\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\")\n        )\n        # All zeros\n        self.assertFalse(validate(\"0\" * 32))\n        # Change one character\n        self.assertFalse(\n            validate(\"4d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e152\")\n        )\n        # Change another character\n        self.assertFalse(\n            validate(\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e153\")\n        )\n        # Truncate\n        self.assertFalse(\n            validate(\"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e15\")\n        )\n        # Lengthen\n        self.assertFalse(\n            validate(\n                \"3d4e60b996ff9c5d5788e333a0cba6f238a22c6c0f94788870e1a9ecd482e1538\"\n            )\n        )\n\n    def test_non_ascii(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET, \"key\", value, clock=self.present\n        )\n        decoded = decode_signed_value(\n            SignedValueTest.SECRET, \"key\", signed, clock=self.present\n        )\n        self.assertEqual(value, decoded)\n\n    def test_key_versioning_read_write_default_key(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=0\n        )\n        decoded = decode_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", signed, clock=self.present\n        )\n        self.assertEqual(value, decoded)\n\n    def test_key_versioning_read_write_non_default_key(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=1\n        )\n        decoded = decode_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", signed, clock=self.present\n        )\n        self.assertEqual(value, decoded)\n\n    def test_key_versioning_invalid_key(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=0\n        )\n        newkeys = SignedValueTest.SECRET_DICT.copy()\n        newkeys.pop(0)\n        decoded = decode_signed_value(newkeys, \"key\", signed, clock=self.present)\n        self.assertEqual(None, decoded)\n\n    def test_key_version_retrieval(self):\n        value = b\"\\xe9\"\n        signed = create_signed_value(\n            SignedValueTest.SECRET_DICT, \"key\", value, clock=self.present, key_version=1\n        )\n        key_version = get_signature_key_version(signed)\n        self.assertEqual(1, key_version)\n\n\nclass XSRFTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            version = int(self.get_argument(\"version\", \"2\"))\n            # This would be a bad idea in a real app, but in this test\n            # it's fine.\n            self.settings[\"xsrf_cookie_version\"] = version\n            self.write(self.xsrf_token)\n\n        def post(self):\n            self.write(\"ok\")\n\n    def get_app_kwargs(self):\n        return dict(xsrf_cookies=True)\n\n    def setUp(self):\n        super(XSRFTest, self).setUp()\n        self.xsrf_token = self.get_token()\n\n    def get_token(self, old_token=None, version=None):\n        if old_token is not None:\n            headers = self.cookie_headers(old_token)\n        else:\n            headers = None\n        response = self.fetch(\n            \"/\" if version is None else (\"/?version=%d\" % version), headers=headers\n        )\n        response.rethrow()\n        return native_str(response.body)\n\n    def cookie_headers(self, token=None):\n        if token is None:\n            token = self.xsrf_token\n        return {\"Cookie\": \"_xsrf=\" + token}\n\n    def test_xsrf_fail_no_token(self):\n        with ExpectLog(gen_log, \".*'_xsrf' argument missing\"):\n            response = self.fetch(\"/\", method=\"POST\", body=b\"\")\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_body_no_cookie(self):\n        with ExpectLog(gen_log, \".*XSRF cookie does not match POST\"):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_argument_invalid_format(self):\n        with ExpectLog(gen_log, \".*'_xsrf' argument has invalid format\"):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                headers=self.cookie_headers(),\n                body=urllib.parse.urlencode(dict(_xsrf=\"3|\")),\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_cookie_invalid_format(self):\n        with ExpectLog(gen_log, \".*XSRF cookie does not match POST\"):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                headers=self.cookie_headers(token=\"3|\"),\n                body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_fail_cookie_no_body(self):\n        with ExpectLog(gen_log, \".*'_xsrf' argument missing\"):\n            response = self.fetch(\n                \"/\", method=\"POST\", body=b\"\", headers=self.cookie_headers()\n            )\n        self.assertEqual(response.code, 403)\n\n    def test_xsrf_success_short_token(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=urllib.parse.urlencode(dict(_xsrf=\"deadbeef\")),\n            headers=self.cookie_headers(token=\"deadbeef\"),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_non_hex_token(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=urllib.parse.urlencode(dict(_xsrf=\"xoxo\")),\n            headers=self.cookie_headers(token=\"xoxo\"),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_post_body(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            headers=self.cookie_headers(),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_query_string(self):\n        response = self.fetch(\n            \"/?\" + urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n            method=\"POST\",\n            body=b\"\",\n            headers=self.cookie_headers(),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_xsrf_success_header(self):\n        response = self.fetch(\n            \"/\",\n            method=\"POST\",\n            body=b\"\",\n            headers=dict(\n                {\"X-Xsrftoken\": self.xsrf_token},  # type: ignore\n                **self.cookie_headers()\n            ),\n        )\n        self.assertEqual(response.code, 200)\n\n    def test_distinct_tokens(self):\n        # Every request gets a distinct token.\n        NUM_TOKENS = 10\n        tokens = set()\n        for i in range(NUM_TOKENS):\n            tokens.add(self.get_token())\n        self.assertEqual(len(tokens), NUM_TOKENS)\n\n    def test_cross_user(self):\n        token2 = self.get_token()\n        # Each token can be used to authenticate its own request.\n        for token in (self.xsrf_token, token2):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=token)),\n                headers=self.cookie_headers(token),\n            )\n            self.assertEqual(response.code, 200)\n        # Sending one in the cookie and the other in the body is not allowed.\n        for cookie_token, body_token in (\n            (self.xsrf_token, token2),\n            (token2, self.xsrf_token),\n        ):\n            with ExpectLog(gen_log, \".*XSRF cookie does not match POST\"):\n                response = self.fetch(\n                    \"/\",\n                    method=\"POST\",\n                    body=urllib.parse.urlencode(dict(_xsrf=body_token)),\n                    headers=self.cookie_headers(cookie_token),\n                )\n            self.assertEqual(response.code, 403)\n\n    def test_refresh_token(self):\n        token = self.xsrf_token\n        tokens_seen = set([token])\n        # A user's token is stable over time.  Refreshing the page in one tab\n        # might update the cookie while an older tab still has the old cookie\n        # in its DOM.  Simulate this scenario by passing a constant token\n        # in the body and re-querying for the token.\n        for i in range(5):\n            token = self.get_token(token)\n            # Tokens are encoded uniquely each time\n            tokens_seen.add(token)\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=self.xsrf_token)),\n                headers=self.cookie_headers(token),\n            )\n            self.assertEqual(response.code, 200)\n        self.assertEqual(len(tokens_seen), 6)\n\n    def test_versioning(self):\n        # Version 1 still produces distinct tokens per request.\n        self.assertNotEqual(self.get_token(version=1), self.get_token(version=1))\n\n        # Refreshed v1 tokens are all identical.\n        v1_token = self.get_token(version=1)\n        for i in range(5):\n            self.assertEqual(self.get_token(v1_token, version=1), v1_token)\n\n        # Upgrade to a v2 version of the same token\n        v2_token = self.get_token(v1_token)\n        self.assertNotEqual(v1_token, v2_token)\n        # Each v1 token can map to many v2 tokens.\n        self.assertNotEqual(v2_token, self.get_token(v1_token))\n\n        # The tokens are cross-compatible.\n        for cookie_token, body_token in ((v1_token, v2_token), (v2_token, v1_token)):\n            response = self.fetch(\n                \"/\",\n                method=\"POST\",\n                body=urllib.parse.urlencode(dict(_xsrf=body_token)),\n                headers=self.cookie_headers(cookie_token),\n            )\n            self.assertEqual(response.code, 200)\n\n\nclass XSRFCookieKwargsTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.write(self.xsrf_token)\n\n    def get_app_kwargs(self):\n        return dict(\n            xsrf_cookies=True, xsrf_cookie_kwargs=dict(httponly=True, expires_days=2)\n        )\n\n    def test_xsrf_httponly(self):\n        response = self.fetch(\"/\")\n        self.assertIn(\"httponly;\", response.headers[\"Set-Cookie\"].lower())\n        self.assertIn(\"expires=\", response.headers[\"Set-Cookie\"].lower())\n        header = response.headers.get(\"Set-Cookie\")\n        match = re.match(\".*; expires=(?P<expires>.+);.*\", header)\n        assert match is not None\n\n        expires = datetime.datetime.utcnow() + datetime.timedelta(days=2)\n        parsed = email.utils.parsedate(match.groupdict()[\"expires\"])\n        assert parsed is not None\n        header_expires = datetime.datetime(*parsed[:6])\n        self.assertTrue(abs((expires - header_expires).total_seconds()) < 10)\n\n\nclass FinishExceptionTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            self.set_status(401)\n            self.set_header(\"WWW-Authenticate\", 'Basic realm=\"something\"')\n            if self.get_argument(\"finish_value\", \"\"):\n                raise Finish(\"authentication required\")\n            else:\n                self.write(\"authentication required\")\n                raise Finish()\n\n    def test_finish_exception(self):\n        for u in [\"/\", \"/?finish_value=1\"]:\n            response = self.fetch(u)\n            self.assertEqual(response.code, 401)\n            self.assertEqual(\n                'Basic realm=\"something\"', response.headers.get(\"WWW-Authenticate\")\n            )\n            self.assertEqual(b\"authentication required\", response.body)\n\n\nclass DecoratorTest(WebTestCase):\n    def get_handlers(self):\n        class RemoveSlashHandler(RequestHandler):\n            @removeslash\n            def get(self):\n                pass\n\n        class AddSlashHandler(RequestHandler):\n            @addslash\n            def get(self):\n                pass\n\n        return [(\"/removeslash/\", RemoveSlashHandler), (\"/addslash\", AddSlashHandler)]\n\n    def test_removeslash(self):\n        response = self.fetch(\"/removeslash/\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/removeslash\")\n\n        response = self.fetch(\"/removeslash/?foo=bar\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/removeslash?foo=bar\")\n\n    def test_addslash(self):\n        response = self.fetch(\"/addslash\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/addslash/\")\n\n        response = self.fetch(\"/addslash?foo=bar\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/addslash/?foo=bar\")\n\n\nclass CacheTest(WebTestCase):\n    def get_handlers(self):\n        class EtagHandler(RequestHandler):\n            def get(self, computed_etag):\n                self.write(computed_etag)\n\n            def compute_etag(self):\n                return self._write_buffer[0]\n\n        return [(\"/etag/(.*)\", EtagHandler)]\n\n    def test_wildcard_etag(self):\n        computed_etag = '\"xyzzy\"'\n        etags = \"*\"\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_strong_etag_match(self):\n        computed_etag = '\"xyzzy\"'\n        etags = '\"xyzzy\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_multiple_strong_etag_match(self):\n        computed_etag = '\"xyzzy1\"'\n        etags = '\"xyzzy1\", \"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_strong_etag_not_match(self):\n        computed_etag = '\"xyzzy\"'\n        etags = '\"xyzzy1\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def test_multiple_strong_etag_not_match(self):\n        computed_etag = '\"xyzzy\"'\n        etags = '\"xyzzy1\", \"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def test_weak_etag_match(self):\n        computed_etag = '\"xyzzy1\"'\n        etags = 'W/\"xyzzy1\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_multiple_weak_etag_match(self):\n        computed_etag = '\"xyzzy2\"'\n        etags = 'W/\"xyzzy1\", W/\"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 304)\n\n    def test_weak_etag_not_match(self):\n        computed_etag = '\"xyzzy2\"'\n        etags = 'W/\"xyzzy1\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def test_multiple_weak_etag_not_match(self):\n        computed_etag = '\"xyzzy3\"'\n        etags = 'W/\"xyzzy1\", W/\"xyzzy2\"'\n        self._test_etag(computed_etag, etags, 200)\n\n    def _test_etag(self, computed_etag, etags, status_code):\n        response = self.fetch(\n            \"/etag/\" + computed_etag, headers={\"If-None-Match\": etags}\n        )\n        self.assertEqual(response.code, status_code)\n\n\nclass RequestSummaryTest(SimpleHandlerTestCase):\n    class Handler(RequestHandler):\n        def get(self):\n            # remote_ip is optional, although it's set by\n            # both HTTPServer and WSGIAdapter.\n            # Clobber it to make sure it doesn't break logging.\n            self.request.remote_ip = None\n            self.finish(self._request_summary())\n\n    def test_missing_remote_ip(self):\n        resp = self.fetch(\"/\")\n        self.assertEqual(resp.body, b\"GET / (None)\")\n\n\nclass HTTPErrorTest(unittest.TestCase):\n    def test_copy(self):\n        e = HTTPError(403, reason=\"Go away\")\n        e2 = copy.copy(e)\n        self.assertIsNot(e, e2)\n        self.assertEqual(e.status_code, e2.status_code)\n        self.assertEqual(e.reason, e2.reason)\n\n\nclass ApplicationTest(AsyncTestCase):\n    def test_listen(self):\n        app = Application([])\n        server = app.listen(0, address=\"127.0.0.1\")\n        server.stop()\n\n\nclass URLSpecReverseTest(unittest.TestCase):\n    def test_reverse(self):\n        self.assertEqual(\"/favicon.ico\", url(r\"/favicon\\.ico\", None).reverse())\n        self.assertEqual(\"/favicon.ico\", url(r\"^/favicon\\.ico$\", None).reverse())\n\n    def test_non_reversible(self):\n        # URLSpecs are non-reversible if they include non-constant\n        # regex features outside capturing groups. Currently, this is\n        # only strictly enforced for backslash-escaped character\n        # classes.\n        paths = [r\"^/api/v\\d+/foo/(\\w+)$\"]\n        for path in paths:\n            # A URLSpec can still be created even if it cannot be reversed.\n            url_spec = url(path, None)\n            try:\n                result = url_spec.reverse()\n                self.fail(\n                    \"did not get expected exception when reversing %s. \"\n                    \"result: %s\" % (path, result)\n                )\n            except ValueError:\n                pass\n\n    def test_reverse_arguments(self):\n        self.assertEqual(\n            \"/api/v1/foo/bar\", url(r\"^/api/v1/foo/(\\w+)$\", None).reverse(\"bar\")\n        )\n        self.assertEqual(\n            \"/api.v1/foo/5/icon.png\",\n            url(r\"/api\\.v1/foo/([0-9]+)/icon\\.png\", None).reverse(5),\n        )\n\n\nclass RedirectHandlerTest(WebTestCase):\n    def get_handlers(self):\n        return [\n            (\"/src\", WebRedirectHandler, {\"url\": \"/dst\"}),\n            (\"/src2\", WebRedirectHandler, {\"url\": \"/dst2?foo=bar\"}),\n            (r\"/(.*?)/(.*?)/(.*)\", WebRedirectHandler, {\"url\": \"/{1}/{0}/{2}\"}),\n        ]\n\n    def test_basic_redirect(self):\n        response = self.fetch(\"/src\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/dst\")\n\n    def test_redirect_with_argument(self):\n        response = self.fetch(\"/src?foo=bar\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/dst?foo=bar\")\n\n    def test_redirect_with_appending_argument(self):\n        response = self.fetch(\"/src2?foo2=bar2\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/dst2?foo=bar&foo2=bar2\")\n\n    def test_redirect_pattern(self):\n        response = self.fetch(\"/a/b/c\", follow_redirects=False)\n        self.assertEqual(response.code, 301)\n        self.assertEqual(response.headers[\"Location\"], \"/b/a/c\")\n",
        "#!/usr/bin/env python\nimport unittest\nimport sys\nimport time\nimport traceback\nimport platform\nimport os\n\n\nclass PytestLikeResult(unittest.TestResult):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.start_time = None\n        # Store (test, outcome, info) for each test:\n        #   outcome ∈ { \"passed\", \"failed\", \"error\", \"skipped\" }\n        #   info can hold the exception or skip reason, etc.\n        self.results = []\n\n    def startTestRun(self):\n        \"\"\"Called once before all tests.\"\"\"\n        self.start_time = time.time()\n        # Print a header similar to pytest\n        print(\"=\" * 29 + \" test session starts \" + \"=\" * 29)\n        # You can customize the platform/versions/paths as you like:\n        print(\n            f\"platform {platform.system().lower()} -- Python {platform.python_version()}\"\n        )\n        print(f\"rootdir: {os.path.abspath(os.curdir)}\")\n\n    def stopTestRun(self):\n        \"\"\"Called once after all tests are done.\"\"\"\n        elapsed = time.time() - self.start_time\n        print()  # blank line after final dot\n\n        # Summarize\n        ntests = len(self.results)\n        print(f\"collected {ntests} items\")\n\n        # Show details of failures/errors\n        failures = [r for r in self.results if r[1] == \"failed\"]\n        errors = [r for r in self.results if r[1] == \"error\"]\n\n        if failures:\n            print(\"\\n\" + \"=\" * 35 + \" FAILURES \" + \"=\" * 35)\n            for test, outcome, err_info in failures:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n        if errors:\n            print(\"\\n\" + \"=\" * 35 + \" ERRORS \" + \"=\" * 37)\n            for test, outcome, err_info in errors:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n\n        # Optional short summary lines for passes/fails/skips\n        print(\"\\n==================== short test summary info ====================\")\n        for test, outcome, info in self.results:\n            if outcome in (\"passed\", \"failed\", \"error\", \"skipped\"):\n                print(f\"{outcome.upper()} {test}\")\n\n        # Final one-line summary: “1 failed, 1 errors, 10 passed in 2.04s”\n        passed_count = sum(1 for r in self.results if r[1] == \"passed\")\n        failed_count = len(failures)\n        error_count = len(errors)\n        skipped_count = sum(1 for r in self.results if r[1] == \"skipped\")\n\n        summary_bits = []\n        if failed_count:\n            summary_bits.append(f\"{failed_count} failed\")\n        if error_count:\n            summary_bits.append(f\"{error_count} error{'s' if error_count != 1 else ''}\")\n        if passed_count:\n            summary_bits.append(f\"{passed_count} passed\")\n        if skipped_count:\n            summary_bits.append(f\"{skipped_count} skipped\")\n\n        summary_str = \", \".join(summary_bits) if summary_bits else \"no tests run\"\n        print(\n            f\"=================== {summary_str} in {elapsed:.2f}s ===================\"\n        )\n\n    def startTest(self, test):\n        \"\"\"Called right before each test method.\"\"\"\n        super().startTest(test)\n        # Print a dot or some indicator\n        print(\".\", end=\"\", flush=True)\n\n    def addSuccess(self, test):\n        super().addSuccess(test)\n        self.results.append((self._test_id(test), \"passed\", None))\n\n    def addFailure(self, test, err):\n        super().addFailure(test, err)\n        self.results.append((self._test_id(test), \"failed\", err))\n\n    def addError(self, test, err):\n        super().addError(test, err)\n        self.results.append((self._test_id(test), \"error\", err))\n\n    def addSkip(self, test, reason):\n        super().addSkip(test, reason)\n        self.results.append((self._test_id(test), \"skipped\", reason))\n\n    def _print_traceback(self, err_info):\n        \"\"\"Helper to print the traceback like pytest does.\"\"\"\n        if isinstance(err_info, tuple) and len(err_info) == 3:\n            exc_type, exc_value, tb = err_info\n            traceback.print_exception(exc_type, exc_value, tb, file=sys.stdout)\n        else:\n            print(str(err_info))\n\n    def _test_id(self, test):\n        try:\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}::{test._testMethodName}\"\n        except AttributeError:\n            # Fallback for _ErrorHolder objects\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}\"\n\n\nclass PytestLikeRunner(unittest.TextTestRunner):\n    \"\"\"A custom TextTestRunner that uses the PytestLikeResult.\"\"\"\n\n    resultclass = PytestLikeResult\n\n    def run(self, test):\n        result = self._makeResult()\n        # Overriding run to ensure we call startTestRun/stopTestRun\n        result.startTestRun()\n        test(result)\n        result.stopTestRun()\n        return result\n\n    def tearDown(self):\n        # Reset signal handlers\n        from twisted.internet import reactor\n\n        reactor.removeAll()\n        reactor.stop()\n        super().tearDown()\n\n\ndef main():\n    loader = unittest.TestLoader()\n    suite = loader.discover(\".\", pattern=\"test_*.py\")  # discover tests in r2e_tests/\n    runner = PytestLikeRunner(verbosity=0)\n    result = runner.run(suite)\n    # Exit with code 0 if all tests passed/skipped, 1 if there were fails/errors\n    sys.exit(not result.wasSuccessful())\n\n\nif __name__ == \"__main__\":\n    main()\n"
    ],
    "test_file_names": [
        "test_1.py",
        "tornado_unittest_runner.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 1 package in 956ms\nPrepared 1 package in 1.44s\nInstalled 1 package in 5ms\n + tornado==6.1.dev1 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239)\nResolved 29 packages in 16ms\nInstalled 29 packages in 99ms\n + attrs==24.3.0\n + automat==24.8.1\n + cachetools==5.5.0\n + cffi==1.17.1\n + chardet==5.2.0\n + colorama==0.4.6\n + constantly==23.10.4\n + distlib==0.3.9\n + exceptiongroup==1.2.2\n + filelock==3.16.1\n + hyperlink==21.0.0\n + idna==3.10\n + incremental==24.7.2\n + iniconfig==2.0.0\n + packaging==24.2\n + platformdirs==4.3.6\n + pluggy==1.5.0\n + pycares==4.5.0\n + pycparser==2.22\n + pycurl==7.45.4\n + pyproject-api==1.8.0\n + pytest==8.3.4\n + setuptools==75.6.0\n + tomli==2.2.1\n + tox==4.23.2\n + twisted==24.11.0\n + typing-extensions==4.12.2\n + virtualenv==20.28.0\n + zope-interface==7.2\n",
    "new_commit_res_code": 1,
    "new_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239\n........................................................................................................................................................................................\ncollected 184 items\n\n=================================== FAILURES ===================================\n__________ GzipTestCase.test_gzip_static __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1758, in test_gzip_static\n    self.assert_compressed(response)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1742, in assert_compressed\n    self.assertEqual(\nAssertionError: None != 'gzip'\n__________ StaticDefaultFilenameTest.test_static_default_filename __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1409, in test_static_default_filename\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticDefaultFilenameTest.test_static_default_redirect __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1414, in test_static_default_redirect\n    self.assertEqual(response.code, 301)\nAssertionError: 404 != 301\n__________ StaticFileTest.test_absolute_static_url __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1127, in test_absolute_static_url\n    self.assertEqual(\nAssertionError: b'http://127.0.0.1:41177/static/robots.txt' != b'http://127.0.0.1:41177/static/robots.txt?v=f71d20196d4caf35b6a670db8c70b03d'\n__________ StaticFileTest.test_root_static_path __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1394, in test_root_static_path\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_compressed_files __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1110, in test_static_compressed_files\n    self.assertEqual(response.headers.get(\"Content-Type\"), \"application/gzip\")\nAssertionError: 'text/html; charset=UTF-8' != 'application/gzip'\n- text/html; charset=UTF-8\n+ application/gzip\n\n__________ StaticFileTest.test_static_etag __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1228, in test_static_etag\n    self.assertEqual(\nAssertionError: None != b'\"f71d20196d4caf35b6a670db8c70b03d\"'\n__________ StaticFileTest.test_static_files __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1102, in test_static_files\n    self.assertTrue(b\"Disallow: /\" in response.body)\nAssertionError: False is not true\n__________ StaticFileTest.test_static_head __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1333, in test_static_head\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_head_range __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1345, in test_static_head_range\n    self.assertEqual(response.code, 206)\nAssertionError: 404 != 206\n__________ StaticFileTest.test_static_if_modified_since_pre_epoch __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1206, in test_static_if_modified_since_pre_epoch\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_invalid_range __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1308, in test_static_invalid_range\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_range_if_none_match __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1360, in test_static_range_if_none_match\n    self.assertEqual(response.code, 304)\nAssertionError: 404 != 304\n__________ StaticFileTest.test_static_unsatisfiable_range_end_less_than_start __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1328, in test_static_unsatisfiable_range_end_less_than_start\n    self.assertEqual(response.code, 416)\nAssertionError: 404 != 416\n__________ StaticFileTest.test_static_unsatisfiable_range_invalid_start __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1321, in test_static_unsatisfiable_range_invalid_start\n    self.assertEqual(response.code, 416)\nAssertionError: 404 != 416\n__________ StaticFileTest.test_static_unsatisfiable_range_zero_suffix __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1314, in test_static_unsatisfiable_range_zero_suffix\n    self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\nAssertionError: None != 'bytes */26'\n__________ StaticFileTest.test_static_url __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1123, in test_static_url\n    self.assertEqual(response.body, b\"/static/robots.txt?v=\" + self.robots_txt_hash)\nAssertionError: b'/static/robots.txt' != b'/static/robots.txt?v=f71d20196d4caf35b6a670db8c70b03d'\n__________ StaticFileTest.test_static_with_range __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1236, in test_static_with_range\n    self.assertEqual(response.code, 206)\nAssertionError: 404 != 206\n__________ StaticFileTest.test_static_with_range_end_edge __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1283, in test_static_with_range_end_edge\n    self.assertEqual(response.body, b\": /\\n\")\nAssertionError: b'<html><title>404: Not Found</title><body>404: Not Found</body></html>' != b': /\\n'\n__________ StaticFileTest.test_static_with_range_full_file __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1250, in test_static_with_range_full_file\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_with_range_full_past_end __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1261, in test_static_with_range_full_past_end\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_with_range_neg_end __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1291, in test_static_with_range_neg_end\n    self.assertEqual(response.body, b\": /\\n\")\nAssertionError: b'<html><title>404: Not Found</title><body>404: Not Found</body></html>' != b': /\\n'\n__________ StaticFileTest.test_static_with_range_neg_past_start __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1299, in test_static_with_range_neg_past_start\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_with_range_partial_past_end __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1272, in test_static_with_range_partial_past_end\n    self.assertEqual(response.code, 206)\nAssertionError: 404 != 206\n__________ StaticFileWithPathTest.test_serve __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1430, in test_serve\n    self.assertEqual(response.body, b\"H\\xc3\\xa9llo\\n\")\nAssertionError: b'<html><title>404: Not Found</title><body>404: Not Found</body></html>' != b'H\\xc3\\xa9llo\\n'\n\n=================================== ERRORS =====================================\n__________ StaticFileTest.test_static_304_etag_modified_bug __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1193, in test_static_304_etag_modified_bug\n    \"If-Modified-Since\": response1.headers[\"Last-Modified\"],\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/httputil.py\", line 216, in __getitem__\n    return self._dict[_normalize_header(name)]\nKeyError: 'Last-Modified'\n__________ StaticFileTest.test_static_304_if_modified_since __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1174, in test_static_304_if_modified_since\n    headers={\"If-Modified-Since\": response1.headers[\"Last-Modified\"]},\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/httputil.py\", line 216, in __getitem__\n    return self._dict[_normalize_header(name)]\nKeyError: 'Last-Modified'\n__________ StaticFileTest.test_static_304_if_none_match __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1183, in test_static_304_if_none_match\n    \"/static/robots.txt\", headers={\"If-None-Match\": response1.headers[\"Etag\"]}\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/httputil.py\", line 216, in __getitem__\n    return self._dict[_normalize_header(name)]\nKeyError: 'Etag'\n__________ StaticFileTest.test_static_if_modified_since_time_zone __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1213, in test_static_if_modified_since_time_zone\n    stat = os.stat(relpath(\"static/robots.txt\"))\nFileNotFoundError: [Errno 2] No such file or directory: '/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/static/robots.txt'\n\n==================== short test summary info ====================\nPASSED r2e_tests.test_1::AllHTTPMethodsTest::test_standard_methods\nPASSED r2e_tests.test_1::ApplicationTest::test_listen\nPASSED r2e_tests.test_1::AuthRedirectTest::test_absolute_auth_redirect\nPASSED r2e_tests.test_1::AuthRedirectTest::test_relative_auth_redirect\nPASSED r2e_tests.test_1::BuggyLoggingTest::test_buggy_log_exception\nPASSED r2e_tests.test_1::CacheTest::test_multiple_strong_etag_match\nPASSED r2e_tests.test_1::CacheTest::test_multiple_strong_etag_not_match\nPASSED r2e_tests.test_1::CacheTest::test_multiple_weak_etag_match\nPASSED r2e_tests.test_1::CacheTest::test_multiple_weak_etag_not_match\nPASSED r2e_tests.test_1::CacheTest::test_strong_etag_match\nPASSED r2e_tests.test_1::CacheTest::test_strong_etag_not_match\nPASSED r2e_tests.test_1::CacheTest::test_weak_etag_match\nPASSED r2e_tests.test_1::CacheTest::test_weak_etag_not_match\nPASSED r2e_tests.test_1::CacheTest::test_wildcard_etag\nPASSED r2e_tests.test_1::ClearAllCookiesTest::test_clear_all_cookies\nPASSED r2e_tests.test_1::ClearHeaderTest::test_clear_header\nPASSED r2e_tests.test_1::ClientCloseTest::test_client_close\nPASSED r2e_tests.test_1::ConnectionCloseTest::test_connection_close\nPASSED r2e_tests.test_1::CookieTest::test_cookie_special_char\nPASSED r2e_tests.test_1::CookieTest::test_get_cookie\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie_domain\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie_expires_days\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie_false_flags\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie_max_age\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie_overwrite\nPASSED r2e_tests.test_1::Custom404Test::test_404\nPASSED r2e_tests.test_1::CustomStaticFileTest::test_serve\nPASSED r2e_tests.test_1::CustomStaticFileTest::test_static_url\nPASSED r2e_tests.test_1::DateHeaderTest::test_date_header\nPASSED r2e_tests.test_1::DecoratedStreamingRequestFlowControlTest::test_flow_control_chunked_body\nPASSED r2e_tests.test_1::DecoratedStreamingRequestFlowControlTest::test_flow_control_compressed_body\nPASSED r2e_tests.test_1::DecoratedStreamingRequestFlowControlTest::test_flow_control_fixed_body\nPASSED r2e_tests.test_1::DecoratorTest::test_addslash\nPASSED r2e_tests.test_1::DecoratorTest::test_removeslash\nPASSED r2e_tests.test_1::Default404Test::test_404\nPASSED r2e_tests.test_1::DefaultHandlerArgumentsTest::test_403\nPASSED r2e_tests.test_1::DefaultHostMatchingTest::test_default_host_matching\nPASSED r2e_tests.test_1::ErrorHandlerXSRFTest::test_404_xsrf\nPASSED r2e_tests.test_1::ErrorHandlerXSRFTest::test_error_xsrf\nPASSED r2e_tests.test_1::ErrorResponseTest::test_default\nPASSED r2e_tests.test_1::ErrorResponseTest::test_failed_write_error\nPASSED r2e_tests.test_1::ErrorResponseTest::test_write_error\nPASSED r2e_tests.test_1::ExceptionHandlerTest::test_http_error\nPASSED r2e_tests.test_1::ExceptionHandlerTest::test_known_error\nPASSED r2e_tests.test_1::ExceptionHandlerTest::test_unknown_error\nPASSED r2e_tests.test_1::FinalReturnTest::test_finish_method_return_future\nPASSED r2e_tests.test_1::FinalReturnTest::test_render_method_return_future\nPASSED r2e_tests.test_1::FinishExceptionTest::test_finish_exception\nPASSED r2e_tests.test_1::FinishInPrepareTest::test_finish_in_prepare\nPASSED r2e_tests.test_1::GetArgumentErrorTest::test_catch_error\nPASSED r2e_tests.test_1::GetCurrentUserTest::test_get_current_user_from_ui_module_is_lazy\nPASSED r2e_tests.test_1::GetCurrentUserTest::test_get_current_user_from_ui_module_works\nSKIPPED r2e_tests.test_1::GetCurrentUserTest::test_get_current_user_is_lazy\nPASSED r2e_tests.test_1::GetCurrentUserTest::test_get_current_user_works\nPASSED r2e_tests.test_1::GzipTestCase::test_gzip\nPASSED r2e_tests.test_1::GzipTestCase::test_gzip_not_requested\nFAILED r2e_tests.test_1::GzipTestCase::test_gzip_static\nPASSED r2e_tests.test_1::GzipTestCase::test_vary_already_present\nPASSED r2e_tests.test_1::GzipTestCase::test_vary_already_present_multiple\nPASSED r2e_tests.test_1::HTTPErrorTest::test_copy\nPASSED r2e_tests.test_1::HandlerByNameTest::test_handler_by_name\nPASSED r2e_tests.test_1::Header204Test::test_204_headers\nPASSED r2e_tests.test_1::Header304Test::test_304_headers\nPASSED r2e_tests.test_1::HostMatchingTest::test_host_matching\nPASSED r2e_tests.test_1::IncorrectContentLengthTest::test_content_length_too_high\nPASSED r2e_tests.test_1::IncorrectContentLengthTest::test_content_length_too_low\nPASSED r2e_tests.test_1::NamedURLSpecGroupsTest::test_named_urlspec_groups\nPASSED r2e_tests.test_1::NativeStreamingRequestFlowControlTest::test_flow_control_chunked_body\nPASSED r2e_tests.test_1::NativeStreamingRequestFlowControlTest::test_flow_control_compressed_body\nPASSED r2e_tests.test_1::NativeStreamingRequestFlowControlTest::test_flow_control_fixed_body\nPASSED r2e_tests.test_1::NonWSGIWebTests::test_empty_flush\nPASSED r2e_tests.test_1::PatchMethodTest::test_other\nPASSED r2e_tests.test_1::PatchMethodTest::test_patch\nPASSED r2e_tests.test_1::PathArgsInPrepareTest::test_kw\nPASSED r2e_tests.test_1::PathArgsInPrepareTest::test_pos\nPASSED r2e_tests.test_1::RaiseWithReasonTest::test_httperror_str\nPASSED r2e_tests.test_1::RaiseWithReasonTest::test_httperror_str_from_httputil\nPASSED r2e_tests.test_1::RaiseWithReasonTest::test_raise_with_reason\nPASSED r2e_tests.test_1::RedirectHandlerTest::test_basic_redirect\nPASSED r2e_tests.test_1::RedirectHandlerTest::test_redirect_pattern\nPASSED r2e_tests.test_1::RedirectHandlerTest::test_redirect_with_appending_argument\nPASSED r2e_tests.test_1::RedirectHandlerTest::test_redirect_with_argument\nPASSED r2e_tests.test_1::RequestEncodingTest::test_error\nPASSED r2e_tests.test_1::RequestEncodingTest::test_group_encoding\nPASSED r2e_tests.test_1::RequestEncodingTest::test_group_question_mark\nPASSED r2e_tests.test_1::RequestEncodingTest::test_slashes\nPASSED r2e_tests.test_1::RequestSummaryTest::test_missing_remote_ip\nPASSED r2e_tests.test_1::SecureCookieV1Test::test_arbitrary_bytes\nPASSED r2e_tests.test_1::SecureCookieV1Test::test_cookie_tampering_future_timestamp\nPASSED r2e_tests.test_1::SecureCookieV1Test::test_round_trip\nPASSED r2e_tests.test_1::SecureCookieV2Test::test_key_version_increment_version\nPASSED r2e_tests.test_1::SecureCookieV2Test::test_key_version_invalidate_version\nPASSED r2e_tests.test_1::SecureCookieV2Test::test_key_version_roundtrip\nPASSED r2e_tests.test_1::SecureCookieV2Test::test_key_version_roundtrip_differing_version\nPASSED r2e_tests.test_1::SecureCookieV2Test::test_round_trip\nPASSED r2e_tests.test_1::SetLazyPropertiesTest::test_set_properties\nPASSED r2e_tests.test_1::SignedValueTest::test_expired\nPASSED r2e_tests.test_1::SignedValueTest::test_key_version_retrieval\nPASSED r2e_tests.test_1::SignedValueTest::test_key_versioning_invalid_key\nPASSED r2e_tests.test_1::SignedValueTest::test_key_versioning_read_write_default_key\nPASSED r2e_tests.test_1::SignedValueTest::test_key_versioning_read_write_non_default_key\nPASSED r2e_tests.test_1::SignedValueTest::test_known_values\nPASSED r2e_tests.test_1::SignedValueTest::test_name_swap\nPASSED r2e_tests.test_1::SignedValueTest::test_non_ascii\nPASSED r2e_tests.test_1::SignedValueTest::test_payload_tampering\nPASSED r2e_tests.test_1::SignedValueTest::test_signature_tampering\nFAILED r2e_tests.test_1::StaticDefaultFilenameTest::test_static_default_filename\nFAILED r2e_tests.test_1::StaticDefaultFilenameTest::test_static_default_redirect\nFAILED r2e_tests.test_1::StaticFileTest::test_absolute_static_url\nPASSED r2e_tests.test_1::StaticFileTest::test_absolute_version_exclusion\nPASSED r2e_tests.test_1::StaticFileTest::test_include_host_override\nPASSED r2e_tests.test_1::StaticFileTest::test_path_traversal_protection\nPASSED r2e_tests.test_1::StaticFileTest::test_relative_version_exclusion\nFAILED r2e_tests.test_1::StaticFileTest::test_root_static_path\nERROR r2e_tests.test_1::StaticFileTest::test_static_304_etag_modified_bug\nERROR r2e_tests.test_1::StaticFileTest::test_static_304_if_modified_since\nERROR r2e_tests.test_1::StaticFileTest::test_static_304_if_none_match\nPASSED r2e_tests.test_1::StaticFileTest::test_static_404\nFAILED r2e_tests.test_1::StaticFileTest::test_static_compressed_files\nFAILED r2e_tests.test_1::StaticFileTest::test_static_etag\nFAILED r2e_tests.test_1::StaticFileTest::test_static_files\nFAILED r2e_tests.test_1::StaticFileTest::test_static_head\nFAILED r2e_tests.test_1::StaticFileTest::test_static_head_range\nFAILED r2e_tests.test_1::StaticFileTest::test_static_if_modified_since_pre_epoch\nERROR r2e_tests.test_1::StaticFileTest::test_static_if_modified_since_time_zone\nFAILED r2e_tests.test_1::StaticFileTest::test_static_invalid_range\nFAILED r2e_tests.test_1::StaticFileTest::test_static_range_if_none_match\nFAILED r2e_tests.test_1::StaticFileTest::test_static_unsatisfiable_range_end_less_than_start\nFAILED r2e_tests.test_1::StaticFileTest::test_static_unsatisfiable_range_invalid_start\nFAILED r2e_tests.test_1::StaticFileTest::test_static_unsatisfiable_range_zero_suffix\nFAILED r2e_tests.test_1::StaticFileTest::test_static_url\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_end_edge\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_full_file\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_full_past_end\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_neg_end\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_neg_past_start\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_partial_past_end\nFAILED r2e_tests.test_1::StaticFileWithPathTest::test_serve\nPASSED r2e_tests.test_1::StatusReasonTest::test_status\nPASSED r2e_tests.test_1::StreamingRequestBodyTest::test_close_during_upload\nPASSED r2e_tests.test_1::StreamingRequestBodyTest::test_early_return\nPASSED r2e_tests.test_1::StreamingRequestBodyTest::test_early_return_with_data\nPASSED r2e_tests.test_1::StreamingRequestBodyTest::test_streaming_body\nPASSED r2e_tests.test_1::UIMethodUIModuleTest::test_ui_method\nPASSED r2e_tests.test_1::URLSpecReverseTest::test_non_reversible\nPASSED r2e_tests.test_1::URLSpecReverseTest::test_reverse\nPASSED r2e_tests.test_1::URLSpecReverseTest::test_reverse_arguments\nPASSED r2e_tests.test_1::UnimplementedHTTPMethodsTest::test_unimplemented_standard_methods\nPASSED r2e_tests.test_1::UnimplementedNonStandardMethodsTest::test_unimplemented_other\nPASSED r2e_tests.test_1::UnimplementedNonStandardMethodsTest::test_unimplemented_patch\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_decode_argument\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_decode_argument_invalid_unicode\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_decode_argument_plus\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_get_argument\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_get_body_arguments\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_get_query_arguments\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_header_injection\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_multi_header\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_no_gzip\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_optional_path\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_redirect\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_reverse_url\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_types\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_uimodule_resources\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_uimodule_unescaped\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_web_redirect\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_web_redirect_double_slash\nPASSED r2e_tests.test_1::XSRFCookieKwargsTest::test_xsrf_httponly\nPASSED r2e_tests.test_1::XSRFTest::test_cross_user\nPASSED r2e_tests.test_1::XSRFTest::test_distinct_tokens\nPASSED r2e_tests.test_1::XSRFTest::test_refresh_token\nPASSED r2e_tests.test_1::XSRFTest::test_versioning\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_fail_argument_invalid_format\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_fail_body_no_cookie\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_fail_cookie_invalid_format\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_fail_cookie_no_body\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_fail_no_token\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_success_header\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_success_non_hex_token\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_success_post_body\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_success_query_string\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_success_short_token\n=================== 25 failed, 4 errors, 154 passed, 1 skipped in 0.55s ===================\n",
    "new_commit_res_stderr": "WARNING:tornado.access:405 HEAD / (127.0.0.1) 0.41ms\nERROR:tornado.access:500 GET / (127.0.0.1) 0.39ms\nWARNING:tornado.access:404 GET / (127.0.0.1) 0.22ms\nWARNING:tornado.access:404 GET / (127.0.0.1) 0.24ms\nWARNING:tornado.access:403 GET / (127.0.0.1) 0.25ms\nWARNING:tornado.access:404 GET /baz (127.0.0.1) 0.25ms\nWARNING:tornado.access:404 GET /foo (127.0.0.1) 0.21ms\nWARNING:tornado.access:404 POST /404 (127.0.0.1) 0.24ms\nWARNING:tornado.access:417 POST /error (127.0.0.1) 0.25ms\nERROR:tornado.access:500 GET /default (127.0.0.1) 0.31ms\nERROR:tornado.access:503 GET /default?status=503 (127.0.0.1) 0.24ms\nWARNING:tornado.access:435 GET /default?status=435 (127.0.0.1) 0.25ms\nERROR:tornado.access:500 GET /failed_write_error (127.0.0.1) 0.30ms\nERROR:tornado.access:500 GET /write_error (127.0.0.1) 1.07ms\nERROR:tornado.access:503 GET /write_error?status=503 (127.0.0.1) 0.25ms\nWARNING:tornado.access:410 GET /?exc=http (127.0.0.1) 0.32ms\nWARNING:tornado.access:403 GET /?exc=permission (127.0.0.1) 0.30ms\nERROR:tornado.access:500 GET /?exc=zero (127.0.0.1) 0.29ms\nWARNING:tornado.access:401 GET / (127.0.0.1) 0.21ms\nWARNING:tornado.access:401 GET /?finish_value=1 (127.0.0.1) 0.22ms\nWARNING:tornado.access:404 GET /robots.txt (127.0.0.1) 0.34ms\nWARNING:tornado.access:404 GET /bar (127.0.0.1) 0.24ms\nWARNING:tornado.access:404 GET /baz (127.0.0.1) 0.22ms\nERROR:tornado.access:682 GET / (127.0.0.1) 0.24ms\nWARNING:tornado.access:400 GET /group/?arg=%25%e9 (127.0.0.1) 0.48ms\nWARNING:tornado.access:404 GET /static/dir/ (127.0.0.1) 0.35ms\nWARNING:tornado.access:404 GET /static/dir (127.0.0.1) 0.30ms\nERROR:tornado.general:Could not open static file '/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/static/robots.txt'\nWARNING:tornado.access:403 HEAD /static/../static_foo.txt (127.0.0.1) 0.34ms\nWARNING:tornado.access:403 GET /static/../static_foo.txt (127.0.0.1) 0.30ms\nWARNING:tornado.access:404 HEAD /root_static/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/static/robots.txt (127.0.0.1) 0.33ms\nWARNING:tornado.access:404 GET /root_static/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/static/robots.txt (127.0.0.1) 0.30ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.33ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.32ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.33ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 HEAD /static/blarg (127.0.0.1) 0.36ms\nWARNING:tornado.access:404 GET /static/blarg (127.0.0.1) 0.33ms\nWARNING:tornado.access:404 GET /static/sample.xml.gz (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.32ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 GET /robots.txt (127.0.0.1) 0.29ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.31ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.41ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.31ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.24ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.25ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.25ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.30ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.25ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.29ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.29ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.30ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.41ms\nWARNING:tornado.access:404 GET /foo/utf8.html (127.0.0.1) 0.34ms\nERROR:tornado.access:682 GET /?code=682&reason=Bar (127.0.0.1) 0.20ms\nERROR:tornado.access:682 GET /?code=682 (127.0.0.1) 0.21ms\nWARNING:tornado.access:401 GET /early_return (127.0.0.1) 0.21ms\nWARNING:tornado.access:401 GET /early_return (127.0.0.1) 0.34ms\nWARNING:tornado.access:405 HEAD / (127.0.0.1) 0.22ms\nWARNING:tornado.access:405 GET / (127.0.0.1) 0.20ms\nWARNING:tornado.access:405 DELETE / (127.0.0.1) 0.21ms\nWARNING:tornado.access:405 OPTIONS / (127.0.0.1) 0.22ms\nWARNING:tornado.access:405 POST / (127.0.0.1) 0.21ms\nWARNING:tornado.access:405 PUT / (127.0.0.1) 0.20ms\nWARNING:tornado.access:405 OTHER / (127.0.0.1) 0.22ms\nWARNING:tornado.access:405 PATCH / (127.0.0.1) 0.22ms\nWARNING:tornado.access:400 GET /typecheck/invalid%FF (127.0.0.1) 0.30ms\nWARNING:tornado.access:400 GET /typecheck/invalid?foo=%FF (127.0.0.1) 0.29ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.40ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.36ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.37ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.35ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.34ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.29ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.29ms\n",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239\n........................................................................................................................................................................................\ncollected 184 items\n\n=================================== FAILURES ===================================\n__________ GzipTestCase.test_gzip_static __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1758, in test_gzip_static\n    self.assert_compressed(response)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1742, in assert_compressed\n    self.assertEqual(\nAssertionError: None != 'gzip'\n__________ StaticDefaultFilenameTest.test_static_default_filename __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1409, in test_static_default_filename\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticDefaultFilenameTest.test_static_default_redirect __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1414, in test_static_default_redirect\n    self.assertEqual(response.code, 301)\nAssertionError: 404 != 301\n__________ StaticFileTest.test_absolute_static_url __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1127, in test_absolute_static_url\n    self.assertEqual(\nAssertionError: b'http://127.0.0.1:37387/static/robots.txt' != b'http://127.0.0.1:37387/static/robots.txt?v=f71d20196d4caf35b6a670db8c70b03d'\n__________ StaticFileTest.test_root_static_path __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1394, in test_root_static_path\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_compressed_files __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1110, in test_static_compressed_files\n    self.assertEqual(response.headers.get(\"Content-Type\"), \"application/gzip\")\nAssertionError: 'text/html; charset=UTF-8' != 'application/gzip'\n- text/html; charset=UTF-8\n+ application/gzip\n\n__________ StaticFileTest.test_static_etag __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1228, in test_static_etag\n    self.assertEqual(\nAssertionError: None != b'\"f71d20196d4caf35b6a670db8c70b03d\"'\n__________ StaticFileTest.test_static_files __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1102, in test_static_files\n    self.assertTrue(b\"Disallow: /\" in response.body)\nAssertionError: False is not true\n__________ StaticFileTest.test_static_head __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1333, in test_static_head\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_head_range __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1345, in test_static_head_range\n    self.assertEqual(response.code, 206)\nAssertionError: 404 != 206\n__________ StaticFileTest.test_static_if_modified_since_pre_epoch __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1206, in test_static_if_modified_since_pre_epoch\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_invalid_range __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1308, in test_static_invalid_range\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_range_if_none_match __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1360, in test_static_range_if_none_match\n    self.assertEqual(response.code, 304)\nAssertionError: 404 != 304\n__________ StaticFileTest.test_static_unsatisfiable_range_end_less_than_start __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1328, in test_static_unsatisfiable_range_end_less_than_start\n    self.assertEqual(response.code, 416)\nAssertionError: 404 != 416\n__________ StaticFileTest.test_static_unsatisfiable_range_invalid_start __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1321, in test_static_unsatisfiable_range_invalid_start\n    self.assertEqual(response.code, 416)\nAssertionError: 404 != 416\n__________ StaticFileTest.test_static_unsatisfiable_range_zero_suffix __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1314, in test_static_unsatisfiable_range_zero_suffix\n    self.assertEqual(response.headers.get(\"Content-Range\"), \"bytes */26\")\nAssertionError: None != 'bytes */26'\n__________ StaticFileTest.test_static_url __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1123, in test_static_url\n    self.assertEqual(response.body, b\"/static/robots.txt?v=\" + self.robots_txt_hash)\nAssertionError: b'/static/robots.txt' != b'/static/robots.txt?v=f71d20196d4caf35b6a670db8c70b03d'\n__________ StaticFileTest.test_static_with_range __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1236, in test_static_with_range\n    self.assertEqual(response.code, 206)\nAssertionError: 404 != 206\n__________ StaticFileTest.test_static_with_range_end_edge __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1283, in test_static_with_range_end_edge\n    self.assertEqual(response.body, b\": /\\n\")\nAssertionError: b'<html><title>404: Not Found</title><body>404: Not Found</body></html>' != b': /\\n'\n__________ StaticFileTest.test_static_with_range_full_file __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1250, in test_static_with_range_full_file\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_with_range_full_past_end __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1261, in test_static_with_range_full_past_end\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_with_range_neg_end __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1291, in test_static_with_range_neg_end\n    self.assertEqual(response.body, b\": /\\n\")\nAssertionError: b'<html><title>404: Not Found</title><body>404: Not Found</body></html>' != b': /\\n'\n__________ StaticFileTest.test_static_with_range_neg_past_start __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1299, in test_static_with_range_neg_past_start\n    self.assertEqual(response.code, 200)\nAssertionError: 404 != 200\n__________ StaticFileTest.test_static_with_range_partial_past_end __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1272, in test_static_with_range_partial_past_end\n    self.assertEqual(response.code, 206)\nAssertionError: 404 != 206\n__________ StaticFileWithPathTest.test_serve __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1430, in test_serve\n    self.assertEqual(response.body, b\"H\\xc3\\xa9llo\\n\")\nAssertionError: b'<html><title>404: Not Found</title><body>404: Not Found</body></html>' != b'H\\xc3\\xa9llo\\n'\n__________ URLSpecReverseTest.test_reverse_arguments __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 3110, in test_reverse_arguments\n    self.assertEqual(\nAssertionError: '/api.v1/foo/5/icon.png' != '/api.v1/foo/5/icon\\\\.png'\n- /api.v1/foo/5/icon.png\n+ /api.v1/foo/5/icon\\.png\n?                   +\n\n\n=================================== ERRORS =====================================\n__________ StaticFileTest.test_static_304_etag_modified_bug __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1193, in test_static_304_etag_modified_bug\n    \"If-Modified-Since\": response1.headers[\"Last-Modified\"],\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/httputil.py\", line 216, in __getitem__\n    return self._dict[_normalize_header(name)]\nKeyError: 'Last-Modified'\n__________ StaticFileTest.test_static_304_if_modified_since __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1174, in test_static_304_if_modified_since\n    headers={\"If-Modified-Since\": response1.headers[\"Last-Modified\"]},\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/httputil.py\", line 216, in __getitem__\n    return self._dict[_normalize_header(name)]\nKeyError: 'Last-Modified'\n__________ StaticFileTest.test_static_304_if_none_match __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1183, in test_static_304_if_none_match\n    \"/static/robots.txt\", headers={\"If-None-Match\": response1.headers[\"Etag\"]}\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/httputil.py\", line 216, in __getitem__\n    return self._dict[_normalize_header(name)]\nKeyError: 'Etag'\n__________ StaticFileTest.test_static_if_modified_since_time_zone __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/tornado/testing.py\", line 98, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/test_1.py\", line 1213, in test_static_if_modified_since_time_zone\n    stat = os.stat(relpath(\"static/robots.txt\"))\nFileNotFoundError: [Errno 2] No such file or directory: '/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/static/robots.txt'\n\n==================== short test summary info ====================\nPASSED r2e_tests.test_1::AllHTTPMethodsTest::test_standard_methods\nPASSED r2e_tests.test_1::ApplicationTest::test_listen\nPASSED r2e_tests.test_1::AuthRedirectTest::test_absolute_auth_redirect\nPASSED r2e_tests.test_1::AuthRedirectTest::test_relative_auth_redirect\nPASSED r2e_tests.test_1::BuggyLoggingTest::test_buggy_log_exception\nPASSED r2e_tests.test_1::CacheTest::test_multiple_strong_etag_match\nPASSED r2e_tests.test_1::CacheTest::test_multiple_strong_etag_not_match\nPASSED r2e_tests.test_1::CacheTest::test_multiple_weak_etag_match\nPASSED r2e_tests.test_1::CacheTest::test_multiple_weak_etag_not_match\nPASSED r2e_tests.test_1::CacheTest::test_strong_etag_match\nPASSED r2e_tests.test_1::CacheTest::test_strong_etag_not_match\nPASSED r2e_tests.test_1::CacheTest::test_weak_etag_match\nPASSED r2e_tests.test_1::CacheTest::test_weak_etag_not_match\nPASSED r2e_tests.test_1::CacheTest::test_wildcard_etag\nPASSED r2e_tests.test_1::ClearAllCookiesTest::test_clear_all_cookies\nPASSED r2e_tests.test_1::ClearHeaderTest::test_clear_header\nPASSED r2e_tests.test_1::ClientCloseTest::test_client_close\nPASSED r2e_tests.test_1::ConnectionCloseTest::test_connection_close\nPASSED r2e_tests.test_1::CookieTest::test_cookie_special_char\nPASSED r2e_tests.test_1::CookieTest::test_get_cookie\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie_domain\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie_expires_days\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie_false_flags\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie_max_age\nPASSED r2e_tests.test_1::CookieTest::test_set_cookie_overwrite\nPASSED r2e_tests.test_1::Custom404Test::test_404\nPASSED r2e_tests.test_1::CustomStaticFileTest::test_serve\nPASSED r2e_tests.test_1::CustomStaticFileTest::test_static_url\nPASSED r2e_tests.test_1::DateHeaderTest::test_date_header\nPASSED r2e_tests.test_1::DecoratedStreamingRequestFlowControlTest::test_flow_control_chunked_body\nPASSED r2e_tests.test_1::DecoratedStreamingRequestFlowControlTest::test_flow_control_compressed_body\nPASSED r2e_tests.test_1::DecoratedStreamingRequestFlowControlTest::test_flow_control_fixed_body\nPASSED r2e_tests.test_1::DecoratorTest::test_addslash\nPASSED r2e_tests.test_1::DecoratorTest::test_removeslash\nPASSED r2e_tests.test_1::Default404Test::test_404\nPASSED r2e_tests.test_1::DefaultHandlerArgumentsTest::test_403\nPASSED r2e_tests.test_1::DefaultHostMatchingTest::test_default_host_matching\nPASSED r2e_tests.test_1::ErrorHandlerXSRFTest::test_404_xsrf\nPASSED r2e_tests.test_1::ErrorHandlerXSRFTest::test_error_xsrf\nPASSED r2e_tests.test_1::ErrorResponseTest::test_default\nPASSED r2e_tests.test_1::ErrorResponseTest::test_failed_write_error\nPASSED r2e_tests.test_1::ErrorResponseTest::test_write_error\nPASSED r2e_tests.test_1::ExceptionHandlerTest::test_http_error\nPASSED r2e_tests.test_1::ExceptionHandlerTest::test_known_error\nPASSED r2e_tests.test_1::ExceptionHandlerTest::test_unknown_error\nPASSED r2e_tests.test_1::FinalReturnTest::test_finish_method_return_future\nPASSED r2e_tests.test_1::FinalReturnTest::test_render_method_return_future\nPASSED r2e_tests.test_1::FinishExceptionTest::test_finish_exception\nPASSED r2e_tests.test_1::FinishInPrepareTest::test_finish_in_prepare\nPASSED r2e_tests.test_1::GetArgumentErrorTest::test_catch_error\nPASSED r2e_tests.test_1::GetCurrentUserTest::test_get_current_user_from_ui_module_is_lazy\nPASSED r2e_tests.test_1::GetCurrentUserTest::test_get_current_user_from_ui_module_works\nSKIPPED r2e_tests.test_1::GetCurrentUserTest::test_get_current_user_is_lazy\nPASSED r2e_tests.test_1::GetCurrentUserTest::test_get_current_user_works\nPASSED r2e_tests.test_1::GzipTestCase::test_gzip\nPASSED r2e_tests.test_1::GzipTestCase::test_gzip_not_requested\nFAILED r2e_tests.test_1::GzipTestCase::test_gzip_static\nPASSED r2e_tests.test_1::GzipTestCase::test_vary_already_present\nPASSED r2e_tests.test_1::GzipTestCase::test_vary_already_present_multiple\nPASSED r2e_tests.test_1::HTTPErrorTest::test_copy\nPASSED r2e_tests.test_1::HandlerByNameTest::test_handler_by_name\nPASSED r2e_tests.test_1::Header204Test::test_204_headers\nPASSED r2e_tests.test_1::Header304Test::test_304_headers\nPASSED r2e_tests.test_1::HostMatchingTest::test_host_matching\nPASSED r2e_tests.test_1::IncorrectContentLengthTest::test_content_length_too_high\nPASSED r2e_tests.test_1::IncorrectContentLengthTest::test_content_length_too_low\nPASSED r2e_tests.test_1::NamedURLSpecGroupsTest::test_named_urlspec_groups\nPASSED r2e_tests.test_1::NativeStreamingRequestFlowControlTest::test_flow_control_chunked_body\nPASSED r2e_tests.test_1::NativeStreamingRequestFlowControlTest::test_flow_control_compressed_body\nPASSED r2e_tests.test_1::NativeStreamingRequestFlowControlTest::test_flow_control_fixed_body\nPASSED r2e_tests.test_1::NonWSGIWebTests::test_empty_flush\nPASSED r2e_tests.test_1::PatchMethodTest::test_other\nPASSED r2e_tests.test_1::PatchMethodTest::test_patch\nPASSED r2e_tests.test_1::PathArgsInPrepareTest::test_kw\nPASSED r2e_tests.test_1::PathArgsInPrepareTest::test_pos\nPASSED r2e_tests.test_1::RaiseWithReasonTest::test_httperror_str\nPASSED r2e_tests.test_1::RaiseWithReasonTest::test_httperror_str_from_httputil\nPASSED r2e_tests.test_1::RaiseWithReasonTest::test_raise_with_reason\nPASSED r2e_tests.test_1::RedirectHandlerTest::test_basic_redirect\nPASSED r2e_tests.test_1::RedirectHandlerTest::test_redirect_pattern\nPASSED r2e_tests.test_1::RedirectHandlerTest::test_redirect_with_appending_argument\nPASSED r2e_tests.test_1::RedirectHandlerTest::test_redirect_with_argument\nPASSED r2e_tests.test_1::RequestEncodingTest::test_error\nPASSED r2e_tests.test_1::RequestEncodingTest::test_group_encoding\nPASSED r2e_tests.test_1::RequestEncodingTest::test_group_question_mark\nPASSED r2e_tests.test_1::RequestEncodingTest::test_slashes\nPASSED r2e_tests.test_1::RequestSummaryTest::test_missing_remote_ip\nPASSED r2e_tests.test_1::SecureCookieV1Test::test_arbitrary_bytes\nPASSED r2e_tests.test_1::SecureCookieV1Test::test_cookie_tampering_future_timestamp\nPASSED r2e_tests.test_1::SecureCookieV1Test::test_round_trip\nPASSED r2e_tests.test_1::SecureCookieV2Test::test_key_version_increment_version\nPASSED r2e_tests.test_1::SecureCookieV2Test::test_key_version_invalidate_version\nPASSED r2e_tests.test_1::SecureCookieV2Test::test_key_version_roundtrip\nPASSED r2e_tests.test_1::SecureCookieV2Test::test_key_version_roundtrip_differing_version\nPASSED r2e_tests.test_1::SecureCookieV2Test::test_round_trip\nPASSED r2e_tests.test_1::SetLazyPropertiesTest::test_set_properties\nPASSED r2e_tests.test_1::SignedValueTest::test_expired\nPASSED r2e_tests.test_1::SignedValueTest::test_key_version_retrieval\nPASSED r2e_tests.test_1::SignedValueTest::test_key_versioning_invalid_key\nPASSED r2e_tests.test_1::SignedValueTest::test_key_versioning_read_write_default_key\nPASSED r2e_tests.test_1::SignedValueTest::test_key_versioning_read_write_non_default_key\nPASSED r2e_tests.test_1::SignedValueTest::test_known_values\nPASSED r2e_tests.test_1::SignedValueTest::test_name_swap\nPASSED r2e_tests.test_1::SignedValueTest::test_non_ascii\nPASSED r2e_tests.test_1::SignedValueTest::test_payload_tampering\nPASSED r2e_tests.test_1::SignedValueTest::test_signature_tampering\nFAILED r2e_tests.test_1::StaticDefaultFilenameTest::test_static_default_filename\nFAILED r2e_tests.test_1::StaticDefaultFilenameTest::test_static_default_redirect\nFAILED r2e_tests.test_1::StaticFileTest::test_absolute_static_url\nPASSED r2e_tests.test_1::StaticFileTest::test_absolute_version_exclusion\nPASSED r2e_tests.test_1::StaticFileTest::test_include_host_override\nPASSED r2e_tests.test_1::StaticFileTest::test_path_traversal_protection\nPASSED r2e_tests.test_1::StaticFileTest::test_relative_version_exclusion\nFAILED r2e_tests.test_1::StaticFileTest::test_root_static_path\nERROR r2e_tests.test_1::StaticFileTest::test_static_304_etag_modified_bug\nERROR r2e_tests.test_1::StaticFileTest::test_static_304_if_modified_since\nERROR r2e_tests.test_1::StaticFileTest::test_static_304_if_none_match\nPASSED r2e_tests.test_1::StaticFileTest::test_static_404\nFAILED r2e_tests.test_1::StaticFileTest::test_static_compressed_files\nFAILED r2e_tests.test_1::StaticFileTest::test_static_etag\nFAILED r2e_tests.test_1::StaticFileTest::test_static_files\nFAILED r2e_tests.test_1::StaticFileTest::test_static_head\nFAILED r2e_tests.test_1::StaticFileTest::test_static_head_range\nFAILED r2e_tests.test_1::StaticFileTest::test_static_if_modified_since_pre_epoch\nERROR r2e_tests.test_1::StaticFileTest::test_static_if_modified_since_time_zone\nFAILED r2e_tests.test_1::StaticFileTest::test_static_invalid_range\nFAILED r2e_tests.test_1::StaticFileTest::test_static_range_if_none_match\nFAILED r2e_tests.test_1::StaticFileTest::test_static_unsatisfiable_range_end_less_than_start\nFAILED r2e_tests.test_1::StaticFileTest::test_static_unsatisfiable_range_invalid_start\nFAILED r2e_tests.test_1::StaticFileTest::test_static_unsatisfiable_range_zero_suffix\nFAILED r2e_tests.test_1::StaticFileTest::test_static_url\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_end_edge\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_full_file\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_full_past_end\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_neg_end\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_neg_past_start\nFAILED r2e_tests.test_1::StaticFileTest::test_static_with_range_partial_past_end\nFAILED r2e_tests.test_1::StaticFileWithPathTest::test_serve\nPASSED r2e_tests.test_1::StatusReasonTest::test_status\nPASSED r2e_tests.test_1::StreamingRequestBodyTest::test_close_during_upload\nPASSED r2e_tests.test_1::StreamingRequestBodyTest::test_early_return\nPASSED r2e_tests.test_1::StreamingRequestBodyTest::test_early_return_with_data\nPASSED r2e_tests.test_1::StreamingRequestBodyTest::test_streaming_body\nPASSED r2e_tests.test_1::UIMethodUIModuleTest::test_ui_method\nPASSED r2e_tests.test_1::URLSpecReverseTest::test_non_reversible\nPASSED r2e_tests.test_1::URLSpecReverseTest::test_reverse\nFAILED r2e_tests.test_1::URLSpecReverseTest::test_reverse_arguments\nPASSED r2e_tests.test_1::UnimplementedHTTPMethodsTest::test_unimplemented_standard_methods\nPASSED r2e_tests.test_1::UnimplementedNonStandardMethodsTest::test_unimplemented_other\nPASSED r2e_tests.test_1::UnimplementedNonStandardMethodsTest::test_unimplemented_patch\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_decode_argument\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_decode_argument_invalid_unicode\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_decode_argument_plus\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_get_argument\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_get_body_arguments\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_get_query_arguments\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_header_injection\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_multi_header\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_no_gzip\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_optional_path\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_redirect\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_reverse_url\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_types\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_uimodule_resources\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_uimodule_unescaped\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_web_redirect\nPASSED r2e_tests.test_1::WSGISafeWebTest::test_web_redirect_double_slash\nPASSED r2e_tests.test_1::XSRFCookieKwargsTest::test_xsrf_httponly\nPASSED r2e_tests.test_1::XSRFTest::test_cross_user\nPASSED r2e_tests.test_1::XSRFTest::test_distinct_tokens\nPASSED r2e_tests.test_1::XSRFTest::test_refresh_token\nPASSED r2e_tests.test_1::XSRFTest::test_versioning\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_fail_argument_invalid_format\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_fail_body_no_cookie\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_fail_cookie_invalid_format\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_fail_cookie_no_body\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_fail_no_token\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_success_header\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_success_non_hex_token\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_success_post_body\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_success_query_string\nPASSED r2e_tests.test_1::XSRFTest::test_xsrf_success_short_token\n=================== 26 failed, 4 errors, 153 passed, 1 skipped in 0.53s ===================\n",
    "old_commit_res_stderr": "WARNING:tornado.access:405 HEAD / (127.0.0.1) 0.33ms\nERROR:tornado.access:500 GET / (127.0.0.1) 0.31ms\nWARNING:tornado.access:404 GET / (127.0.0.1) 0.23ms\nWARNING:tornado.access:404 GET / (127.0.0.1) 0.25ms\nWARNING:tornado.access:403 GET / (127.0.0.1) 0.22ms\nWARNING:tornado.access:404 GET /baz (127.0.0.1) 0.22ms\nWARNING:tornado.access:404 GET /foo (127.0.0.1) 0.21ms\nWARNING:tornado.access:404 POST /404 (127.0.0.1) 0.25ms\nWARNING:tornado.access:417 POST /error (127.0.0.1) 0.22ms\nERROR:tornado.access:500 GET /default (127.0.0.1) 0.30ms\nERROR:tornado.access:503 GET /default?status=503 (127.0.0.1) 0.23ms\nWARNING:tornado.access:435 GET /default?status=435 (127.0.0.1) 0.23ms\nERROR:tornado.access:500 GET /failed_write_error (127.0.0.1) 0.28ms\nERROR:tornado.access:500 GET /write_error (127.0.0.1) 0.27ms\nERROR:tornado.access:503 GET /write_error?status=503 (127.0.0.1) 0.21ms\nWARNING:tornado.access:410 GET /?exc=http (127.0.0.1) 0.29ms\nWARNING:tornado.access:403 GET /?exc=permission (127.0.0.1) 0.29ms\nERROR:tornado.access:500 GET /?exc=zero (127.0.0.1) 0.27ms\nWARNING:tornado.access:401 GET / (127.0.0.1) 0.22ms\nWARNING:tornado.access:401 GET /?finish_value=1 (127.0.0.1) 0.23ms\nWARNING:tornado.access:404 GET /robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 GET /bar (127.0.0.1) 0.22ms\nWARNING:tornado.access:404 GET /baz (127.0.0.1) 0.20ms\nERROR:tornado.access:682 GET / (127.0.0.1) 0.25ms\nWARNING:tornado.access:400 GET /group/?arg=%25%e9 (127.0.0.1) 0.45ms\nWARNING:tornado.access:404 GET /static/dir/ (127.0.0.1) 0.43ms\nWARNING:tornado.access:404 GET /static/dir (127.0.0.1) 0.41ms\nERROR:tornado.general:Could not open static file '/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/static/robots.txt'\nWARNING:tornado.access:403 HEAD /static/../static_foo.txt (127.0.0.1) 0.34ms\nWARNING:tornado.access:403 GET /static/../static_foo.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 HEAD /root_static/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/static/robots.txt (127.0.0.1) 0.32ms\nWARNING:tornado.access:404 GET /root_static/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_32377f2c2cdd95ade0e40fabdcc1364e07ea2239/r2e_tests/static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.32ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.32ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.30ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.29ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 HEAD /static/blarg (127.0.0.1) 0.29ms\nWARNING:tornado.access:404 GET /static/blarg (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 GET /static/sample.xml.gz (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.31ms\nWARNING:tornado.access:404 GET /robots.txt (127.0.0.1) 0.30ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.29ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.30ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.25ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.41ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.41ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.25ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.28ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.25ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.27ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.25ms\nWARNING:tornado.access:404 HEAD /static/robots.txt (127.0.0.1) 0.26ms\nWARNING:tornado.access:404 GET /static/robots.txt (127.0.0.1) 0.37ms\nWARNING:tornado.access:404 GET /foo/utf8.html (127.0.0.1) 0.29ms\nERROR:tornado.access:682 GET /?code=682&reason=Bar (127.0.0.1) 0.22ms\nERROR:tornado.access:682 GET /?code=682 (127.0.0.1) 0.26ms\nWARNING:tornado.access:401 GET /early_return (127.0.0.1) 0.20ms\nWARNING:tornado.access:401 GET /early_return (127.0.0.1) 0.36ms\nWARNING:tornado.access:405 HEAD / (127.0.0.1) 0.27ms\nWARNING:tornado.access:405 GET / (127.0.0.1) 0.21ms\nWARNING:tornado.access:405 DELETE / (127.0.0.1) 0.22ms\nWARNING:tornado.access:405 OPTIONS / (127.0.0.1) 0.28ms\nWARNING:tornado.access:405 POST / (127.0.0.1) 0.23ms\nWARNING:tornado.access:405 PUT / (127.0.0.1) 0.22ms\nWARNING:tornado.access:405 OTHER / (127.0.0.1) 0.39ms\nWARNING:tornado.access:405 PATCH / (127.0.0.1) 0.21ms\nWARNING:tornado.access:400 GET /typecheck/invalid%FF (127.0.0.1) 0.31ms\nWARNING:tornado.access:400 GET /typecheck/invalid?foo=%FF (127.0.0.1) 0.27ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.36ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.37ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.41ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.35ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.36ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.30ms\nWARNING:tornado.access:403 POST / (127.0.0.1) 0.26ms\n"
}
tornado/routing.py tornado/test/web_test.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Incorrect URL Reversal with Escaped Characters

**Description:**
When reversing a URL that includes escaped regex characters, the generated URL incorrectly retains the escape characters, resulting in an invalid path.

**Example Code:**
```python
from tornado.routing import url

# Attempting to reverse a URL with escaped regex
reversed_url = url(r"/api\.v1/foo/([0-9]+)/icon\.png", None).reverse(5)
print(reversed_url)  # Outputs: /api.v1/foo/5/icon\.png
```

**Expected Behavior:**
The reversed URL should be `/api.v1/foo/5/icon.png` without the backslash before `.png`.

**Actual Behavior:**
The reversed URL incorrectly includes a backslash, resulting in `/api.v1/foo/5/icon\.png`.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### tornado/routing.py
# Copyright 2015 The Tornado Authors
#
# 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.

"""Flexible routing implementation.

Tornado routes HTTP requests to appropriate handlers using `Router`
class implementations. The `tornado.web.Application` class is a
`Router` implementation and may be used directly, or the classes in
this module may be used for additional flexibility. The `RuleRouter`
class can match on more criteria than `.Application`, or the `Router`
interface can be subclassed for maximum customization.

`Router` interface extends `~.httputil.HTTPServerConnectionDelegate`
to provide additional routing capabilities. This also means that any
`Router` implementation can be used directly as a ``request_callback``
for `~.httpserver.HTTPServer` constructor.

`Router` subclass must implement a ``find_handler`` method to provide
a suitable `~.httputil.HTTPMessageDelegate` instance to handle the
request:

.. code-block:: python

    class CustomRouter(Router):
        def find_handler(self, request, **kwargs):
            # some routing logic providing a suitable HTTPMessageDelegate instance
            return MessageDelegate(request.connection)

    class MessageDelegate(HTTPMessageDelegate):
        def __init__(self, connection):
            self.connection = connection

        def finish(self):
            self.connection.write_headers(
                ResponseStartLine("HTTP/1.1", 200, "OK"),
                HTTPHeaders({"Content-Length": "2"}),
                b"OK")
            self.connection.finish()

    router = CustomRouter()
    server = HTTPServer(router)

The main responsibility of `Router` implementation is to provide a
mapping from a request to `~.httputil.HTTPMessageDelegate` instance
that will handle this request. In the example above we can see that
routing is possible even without instantiating an `~.web.Application`.

For routing to `~.web.RequestHandler` implementations we need an
`~.web.Application` instance. `~.web.Application.get_handler_delegate`
provides a convenient way to create `~.httputil.HTTPMessageDelegate`
for a given request and `~.web.RequestHandler`.

Here is a simple example of how we can we route to
`~.web.RequestHandler` subclasses by HTTP method:

.. code-block:: python

    resources = {}

    class GetResource(RequestHandler):
        def get(self, path):
            if path not in resources:
                raise HTTPError(404)

            self.finish(resources[path])

    class PostResource(RequestHandler):
        def post(self, path):
            resources[path] = self.request.body

    class HTTPMethodRouter(Router):
        def __init__(self, app):
            self.app = app

        def find_handler(self, request, **kwargs):
            handler = GetResource if request.method == "GET" else PostResource
            return self.app.get_handler_delegate(request, handler, path_args=[request.path])

    router = HTTPMethodRouter(Application())
    server = HTTPServer(router)

`ReversibleRouter` interface adds the ability to distinguish between
the routes and reverse them to the original urls using route's name
and additional arguments. `~.web.Application` is itself an
implementation of `ReversibleRouter` class.

`RuleRouter` and `ReversibleRuleRouter` are implementations of
`Router` and `ReversibleRouter` interfaces and can be used for
creating rule-based routing configurations.

Rules are instances of `Rule` class. They contain a `Matcher`, which
provides the logic for determining whether the rule is a match for a
particular request and a target, which can be one of the following.

1) An instance of `~.httputil.HTTPServerConnectionDelegate`:

.. code-block:: python

    router = RuleRouter([
        Rule(PathMatches("/handler"), ConnectionDelegate()),
        # ... more rules
    ])

    class ConnectionDelegate(HTTPServerConnectionDelegate):
        def start_request(self, server_conn, request_conn):
            return MessageDelegate(request_conn)

2) A callable accepting a single argument of `~.httputil.HTTPServerRequest` type:

.. code-block:: python

    router = RuleRouter([
        Rule(PathMatches("/callable"), request_callable)
    ])

    def request_callable(request):
        request.write(b"HTTP/1.1 200 OK\\r\\nContent-Length: 2\\r\\n\\r\\nOK")
        request.finish()

3) Another `Router` instance:

.. code-block:: python

    router = RuleRouter([
        Rule(PathMatches("/router.*"), CustomRouter())
    ])

Of course a nested `RuleRouter` or a `~.web.Application` is allowed:

.. code-block:: python

    router = RuleRouter([
        Rule(HostMatches("example.com"), RuleRouter([
            Rule(PathMatches("/app1/.*"), Application([(r"/app1/handler", Handler)]))),
        ]))
    ])

    server = HTTPServer(router)

In the example below `RuleRouter` is used to route between applications:

.. code-block:: python

    app1 = Application([
        (r"/app1/handler", Handler1),
        # other handlers ...
    ])

    app2 = Application([
        (r"/app2/handler", Handler2),
        # other handlers ...
    ])

    router = RuleRouter([
        Rule(PathMatches("/app1.*"), app1),
        Rule(PathMatches("/app2.*"), app2)
    ])

    server = HTTPServer(router)

For more information on application-level routing see docs for `~.web.Application`.

.. versionadded:: 4.5

"""

import re
from functools import partial

from tornado import httputil
from tornado.httpserver import _CallableAdapter
from tornado.escape import url_escape, url_unescape, utf8
from tornado.log import app_log
from tornado.util import basestring_type, import_object, re_unescape, unicode_type

from typing import Any, Union, Optional, Awaitable, List, Dict, Pattern, Tuple, overload


class Router(httputil.HTTPServerConnectionDelegate):
    """Abstract router interface."""

    def find_handler(
        self, request: httputil.HTTPServerRequest, **kwargs: Any
    ) -> Optional[httputil.HTTPMessageDelegate]:
        """Must be implemented to return an appropriate instance of `~.httputil.HTTPMessageDelegate`
        that can serve the request.
        Routing implementations may pass additional kwargs to extend the routing logic.

        :arg httputil.HTTPServerRequest request: current HTTP request.
        :arg kwargs: additional keyword arguments passed by routing implementation.
        :returns: an instance of `~.httputil.HTTPMessageDelegate` that will be used to
            process the request.
        """
        raise NotImplementedError()

    def start_request(
        self, server_conn: object, request_conn: httputil.HTTPConnection
    ) -> httputil.HTTPMessageDelegate:
        return _RoutingDelegate(self, server_conn, request_conn)


class ReversibleRouter(Router):
    """Abstract router interface for routers that can handle named routes
    and support reversing them to original urls.
    """

    def reverse_url(self, name: str, *args: Any) -> Optional[str]:
        """Returns url string for a given route name and arguments
        or ``None`` if no match is found.

        :arg str name: route name.
        :arg args: url parameters.
        :returns: parametrized url string for a given route name (or ``None``).
        """
        raise NotImplementedError()


class _RoutingDelegate(httputil.HTTPMessageDelegate):
    def __init__(
        self, router: Router, server_conn: object, request_conn: httputil.HTTPConnection
    ) -> None:
        self.server_conn = server_conn
        self.request_conn = request_conn
        self.delegate = None  # type: Optional[httputil.HTTPMessageDelegate]
        self.router = router  # type: Router

    def headers_received(
        self,
        start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine],
        headers: httputil.HTTPHeaders,
    ) -> Optional[Awaitable[None]]:
        assert isinstance(start_line, httputil.RequestStartLine)
        request = httputil.HTTPServerRequest(
            connection=self.request_conn,
            server_connection=self.server_conn,
            start_line=start_line,
            headers=headers,
        )

        self.delegate = self.router.find_handler(request)
        if self.delegate is None:
            app_log.debug(
                "Delegate for %s %s request not found",
                start_line.method,
                start_line.path,
            )
            self.delegate = _DefaultMessageDelegate(self.request_conn)

        return self.delegate.headers_received(start_line, headers)

    def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:
        assert self.delegate is not None
        return self.delegate.data_received(chunk)

    def finish(self) -> None:
        assert self.delegate is not None
        self.delegate.finish()

    def on_connection_close(self) -> None:
        assert self.delegate is not None
        self.delegate.on_connection_close()


class _DefaultMessageDelegate(httputil.HTTPMessageDelegate):
    def __init__(self, connection: httputil.HTTPConnection) -> None:
        self.connection = connection

    def finish(self) -> None:
        self.connection.write_headers(
            httputil.ResponseStartLine("HTTP/1.1", 404, "Not Found"),
            httputil.HTTPHeaders(),
        )
        self.connection.finish()


# _RuleList can either contain pre-constructed Rules or a sequence of
# arguments to be passed to the Rule constructor.
_RuleList = List[
    Union[
        "Rule",
        List[Any],  # Can't do detailed typechecking of lists.
        Tuple[Union[str, "Matcher"], Any],
        Tuple[Union[str, "Matcher"], Any, Dict[str, Any]],
        Tuple[Union[str, "Matcher"], Any, Dict[str, Any], str],
    ]
]


class RuleRouter(Router):
    """Rule-based router implementation."""

    def __init__(self, rules: Optional[_RuleList] = None) -> None:
        """Constructs a router from an ordered list of rules::

            RuleRouter([
                Rule(PathMatches("/handler"), Target),
                # ... more rules
            ])

        You can also omit explicit `Rule` constructor and use tuples of arguments::

            RuleRouter([
                (PathMatches("/handler"), Target),
            ])

        `PathMatches` is a default matcher, so the example above can be simplified::

            RuleRouter([
                ("/handler", Target),
            ])

        In the examples above, ``Target`` can be a nested `Router` instance, an instance of
        `~.httputil.HTTPServerConnectionDelegate` or an old-style callable,
        accepting a request argument.

        :arg rules: a list of `Rule` instances or tuples of `Rule`
            constructor arguments.
        """
        self.rules = []  # type: List[Rule]
        if rules:
            self.add_rules(rules)

    def add_rules(self, rules: _RuleList) -> None:
        """Appends new rules to the router.

        :arg rules: a list of Rule instances (or tuples of arguments, which are
            passed to Rule constructor).
        """
        for rule in rules:
            if isinstance(rule, (tuple, list)):
                assert len(rule) in (2, 3, 4)
                if isinstance(rule[0], basestring_type):
                    rule = Rule(PathMatches(rule[0]), *rule[1:])
                else:
                    rule = Rule(*rule)

            self.rules.append(self.process_rule(rule))

    def process_rule(self, rule: "Rule") -> "Rule":
        """Override this method for additional preprocessing of each rule.

        :arg Rule rule: a rule to be processed.
        :returns: the same or modified Rule instance.
        """
        return rule

    def find_handler(
        self, request: httputil.HTTPServerRequest, **kwargs: Any
    ) -> Optional[httputil.HTTPMessageDelegate]:
        for rule in self.rules:
            target_params = rule.matcher.match(request)
            if target_params is not None:
                if rule.target_kwargs:
                    target_params["target_kwargs"] = rule.target_kwargs

                delegate = self.get_target_delegate(
                    rule.target, request, **target_params
                )

                if delegate is not None:
                    return delegate

        return None

    def get_target_delegate(
        self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any
    ) -> Optional[httputil.HTTPMessageDelegate]:
        """Returns an instance of `~.httputil.HTTPMessageDelegate` for a
        Rule's target. This method is called by `~.find_handler` and can be
        extended to provide additional target types.

        :arg target: a Rule's target.
        :arg httputil.HTTPServerRequest request: current request.
        :arg target_params: additional parameters that can be useful
            for `~.httputil.HTTPMessageDelegate` creation.
        """
        if isinstance(target, Router):
            return target.find_handler(request, **target_params)

        elif isinstance(target, httputil.HTTPServerConnectionDelegate):
            assert request.connection is not None
            return target.start_request(request.server_connection, request.connection)

        elif callable(target):
            assert request.connection is not None
            return _CallableAdapter(
                partial(target, **target_params), request.connection
            )

        return None


class ReversibleRuleRouter(ReversibleRouter, RuleRouter):
    """A rule-based router that implements ``reverse_url`` method.

    Each rule added to this router may have a ``name`` attribute that can be
    used to reconstruct an original uri. The actual reconstruction takes place
    in a rule's matcher (see `Matcher.reverse`).
    """

    def __init__(self, rules: Optional[_RuleList] = None) -> None:
        self.named_rules = {}  # type: Dict[str, Any]
        super(ReversibleRuleRouter, self).__init__(rules)

    def process_rule(self, rule: "Rule") -> "Rule":
        rule = super(ReversibleRuleRouter, self).process_rule(rule)

        if rule.name:
            if rule.name in self.named_rules:
                app_log.warning(
                    "Multiple handlers named %s; replacing previous value", rule.name
                )
            self.named_rules[rule.name] = rule

        return rule

    def reverse_url(self, name: str, *args: Any) -> Optional[str]:
        if name in self.named_rules:
            return self.named_rules[name].matcher.reverse(*args)

        for rule in self.rules:
            if isinstance(rule.target, ReversibleRouter):
                reversed_url = rule.target.reverse_url(name, *args)
                if reversed_url is not None:
                    return reversed_url

        return None


class Rule(object):
    """A routing rule."""

    def __init__(
        self,
        matcher: "Matcher",
        target: Any,
        target_kwargs: Optional[Dict[str, Any]] = None,
        name: Optional[str] = None,
    ) -> None:
        """Constructs a Rule instance.

        :arg Matcher matcher: a `Matcher` instance used for determining
            whether the rule should be considered a match for a specific
            request.
        :arg target: a Rule's target (typically a ``RequestHandler`` or
            `~.httputil.HTTPServerConnectionDelegate` subclass or even a nested `Router`,
            depending on routing implementation).
        :arg dict target_kwargs: a dict of parameters that can be useful
            at the moment of target instantiation (for example, ``status_code``
            for a ``RequestHandler`` subclass). They end up in
            ``target_params['target_kwargs']`` of `RuleRouter.get_target_delegate`
            method.
        :arg str name: the name of the rule that can be used to find it
            in `ReversibleRouter.reverse_url` implementation.
        """
        if isinstance(target, str):
            # import the Module and instantiate the class
            # Must be a fully qualified name (module.ClassName)
            target = import_object(target)

        self.matcher = matcher  # type: Matcher
        self.target = target
        self.target_kwargs = target_kwargs if target_kwargs else {}
        self.name = name

    def reverse(self, *args: Any) -> Optional[str]:
        return self.matcher.reverse(*args)

    def __repr__(self) -> str:
        return "%s(%r, %s, kwargs=%r, name=%r)" % (
            self.__class__.__name__,
            self.matcher,
            self.target,
            self.target_kwargs,
            self.name,
        )


class Matcher(object):
    """Represents a matcher for request features."""

    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:
        """Matches current instance against the request.

        :arg httputil.HTTPServerRequest request: current HTTP request
        :returns: a dict of parameters to be passed to the target handler
            (for example, ``handler_kwargs``, ``path_args``, ``path_kwargs``
            can be passed for proper `~.web.RequestHandler` instantiation).
            An empty dict is a valid (and common) return value to indicate a match
            when the argument-passing features are not used.
            ``None`` must be returned to indicate that there is no match."""
        raise NotImplementedError()

    def reverse(self, *args: Any) -> Optional[str]:
        """Reconstructs full url from matcher instance and additional arguments."""
        return None


class AnyMatches(Matcher):
    """Matches any request."""

    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:
        return {}


class HostMatches(Matcher):
    """Matches requests from hosts specified by ``host_pattern`` regex."""

    def __init__(self, host_pattern: Union[str, Pattern]) -> None:
        if isinstance(host_pattern, basestring_type):
            if not host_pattern.endswith("$"):
                host_pattern += "$"
            self.host_pattern = re.compile(host_pattern)
        else:
            self.host_pattern = host_pattern

    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:
        if self.host_pattern.match(request.host_name):
            return {}

        return None


class DefaultHostMatches(Matcher):
    """Matches requests from host that is equal to application's default_host.
    Always returns no match if ``X-Real-Ip`` header is present.
    """

    def __init__(self, application: Any, host_pattern: Pattern) -> None:
        self.application = application
        self.host_pattern = host_pattern

    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:
        # Look for default host if not behind load balancer (for debugging)
        if "X-Real-Ip" not in request.headers:
            if self.host_pattern.match(self.application.default_host):
                return {}
        return None


class PathMatches(Matcher):
    """Matches requests with paths specified by ``path_pattern`` regex."""

    def __init__(self, path_pattern: Union[str, Pattern]) -> None:
        if isinstance(path_pattern, basestring_type):
            if not path_pattern.endswith("$"):
                path_pattern += "$"
            self.regex = re.compile(path_pattern)
        else:
            self.regex = path_pattern

        assert len(self.regex.groupindex) in (0, self.regex.groups), (
            "groups in url regexes must either be all named or all "
            "positional: %r" % self.regex.pattern
        )

        self._path, self._group_count = self._find_groups()

    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:
        match = self.regex.match(request.path)
        if match is None:
            return None
        if not self.regex.groups:
            return {}

        path_args = []  # type: List[bytes]
        path_kwargs = {}  # type: Dict[str, bytes]

        # Pass matched groups to the handler.  Since
        # match.groups() includes both named and
        # unnamed groups, we want to use either groups
        # or groupdict but not both.
        if self.regex.groupindex:
            path_kwargs = dict(
                (str(k), _unquote_or_none(v)) for (k, v) in match.groupdict().items()
            )
        else:
            path_args = [_unquote_or_none(s) for s in match.groups()]

        return dict(path_args=path_args, path_kwargs=path_kwargs)

    def reverse(self, *args: Any) -> Optional[str]:
        if self._path is None:
            raise ValueError("Cannot reverse url regex " + self.regex.pattern)
        assert len(args) == self._group_count, (
            "required number of arguments " "not found"
        )
        if not len(args):
            return self._path
        converted_args = []
        for a in args:
            if not isinstance(a, (unicode_type, bytes)):
                a = str(a)
            converted_args.append(url_escape(utf8(a), plus=False))
        return self._path % tuple(converted_args)

    def _find_groups(self) -> Tuple[Optional[str], Optional[int]]:
        """Returns a tuple (reverse string, group count) for a url.

        For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method
        would return ('/%s/%s/', 2).
        """
        pattern = self.regex.pattern
        if pattern.startswith("^"):
            pattern = pattern[1:]
        if pattern.endswith("$"):
            pattern = pattern[:-1]

        if self.regex.groups != pattern.count("("):
            # The pattern is too complicated for our simplistic matching,
            # so we can't support reversing it.
            return None, None

        pieces = []
        for fragment in pattern.split("("):
            if ")" in fragment:
                paren_loc = fragment.index(")")
                if paren_loc >= 0:
                    pieces.append("%s" + fragment[paren_loc + 1 :])
            else:
                try:
                    unescaped_fragment = re_unescape(fragment)
                except ValueError:
                    # If we can't unescape part of it, we can't
                    # reverse this url.
                    return (None, None)
                pieces.append(unescaped_fragment)

        return "".join(pieces), self.regex.groups


class URLSpec(Rule):
    """Specifies mappings between URLs and handlers.

    .. versionchanged: 4.5
       `URLSpec` is now a subclass of a `Rule` with `PathMatches` matcher and is preserved for
       backwards compatibility.
    """

    def __init__(
        self,
        pattern: Union[str, Pattern],
        handler: Any,
        kwargs: Optional[Dict[str, Any]] = None,
        name: Optional[str] = None,
    ) -> None:
        """Parameters:

        * ``pattern``: Regular expression to be matched. Any capturing
          groups in the regex will be passed in to the handler's
          get/post/etc methods as arguments (by keyword if named, by
          position if unnamed. Named and unnamed capturing groups
          may not be mixed in the same rule).

        * ``handler``: `~.web.RequestHandler` subclass to be invoked.

        * ``kwargs`` (optional): A dictionary of additional arguments
          to be passed to the handler's constructor.

        * ``name`` (optional): A name for this handler.  Used by
          `~.web.Application.reverse_url`.

        """
        matcher = PathMatches(pattern)
        super(URLSpec, self).__init__(matcher, handler, kwargs, name)

        self.regex = matcher.regex
        self.handler_class = self.target
        self.kwargs = kwargs

    def __repr__(self) -> str:
        return "%s(%r, %s, kwargs=%r, name=%r)" % (
            self.__class__.__name__,
            self.regex.pattern,
            self.handler_class,
            self.kwargs,
            self.name,
        )


@overload
def _unquote_or_none(s: str) -> bytes:
    pass


@overload  # noqa: F811
def _unquote_or_none(s: None) -> None:
    pass


def _unquote_or_none(s: Optional[str]) -> Optional[bytes]:  # noqa: F811
    """None-safe wrapper around url_unescape to handle unmatched optional
    groups correctly.

    Note that args are passed as bytes so the handler can decide what
    encoding to use.
    """
    if s is None:
        return s
    return url_unescape(s, encoding=None, plus=False)

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** Incorrect URL Reversal with Escaped Characters

**Description:**
When reversing a URL that includes escaped regex characters, the generated URL incorrectly retains the escape characters, resulting in an invalid path.

**Example Code:**
```python
from tornado.routing import url

# Attempting to reverse a URL with escaped regex
reversed_url = url(r"/api\.v1/foo/([0-9]+)/icon\.png", None).reverse(5)
print(reversed_url)  # Outputs: /api.v1/foo/5/icon\.png
```

**Expected Behavior:**
The reversed URL should be `/api.v1/foo/5/icon.png` without the backslash before `.png`.

**Actual Behavior:**
The reversed URL incorrectly includes a backslash, resulting in `/api.v1/foo/5/icon\.png`.

[/ISSUE]
{
    "AllHTTPMethodsTest.test_standard_methods": "PASSED",
    "ApplicationTest.test_listen": "PASSED",
    "AuthRedirectTest.test_absolute_auth_redirect": "PASSED",
    "AuthRedirectTest.test_relative_auth_redirect": "PASSED",
    "BuggyLoggingTest.test_buggy_log_exception": "PASSED",
    "CacheTest.test_multiple_strong_etag_match": "PASSED",
    "CacheTest.test_multiple_strong_etag_not_match": "PASSED",
    "CacheTest.test_multiple_weak_etag_match": "PASSED",
    "CacheTest.test_multiple_weak_etag_not_match": "PASSED",
    "CacheTest.test_strong_etag_match": "PASSED",
    "CacheTest.test_strong_etag_not_match": "PASSED",
    "CacheTest.test_weak_etag_match": "PASSED",
    "CacheTest.test_weak_etag_not_match": "PASSED",
    "CacheTest.test_wildcard_etag": "PASSED",
    "ClearAllCookiesTest.test_clear_all_cookies": "PASSED",
    "ClearHeaderTest.test_clear_header": "PASSED",
    "ClientCloseTest.test_client_close": "PASSED",
    "ConnectionCloseTest.test_connection_close": "PASSED",
    "CookieTest.test_cookie_special_char": "PASSED",
    "CookieTest.test_get_cookie": "PASSED",
    "CookieTest.test_set_cookie": "PASSED",
    "CookieTest.test_set_cookie_domain": "PASSED",
    "CookieTest.test_set_cookie_expires_days": "PASSED",
    "CookieTest.test_set_cookie_false_flags": "PASSED",
    "CookieTest.test_set_cookie_max_age": "PASSED",
    "CookieTest.test_set_cookie_overwrite": "PASSED",
    "Custom404Test.test_404": "PASSED",
    "CustomStaticFileTest.test_serve": "PASSED",
    "CustomStaticFileTest.test_static_url": "PASSED",
    "DateHeaderTest.test_date_header": "PASSED",
    "DecoratedStreamingRequestFlowControlTest.test_flow_control_chunked_body": "PASSED",
    "DecoratedStreamingRequestFlowControlTest.test_flow_control_compressed_body": "PASSED",
    "DecoratedStreamingRequestFlowControlTest.test_flow_control_fixed_body": "PASSED",
    "DecoratorTest.test_addslash": "PASSED",
    "DecoratorTest.test_removeslash": "PASSED",
    "Default404Test.test_404": "PASSED",
    "DefaultHandlerArgumentsTest.test_403": "PASSED",
    "DefaultHostMatchingTest.test_default_host_matching": "PASSED",
    "ErrorHandlerXSRFTest.test_404_xsrf": "PASSED",
    "ErrorHandlerXSRFTest.test_error_xsrf": "PASSED",
    "ErrorResponseTest.test_default": "PASSED",
    "ErrorResponseTest.test_failed_write_error": "PASSED",
    "ErrorResponseTest.test_write_error": "PASSED",
    "ExceptionHandlerTest.test_http_error": "PASSED",
    "ExceptionHandlerTest.test_known_error": "PASSED",
    "ExceptionHandlerTest.test_unknown_error": "PASSED",
    "FinalReturnTest.test_finish_method_return_future": "PASSED",
    "FinalReturnTest.test_render_method_return_future": "PASSED",
    "FinishExceptionTest.test_finish_exception": "PASSED",
    "FinishInPrepareTest.test_finish_in_prepare": "PASSED",
    "GetArgumentErrorTest.test_catch_error": "PASSED",
    "GetCurrentUserTest.test_get_current_user_from_ui_module_is_lazy": "PASSED",
    "GetCurrentUserTest.test_get_current_user_from_ui_module_works": "PASSED",
    "GetCurrentUserTest.test_get_current_user_works": "PASSED",
    "GzipTestCase.test_gzip": "PASSED",
    "GzipTestCase.test_gzip_not_requested": "PASSED",
    "GzipTestCase.test_gzip_static": "FAILED",
    "GzipTestCase.test_vary_already_present": "PASSED",
    "GzipTestCase.test_vary_already_present_multiple": "PASSED",
    "HTTPErrorTest.test_copy": "PASSED",
    "HandlerByNameTest.test_handler_by_name": "PASSED",
    "Header204Test.test_204_headers": "PASSED",
    "Header304Test.test_304_headers": "PASSED",
    "HostMatchingTest.test_host_matching": "PASSED",
    "IncorrectContentLengthTest.test_content_length_too_high": "PASSED",
    "IncorrectContentLengthTest.test_content_length_too_low": "PASSED",
    "NamedURLSpecGroupsTest.test_named_urlspec_groups": "PASSED",
    "NativeStreamingRequestFlowControlTest.test_flow_control_chunked_body": "PASSED",
    "NativeStreamingRequestFlowControlTest.test_flow_control_compressed_body": "PASSED",
    "NativeStreamingRequestFlowControlTest.test_flow_control_fixed_body": "PASSED",
    "NonWSGIWebTests.test_empty_flush": "PASSED",
    "PatchMethodTest.test_other": "PASSED",
    "PatchMethodTest.test_patch": "PASSED",
    "PathArgsInPrepareTest.test_kw": "PASSED",
    "PathArgsInPrepareTest.test_pos": "PASSED",
    "RaiseWithReasonTest.test_httperror_str": "PASSED",
    "RaiseWithReasonTest.test_httperror_str_from_httputil": "PASSED",
    "RaiseWithReasonTest.test_raise_with_reason": "PASSED",
    "RedirectHandlerTest.test_basic_redirect": "PASSED",
    "RedirectHandlerTest.test_redirect_pattern": "PASSED",
    "RedirectHandlerTest.test_redirect_with_appending_argument": "PASSED",
    "RedirectHandlerTest.test_redirect_with_argument": "PASSED",
    "RequestEncodingTest.test_error": "PASSED",
    "RequestEncodingTest.test_group_encoding": "PASSED",
    "RequestEncodingTest.test_group_question_mark": "PASSED",
    "RequestEncodingTest.test_slashes": "PASSED",
    "RequestSummaryTest.test_missing_remote_ip": "PASSED",
    "SecureCookieV1Test.test_arbitrary_bytes": "PASSED",
    "SecureCookieV1Test.test_cookie_tampering_future_timestamp": "PASSED",
    "SecureCookieV1Test.test_round_trip": "PASSED",
    "SecureCookieV2Test.test_key_version_increment_version": "PASSED",
    "SecureCookieV2Test.test_key_version_invalidate_version": "PASSED",
    "SecureCookieV2Test.test_key_version_roundtrip": "PASSED",
    "SecureCookieV2Test.test_key_version_roundtrip_differing_version": "PASSED",
    "SecureCookieV2Test.test_round_trip": "PASSED",
    "SetLazyPropertiesTest.test_set_properties": "PASSED",
    "SignedValueTest.test_expired": "PASSED",
    "SignedValueTest.test_key_version_retrieval": "PASSED",
    "SignedValueTest.test_key_versioning_invalid_key": "PASSED",
    "SignedValueTest.test_key_versioning_read_write_default_key": "PASSED",
    "SignedValueTest.test_key_versioning_read_write_non_default_key": "PASSED",
    "SignedValueTest.test_known_values": "PASSED",
    "SignedValueTest.test_name_swap": "PASSED",
    "SignedValueTest.test_non_ascii": "PASSED",
    "SignedValueTest.test_payload_tampering": "PASSED",
    "SignedValueTest.test_signature_tampering": "PASSED",
    "StaticDefaultFilenameTest.test_static_default_filename": "FAILED",
    "StaticDefaultFilenameTest.test_static_default_redirect": "FAILED",
    "StaticFileTest.test_absolute_static_url": "FAILED",
    "StaticFileTest.test_absolute_version_exclusion": "PASSED",
    "StaticFileTest.test_include_host_override": "PASSED",
    "StaticFileTest.test_path_traversal_protection": "PASSED",
    "StaticFileTest.test_relative_version_exclusion": "PASSED",
    "StaticFileTest.test_root_static_path": "FAILED",
    "StaticFileTest.test_static_304_etag_modified_bug": "ERROR",
    "StaticFileTest.test_static_304_if_modified_since": "ERROR",
    "StaticFileTest.test_static_304_if_none_match": "ERROR",
    "StaticFileTest.test_static_404": "PASSED",
    "StaticFileTest.test_static_compressed_files": "FAILED",
    "StaticFileTest.test_static_etag": "FAILED",
    "StaticFileTest.test_static_files": "FAILED",
    "StaticFileTest.test_static_head": "FAILED",
    "StaticFileTest.test_static_head_range": "FAILED",
    "StaticFileTest.test_static_if_modified_since_pre_epoch": "FAILED",
    "StaticFileTest.test_static_if_modified_since_time_zone": "ERROR",
    "StaticFileTest.test_static_invalid_range": "FAILED",
    "StaticFileTest.test_static_range_if_none_match": "FAILED",
    "StaticFileTest.test_static_unsatisfiable_range_end_less_than_start": "FAILED",
    "StaticFileTest.test_static_unsatisfiable_range_invalid_start": "FAILED",
    "StaticFileTest.test_static_unsatisfiable_range_zero_suffix": "FAILED",
    "StaticFileTest.test_static_url": "FAILED",
    "StaticFileTest.test_static_with_range": "FAILED",
    "StaticFileTest.test_static_with_range_end_edge": "FAILED",
    "StaticFileTest.test_static_with_range_full_file": "FAILED",
    "StaticFileTest.test_static_with_range_full_past_end": "FAILED",
    "StaticFileTest.test_static_with_range_neg_end": "FAILED",
    "StaticFileTest.test_static_with_range_neg_past_start": "FAILED",
    "StaticFileTest.test_static_with_range_partial_past_end": "FAILED",
    "StaticFileWithPathTest.test_serve": "FAILED",
    "StatusReasonTest.test_status": "PASSED",
    "StreamingRequestBodyTest.test_close_during_upload": "PASSED",
    "StreamingRequestBodyTest.test_early_return": "PASSED",
    "StreamingRequestBodyTest.test_early_return_with_data": "PASSED",
    "StreamingRequestBodyTest.test_streaming_body": "PASSED",
    "UIMethodUIModuleTest.test_ui_method": "PASSED",
    "URLSpecReverseTest.test_non_reversible": "PASSED",
    "URLSpecReverseTest.test_reverse": "PASSED",
    "URLSpecReverseTest.test_reverse_arguments": "PASSED",
    "UnimplementedHTTPMethodsTest.test_unimplemented_standard_methods": "PASSED",
    "UnimplementedNonStandardMethodsTest.test_unimplemented_other": "PASSED",
    "UnimplementedNonStandardMethodsTest.test_unimplemented_patch": "PASSED",
    "WSGISafeWebTest.test_decode_argument": "PASSED",
    "WSGISafeWebTest.test_decode_argument_invalid_unicode": "PASSED",
    "WSGISafeWebTest.test_decode_argument_plus": "PASSED",
    "WSGISafeWebTest.test_get_argument": "PASSED",
    "WSGISafeWebTest.test_get_body_arguments": "PASSED",
    "WSGISafeWebTest.test_get_query_arguments": "PASSED",
    "WSGISafeWebTest.test_header_injection": "PASSED",
    "WSGISafeWebTest.test_multi_header": "PASSED",
    "WSGISafeWebTest.test_no_gzip": "PASSED",
    "WSGISafeWebTest.test_optional_path": "PASSED",
    "WSGISafeWebTest.test_redirect": "PASSED",
    "WSGISafeWebTest.test_reverse_url": "PASSED",
    "WSGISafeWebTest.test_types": "PASSED",
    "WSGISafeWebTest.test_uimodule_resources": "PASSED",
    "WSGISafeWebTest.test_uimodule_unescaped": "PASSED",
    "WSGISafeWebTest.test_web_redirect": "PASSED",
    "WSGISafeWebTest.test_web_redirect_double_slash": "PASSED",
    "XSRFCookieKwargsTest.test_xsrf_httponly": "PASSED",
    "XSRFTest.test_cross_user": "PASSED",
    "XSRFTest.test_distinct_tokens": "PASSED",
    "XSRFTest.test_refresh_token": "PASSED",
    "XSRFTest.test_versioning": "PASSED",
    "XSRFTest.test_xsrf_fail_argument_invalid_format": "PASSED",
    "XSRFTest.test_xsrf_fail_body_no_cookie": "PASSED",
    "XSRFTest.test_xsrf_fail_cookie_invalid_format": "PASSED",
    "XSRFTest.test_xsrf_fail_cookie_no_body": "PASSED",
    "XSRFTest.test_xsrf_fail_no_token": "PASSED",
    "XSRFTest.test_xsrf_success_header": "PASSED",
    "XSRFTest.test_xsrf_success_non_hex_token": "PASSED",
    "XSRFTest.test_xsrf_success_post_body": "PASSED",
    "XSRFTest.test_xsrf_success_query_string": "PASSED",
    "XSRFTest.test_xsrf_success_short_token": "PASSED"
}
diff --git a/tornado/routing.py b/tornado/routing.py
index a35ac348..e137a728 100644
--- a/tornado/routing.py
+++ b/tornado/routing.py
@@ -627,7 +627,13 @@ class PathMatches(Matcher):
             if ")" in fragment:
                 paren_loc = fragment.index(")")
                 if paren_loc >= 0:
-                    pieces.append("%s" + fragment[paren_loc + 1 :])
+                    try:
+                        unescaped_fragment = re_unescape(fragment[paren_loc + 1 :])
+                    except ValueError:
+                        # If we can't unescape part of it, we can't
+                        # reverse this url.
+                        return (None, None)
+                    pieces.append("%s" + unescaped_fragment)
             else:
                 try:
                     unescaped_fragment = re_unescape(fragment)

{"tornado/routing.py": "# Copyright 2015 The Tornado Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\n\"\"\"Flexible routing implementation.\n\nTornado routes HTTP requests to appropriate handlers using `Router`\nclass implementations. The `tornado.web.Application` class is a\n`Router` implementation and may be used directly, or the classes in\nthis module may be used for additional flexibility. The `RuleRouter`\nclass can match on more criteria than `.Application`, or the `Router`\ninterface can be subclassed for maximum customization.\n\n`Router` interface extends `~.httputil.HTTPServerConnectionDelegate`\nto provide additional routing capabilities. This also means that any\n`Router` implementation can be used directly as a ``request_callback``\nfor `~.httpserver.HTTPServer` constructor.\n\n`Router` subclass must implement a ``find_handler`` method to provide\na suitable `~.httputil.HTTPMessageDelegate` instance to handle the\nrequest:\n\n.. code-block:: python\n\n    class CustomRouter(Router):\n        def find_handler(self, request, **kwargs):\n            # some routing logic providing a suitable HTTPMessageDelegate instance\n            return MessageDelegate(request.connection)\n\n    class MessageDelegate(HTTPMessageDelegate):\n        def __init__(self, connection):\n            self.connection = connection\n\n        def finish(self):\n            self.connection.write_headers(\n                ResponseStartLine(\"HTTP/1.1\", 200, \"OK\"),\n                HTTPHeaders({\"Content-Length\": \"2\"}),\n                b\"OK\")\n            self.connection.finish()\n\n    router = CustomRouter()\n    server = HTTPServer(router)\n\nThe main responsibility of `Router` implementation is to provide a\nmapping from a request to `~.httputil.HTTPMessageDelegate` instance\nthat will handle this request. In the example above we can see that\nrouting is possible even without instantiating an `~.web.Application`.\n\nFor routing to `~.web.RequestHandler` implementations we need an\n`~.web.Application` instance. `~.web.Application.get_handler_delegate`\nprovides a convenient way to create `~.httputil.HTTPMessageDelegate`\nfor a given request and `~.web.RequestHandler`.\n\nHere is a simple example of how we can we route to\n`~.web.RequestHandler` subclasses by HTTP method:\n\n.. code-block:: python\n\n    resources = {}\n\n    class GetResource(RequestHandler):\n        def get(self, path):\n            if path not in resources:\n                raise HTTPError(404)\n\n            self.finish(resources[path])\n\n    class PostResource(RequestHandler):\n        def post(self, path):\n            resources[path] = self.request.body\n\n    class HTTPMethodRouter(Router):\n        def __init__(self, app):\n            self.app = app\n\n        def find_handler(self, request, **kwargs):\n            handler = GetResource if request.method == \"GET\" else PostResource\n            return self.app.get_handler_delegate(request, handler, path_args=[request.path])\n\n    router = HTTPMethodRouter(Application())\n    server = HTTPServer(router)\n\n`ReversibleRouter` interface adds the ability to distinguish between\nthe routes and reverse them to the original urls using route's name\nand additional arguments. `~.web.Application` is itself an\nimplementation of `ReversibleRouter` class.\n\n`RuleRouter` and `ReversibleRuleRouter` are implementations of\n`Router` and `ReversibleRouter` interfaces and can be used for\ncreating rule-based routing configurations.\n\nRules are instances of `Rule` class. They contain a `Matcher`, which\nprovides the logic for determining whether the rule is a match for a\nparticular request and a target, which can be one of the following.\n\n1) An instance of `~.httputil.HTTPServerConnectionDelegate`:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/handler\"), ConnectionDelegate()),\n        # ... more rules\n    ])\n\n    class ConnectionDelegate(HTTPServerConnectionDelegate):\n        def start_request(self, server_conn, request_conn):\n            return MessageDelegate(request_conn)\n\n2) A callable accepting a single argument of `~.httputil.HTTPServerRequest` type:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/callable\"), request_callable)\n    ])\n\n    def request_callable(request):\n        request.write(b\"HTTP/1.1 200 OK\\\\r\\\\nContent-Length: 2\\\\r\\\\n\\\\r\\\\nOK\")\n        request.finish()\n\n3) Another `Router` instance:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/router.*\"), CustomRouter())\n    ])\n\nOf course a nested `RuleRouter` or a `~.web.Application` is allowed:\n\n.. code-block:: python\n\n    router = RuleRouter([\n        Rule(HostMatches(\"example.com\"), RuleRouter([\n            Rule(PathMatches(\"/app1/.*\"), Application([(r\"/app1/handler\", Handler)]))),\n        ]))\n    ])\n\n    server = HTTPServer(router)\n\nIn the example below `RuleRouter` is used to route between applications:\n\n.. code-block:: python\n\n    app1 = Application([\n        (r\"/app1/handler\", Handler1),\n        # other handlers ...\n    ])\n\n    app2 = Application([\n        (r\"/app2/handler\", Handler2),\n        # other handlers ...\n    ])\n\n    router = RuleRouter([\n        Rule(PathMatches(\"/app1.*\"), app1),\n        Rule(PathMatches(\"/app2.*\"), app2)\n    ])\n\n    server = HTTPServer(router)\n\nFor more information on application-level routing see docs for `~.web.Application`.\n\n.. versionadded:: 4.5\n\n\"\"\"\n\nimport re\nfrom functools import partial\n\nfrom tornado import httputil\nfrom tornado.httpserver import _CallableAdapter\nfrom tornado.escape import url_escape, url_unescape, utf8\nfrom tornado.log import app_log\nfrom tornado.util import basestring_type, import_object, re_unescape, unicode_type\n\nfrom typing import Any, Union, Optional, Awaitable, List, Dict, Pattern, Tuple, overload\n\n\nclass Router(httputil.HTTPServerConnectionDelegate):\n    \"\"\"Abstract router interface.\"\"\"\n\n    def find_handler(\n        self, request: httputil.HTTPServerRequest, **kwargs: Any\n    ) -> Optional[httputil.HTTPMessageDelegate]:\n        \"\"\"Must be implemented to return an appropriate instance of `~.httputil.HTTPMessageDelegate`\n        that can serve the request.\n        Routing implementations may pass additional kwargs to extend the routing logic.\n\n        :arg httputil.HTTPServerRequest request: current HTTP request.\n        :arg kwargs: additional keyword arguments passed by routing implementation.\n        :returns: an instance of `~.httputil.HTTPMessageDelegate` that will be used to\n            process the request.\n        \"\"\"\n        raise NotImplementedError()\n\n    def start_request(\n        self, server_conn: object, request_conn: httputil.HTTPConnection\n    ) -> httputil.HTTPMessageDelegate:\n        return _RoutingDelegate(self, server_conn, request_conn)\n\n\nclass ReversibleRouter(Router):\n    \"\"\"Abstract router interface for routers that can handle named routes\n    and support reversing them to original urls.\n    \"\"\"\n\n    def reverse_url(self, name: str, *args: Any) -> Optional[str]:\n        \"\"\"Returns url string for a given route name and arguments\n        or ``None`` if no match is found.\n\n        :arg str name: route name.\n        :arg args: url parameters.\n        :returns: parametrized url string for a given route name (or ``None``).\n        \"\"\"\n        raise NotImplementedError()\n\n\nclass _RoutingDelegate(httputil.HTTPMessageDelegate):\n    def __init__(\n        self, router: Router, server_conn: object, request_conn: httputil.HTTPConnection\n    ) -> None:\n        self.server_conn = server_conn\n        self.request_conn = request_conn\n        self.delegate = None  # type: Optional[httputil.HTTPMessageDelegate]\n        self.router = router  # type: Router\n\n    def headers_received(\n        self,\n        start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine],\n        headers: httputil.HTTPHeaders,\n    ) -> Optional[Awaitable[None]]:\n        assert isinstance(start_line, httputil.RequestStartLine)\n        request = httputil.HTTPServerRequest(\n            connection=self.request_conn,\n            server_connection=self.server_conn,\n            start_line=start_line,\n            headers=headers,\n        )\n\n        self.delegate = self.router.find_handler(request)\n        if self.delegate is None:\n            app_log.debug(\n                \"Delegate for %s %s request not found\",\n                start_line.method,\n                start_line.path,\n            )\n            self.delegate = _DefaultMessageDelegate(self.request_conn)\n\n        return self.delegate.headers_received(start_line, headers)\n\n    def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:\n        assert self.delegate is not None\n        return self.delegate.data_received(chunk)\n\n    def finish(self) -> None:\n        assert self.delegate is not None\n        self.delegate.finish()\n\n    def on_connection_close(self) -> None:\n        assert self.delegate is not None\n        self.delegate.on_connection_close()\n\n\nclass _DefaultMessageDelegate(httputil.HTTPMessageDelegate):\n    def __init__(self, connection: httputil.HTTPConnection) -> None:\n        self.connection = connection\n\n    def finish(self) -> None:\n        self.connection.write_headers(\n            httputil.ResponseStartLine(\"HTTP/1.1\", 404, \"Not Found\"),\n            httputil.HTTPHeaders(),\n        )\n        self.connection.finish()\n\n\n# _RuleList can either contain pre-constructed Rules or a sequence of\n# arguments to be passed to the Rule constructor.\n_RuleList = List[\n    Union[\n        \"Rule\",\n        List[Any],  # Can't do detailed typechecking of lists.\n        Tuple[Union[str, \"Matcher\"], Any],\n        Tuple[Union[str, \"Matcher\"], Any, Dict[str, Any]],\n        Tuple[Union[str, \"Matcher\"], Any, Dict[str, Any], str],\n    ]\n]\n\n\nclass RuleRouter(Router):\n    \"\"\"Rule-based router implementation.\"\"\"\n\n    def __init__(self, rules: Optional[_RuleList] = None) -> None:\n        \"\"\"Constructs a router from an ordered list of rules::\n\n            RuleRouter([\n                Rule(PathMatches(\"/handler\"), Target),\n                # ... more rules\n            ])\n\n        You can also omit explicit `Rule` constructor and use tuples of arguments::\n\n            RuleRouter([\n                (PathMatches(\"/handler\"), Target),\n            ])\n\n        `PathMatches` is a default matcher, so the example above can be simplified::\n\n            RuleRouter([\n                (\"/handler\", Target),\n            ])\n\n        In the examples above, ``Target`` can be a nested `Router` instance, an instance of\n        `~.httputil.HTTPServerConnectionDelegate` or an old-style callable,\n        accepting a request argument.\n\n        :arg rules: a list of `Rule` instances or tuples of `Rule`\n            constructor arguments.\n        \"\"\"\n        self.rules = []  # type: List[Rule]\n        if rules:\n            self.add_rules(rules)\n\n    def add_rules(self, rules: _RuleList) -> None:\n        \"\"\"Appends new rules to the router.\n\n        :arg rules: a list of Rule instances (or tuples of arguments, which are\n            passed to Rule constructor).\n        \"\"\"\n        for rule in rules:\n            if isinstance(rule, (tuple, list)):\n                assert len(rule) in (2, 3, 4)\n                if isinstance(rule[0], basestring_type):\n                    rule = Rule(PathMatches(rule[0]), *rule[1:])\n                else:\n                    rule = Rule(*rule)\n\n            self.rules.append(self.process_rule(rule))\n\n    def process_rule(self, rule: \"Rule\") -> \"Rule\":\n        \"\"\"Override this method for additional preprocessing of each rule.\n\n        :arg Rule rule: a rule to be processed.\n        :returns: the same or modified Rule instance.\n        \"\"\"\n        return rule\n\n    def find_handler(\n        self, request: httputil.HTTPServerRequest, **kwargs: Any\n    ) -> Optional[httputil.HTTPMessageDelegate]:\n        for rule in self.rules:\n            target_params = rule.matcher.match(request)\n            if target_params is not None:\n                if rule.target_kwargs:\n                    target_params[\"target_kwargs\"] = rule.target_kwargs\n\n                delegate = self.get_target_delegate(\n                    rule.target, request, **target_params\n                )\n\n                if delegate is not None:\n                    return delegate\n\n        return None\n\n    def get_target_delegate(\n        self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any\n    ) -> Optional[httputil.HTTPMessageDelegate]:\n        \"\"\"Returns an instance of `~.httputil.HTTPMessageDelegate` for a\n        Rule's target. This method is called by `~.find_handler` and can be\n        extended to provide additional target types.\n\n        :arg target: a Rule's target.\n        :arg httputil.HTTPServerRequest request: current request.\n        :arg target_params: additional parameters that can be useful\n            for `~.httputil.HTTPMessageDelegate` creation.\n        \"\"\"\n        if isinstance(target, Router):\n            return target.find_handler(request, **target_params)\n\n        elif isinstance(target, httputil.HTTPServerConnectionDelegate):\n            assert request.connection is not None\n            return target.start_request(request.server_connection, request.connection)\n\n        elif callable(target):\n            assert request.connection is not None\n            return _CallableAdapter(\n                partial(target, **target_params), request.connection\n            )\n\n        return None\n\n\nclass ReversibleRuleRouter(ReversibleRouter, RuleRouter):\n    \"\"\"A rule-based router that implements ``reverse_url`` method.\n\n    Each rule added to this router may have a ``name`` attribute that can be\n    used to reconstruct an original uri. The actual reconstruction takes place\n    in a rule's matcher (see `Matcher.reverse`).\n    \"\"\"\n\n    def __init__(self, rules: Optional[_RuleList] = None) -> None:\n        self.named_rules = {}  # type: Dict[str, Any]\n        super(ReversibleRuleRouter, self).__init__(rules)\n\n    def process_rule(self, rule: \"Rule\") -> \"Rule\":\n        rule = super(ReversibleRuleRouter, self).process_rule(rule)\n\n        if rule.name:\n            if rule.name in self.named_rules:\n                app_log.warning(\n                    \"Multiple handlers named %s; replacing previous value\", rule.name\n                )\n            self.named_rules[rule.name] = rule\n\n        return rule\n\n    def reverse_url(self, name: str, *args: Any) -> Optional[str]:\n        if name in self.named_rules:\n            return self.named_rules[name].matcher.reverse(*args)\n\n        for rule in self.rules:\n            if isinstance(rule.target, ReversibleRouter):\n                reversed_url = rule.target.reverse_url(name, *args)\n                if reversed_url is not None:\n                    return reversed_url\n\n        return None\n\n\nclass Rule(object):\n    \"\"\"A routing rule.\"\"\"\n\n    def __init__(\n        self,\n        matcher: \"Matcher\",\n        target: Any,\n        target_kwargs: Optional[Dict[str, Any]] = None,\n        name: Optional[str] = None,\n    ) -> None:\n        \"\"\"Constructs a Rule instance.\n\n        :arg Matcher matcher: a `Matcher` instance used for determining\n            whether the rule should be considered a match for a specific\n            request.\n        :arg target: a Rule's target (typically a ``RequestHandler`` or\n            `~.httputil.HTTPServerConnectionDelegate` subclass or even a nested `Router`,\n            depending on routing implementation).\n        :arg dict target_kwargs: a dict of parameters that can be useful\n            at the moment of target instantiation (for example, ``status_code``\n            for a ``RequestHandler`` subclass). They end up in\n            ``target_params['target_kwargs']`` of `RuleRouter.get_target_delegate`\n            method.\n        :arg str name: the name of the rule that can be used to find it\n            in `ReversibleRouter.reverse_url` implementation.\n        \"\"\"\n        if isinstance(target, str):\n            # import the Module and instantiate the class\n            # Must be a fully qualified name (module.ClassName)\n            target = import_object(target)\n\n        self.matcher = matcher  # type: Matcher\n        self.target = target\n        self.target_kwargs = target_kwargs if target_kwargs else {}\n        self.name = name\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        return self.matcher.reverse(*args)\n\n    def __repr__(self) -> str:\n        return \"%s(%r, %s, kwargs=%r, name=%r)\" % (\n            self.__class__.__name__,\n            self.matcher,\n            self.target,\n            self.target_kwargs,\n            self.name,\n        )\n\n\nclass Matcher(object):\n    \"\"\"Represents a matcher for request features.\"\"\"\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        \"\"\"Matches current instance against the request.\n\n        :arg httputil.HTTPServerRequest request: current HTTP request\n        :returns: a dict of parameters to be passed to the target handler\n            (for example, ``handler_kwargs``, ``path_args``, ``path_kwargs``\n            can be passed for proper `~.web.RequestHandler` instantiation).\n            An empty dict is a valid (and common) return value to indicate a match\n            when the argument-passing features are not used.\n            ``None`` must be returned to indicate that there is no match.\"\"\"\n        raise NotImplementedError()\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        \"\"\"Reconstructs full url from matcher instance and additional arguments.\"\"\"\n        return None\n\n\nclass AnyMatches(Matcher):\n    \"\"\"Matches any request.\"\"\"\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        return {}\n\n\nclass HostMatches(Matcher):\n    \"\"\"Matches requests from hosts specified by ``host_pattern`` regex.\"\"\"\n\n    def __init__(self, host_pattern: Union[str, Pattern]) -> None:\n        if isinstance(host_pattern, basestring_type):\n            if not host_pattern.endswith(\"$\"):\n                host_pattern += \"$\"\n            self.host_pattern = re.compile(host_pattern)\n        else:\n            self.host_pattern = host_pattern\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        if self.host_pattern.match(request.host_name):\n            return {}\n\n        return None\n\n\nclass DefaultHostMatches(Matcher):\n    \"\"\"Matches requests from host that is equal to application's default_host.\n    Always returns no match if ``X-Real-Ip`` header is present.\n    \"\"\"\n\n    def __init__(self, application: Any, host_pattern: Pattern) -> None:\n        self.application = application\n        self.host_pattern = host_pattern\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        # Look for default host if not behind load balancer (for debugging)\n        if \"X-Real-Ip\" not in request.headers:\n            if self.host_pattern.match(self.application.default_host):\n                return {}\n        return None\n\n\nclass PathMatches(Matcher):\n    \"\"\"Matches requests with paths specified by ``path_pattern`` regex.\"\"\"\n\n    def __init__(self, path_pattern: Union[str, Pattern]) -> None:\n        if isinstance(path_pattern, basestring_type):\n            if not path_pattern.endswith(\"$\"):\n                path_pattern += \"$\"\n            self.regex = re.compile(path_pattern)\n        else:\n            self.regex = path_pattern\n\n        assert len(self.regex.groupindex) in (0, self.regex.groups), (\n            \"groups in url regexes must either be all named or all \"\n            \"positional: %r\" % self.regex.pattern\n        )\n\n        self._path, self._group_count = self._find_groups()\n\n    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:\n        match = self.regex.match(request.path)\n        if match is None:\n            return None\n        if not self.regex.groups:\n            return {}\n\n        path_args = []  # type: List[bytes]\n        path_kwargs = {}  # type: Dict[str, bytes]\n\n        # Pass matched groups to the handler.  Since\n        # match.groups() includes both named and\n        # unnamed groups, we want to use either groups\n        # or groupdict but not both.\n        if self.regex.groupindex:\n            path_kwargs = dict(\n                (str(k), _unquote_or_none(v)) for (k, v) in match.groupdict().items()\n            )\n        else:\n            path_args = [_unquote_or_none(s) for s in match.groups()]\n\n        return dict(path_args=path_args, path_kwargs=path_kwargs)\n\n    def reverse(self, *args: Any) -> Optional[str]:\n        if self._path is None:\n            raise ValueError(\"Cannot reverse url regex \" + self.regex.pattern)\n        assert len(args) == self._group_count, (\n            \"required number of arguments \" \"not found\"\n        )\n        if not len(args):\n            return self._path\n        converted_args = []\n        for a in args:\n            if not isinstance(a, (unicode_type, bytes)):\n                a = str(a)\n            converted_args.append(url_escape(utf8(a), plus=False))\n        return self._path % tuple(converted_args)\n\n    def _find_groups(self) -> Tuple[Optional[str], Optional[int]]:\n        \"\"\"Returns a tuple (reverse string, group count) for a url.\n\n        For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method\n        would return ('/%s/%s/', 2).\n        \"\"\"\n        pattern = self.regex.pattern\n        if pattern.startswith(\"^\"):\n            pattern = pattern[1:]\n        if pattern.endswith(\"$\"):\n            pattern = pattern[:-1]\n\n        if self.regex.groups != pattern.count(\"(\"):\n            # The pattern is too complicated for our simplistic matching,\n            # so we can't support reversing it.\n            return None, None\n\n        pieces = []\n        for fragment in pattern.split(\"(\"):\n            if \")\" in fragment:\n                paren_loc = fragment.index(\")\")\n                if paren_loc >= 0:\n                    pieces.append(\"%s\" + fragment[paren_loc + 1 :])\n            else:\n                try:\n                    unescaped_fragment = re_unescape(fragment)\n                except ValueError:\n                    # If we can't unescape part of it, we can't\n                    # reverse this url.\n                    return (None, None)\n                pieces.append(unescaped_fragment)\n\n        return \"\".join(pieces), self.regex.groups\n\n\nclass URLSpec(Rule):\n    \"\"\"Specifies mappings between URLs and handlers.\n\n    .. versionchanged: 4.5\n       `URLSpec` is now a subclass of a `Rule` with `PathMatches` matcher and is preserved for\n       backwards compatibility.\n    \"\"\"\n\n    def __init__(\n        self,\n        pattern: Union[str, Pattern],\n        handler: Any,\n        kwargs: Optional[Dict[str, Any]] = None,\n        name: Optional[str] = None,\n    ) -> None:\n        \"\"\"Parameters:\n\n        * ``pattern``: Regular expression to be matched. Any capturing\n          groups in the regex will be passed in to the handler's\n          get/post/etc methods as arguments (by keyword if named, by\n          position if unnamed. Named and unnamed capturing groups\n          may not be mixed in the same rule).\n\n        * ``handler``: `~.web.RequestHandler` subclass to be invoked.\n\n        * ``kwargs`` (optional): A dictionary of additional arguments\n          to be passed to the handler's constructor.\n\n        * ``name`` (optional): A name for this handler.  Used by\n          `~.web.Application.reverse_url`.\n\n        \"\"\"\n        matcher = PathMatches(pattern)\n        super(URLSpec, self).__init__(matcher, handler, kwargs, name)\n\n        self.regex = matcher.regex\n        self.handler_class = self.target\n        self.kwargs = kwargs\n\n    def __repr__(self) -> str:\n        return \"%s(%r, %s, kwargs=%r, name=%r)\" % (\n            self.__class__.__name__,\n            self.regex.pattern,\n            self.handler_class,\n            self.kwargs,\n            self.name,\n        )\n\n\n@overload\ndef _unquote_or_none(s: str) -> bytes:\n    pass\n\n\n@overload  # noqa: F811\ndef _unquote_or_none(s: None) -> None:\n    pass\n\n\ndef _unquote_or_none(s: Optional[str]) -> Optional[bytes]:  # noqa: F811\n    \"\"\"None-safe wrapper around url_unescape to handle unmatched optional\n    groups correctly.\n\n    Note that args are passed as bytes so the handler can decide what\n    encoding to use.\n    \"\"\"\n    if s is None:\n        return s\n    return url_unescape(s, encoding=None, plus=False)\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Incorrect URL Reversal with Escaped Characters

**Description:**
When reversing a URL that includes escaped regex characters, the generated URL incorrectly retains the escape characters, resulting in an invalid path.

**Example Code:**
```python
from tornado.routing import url

# Attempting to reverse a URL with escaped regex
reversed_url = url(r"/api\.v1/foo/([0-9]+)/icon\.png", None).reverse(5)
print(reversed_url)  # Outputs: /api.v1/foo/5/icon\.png
```

**Expected Behavior:**
The reversed URL should be `/api.v1/foo/5/icon.png` without the backslash before `.png`.

**Actual Behavior:**
The reversed URL incorrectly includes a backslash, resulting in `/api.v1/foo/5/icon\.png`.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### tornado/routing.py
# Copyright 2015 The Tornado Authors
#
# 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.

"""Flexible routing implementation.

Tornado routes HTTP requests to appropriate handlers using `Router`
class implementations. The `tornado.web.Application` class is a
`Router` implementation and may be used directly, or the classes in
this module may be used for additional flexibility. The `RuleRouter`
class can match on more criteria than `.Application`, or the `Router`
interface can be subclassed for maximum customization.

`Router` interface extends `~.httputil.HTTPServerConnectionDelegate`
to provide additional routing capabilities. This also means that any
`Router` implementation can be used directly as a ``request_callback``
for `~.httpserver.HTTPServer` constructor.

`Router` subclass must implement a ``find_handler`` method to provide
a suitable `~.httputil.HTTPMessageDelegate` instance to handle the
request:

.. code-block:: python

    class CustomRouter(Router):
        def find_handler(self, request, **kwargs):
            # some routing logic providing a suitable HTTPMessageDelegate instance
            return MessageDelegate(request.connection)

    class MessageDelegate(HTTPMessageDelegate):
        def __init__(self, connection):
            self.connection = connection

        def finish(self):
            self.connection.write_headers(
                ResponseStartLine("HTTP/1.1", 200, "OK"),
                HTTPHeaders({"Content-Length": "2"}),
                b"OK")
            self.connection.finish()

    router = CustomRouter()
    server = HTTPServer(router)

The main responsibility of `Router` implementation is to provide a
mapping from a request to `~.httputil.HTTPMessageDelegate` instance
that will handle this request. In the example above we can see that
routing is possible even without instantiating an `~.web.Application`.

For routing to `~.web.RequestHandler` implementations we need an
`~.web.Application` instance. `~.web.Application.get_handler_delegate`
provides a convenient way to create `~.httputil.HTTPMessageDelegate`
for a given request and `~.web.RequestHandler`.

Here is a simple example of how we can we route to
`~.web.RequestHandler` subclasses by HTTP method:

.. code-block:: python

    resources = {}

    class GetResource(RequestHandler):
        def get(self, path):
            if path not in resources:
                raise HTTPError(404)

            self.finish(resources[path])

    class PostResource(RequestHandler):
        def post(self, path):
            resources[path] = self.request.body

    class HTTPMethodRouter(Router):
        def __init__(self, app):
            self.app = app

        def find_handler(self, request, **kwargs):
            handler = GetResource if request.method == "GET" else PostResource
            return self.app.get_handler_delegate(request, handler, path_args=[request.path])

    router = HTTPMethodRouter(Application())
    server = HTTPServer(router)

`ReversibleRouter` interface adds the ability to distinguish between
the routes and reverse them to the original urls using route's name
and additional arguments. `~.web.Application` is itself an
implementation of `ReversibleRouter` class.

`RuleRouter` and `ReversibleRuleRouter` are implementations of
`Router` and `ReversibleRouter` interfaces and can be used for
creating rule-based routing configurations.

Rules are instances of `Rule` class. They contain a `Matcher`, which
provides the logic for determining whether the rule is a match for a
particular request and a target, which can be one of the following.

1) An instance of `~.httputil.HTTPServerConnectionDelegate`:

.. code-block:: python

    router = RuleRouter([
        Rule(PathMatches("/handler"), ConnectionDelegate()),
        # ... more rules
    ])

    class ConnectionDelegate(HTTPServerConnectionDelegate):
        def start_request(self, server_conn, request_conn):
            return MessageDelegate(request_conn)

2) A callable accepting a single argument of `~.httputil.HTTPServerRequest` type:

.. code-block:: python

    router = RuleRouter([
        Rule(PathMatches("/callable"), request_callable)
    ])

    def request_callable(request):
        request.write(b"HTTP/1.1 200 OK\\r\\nContent-Length: 2\\r\\n\\r\\nOK")
        request.finish()

3) Another `Router` instance:

.. code-block:: python

    router = RuleRouter([
        Rule(PathMatches("/router.*"), CustomRouter())
    ])

Of course a nested `RuleRouter` or a `~.web.Application` is allowed:

.. code-block:: python

    router = RuleRouter([
        Rule(HostMatches("example.com"), RuleRouter([
            Rule(PathMatches("/app1/.*"), Application([(r"/app1/handler", Handler)]))),
        ]))
    ])

    server = HTTPServer(router)

In the example below `RuleRouter` is used to route between applications:

.. code-block:: python

    app1 = Application([
        (r"/app1/handler", Handler1),
        # other handlers ...
    ])

    app2 = Application([
        (r"/app2/handler", Handler2),
        # other handlers ...
    ])

    router = RuleRouter([
        Rule(PathMatches("/app1.*"), app1),
        Rule(PathMatches("/app2.*"), app2)
    ])

    server = HTTPServer(router)

For more information on application-level routing see docs for `~.web.Application`.

.. versionadded:: 4.5

"""

import re
from functools import partial

from tornado import httputil
from tornado.httpserver import _CallableAdapter
from tornado.escape import url_escape, url_unescape, utf8
from tornado.log import app_log
from tornado.util import basestring_type, import_object, re_unescape, unicode_type

from typing import Any, Union, Optional, Awaitable, List, Dict, Pattern, Tuple, overload


class Router(httputil.HTTPServerConnectionDelegate):
    """Abstract router interface."""

    def find_handler(
        self, request: httputil.HTTPServerRequest, **kwargs: Any
    ) -> Optional[httputil.HTTPMessageDelegate]:
        """Must be implemented to return an appropriate instance of `~.httputil.HTTPMessageDelegate`
        that can serve the request.
        Routing implementations may pass additional kwargs to extend the routing logic.

        :arg httputil.HTTPServerRequest request: current HTTP request.
        :arg kwargs: additional keyword arguments passed by routing implementation.
        :returns: an instance of `~.httputil.HTTPMessageDelegate` that will be used to
            process the request.
        """
        raise NotImplementedError()

    def start_request(
        self, server_conn: object, request_conn: httputil.HTTPConnection
    ) -> httputil.HTTPMessageDelegate:
        return _RoutingDelegate(self, server_conn, request_conn)


class ReversibleRouter(Router):
    """Abstract router interface for routers that can handle named routes
    and support reversing them to original urls.
    """

    def reverse_url(self, name: str, *args: Any) -> Optional[str]:
        """Returns url string for a given route name and arguments
        or ``None`` if no match is found.

        :arg str name: route name.
        :arg args: url parameters.
        :returns: parametrized url string for a given route name (or ``None``).
        """
        raise NotImplementedError()


class _RoutingDelegate(httputil.HTTPMessageDelegate):
    def __init__(
        self, router: Router, server_conn: object, request_conn: httputil.HTTPConnection
    ) -> None:
        self.server_conn = server_conn
        self.request_conn = request_conn
        self.delegate = None  # type: Optional[httputil.HTTPMessageDelegate]
        self.router = router  # type: Router

    def headers_received(
        self,
        start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine],
        headers: httputil.HTTPHeaders,
    ) -> Optional[Awaitable[None]]:
        assert isinstance(start_line, httputil.RequestStartLine)
        request = httputil.HTTPServerRequest(
            connection=self.request_conn,
            server_connection=self.server_conn,
            start_line=start_line,
            headers=headers,
        )

        self.delegate = self.router.find_handler(request)
        if self.delegate is None:
            app_log.debug(
                "Delegate for %s %s request not found",
                start_line.method,
                start_line.path,
            )
            self.delegate = _DefaultMessageDelegate(self.request_conn)

        return self.delegate.headers_received(start_line, headers)

    def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:
        assert self.delegate is not None
        return self.delegate.data_received(chunk)

    def finish(self) -> None:
        assert self.delegate is not None
        self.delegate.finish()

    def on_connection_close(self) -> None:
        assert self.delegate is not None
        self.delegate.on_connection_close()


class _DefaultMessageDelegate(httputil.HTTPMessageDelegate):
    def __init__(self, connection: httputil.HTTPConnection) -> None:
        self.connection = connection

    def finish(self) -> None:
        self.connection.write_headers(
            httputil.ResponseStartLine("HTTP/1.1", 404, "Not Found"),
            httputil.HTTPHeaders(),
        )
        self.connection.finish()


# _RuleList can either contain pre-constructed Rules or a sequence of
# arguments to be passed to the Rule constructor.
_RuleList = List[
    Union[
        "Rule",
        List[Any],  # Can't do detailed typechecking of lists.
        Tuple[Union[str, "Matcher"], Any],
        Tuple[Union[str, "Matcher"], Any, Dict[str, Any]],
        Tuple[Union[str, "Matcher"], Any, Dict[str, Any], str],
    ]
]


class RuleRouter(Router):
    """Rule-based router implementation."""

    def __init__(self, rules: Optional[_RuleList] = None) -> None:
        """Constructs a router from an ordered list of rules::

            RuleRouter([
                Rule(PathMatches("/handler"), Target),
                # ... more rules
            ])

        You can also omit explicit `Rule` constructor and use tuples of arguments::

            RuleRouter([
                (PathMatches("/handler"), Target),
            ])

        `PathMatches` is a default matcher, so the example above can be simplified::

            RuleRouter([
                ("/handler", Target),
            ])

        In the examples above, ``Target`` can be a nested `Router` instance, an instance of
        `~.httputil.HTTPServerConnectionDelegate` or an old-style callable,
        accepting a request argument.

        :arg rules: a list of `Rule` instances or tuples of `Rule`
            constructor arguments.
        """
        self.rules = []  # type: List[Rule]
        if rules:
            self.add_rules(rules)

    def add_rules(self, rules: _RuleList) -> None:
        """Appends new rules to the router.

        :arg rules: a list of Rule instances (or tuples of arguments, which are
            passed to Rule constructor).
        """
        for rule in rules:
            if isinstance(rule, (tuple, list)):
                assert len(rule) in (2, 3, 4)
                if isinstance(rule[0], basestring_type):
                    rule = Rule(PathMatches(rule[0]), *rule[1:])
                else:
                    rule = Rule(*rule)

            self.rules.append(self.process_rule(rule))

    def process_rule(self, rule: "Rule") -> "Rule":
        """Override this method for additional preprocessing of each rule.

        :arg Rule rule: a rule to be processed.
        :returns: the same or modified Rule instance.
        """
        return rule

    def find_handler(
        self, request: httputil.HTTPServerRequest, **kwargs: Any
    ) -> Optional[httputil.HTTPMessageDelegate]:
        for rule in self.rules:
            target_params = rule.matcher.match(request)
            if target_params is not None:
                if rule.target_kwargs:
                    target_params["target_kwargs"] = rule.target_kwargs

                delegate = self.get_target_delegate(
                    rule.target, request, **target_params
                )

                if delegate is not None:
                    return delegate

        return None

    def get_target_delegate(
        self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any
    ) -> Optional[httputil.HTTPMessageDelegate]:
        """Returns an instance of `~.httputil.HTTPMessageDelegate` for a
        Rule's target. This method is called by `~.find_handler` and can be
        extended to provide additional target types.

        :arg target: a Rule's target.
        :arg httputil.HTTPServerRequest request: current request.
        :arg target_params: additional parameters that can be useful
            for `~.httputil.HTTPMessageDelegate` creation.
        """
        if isinstance(target, Router):
            return target.find_handler(request, **target_params)

        elif isinstance(target, httputil.HTTPServerConnectionDelegate):
            assert request.connection is not None
            return target.start_request(request.server_connection, request.connection)

        elif callable(target):
            assert request.connection is not None
            return _CallableAdapter(
                partial(target, **target_params), request.connection
            )

        return None


class ReversibleRuleRouter(ReversibleRouter, RuleRouter):
    """A rule-based router that implements ``reverse_url`` method.

    Each rule added to this router may have a ``name`` attribute that can be
    used to reconstruct an original uri. The actual reconstruction takes place
    in a rule's matcher (see `Matcher.reverse`).
    """

    def __init__(self, rules: Optional[_RuleList] = None) -> None:
        self.named_rules = {}  # type: Dict[str, Any]
        super(ReversibleRuleRouter, self).__init__(rules)

    def process_rule(self, rule: "Rule") -> "Rule":
        rule = super(ReversibleRuleRouter, self).process_rule(rule)

        if rule.name:
            if rule.name in self.named_rules:
                app_log.warning(
                    "Multiple handlers named %s; replacing previous value", rule.name
                )
            self.named_rules[rule.name] = rule

        return rule

    def reverse_url(self, name: str, *args: Any) -> Optional[str]:
        if name in self.named_rules:
            return self.named_rules[name].matcher.reverse(*args)

        for rule in self.rules:
            if isinstance(rule.target, ReversibleRouter):
                reversed_url = rule.target.reverse_url(name, *args)
                if reversed_url is not None:
                    return reversed_url

        return None


class Rule(object):
    """A routing rule."""

    def __init__(
        self,
        matcher: "Matcher",
        target: Any,
        target_kwargs: Optional[Dict[str, Any]] = None,
        name: Optional[str] = None,
    ) -> None:
        """Constructs a Rule instance.

        :arg Matcher matcher: a `Matcher` instance used for determining
            whether the rule should be considered a match for a specific
            request.
        :arg target: a Rule's target (typically a ``RequestHandler`` or
            `~.httputil.HTTPServerConnectionDelegate` subclass or even a nested `Router`,
            depending on routing implementation).
        :arg dict target_kwargs: a dict of parameters that can be useful
            at the moment of target instantiation (for example, ``status_code``
            for a ``RequestHandler`` subclass). They end up in
            ``target_params['target_kwargs']`` of `RuleRouter.get_target_delegate`
            method.
        :arg str name: the name of the rule that can be used to find it
            in `ReversibleRouter.reverse_url` implementation.
        """
        if isinstance(target, str):
            # import the Module and instantiate the class
            # Must be a fully qualified name (module.ClassName)
            target = import_object(target)

        self.matcher = matcher  # type: Matcher
        self.target = target
        self.target_kwargs = target_kwargs if target_kwargs else {}
        self.name = name

    def reverse(self, *args: Any) -> Optional[str]:
        return self.matcher.reverse(*args)

    def __repr__(self) -> str:
        return "%s(%r, %s, kwargs=%r, name=%r)" % (
            self.__class__.__name__,
            self.matcher,
            self.target,
            self.target_kwargs,
            self.name,
        )


class Matcher(object):
    """Represents a matcher for request features."""

    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:
        """Matches current instance against the request.

        :arg httputil.HTTPServerRequest request: current HTTP request
        :returns: a dict of parameters to be passed to the target handler
            (for example, ``handler_kwargs``, ``path_args``, ``path_kwargs``
            can be passed for proper `~.web.RequestHandler` instantiation).
            An empty dict is a valid (and common) return value to indicate a match
            when the argument-passing features are not used.
            ``None`` must be returned to indicate that there is no match."""
        raise NotImplementedError()

    def reverse(self, *args: Any) -> Optional[str]:
        """Reconstructs full url from matcher instance and additional arguments."""
        return None


class AnyMatches(Matcher):
    """Matches any request."""

    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:
        return {}


class HostMatches(Matcher):
    """Matches requests from hosts specified by ``host_pattern`` regex."""

    def __init__(self, host_pattern: Union[str, Pattern]) -> None:
        if isinstance(host_pattern, basestring_type):
            if not host_pattern.endswith("$"):
                host_pattern += "$"
            self.host_pattern = re.compile(host_pattern)
        else:
            self.host_pattern = host_pattern

    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:
        if self.host_pattern.match(request.host_name):
            return {}

        return None


class DefaultHostMatches(Matcher):
    """Matches requests from host that is equal to application's default_host.
    Always returns no match if ``X-Real-Ip`` header is present.
    """

    def __init__(self, application: Any, host_pattern: Pattern) -> None:
        self.application = application
        self.host_pattern = host_pattern

    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:
        # Look for default host if not behind load balancer (for debugging)
        if "X-Real-Ip" not in request.headers:
            if self.host_pattern.match(self.application.default_host):
                return {}
        return None


class PathMatches(Matcher):
    """Matches requests with paths specified by ``path_pattern`` regex."""

    def __init__(self, path_pattern: Union[str, Pattern]) -> None:
        if isinstance(path_pattern, basestring_type):
            if not path_pattern.endswith("$"):
                path_pattern += "$"
            self.regex = re.compile(path_pattern)
        else:
            self.regex = path_pattern

        assert len(self.regex.groupindex) in (0, self.regex.groups), (
            "groups in url regexes must either be all named or all "
            "positional: %r" % self.regex.pattern
        )

        self._path, self._group_count = self._find_groups()

    def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]:
        match = self.regex.match(request.path)
        if match is None:
            return None
        if not self.regex.groups:
            return {}

        path_args = []  # type: List[bytes]
        path_kwargs = {}  # type: Dict[str, bytes]

        # Pass matched groups to the handler.  Since
        # match.groups() includes both named and
        # unnamed groups, we want to use either groups
        # or groupdict but not both.
        if self.regex.groupindex:
            path_kwargs = dict(
                (str(k), _unquote_or_none(v)) for (k, v) in match.groupdict().items()
            )
        else:
            path_args = [_unquote_or_none(s) for s in match.groups()]

        return dict(path_args=path_args, path_kwargs=path_kwargs)

    def reverse(self, *args: Any) -> Optional[str]:
        if self._path is None:
            raise ValueError("Cannot reverse url regex " + self.regex.pattern)
        assert len(args) == self._group_count, (
            "required number of arguments " "not found"
        )
        if not len(args):
            return self._path
        converted_args = []
        for a in args:
            if not isinstance(a, (unicode_type, bytes)):
                a = str(a)
            converted_args.append(url_escape(utf8(a), plus=False))
        return self._path % tuple(converted_args)

    def _find_groups(self) -> Tuple[Optional[str], Optional[int]]:
        """Returns a tuple (reverse string, group count) for a url.

        For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method
        would return ('/%s/%s/', 2).
        """
        pattern = self.regex.pattern
        if pattern.startswith("^"):
            pattern = pattern[1:]
        if pattern.endswith("$"):
            pattern = pattern[:-1]

        if self.regex.groups != pattern.count("("):
            # The pattern is too complicated for our simplistic matching,
            # so we can't support reversing it.
            return None, None

        pieces = []
        for fragment in pattern.split("("):
            if ")" in fragment:
                paren_loc = fragment.index(")")
                if paren_loc >= 0:
                    pieces.append("%s" + fragment[paren_loc + 1 :])
            else:
                try:
                    unescaped_fragment = re_unescape(fragment)
                except ValueError:
                    # If we can't unescape part of it, we can't
                    # reverse this url.
                    return (None, None)
                pieces.append(unescaped_fragment)

        return "".join(pieces), self.regex.groups


class URLSpec(Rule):
    """Specifies mappings between URLs and handlers.

    .. versionchanged: 4.5
       `URLSpec` is now a subclass of a `Rule` with `PathMatches` matcher and is preserved for
       backwards compatibility.
    """

    def __init__(
        self,
        pattern: Union[str, Pattern],
        handler: Any,
        kwargs: Optional[Dict[str, Any]] = None,
        name: Optional[str] = None,
    ) -> None:
        """Parameters:

        * ``pattern``: Regular expression to be matched. Any capturing
          groups in the regex will be passed in to the handler's
          get/post/etc methods as arguments (by keyword if named, by
          position if unnamed. Named and unnamed capturing groups
          may not be mixed in the same rule).

        * ``handler``: `~.web.RequestHandler` subclass to be invoked.

        * ``kwargs`` (optional): A dictionary of additional arguments
          to be passed to the handler's constructor.

        * ``name`` (optional): A name for this handler.  Used by
          `~.web.Application.reverse_url`.

        """
        matcher = PathMatches(pattern)
        super(URLSpec, self).__init__(matcher, handler, kwargs, name)

        self.regex = matcher.regex
        self.handler_class = self.target
        self.kwargs = kwargs

    def __repr__(self) -> str:
        return "%s(%r, %s, kwargs=%r, name=%r)" % (
            self.__class__.__name__,
            self.regex.pattern,
            self.handler_class,
            self.kwargs,
            self.name,
        )


@overload
def _unquote_or_none(s: str) -> bytes:
    pass


@overload  # noqa: F811
def _unquote_or_none(s: None) -> None:
    pass


def _unquote_or_none(s: Optional[str]) -> Optional[bytes]:  # noqa: F811
    """None-safe wrapper around url_unescape to handle unmatched optional
    groups correctly.

    Note that args are passed as bytes so the handler can decide what
    encoding to use.
    """
    if s is None:
        return s
    return url_unescape(s, encoding=None, plus=False)

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user