{
    "file_diffs": [
        {
            "old_file_content": "CHANGES\n=======\n\n0.12.1 (Unreleased)\n-------------------\n\n- ...\n\n\n0.12.0 (12-12-2014)\n-------------------\n\n- Deep refactoring of `aiohttp.web` in backward-incompatible manner.\n  Sorry, we have to do this.\n\n- Automatically force aiohttp.web handlers to coroutines in\n  `UrlDispatcher.add_route()` #186\n\n- Rename `Request.POST()` function to `Request.post()`\n\n- Added POST attribute\n\n- Response processing refactoring: constructor does't accept Request instance anymore.\n\n- Pass application instance to finish callback\n\n- Exceptions refactoring\n\n- Do not unquote query string in `aiohttp.web.Request`\n\n- Fix concurrent access to payload in `RequestHandle.handle_request()`\n\n- Add access logging to `aiohttp.web`\n\n- Gunicorn worker for `aiohttp.web`\n\n- Removed deprecated `AsyncGunicornWorker`\n\n- Removed deprecated HttpClient\n\n\n0.11.0 (11-29-2014)\n-------------------\n\n- Support named routes in `aiohttp.web.UrlDispatcher` #179\n\n- Make websocket subprotocols conform to spec #181\n\n\n0.10.2 (11-19-2014)\n-------------------\n\n- Don't unquote `environ['PATH_INFO']` in wsgi.py #177\n\n\n0.10.1 (11-17-2014)\n-------------------\n\n- aiohttp.web.HTTPException and descendants now files response body\n  with string like `404: NotFound`\n\n- Fix multidict `__iter__`, the method should iterate over keys, not (key, value) pairs.\n\n\n0.10.0 (11-13-2014)\n-------------------\n\n- Add aiohttp.web subpackage for highlevel http server support.\n\n- Add *reason* optional parameter to aiohttp.protocol.Response ctor.\n\n- Fix aiohttp.client bug for sending file without content-type.\n\n- Change error text for connection closed between server responses\n  from 'Can not read status line' to explicit 'Connection closed by\n  server'\n\n- Drop closed connections from connector #173\n\n- Set server.transport to None on .closing() #172\n\n\n0.9.3 (10-30-2014)\n------------------\n\n- Fix compatibility with asyncio 3.4.1+ #170\n\n\n0.9.2 (10-16-2014)\n------------------\n\n- Improve redirect handling #157\n\n- Send raw files as is #153\n\n- Better websocket support #150\n\n\n0.9.1 (08-30-2014)\n------------------\n\n- Added MultiDict support for client request params and data #114.\n\n- Fixed parameter type for IncompleteRead exception #118.\n\n- Strictly require ASCII headers names and values #137\n\n- Keep port in ProxyConnector #128.\n\n- Python 3.4.1 compatibility #131.\n\n\n0.9.0 (07-08-2014)\n------------------\n\n- Better client basic authentication support #112.\n\n- Fixed incorrect line splitting in HttpRequestParser #97.\n\n- Support StreamReader and DataQueue as request data.\n\n- Client files handling refactoring #20.\n\n- Backward incompatible: Replace DataQueue with StreamReader for request payload #87.\n\n\n0.8.4 (07-04-2014)\n------------------\n\n- Change ProxyConnector authorization parameters.\n\n\n0.8.3 (07-03-2014)\n------------------\n\n- Publish TCPConnector properties: verify_ssl, family, resolve, resolved_hosts.\n\n- Don't parse message body for HEAD responses.\n\n- Refactor client response decoding.\n\n\n0.8.2 (06-22-2014)\n------------------\n\n- Make ProxyConnector.proxy immutable property.\n\n- Make UnixConnector.path immutable property.\n\n- Fix resource leak for aiohttp.request() with implicit connector.\n\n- Rename Connector's reuse_timeout to keepalive_timeout.\n\n\n0.8.1 (06-18-2014)\n------------------\n\n- Use case insensitive multidict for server request/response headers.\n\n- MultiDict.getall() accepts default value.\n\n- Catch server ConnectionError.\n\n- Accept MultiDict (and derived) instances in aiohttp.request header argument.\n\n- Proxy 'CONNECT' support.\n\n\n0.8.0 (06-06-2014)\n------------------\n\n- Add support for utf-8 values in HTTP headers\n\n- Allow to use custom response class instead of HttpResponse\n\n- Use MultiDict for client request headers\n\n- Use MultiDict for server request/response headers\n\n- Store response headers in ClientResponse.headers attribute\n\n- Get rid of timeout parameter in aiohttp.client API\n\n- Exceptions refactoring\n\n\n0.7.3 (05-20-2014)\n------------------\n\n- Simple HTTP proxy support.\n\n\n0.7.2 (05-14-2014)\n------------------\n\n- Get rid of __del__ methods\n\n- Use ResourceWarning instead of logging warning record.\n\n\n0.7.1 (04-28-2014)\n------------------\n\n- Do not unquote client request urls.\n\n- Allow multple waiters on transport drain.\n\n- Do not return client connection to pool in case of exceptions.\n\n- Rename SocketConnector to TCPConnector and UnixSocketConnector to UnixConnector.\n\n\n0.7.0 (04-16-2014)\n------------------\n\n- Connection flow control.\n\n- Http client session/connection pool refactoring.\n\n- Better handling for bad server requests.\n\n\n0.6.5 (03-29-2014)\n------------------\n\n- Added client session reuse timeout.\n\n- Better client request cancellation support.\n\n- Better handling responses without content length.\n\n- Added HttpClient verify_ssl parameter support.\n\n\n0.6.4 (02-27-2014)\n------------------\n\n- Log content-length missing warning only for put and post requests.\n\n\n0.6.3 (02-27-2014)\n------------------\n\n- Better support for server exit.\n\n- Read response body until eof if content-length is not defined #14\n\n\n0.6.2 (02-18-2014)\n------------------\n\n- Fix trailing char in allowed_methods.\n\n- Start slow request timer for first request.\n\n\n0.6.1 (02-17-2014)\n------------------\n\n- Added utility method HttpResponse.read_and_close()\n\n- Added slow request timeout.\n\n- Enable socket SO_KEEPALIVE if available. (@polymorphm)\n\n\n0.6.0 (02-12-2014)\n------------------\n\n- Better handling for process exit.\n\n\n0.5.0 (01-29-2014)\n------------------\n\n- Allow to use custom HttpRequest client class.\n\n- Use gunicorn keepalive setting for async worker.\n\n- Log leaking responses.\n\n- python 3.4 compatibility\n\n\n0.4.4 (11-15-2013)\n------------------\n\n- Resolve only AF_INET family, because it is not clear how to pass extra info to asyncio.\n\n\n0.4.3 (11-15-2013)\n------------------\n\n- Allow to wait completion of request with `HttpResponse.wait_for_close()`\n\n\n0.4.2 (11-14-2013)\n------------------\n\n- Handle exception in client request stream.\n\n- Prevent host resolving for each client request.\n\n\n0.4.1 (11-12-2013)\n------------------\n\n- Added client support for `expect: 100-continue` header.\n\n\n0.4 (11-06-2013)\n----------------\n\n- Added custom wsgi application close procedure\n\n- Fixed concurrent host failure in HttpClient\n\n\n0.3 (11-04-2013)\n----------------\n\n- Added PortMapperWorker\n\n- Added HttpClient\n\n- Added tcp connection timeout to http client\n\n- Better client connection errors handling\n\n- Gracefully handle process exit\n\n\n0.2\n---\n\n- Fix packaging\n",
            "new_file_content": "CHANGES\n=======\n\n0.12.1 (Unreleased)\n-------------------\n\n- `StreamResponse.charset` converts value to lower-case on assigning.\n\n\n0.12.0 (12-12-2014)\n-------------------\n\n- Deep refactoring of `aiohttp.web` in backward-incompatible manner.\n  Sorry, we have to do this.\n\n- Automatically force aiohttp.web handlers to coroutines in\n  `UrlDispatcher.add_route()` #186\n\n- Rename `Request.POST()` function to `Request.post()`\n\n- Added POST attribute\n\n- Response processing refactoring: constructor does't accept Request instance anymore.\n\n- Pass application instance to finish callback\n\n- Exceptions refactoring\n\n- Do not unquote query string in `aiohttp.web.Request`\n\n- Fix concurrent access to payload in `RequestHandle.handle_request()`\n\n- Add access logging to `aiohttp.web`\n\n- Gunicorn worker for `aiohttp.web`\n\n- Removed deprecated `AsyncGunicornWorker`\n\n- Removed deprecated HttpClient\n\n\n0.11.0 (11-29-2014)\n-------------------\n\n- Support named routes in `aiohttp.web.UrlDispatcher` #179\n\n- Make websocket subprotocols conform to spec #181\n\n\n0.10.2 (11-19-2014)\n-------------------\n\n- Don't unquote `environ['PATH_INFO']` in wsgi.py #177\n\n\n0.10.1 (11-17-2014)\n-------------------\n\n- aiohttp.web.HTTPException and descendants now files response body\n  with string like `404: NotFound`\n\n- Fix multidict `__iter__`, the method should iterate over keys, not (key, value) pairs.\n\n\n0.10.0 (11-13-2014)\n-------------------\n\n- Add aiohttp.web subpackage for highlevel http server support.\n\n- Add *reason* optional parameter to aiohttp.protocol.Response ctor.\n\n- Fix aiohttp.client bug for sending file without content-type.\n\n- Change error text for connection closed between server responses\n  from 'Can not read status line' to explicit 'Connection closed by\n  server'\n\n- Drop closed connections from connector #173\n\n- Set server.transport to None on .closing() #172\n\n\n0.9.3 (10-30-2014)\n------------------\n\n- Fix compatibility with asyncio 3.4.1+ #170\n\n\n0.9.2 (10-16-2014)\n------------------\n\n- Improve redirect handling #157\n\n- Send raw files as is #153\n\n- Better websocket support #150\n\n\n0.9.1 (08-30-2014)\n------------------\n\n- Added MultiDict support for client request params and data #114.\n\n- Fixed parameter type for IncompleteRead exception #118.\n\n- Strictly require ASCII headers names and values #137\n\n- Keep port in ProxyConnector #128.\n\n- Python 3.4.1 compatibility #131.\n\n\n0.9.0 (07-08-2014)\n------------------\n\n- Better client basic authentication support #112.\n\n- Fixed incorrect line splitting in HttpRequestParser #97.\n\n- Support StreamReader and DataQueue as request data.\n\n- Client files handling refactoring #20.\n\n- Backward incompatible: Replace DataQueue with StreamReader for request payload #87.\n\n\n0.8.4 (07-04-2014)\n------------------\n\n- Change ProxyConnector authorization parameters.\n\n\n0.8.3 (07-03-2014)\n------------------\n\n- Publish TCPConnector properties: verify_ssl, family, resolve, resolved_hosts.\n\n- Don't parse message body for HEAD responses.\n\n- Refactor client response decoding.\n\n\n0.8.2 (06-22-2014)\n------------------\n\n- Make ProxyConnector.proxy immutable property.\n\n- Make UnixConnector.path immutable property.\n\n- Fix resource leak for aiohttp.request() with implicit connector.\n\n- Rename Connector's reuse_timeout to keepalive_timeout.\n\n\n0.8.1 (06-18-2014)\n------------------\n\n- Use case insensitive multidict for server request/response headers.\n\n- MultiDict.getall() accepts default value.\n\n- Catch server ConnectionError.\n\n- Accept MultiDict (and derived) instances in aiohttp.request header argument.\n\n- Proxy 'CONNECT' support.\n\n\n0.8.0 (06-06-2014)\n------------------\n\n- Add support for utf-8 values in HTTP headers\n\n- Allow to use custom response class instead of HttpResponse\n\n- Use MultiDict for client request headers\n\n- Use MultiDict for server request/response headers\n\n- Store response headers in ClientResponse.headers attribute\n\n- Get rid of timeout parameter in aiohttp.client API\n\n- Exceptions refactoring\n\n\n0.7.3 (05-20-2014)\n------------------\n\n- Simple HTTP proxy support.\n\n\n0.7.2 (05-14-2014)\n------------------\n\n- Get rid of __del__ methods\n\n- Use ResourceWarning instead of logging warning record.\n\n\n0.7.1 (04-28-2014)\n------------------\n\n- Do not unquote client request urls.\n\n- Allow multple waiters on transport drain.\n\n- Do not return client connection to pool in case of exceptions.\n\n- Rename SocketConnector to TCPConnector and UnixSocketConnector to UnixConnector.\n\n\n0.7.0 (04-16-2014)\n------------------\n\n- Connection flow control.\n\n- Http client session/connection pool refactoring.\n\n- Better handling for bad server requests.\n\n\n0.6.5 (03-29-2014)\n------------------\n\n- Added client session reuse timeout.\n\n- Better client request cancellation support.\n\n- Better handling responses without content length.\n\n- Added HttpClient verify_ssl parameter support.\n\n\n0.6.4 (02-27-2014)\n------------------\n\n- Log content-length missing warning only for put and post requests.\n\n\n0.6.3 (02-27-2014)\n------------------\n\n- Better support for server exit.\n\n- Read response body until eof if content-length is not defined #14\n\n\n0.6.2 (02-18-2014)\n------------------\n\n- Fix trailing char in allowed_methods.\n\n- Start slow request timer for first request.\n\n\n0.6.1 (02-17-2014)\n------------------\n\n- Added utility method HttpResponse.read_and_close()\n\n- Added slow request timeout.\n\n- Enable socket SO_KEEPALIVE if available. (@polymorphm)\n\n\n0.6.0 (02-12-2014)\n------------------\n\n- Better handling for process exit.\n\n\n0.5.0 (01-29-2014)\n------------------\n\n- Allow to use custom HttpRequest client class.\n\n- Use gunicorn keepalive setting for async worker.\n\n- Log leaking responses.\n\n- python 3.4 compatibility\n\n\n0.4.4 (11-15-2013)\n------------------\n\n- Resolve only AF_INET family, because it is not clear how to pass extra info to asyncio.\n\n\n0.4.3 (11-15-2013)\n------------------\n\n- Allow to wait completion of request with `HttpResponse.wait_for_close()`\n\n\n0.4.2 (11-14-2013)\n------------------\n\n- Handle exception in client request stream.\n\n- Prevent host resolving for each client request.\n\n\n0.4.1 (11-12-2013)\n------------------\n\n- Added client support for `expect: 100-continue` header.\n\n\n0.4 (11-06-2013)\n----------------\n\n- Added custom wsgi application close procedure\n\n- Fixed concurrent host failure in HttpClient\n\n\n0.3 (11-04-2013)\n----------------\n\n- Added PortMapperWorker\n\n- Added HttpClient\n\n- Added tcp connection timeout to http client\n\n- Better client connection errors handling\n\n- Gracefully handle process exit\n\n\n0.2\n---\n\n- Fix packaging\n",
            "header": {
                "file": {
                    "path": "CHANGES.txt"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "45e91c3c",
                "new_commit_hash": "3c935a46",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/CHANGES.txt"
            },
            "plus_file": {
                "path": "b/CHANGES.txt"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 4,
                            "length": 7
                        },
                        "new_range": {
                            "start": 4,
                            "length": 7
                        },
                        "section": "CHANGES"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "0.12.1 (Unreleased)",
                                "type": "context"
                            },
                            {
                                "content": "-------------------",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "- ...",
                                "type": "deleted"
                            },
                            {
                                "content": "- `StreamResponse.charset` converts value to lower-case on assigning.",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "0.12.0 (12-12-2014)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "import abc\nimport asyncio\nimport binascii\nimport collections\nimport mimetypes\nimport cgi\nimport http.cookies\nimport io\nimport json\nimport re\nimport os\n\nfrom urllib.parse import urlsplit, parse_qsl, urlencode, unquote\n\nfrom .abc import AbstractRouter, AbstractMatchInfo\nfrom .helpers import reify\nfrom .log import web_logger\nfrom .multidict import (CaseInsensitiveMultiDict,\n                        CaseInsensitiveMutableMultiDict,\n                        MultiDict,\n                        MutableMultiDict)\nfrom .protocol import Response as ResponseImpl, HttpVersion, HttpVersion11\nfrom .server import ServerHttpProtocol\nfrom .streams import EOF_MARKER\n\n\n__all__ = [\n    'Application',\n    'HttpVersion',\n    'RequestHandler',\n    'RequestHandlerFactory',\n    'Request',\n    'StreamResponse',\n    'Response',\n    'UrlDispatcher',\n    'UrlMappingMatchInfo',\n    'HTTPException',\n    'HTTPError',\n    'HTTPRedirection',\n    'HTTPSuccessful',\n    'HTTPOk',\n    'HTTPCreated',\n    'HTTPAccepted',\n    'HTTPNonAuthoritativeInformation',\n    'HTTPNoContent',\n    'HTTPResetContent',\n    'HTTPPartialContent',\n    'HTTPMultipleChoices',\n    'HTTPMovedPermanently',\n    'HTTPFound',\n    'HTTPSeeOther',\n    'HTTPNotModified',\n    'HTTPUseProxy',\n    'HTTPTemporaryRedirect',\n    'HTTPClientError',\n    'HTTPBadRequest',\n    'HTTPUnauthorized',\n    'HTTPPaymentRequired',\n    'HTTPForbidden',\n    'HTTPNotFound',\n    'HTTPMethodNotAllowed',\n    'HTTPNotAcceptable',\n    'HTTPProxyAuthenticationRequired',\n    'HTTPRequestTimeout',\n    'HTTPConflict',\n    'HTTPGone',\n    'HTTPLengthRequired',\n    'HTTPPreconditionFailed',\n    'HTTPRequestEntityTooLarge',\n    'HTTPRequestURITooLong',\n    'HTTPUnsupportedMediaType',\n    'HTTPRequestRangeNotSatisfiable',\n    'HTTPExpectationFailed',\n    'HTTPServerError',\n    'HTTPInternalServerError',\n    'HTTPNotImplemented',\n    'HTTPBadGateway',\n    'HTTPServiceUnavailable',\n    'HTTPGatewayTimeout',\n    'HTTPVersionNotSupported',\n]\n\n\nsentinel = object()\n\n\nclass HeadersMixin:\n\n    _content_type = None\n    _content_dict = None\n    _stored_content_type = sentinel\n\n    def _parse_content_type(self, raw):\n        self._stored_content_type = raw\n        if raw is None:\n            # default value according to RFC 2616\n            self._content_type = 'application/octet-stream'\n            self._content_dict = {}\n        else:\n            self._content_type, self._content_dict = cgi.parse_header(raw)\n\n    @property\n    def content_type(self):\n        \"\"\"The value of content part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get('Content-Type')\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_type\n\n    @property\n    def charset(self):\n        \"\"\"The value of charset part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get('Content-Type')\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_dict.get('charset')\n\n    @property\n    def content_length(self):\n        \"\"\"The value of Content-Length HTTP header.\"\"\"\n        l = self.headers.get('Content-Length')\n        if l is None:\n            return None\n        else:\n            return int(l)\n\n\nFileField = collections.namedtuple('Field', 'name filename file content_type')\n\n\n############################################################\n# HTTP Request\n############################################################\n\n\nclass Request(HeadersMixin):\n\n    def __init__(self, app, message, payload, transport, writer,\n                 keep_alive_timeout):\n        self._app = app\n        self._version = message.version\n        self._transport = transport\n        self._writer = writer\n        self._method = message.method\n        self._host = message.headers.get('HOST')\n        self._path_qs = message.path\n        res = urlsplit(message.path)\n        self._path = unquote(res.path)\n        self._query_string = res.query\n        self._post = None\n        self._post_files_cache = None\n        self._headers = CaseInsensitiveMultiDict._from_uppercase_multidict(\n            message.headers)\n\n        if self._version < HttpVersion11:\n            self._keep_alive = False\n        elif message.should_close:\n            self._keep_alive = False\n        else:\n            self._keep_alive = bool(keep_alive_timeout)\n\n        # matchdict, route_name, handler\n        # or information about traversal lookup\n        self._match_info = None  # initialized after route resolving\n\n        self._payload = payload\n        self._cookies = None\n\n    @property\n    def method(self):\n        \"\"\"Read only property for getting HTTP method.\n\n        The value is upper-cased str like 'GET', 'POST', 'PUT' etc.\n        \"\"\"\n        return self._method\n\n    @property\n    def version(self):\n        \"\"\"Read only property for getting HTTP version of request.\n\n        Returns aiohttp.protocol.HttpVersion instance.\n        \"\"\"\n        return self._version\n\n    @property\n    def host(self):\n        \"\"\"Read only property for getting *HOST* header of request.\n\n        Returns str or None if HTTP request has no HOST header.\n        \"\"\"\n        return self._host\n\n    @property\n    def path_qs(self):\n        \"\"\"The URL including PATH_INFO and the query string.\n\n        E.g, /app/blog?id=10\n        \"\"\"\n        return self._path_qs\n\n    @property\n    def path(self):\n        \"\"\"The URL including *PATH INFO* without the host or scheme.\n\n        E.g., ``/app/blog``\n        \"\"\"\n        return self._path\n\n    @property\n    def query_string(self):\n        \"\"\"The query string in the URL.\n\n        E.g., id=10\n        \"\"\"\n        return self._query_string\n\n    @reify\n    def GET(self):\n        \"\"\"A multidict with all the variables in the query string.\n\n        Lazy property.\n        \"\"\"\n        return MultiDict(parse_qsl(self._query_string))\n\n    @reify\n    def POST(self):\n        \"\"\"A multidict with all the variables in the POST parameters.\n\n        post() methods has to be called before using this attribute.\n        \"\"\"\n        if self._post is None:\n            raise RuntimeError(\"POST is not available before post()\")\n        return self._post\n\n    @property\n    def headers(self):\n        \"\"\"A case-insensitive multidict with all headers.\n\n        Lazy property.\n        \"\"\"\n        return self._headers\n\n    @property\n    def keep_alive(self):\n        \"\"\"Is keepalive enabled by client?\"\"\"\n        return self._keep_alive\n\n    @property\n    def match_info(self):\n        \"\"\"Result of route resolving.\"\"\"\n        return self._match_info\n\n    @property\n    def app(self):\n        \"\"\"Application instance.\"\"\"\n        return self._app\n\n    @property\n    def transport(self):\n        \"\"\"Transport used for request processing.\"\"\"\n        return self._transport\n\n    @property\n    def cookies(self):\n        \"\"\"Return request cookies.\n\n        A read-only dictionary-like object.\n        \"\"\"\n        if self._cookies is None:\n            raw = self.headers.get('COOKIE', '')\n            parsed = http.cookies.SimpleCookie(raw)\n            self._cookies = MultiDict({key: val.value\n                                       for key, val in parsed.items()})\n        return self._cookies\n\n    @property\n    def payload(self):\n        \"\"\"Return raw paiload stream.\"\"\"\n        return self._payload\n\n    @asyncio.coroutine\n    def release(self):\n        \"\"\"Release request.\n\n        Eat unread part of HTTP BODY if present.\n        \"\"\"\n        chunk = yield from self._payload.readany()\n        while chunk is not EOF_MARKER or chunk:\n            chunk = yield from self._payload.readany()\n\n    @asyncio.coroutine\n    def read(self):\n        \"\"\"Read request body if present.\n\n        Returns bytes object with full request content.\n        \"\"\"\n        body = bytearray()\n        while True:\n            chunk = yield from self._payload.readany()\n            body.extend(chunk)\n            if chunk is EOF_MARKER:\n                break\n        return bytes(body)\n\n    @asyncio.coroutine\n    def text(self):\n        \"\"\"Return BODY as text using encoding from .charset.\"\"\"\n        bytes_body = yield from self.read()\n        encoding = self.charset or 'utf-8'\n        return bytes_body.decode(encoding)\n\n    @asyncio.coroutine\n    def json(self, *, loader=json.loads):\n        \"\"\"Return BODY as JSON.\"\"\"\n        body = yield from self.text()\n        return loader(body)\n\n    @asyncio.coroutine\n    def post(self):\n        \"\"\"Return POST parameters.\"\"\"\n        if self._post is not None:\n            return self._post\n        if self.method not in ('POST', 'PUT', 'PATCH'):\n            self._post = MultiDict()\n            return self._post\n\n        content_type = self.content_type\n        if (content_type not in ('',\n                                 'application/x-www-form-urlencoded',\n                                 'multipart/form-data')):\n            self._post = MultiDict()\n            return self._post\n\n        body = yield from self.read()\n        content_charset = self.charset or 'utf-8'\n\n        environ = {'REQUEST_METHOD': self.method,\n                   'CONTENT_LENGTH': str(len(body)),\n                   'QUERY_STRING': '',\n                   'CONTENT_TYPE': self.headers.get('CONTENT-TYPE')}\n\n        fs = cgi.FieldStorage(fp=io.BytesIO(body),\n                              environ=environ,\n                              keep_blank_values=True,\n                              encoding=content_charset)\n\n        supported_tranfer_encoding = {\n            'base64': binascii.a2b_base64,\n            'quoted-printable': binascii.a2b_qp\n        }\n\n        out = MutableMultiDict()\n        for field in fs.list or ():\n            transfer_encoding = field.headers.get(\n                'Content-Transfer-Encoding', None)\n            if field.filename:\n                ff = FileField(field.name,\n                               field.filename,\n                               field.file,  # N.B. file closed error\n                               field.type)\n                if self._post_files_cache is None:\n                    self._post_files_cache = {}\n                self._post_files_cache[field.name] = field\n                out.add(field.name, ff)\n            else:\n                value = field.value\n                if transfer_encoding in supported_tranfer_encoding:\n                    # binascii accepts bytes\n                    value = value.encode('utf-8')\n                    value = supported_tranfer_encoding[\n                        transfer_encoding](value)\n                out.add(field.name, value)\n\n        self._post = MultiDict(out.items(getall=True))\n        return self._post\n\n    # @asyncio.coroutine\n    # def start_websocket(self):\n    #     \"\"\"Upgrade connection to websocket.\n\n    #     Returns (reader, writer) pair.\n    #     \"\"\"\n\n    #     upgrade = 'websocket' in message.headers.get('UPGRADE', '').lower()\n    #     if not upgrade:\n    #         pass\n\n\n############################################################\n# HTTP Response classes\n############################################################\n\n\nclass StreamResponse(HeadersMixin):\n\n    def __init__(self, *, status=200, reason=None):\n        self._body = None\n        self._keep_alive = None\n        self._headers = CaseInsensitiveMutableMultiDict()\n        self._cookies = http.cookies.SimpleCookie()\n        self.set_status(status, reason)\n\n        self._req = None\n        self._resp_impl = None\n        self._eof_sent = False\n\n    def _copy_cookies(self):\n        for cookie in self._cookies.values():\n            value = cookie.output(header='')[1:]\n            self.headers.add('Set-Cookie', value)\n\n    @property\n    def status(self):\n        return self._status\n\n    @property\n    def reason(self):\n        return self._reason\n\n    def set_status(self, status, reason=None):\n        self._status = int(status)\n        if reason is None:\n            reason = ResponseImpl.calc_reason(status)\n        self._reason = reason\n\n    @property\n    def keep_alive(self):\n        return self._keep_alive\n\n    def force_close(self):\n        self._keep_alive = False\n\n    @property\n    def headers(self):\n        return self._headers\n\n    @property\n    def cookies(self):\n        return self._cookies\n\n    def set_cookie(self, name, value, *, expires=None,\n                   domain=None, max_age=None, path=None,\n                   secure=None, httponly=None, version=None):\n        \"\"\"Set or update response cookie.\n\n        Sets new cookie or updates existent with new value.\n        Also updates only those params which are not None.\n        \"\"\"\n\n        old = self._cookies.get(name)\n        if old is not None and old.coded_value == '':\n            # deleted cookie\n            self._cookies.pop(name, None)\n\n        self._cookies[name] = value\n        c = self._cookies[name]\n        if expires is not None:\n            c['expires'] = expires\n        if domain is not None:\n            c['domain'] = domain\n        if max_age is not None:\n            c['max-age'] = max_age\n        if path is not None:\n            c['path'] = path\n        if secure is not None:\n            c['secure'] = secure\n        if httponly is not None:\n            c['httponly'] = httponly\n        if version is not None:\n            c['version'] = version\n\n    def del_cookie(self, name, *, domain=None, path=None):\n        \"\"\"Delete cookie.\n\n        Creates new empty expired cookie.\n        \"\"\"\n        # TODO: do we need domain/path here?\n        self._cookies.pop(name, None)\n        self.set_cookie(name, '', max_age=0, domain=domain, path=path)\n\n    @property\n    def content_length(self):\n        # Just a placeholder for adding setter\n        return super().content_length\n\n    @content_length.setter\n    def content_length(self, value):\n        if value is not None:\n            value = int(value)\n            # TODO: raise error if chunked enabled\n            self.headers['Content-Length'] = str(value)\n        elif 'Content-Length' in self.headers:\n            del self.headers['Content-Length']\n\n    @property\n    def content_type(self):\n        # Just a placeholder for adding setter\n        return super().content_type\n\n    @content_type.setter\n    def content_type(self, value):\n        self.content_type  # read header values if needed\n        self._content_type = str(value)\n        self._generate_content_type_header()\n\n    @property\n    def charset(self):\n        # Just a placeholder for adding setter\n        return super().charset\n\n    @charset.setter\n    def charset(self, value):\n        ctype = self.content_type  # read header values if needed\n        if ctype == 'application/octet-stream':\n            raise RuntimeError(\"Setting charset for application/octet-stream \"\n                               \"doesn't make sense, setup content_type first\")\n        if value is None:\n            self._content_dict.pop('charset', None)\n        else:\n            self._content_dict['charset'] = str(value)\n        self._generate_content_type_header()\n\n    def _generate_content_type_header(self):\n        params = '; '.join(\"%s=%s\" % i for i in self._content_dict.items())\n        if params:\n            ctype = self._content_type + '; ' + params\n        else:\n            ctype = self._content_type\n        self.headers['Content-Type'] = ctype\n\n    def start(self, request):\n        if self._resp_impl is not None:\n            if self._req is not request:\n                raise RuntimeError(\n                    'Response has been started with different request.')\n            return self._resp_impl\n\n        self._req = request\n\n        keep_alive = self._keep_alive\n        if keep_alive is None:\n            keep_alive = request.keep_alive\n        self._keep_alive = keep_alive\n\n        resp_impl = self._resp_impl = ResponseImpl(\n            request._writer,\n            self._status,\n            request.version,\n            not keep_alive,\n            self._reason)\n\n        self._copy_cookies()\n\n        headers = self.headers.items(getall=True)\n        for key, val in headers:\n            resp_impl.add_header(key, val)\n\n        resp_impl.send_headers()\n        return resp_impl\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)',\n                            type(data))\n\n        if self._eof_sent:\n            raise RuntimeError(\"Cannot call write() after write_eof()\")\n        if self._resp_impl is None:\n            raise RuntimeError(\"Cannot call write() before start()\")\n\n        if data:\n            return self._resp_impl.write(data)\n        else:\n            return ()\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self._resp_impl.write_eof()\n        self._eof_sent = True\n\n\nclass Response(StreamResponse):\n\n    def __init__(self, *, body=None, status=200,\n                 reason=None, headers=None,\n                 text=None, content_type=None):\n        super().__init__(status=status, reason=reason)\n\n        if headers is not None:\n            self.headers.extend(headers)\n        if content_type:\n            self.content_type = content_type\n\n        if body is not None and text is not None:\n            raise ValueError(\"body and text are not allowed together.\")\n        elif body is not None:\n            self.body = body\n        elif text is not None:\n            self.text = text\n        else:\n            self.body = None\n\n    @property\n    def body(self):\n        return self._body\n\n    @body.setter\n    def body(self, body):\n        if body is not None and not isinstance(body, bytes):\n            raise TypeError('body argument must be bytes (%r)', type(body))\n        self._body = body\n        if body is not None:\n            self.content_length = len(body)\n        else:\n            self.content_length = 0\n\n    @property\n    def text(self):\n        return self._body.decode(self.charset or 'utf-8')\n\n    @text.setter\n    def text(self, text):\n        if text is not None and not isinstance(text, str):\n            raise TypeError('text argument must be str (%r)', type(text))\n\n        if self.content_type == 'application/octet-stream':\n            self.content_type = 'text/plain'\n        if self.charset is None:\n            self.charset = 'utf-8'\n\n        self.body = text.encode(self.charset)\n\n    @asyncio.coroutine\n    def write_eof(self):\n        body = self._body\n        if body is not None:\n            self.write(body)\n        yield from super().write_eof()\n\n\n############################################################\n# HTTP Exceptions\n############################################################\n\nclass HTTPException(Response, Exception):\n\n    # You should set in subclasses:\n    # status = 200\n\n    status_code = None\n\n    def __init__(self, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        Response.__init__(self, status=self.status_code,\n                          headers=headers, reason=reason,\n                          body=body, text=text, content_type=content_type)\n        Exception.__init__(self, self.reason)\n        if self.body is None:\n            self.text = \"{}: {}\".format(self.status, self.reason)\n\n\nclass HTTPError(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 400s and 500s.\"\"\"\n\n\nclass HTTPRedirection(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 300s.\"\"\"\n\n\nclass HTTPSuccessful(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 200s.\"\"\"\n\n\nclass HTTPOk(HTTPSuccessful):\n    status_code = 200\n\n\nclass HTTPCreated(HTTPSuccessful):\n    status_code = 201\n\n\nclass HTTPAccepted(HTTPSuccessful):\n    status_code = 202\n\n\nclass HTTPNonAuthoritativeInformation(HTTPSuccessful):\n    status_code = 203\n\n\nclass HTTPNoContent(HTTPSuccessful):\n    status_code = 204\n\n\nclass HTTPResetContent(HTTPSuccessful):\n    status_code = 205\n\n\nclass HTTPPartialContent(HTTPSuccessful):\n    status_code = 206\n\n\n############################################################\n# 3xx redirection\n############################################################\n\n\nclass _HTTPMove(HTTPRedirection):\n\n    def __init__(self, location, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        if not location:\n            raise ValueError(\"HTTP redirects need a location to redirect to.\")\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Location'] = location\n        self.location = location\n\n\nclass HTTPMultipleChoices(_HTTPMove):\n    status_code = 300\n\n\nclass HTTPMovedPermanently(_HTTPMove):\n    status_code = 301\n\n\nclass HTTPFound(_HTTPMove):\n    status_code = 302\n\n\n# This one is safe after a POST (the redirected location will be\n# retrieved with GET):\nclass HTTPSeeOther(_HTTPMove):\n    status_code = 303\n\n\nclass HTTPNotModified(HTTPRedirection):\n    # FIXME: this should include a date or etag header\n    status_code = 304\n\n\nclass HTTPUseProxy(_HTTPMove):\n    # Not a move, but looks a little like one\n    status_code = 305\n\n\nclass HTTPTemporaryRedirect(_HTTPMove):\n    status_code = 307\n\n\n############################################################\n# 4xx client error\n############################################################\n\n\nclass HTTPClientError(HTTPError):\n    pass\n\n\nclass HTTPBadRequest(HTTPClientError):\n    status_code = 400\n\n\nclass HTTPUnauthorized(HTTPClientError):\n    status_code = 401\n\n\nclass HTTPPaymentRequired(HTTPClientError):\n    status_code = 402\n\n\nclass HTTPForbidden(HTTPClientError):\n    status_code = 403\n\n\nclass HTTPNotFound(HTTPClientError):\n    status_code = 404\n\n\nclass HTTPMethodNotAllowed(HTTPClientError):\n    status_code = 405\n\n    def __init__(self, method, allowed_methods, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        allow = ','.join(sorted(allowed_methods))\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Allow'] = allow\n        self.allowed_methods = allowed_methods\n        self.method = method.upper()\n\n\nclass HTTPNotAcceptable(HTTPClientError):\n    status_code = 406\n\n\nclass HTTPProxyAuthenticationRequired(HTTPClientError):\n    status_code = 407\n\n\nclass HTTPRequestTimeout(HTTPClientError):\n    status_code = 408\n\n\nclass HTTPConflict(HTTPClientError):\n    status_code = 409\n\n\nclass HTTPGone(HTTPClientError):\n    status_code = 410\n\n\nclass HTTPLengthRequired(HTTPClientError):\n    status_code = 411\n\n\nclass HTTPPreconditionFailed(HTTPClientError):\n    status_code = 412\n\n\nclass HTTPRequestEntityTooLarge(HTTPClientError):\n    status_code = 413\n\n\nclass HTTPRequestURITooLong(HTTPClientError):\n    status_code = 414\n\n\nclass HTTPUnsupportedMediaType(HTTPClientError):\n    status_code = 415\n\n\nclass HTTPRequestRangeNotSatisfiable(HTTPClientError):\n    status_code = 416\n\n\nclass HTTPExpectationFailed(HTTPClientError):\n    status_code = 417\n\n\n############################################################\n# 5xx Server Error\n############################################################\n#  Response status codes beginning with the digit \"5\" indicate cases in\n#  which the server is aware that it has erred or is incapable of\n#  performing the request. Except when responding to a HEAD request, the\n#  server SHOULD include an entity containing an explanation of the error\n#  situation, and whether it is a temporary or permanent condition. User\n#  agents SHOULD display any included entity to the user. These response\n#  codes are applicable to any request method.\n\n\nclass HTTPServerError(HTTPError):\n    pass\n\n\nclass HTTPInternalServerError(HTTPServerError):\n    status_code = 500\n\n\nclass HTTPNotImplemented(HTTPServerError):\n    status_code = 501\n\n\nclass HTTPBadGateway(HTTPServerError):\n    status_code = 502\n\n\nclass HTTPServiceUnavailable(HTTPServerError):\n    status_code = 503\n\n\nclass HTTPGatewayTimeout(HTTPServerError):\n    status_code = 504\n\n\nclass HTTPVersionNotSupported(HTTPServerError):\n    status_code = 505\n\n\n############################################################\n# UrlDispatcher implementation\n############################################################\n\n\nclass UrlMappingMatchInfo(dict, AbstractMatchInfo):\n\n    def __init__(self, match_dict, route):\n        super().__init__(match_dict)\n        self._route = route\n\n    @property\n    def handler(self):\n        return self._route.handler\n\n    @property\n    def route(self):\n        return self._route\n\n\nclass Route(metaclass=abc.ABCMeta):\n\n    def __init__(self, method, handler, name):\n        self._method = method\n        self._handler = handler\n        self._name = name\n\n    @property\n    def method(self):\n        return self._method\n\n    @property\n    def handler(self):\n        return self._handler\n\n    @property\n    def name(self):\n        return self._name\n\n    @abc.abstractmethod\n    def match(self, path):\n        \"\"\"Return dict with info for given path or\n        None if route cannot process path.\"\"\"\n\n    @abc.abstractmethod\n    def url(self, **kwargs):\n        \"\"\"Construct url for route with additional params.\"\"\"\n\n    @staticmethod\n    def _append_query(url, query):\n        if query is not None:\n            return url + \"?\" + urlencode(query)\n        else:\n            return url\n\n\nclass PlainRoute(Route):\n\n    def __init__(self, method, handler, name, path):\n        super().__init__(method, handler, name)\n        self._path = path\n\n    def match(self, path):\n        # string comparsion about 10 times faster than regexp matching\n        if self._path == path:\n            return {}\n        else:\n            return None\n\n    def url(self, *, query=None):\n        return self._append_query(self._path, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<PlainRoute {name}[{method}] {path} -> {handler!r}\".format(\n            name=name, method=self.method, path=self._path,\n            handler=self.handler)\n\n\nclass DynamicRoute(Route):\n\n    def __init__(self, method, handler, name, pattern, formatter):\n        super().__init__(method, handler, name)\n        self._pattern = pattern\n        self._formatter = formatter\n\n    def match(self, path):\n        match = self._pattern.match(path)\n        if match is None:\n            return None\n        else:\n            return match.groupdict()\n\n    def url(self, *, parts, query=None):\n        url = self._formatter.format_map(parts)\n        return self._append_query(url, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return (\"<DynamicRoute {name}[{method}] {formatter} -> {handler!r}\"\n                .format(name=name, method=self.method,\n                        formatter=self._formatter, handler=self.handler))\n\n\nclass StaticRoute(Route):\n\n    def __init__(self, name, prefix, directory):\n        assert prefix.startswith('/'), prefix\n        assert prefix.endswith('/'), prefix\n        super().__init__('GET', self.handle, name)\n        self._prefix = prefix\n        self._prefix_len = len(self._prefix)\n        self._directory = directory\n\n    def match(self, path):\n        if not path.startswith(self._prefix):\n            return None\n        return {'filename': path[self._prefix_len:]}\n\n    def url(self, *, filename, query=None):\n        while filename.startswith('/'):\n            filename = filename[1:]\n        url = self._prefix + filename\n        return self._append_query(url, query)\n\n    @asyncio.coroutine\n    def handle(self, request):\n        resp = StreamResponse()\n        filename = request.match_info['filename']\n        filepath = os.path.join(self._directory, filename)\n        if '..' in filename:\n            raise HTTPNotFound()\n        if not os.path.exists(filepath) or not os.path.isfile(filepath):\n            raise HTTPNotFound()\n\n        ct = mimetypes.guess_type(filename)[0]\n        if not ct:\n            ct = 'application/octet-stream'\n        resp.content_type = ct\n\n        resp.headers['transfer-encoding'] = 'chunked'\n        resp.start(request)\n\n        with open(filepath, 'rb') as f:\n            chunk = f.read(8192)\n            while chunk:\n                resp.write(chunk)\n                chunk = f.read(8192)\n\n        yield from resp.write_eof()\n        return resp\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<StaticRoute {name}[{method}] {path} -> {directory!r}\".format(\n            name=name, method=self.method, path=self._prefix,\n            directory=self._directory)\n\n\nclass UrlDispatcher(AbstractRouter, collections.abc.Mapping):\n\n    DYN = re.compile(r'^\\{[a-zA-Z][_a-zA-Z0-9]*\\}$')\n    GOOD = r'[^{}/]+'\n    PLAIN = re.compile('^' + GOOD + '$')\n\n    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}\n\n    def __init__(self):\n        super().__init__()\n        self._urls = []\n        self._routes = {}\n\n    @asyncio.coroutine\n    def resolve(self, request):\n        path = request.path\n        method = request.method\n        allowed_methods = set()\n        for route in self._urls:\n            match_dict = route.match(path)\n            if match_dict is None:\n                continue\n            route_method = route.method\n            if route_method != method:\n                allowed_methods.add(route_method)\n            else:\n                return UrlMappingMatchInfo(match_dict, route)\n        else:\n            if allowed_methods:\n                raise HTTPMethodNotAllowed(method, allowed_methods)\n            else:\n                raise HTTPNotFound()\n\n    def __iter__(self):\n        return iter(self._routes)\n\n    def __len__(self):\n        return len(self._routes)\n\n    def __contains__(self, name):\n        return name in self._routes\n\n    def __getitem__(self, name):\n        return self._routes[name]\n\n    def _register_endpoint(self, route):\n        name = route.name\n        if name is not None:\n            if name in self._routes:\n                raise ValueError('Duplicate {!r}, '\n                                 'already handled by {!r}'\n                                 .format(name, self._routes[name]))\n            else:\n                self._routes[name] = route\n        self._urls.append(route)\n\n    def add_route(self, method, path, handler, *, name=None):\n        assert path.startswith('/')\n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n        parts = []\n        factory = PlainRoute\n        for part in path.split('/'):\n            if not part:\n                continue\n            if self.DYN.match(part):\n                parts.append('(?P<' + part[1:-1] + '>' + self.GOOD + ')')\n                factory = DynamicRoute\n            elif self.PLAIN.match(part):\n                parts.append(re.escape(part))\n            else:\n                raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\n        if factory is PlainRoute:\n            route = PlainRoute(method, handler, name, path)\n        else:\n            pattern = '/' + '/'.join(parts)\n            if path.endswith('/') and pattern != '/':\n                pattern += '/'\n            compiled = re.compile('^' + pattern + '$')\n            route = DynamicRoute(method, handler, name, compiled, path)\n        self._register_endpoint(route)\n        return route\n\n    def add_static(self, prefix, path, *, name=None):\n        \"\"\"\n        Adds static files view\n        :param prefix - url prefix\n        :param path - folder with files\n        \"\"\"\n        assert prefix.startswith('/')\n        assert os.path.isdir(path), 'Path does not directory %s' % path\n        path = os.path.abspath(path)\n        if not prefix.endswith('/'):\n            prefix += '/'\n        route = StaticRoute(name, prefix, path)\n        self._register_endpoint(route)\n        return route\n\n\n############################################################\n# Application implementation\n############################################################\n\n\nclass RequestHandler(ServerHttpProtocol):\n\n    def __init__(self, manager, app, router, **kwargs):\n        super().__init__(**kwargs)\n\n        self._manager = manager\n        self._app = app\n        self._router = router\n\n    def connection_made(self, transport):\n        super().connection_made(transport)\n\n        self._manager.connection_made(self, transport)\n\n    def connection_lost(self, exc):\n        self._manager.connection_lost(self, exc)\n\n        super().connection_lost(exc)\n\n    @asyncio.coroutine\n    def handle_request(self, message, payload):\n        now = self._loop.time()\n\n        request = Request(self._app, message, payload,\n                          self.transport, self.writer, self.keep_alive_timeout)\n        try:\n            match_info = yield from self._router.resolve(request)\n\n            assert isinstance(match_info, AbstractMatchInfo), match_info\n\n            request._match_info = match_info\n            handler = match_info.handler\n\n            resp = yield from handler(request)\n            if not isinstance(resp, StreamResponse):\n                raise RuntimeError(\n                    (\"Handler should return response instance, got {!r}\")\n                    .format(type(resp)))\n        except HTTPException as exc:\n            resp = exc\n\n        resp_msg = resp.start(request)\n        yield from resp.write_eof()\n\n        # notify server about keep-alive\n        self.keep_alive(resp_msg.keep_alive())\n\n        # log access\n        self.log_access(message, None, resp_msg, self._loop.time() - now)\n\n\nclass RequestHandlerFactory:\n\n    def __init__(self, app, router, *,\n                 handler=RequestHandler, loop=None, **kwargs):\n        self._app = app\n        self._router = router\n        self._handler = handler\n        self._loop = loop\n        self._connections = {}\n        self._kwargs = kwargs\n        self._kwargs.setdefault('logger', app.logger)\n\n    @property\n    def connections(self):\n        return list(self._connections.keys())\n\n    def connection_made(self, handler, transport):\n        self._connections[handler] = transport\n\n    def connection_lost(self, handler, exc=None):\n        if handler in self._connections:\n            del self._connections[handler]\n\n    @asyncio.coroutine\n    def finish_connections(self, timeout=None):\n        for handler in self._connections.keys():\n            handler.closing()\n\n        def cleanup():\n            sleep = 0.05\n            while self._connections:\n                yield from asyncio.sleep(sleep, loop=self._loop)\n                if sleep < 5:\n                    sleep = sleep * 2\n\n        if timeout:\n            try:\n                yield from asyncio.wait_for(\n                    cleanup(), timeout, loop=self._loop)\n            except asyncio.TimeoutError:\n                self._app.logger.warning(\n                    \"Not all connections are closed (pending: %d)\",\n                    len(self._connections))\n\n        for transport in self._connections.values():\n            transport.close()\n\n        self._connections.clear()\n\n    def __call__(self):\n        return self._handler(\n            self, self._app, self._router, loop=self._loop, **self._kwargs)\n\n\nclass Application(dict):\n\n    def __init__(self, *, logger=web_logger, loop=None,\n                 router=None, handler_factory=RequestHandlerFactory, **kwargs):\n        # TODO: explicitly accept *debug* param\n        if loop is None:\n            loop = asyncio.get_event_loop()\n        if router is None:\n            router = UrlDispatcher()\n        assert isinstance(router, AbstractRouter), router\n\n        self._router = router\n        self._handler_factory = handler_factory\n        self._finish_callbacks = []\n        self._loop = loop\n        self.logger = logger\n\n        self.update(**kwargs)\n\n    @property\n    def router(self):\n        return self._router\n\n    @property\n    def loop(self):\n        return self._loop\n\n    def make_handler(self, **kwargs):\n        return self._handler_factory(\n            self, self.router, loop=self.loop, **kwargs)\n\n    @asyncio.coroutine\n    def finish(self):\n        callbacks = self._finish_callbacks\n        self._finish_callbacks = []\n\n        for (cb, args, kwargs) in callbacks:\n            try:\n                res = cb(self, *args, **kwargs)\n                if (asyncio.iscoroutine(res) or\n                        isinstance(res, asyncio.Future)):\n                    yield from res\n            except Exception as exc:\n                self._loop.call_exception_handler({\n                    'message': \"Error in finish callback\",\n                    'exception': exc,\n                    'application': self,\n                })\n\n    def register_on_finish(self, func, *args, **kwargs):\n        self._finish_callbacks.insert(0, (func, args, kwargs))\n",
            "new_file_content": "import abc\nimport asyncio\nimport binascii\nimport collections\nimport mimetypes\nimport cgi\nimport http.cookies\nimport io\nimport json\nimport re\nimport os\n\nfrom urllib.parse import urlsplit, parse_qsl, urlencode, unquote\n\nfrom .abc import AbstractRouter, AbstractMatchInfo\nfrom .helpers import reify\nfrom .log import web_logger\nfrom .multidict import (CaseInsensitiveMultiDict,\n                        CaseInsensitiveMutableMultiDict,\n                        MultiDict,\n                        MutableMultiDict)\nfrom .protocol import Response as ResponseImpl, HttpVersion, HttpVersion11\nfrom .server import ServerHttpProtocol\nfrom .streams import EOF_MARKER\n\n\n__all__ = [\n    'Application',\n    'HttpVersion',\n    'RequestHandler',\n    'RequestHandlerFactory',\n    'Request',\n    'StreamResponse',\n    'Response',\n    'UrlDispatcher',\n    'UrlMappingMatchInfo',\n    'HTTPException',\n    'HTTPError',\n    'HTTPRedirection',\n    'HTTPSuccessful',\n    'HTTPOk',\n    'HTTPCreated',\n    'HTTPAccepted',\n    'HTTPNonAuthoritativeInformation',\n    'HTTPNoContent',\n    'HTTPResetContent',\n    'HTTPPartialContent',\n    'HTTPMultipleChoices',\n    'HTTPMovedPermanently',\n    'HTTPFound',\n    'HTTPSeeOther',\n    'HTTPNotModified',\n    'HTTPUseProxy',\n    'HTTPTemporaryRedirect',\n    'HTTPClientError',\n    'HTTPBadRequest',\n    'HTTPUnauthorized',\n    'HTTPPaymentRequired',\n    'HTTPForbidden',\n    'HTTPNotFound',\n    'HTTPMethodNotAllowed',\n    'HTTPNotAcceptable',\n    'HTTPProxyAuthenticationRequired',\n    'HTTPRequestTimeout',\n    'HTTPConflict',\n    'HTTPGone',\n    'HTTPLengthRequired',\n    'HTTPPreconditionFailed',\n    'HTTPRequestEntityTooLarge',\n    'HTTPRequestURITooLong',\n    'HTTPUnsupportedMediaType',\n    'HTTPRequestRangeNotSatisfiable',\n    'HTTPExpectationFailed',\n    'HTTPServerError',\n    'HTTPInternalServerError',\n    'HTTPNotImplemented',\n    'HTTPBadGateway',\n    'HTTPServiceUnavailable',\n    'HTTPGatewayTimeout',\n    'HTTPVersionNotSupported',\n]\n\n\nsentinel = object()\n\n\nclass HeadersMixin:\n\n    _content_type = None\n    _content_dict = None\n    _stored_content_type = sentinel\n\n    def _parse_content_type(self, raw):\n        self._stored_content_type = raw\n        if raw is None:\n            # default value according to RFC 2616\n            self._content_type = 'application/octet-stream'\n            self._content_dict = {}\n        else:\n            self._content_type, self._content_dict = cgi.parse_header(raw)\n\n    @property\n    def content_type(self):\n        \"\"\"The value of content part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get('Content-Type')\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_type\n\n    @property\n    def charset(self):\n        \"\"\"The value of charset part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get('Content-Type')\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_dict.get('charset')\n\n    @property\n    def content_length(self):\n        \"\"\"The value of Content-Length HTTP header.\"\"\"\n        l = self.headers.get('Content-Length')\n        if l is None:\n            return None\n        else:\n            return int(l)\n\n\nFileField = collections.namedtuple('Field', 'name filename file content_type')\n\n\n############################################################\n# HTTP Request\n############################################################\n\n\nclass Request(HeadersMixin):\n\n    def __init__(self, app, message, payload, transport, writer,\n                 keep_alive_timeout):\n        self._app = app\n        self._version = message.version\n        self._transport = transport\n        self._writer = writer\n        self._method = message.method\n        self._host = message.headers.get('HOST')\n        self._path_qs = message.path\n        res = urlsplit(message.path)\n        self._path = unquote(res.path)\n        self._query_string = res.query\n        self._post = None\n        self._post_files_cache = None\n        self._headers = CaseInsensitiveMultiDict._from_uppercase_multidict(\n            message.headers)\n\n        if self._version < HttpVersion11:\n            self._keep_alive = False\n        elif message.should_close:\n            self._keep_alive = False\n        else:\n            self._keep_alive = bool(keep_alive_timeout)\n\n        # matchdict, route_name, handler\n        # or information about traversal lookup\n        self._match_info = None  # initialized after route resolving\n\n        self._payload = payload\n        self._cookies = None\n\n    @property\n    def method(self):\n        \"\"\"Read only property for getting HTTP method.\n\n        The value is upper-cased str like 'GET', 'POST', 'PUT' etc.\n        \"\"\"\n        return self._method\n\n    @property\n    def version(self):\n        \"\"\"Read only property for getting HTTP version of request.\n\n        Returns aiohttp.protocol.HttpVersion instance.\n        \"\"\"\n        return self._version\n\n    @property\n    def host(self):\n        \"\"\"Read only property for getting *HOST* header of request.\n\n        Returns str or None if HTTP request has no HOST header.\n        \"\"\"\n        return self._host\n\n    @property\n    def path_qs(self):\n        \"\"\"The URL including PATH_INFO and the query string.\n\n        E.g, /app/blog?id=10\n        \"\"\"\n        return self._path_qs\n\n    @property\n    def path(self):\n        \"\"\"The URL including *PATH INFO* without the host or scheme.\n\n        E.g., ``/app/blog``\n        \"\"\"\n        return self._path\n\n    @property\n    def query_string(self):\n        \"\"\"The query string in the URL.\n\n        E.g., id=10\n        \"\"\"\n        return self._query_string\n\n    @reify\n    def GET(self):\n        \"\"\"A multidict with all the variables in the query string.\n\n        Lazy property.\n        \"\"\"\n        return MultiDict(parse_qsl(self._query_string))\n\n    @reify\n    def POST(self):\n        \"\"\"A multidict with all the variables in the POST parameters.\n\n        post() methods has to be called before using this attribute.\n        \"\"\"\n        if self._post is None:\n            raise RuntimeError(\"POST is not available before post()\")\n        return self._post\n\n    @property\n    def headers(self):\n        \"\"\"A case-insensitive multidict with all headers.\n\n        Lazy property.\n        \"\"\"\n        return self._headers\n\n    @property\n    def keep_alive(self):\n        \"\"\"Is keepalive enabled by client?\"\"\"\n        return self._keep_alive\n\n    @property\n    def match_info(self):\n        \"\"\"Result of route resolving.\"\"\"\n        return self._match_info\n\n    @property\n    def app(self):\n        \"\"\"Application instance.\"\"\"\n        return self._app\n\n    @property\n    def transport(self):\n        \"\"\"Transport used for request processing.\"\"\"\n        return self._transport\n\n    @property\n    def cookies(self):\n        \"\"\"Return request cookies.\n\n        A read-only dictionary-like object.\n        \"\"\"\n        if self._cookies is None:\n            raw = self.headers.get('COOKIE', '')\n            parsed = http.cookies.SimpleCookie(raw)\n            self._cookies = MultiDict({key: val.value\n                                       for key, val in parsed.items()})\n        return self._cookies\n\n    @property\n    def payload(self):\n        \"\"\"Return raw paiload stream.\"\"\"\n        return self._payload\n\n    @asyncio.coroutine\n    def release(self):\n        \"\"\"Release request.\n\n        Eat unread part of HTTP BODY if present.\n        \"\"\"\n        chunk = yield from self._payload.readany()\n        while chunk is not EOF_MARKER or chunk:\n            chunk = yield from self._payload.readany()\n\n    @asyncio.coroutine\n    def read(self):\n        \"\"\"Read request body if present.\n\n        Returns bytes object with full request content.\n        \"\"\"\n        body = bytearray()\n        while True:\n            chunk = yield from self._payload.readany()\n            body.extend(chunk)\n            if chunk is EOF_MARKER:\n                break\n        return bytes(body)\n\n    @asyncio.coroutine\n    def text(self):\n        \"\"\"Return BODY as text using encoding from .charset.\"\"\"\n        bytes_body = yield from self.read()\n        encoding = self.charset or 'utf-8'\n        return bytes_body.decode(encoding)\n\n    @asyncio.coroutine\n    def json(self, *, loader=json.loads):\n        \"\"\"Return BODY as JSON.\"\"\"\n        body = yield from self.text()\n        return loader(body)\n\n    @asyncio.coroutine\n    def post(self):\n        \"\"\"Return POST parameters.\"\"\"\n        if self._post is not None:\n            return self._post\n        if self.method not in ('POST', 'PUT', 'PATCH'):\n            self._post = MultiDict()\n            return self._post\n\n        content_type = self.content_type\n        if (content_type not in ('',\n                                 'application/x-www-form-urlencoded',\n                                 'multipart/form-data')):\n            self._post = MultiDict()\n            return self._post\n\n        body = yield from self.read()\n        content_charset = self.charset or 'utf-8'\n\n        environ = {'REQUEST_METHOD': self.method,\n                   'CONTENT_LENGTH': str(len(body)),\n                   'QUERY_STRING': '',\n                   'CONTENT_TYPE': self.headers.get('CONTENT-TYPE')}\n\n        fs = cgi.FieldStorage(fp=io.BytesIO(body),\n                              environ=environ,\n                              keep_blank_values=True,\n                              encoding=content_charset)\n\n        supported_tranfer_encoding = {\n            'base64': binascii.a2b_base64,\n            'quoted-printable': binascii.a2b_qp\n        }\n\n        out = MutableMultiDict()\n        for field in fs.list or ():\n            transfer_encoding = field.headers.get(\n                'Content-Transfer-Encoding', None)\n            if field.filename:\n                ff = FileField(field.name,\n                               field.filename,\n                               field.file,  # N.B. file closed error\n                               field.type)\n                if self._post_files_cache is None:\n                    self._post_files_cache = {}\n                self._post_files_cache[field.name] = field\n                out.add(field.name, ff)\n            else:\n                value = field.value\n                if transfer_encoding in supported_tranfer_encoding:\n                    # binascii accepts bytes\n                    value = value.encode('utf-8')\n                    value = supported_tranfer_encoding[\n                        transfer_encoding](value)\n                out.add(field.name, value)\n\n        self._post = MultiDict(out.items(getall=True))\n        return self._post\n\n    # @asyncio.coroutine\n    # def start_websocket(self):\n    #     \"\"\"Upgrade connection to websocket.\n\n    #     Returns (reader, writer) pair.\n    #     \"\"\"\n\n    #     upgrade = 'websocket' in message.headers.get('UPGRADE', '').lower()\n    #     if not upgrade:\n    #         pass\n\n\n############################################################\n# HTTP Response classes\n############################################################\n\n\nclass StreamResponse(HeadersMixin):\n\n    def __init__(self, *, status=200, reason=None):\n        self._body = None\n        self._keep_alive = None\n        self._headers = CaseInsensitiveMutableMultiDict()\n        self._cookies = http.cookies.SimpleCookie()\n        self.set_status(status, reason)\n\n        self._req = None\n        self._resp_impl = None\n        self._eof_sent = False\n\n    def _copy_cookies(self):\n        for cookie in self._cookies.values():\n            value = cookie.output(header='')[1:]\n            self.headers.add('Set-Cookie', value)\n\n    @property\n    def status(self):\n        return self._status\n\n    @property\n    def reason(self):\n        return self._reason\n\n    def set_status(self, status, reason=None):\n        self._status = int(status)\n        if reason is None:\n            reason = ResponseImpl.calc_reason(status)\n        self._reason = reason\n\n    @property\n    def keep_alive(self):\n        return self._keep_alive\n\n    def force_close(self):\n        self._keep_alive = False\n\n    @property\n    def headers(self):\n        return self._headers\n\n    @property\n    def cookies(self):\n        return self._cookies\n\n    def set_cookie(self, name, value, *, expires=None,\n                   domain=None, max_age=None, path=None,\n                   secure=None, httponly=None, version=None):\n        \"\"\"Set or update response cookie.\n\n        Sets new cookie or updates existent with new value.\n        Also updates only those params which are not None.\n        \"\"\"\n\n        old = self._cookies.get(name)\n        if old is not None and old.coded_value == '':\n            # deleted cookie\n            self._cookies.pop(name, None)\n\n        self._cookies[name] = value\n        c = self._cookies[name]\n        if expires is not None:\n            c['expires'] = expires\n        if domain is not None:\n            c['domain'] = domain\n        if max_age is not None:\n            c['max-age'] = max_age\n        if path is not None:\n            c['path'] = path\n        if secure is not None:\n            c['secure'] = secure\n        if httponly is not None:\n            c['httponly'] = httponly\n        if version is not None:\n            c['version'] = version\n\n    def del_cookie(self, name, *, domain=None, path=None):\n        \"\"\"Delete cookie.\n\n        Creates new empty expired cookie.\n        \"\"\"\n        # TODO: do we need domain/path here?\n        self._cookies.pop(name, None)\n        self.set_cookie(name, '', max_age=0, domain=domain, path=path)\n\n    @property\n    def content_length(self):\n        # Just a placeholder for adding setter\n        return super().content_length\n\n    @content_length.setter\n    def content_length(self, value):\n        if value is not None:\n            value = int(value)\n            # TODO: raise error if chunked enabled\n            self.headers['Content-Length'] = str(value)\n        elif 'Content-Length' in self.headers:\n            del self.headers['Content-Length']\n\n    @property\n    def content_type(self):\n        # Just a placeholder for adding setter\n        return super().content_type\n\n    @content_type.setter\n    def content_type(self, value):\n        self.content_type  # read header values if needed\n        self._content_type = str(value)\n        self._generate_content_type_header()\n\n    @property\n    def charset(self):\n        # Just a placeholder for adding setter\n        return super().charset\n\n    @charset.setter\n    def charset(self, value):\n        ctype = self.content_type  # read header values if needed\n        if ctype == 'application/octet-stream':\n            raise RuntimeError(\"Setting charset for application/octet-stream \"\n                               \"doesn't make sense, setup content_type first\")\n        if value is None:\n            self._content_dict.pop('charset', None)\n        else:\n            self._content_dict['charset'] = str(value).lower()\n        self._generate_content_type_header()\n\n    def _generate_content_type_header(self):\n        params = '; '.join(\"%s=%s\" % i for i in self._content_dict.items())\n        if params:\n            ctype = self._content_type + '; ' + params\n        else:\n            ctype = self._content_type\n        self.headers['Content-Type'] = ctype\n\n    def start(self, request):\n        if self._resp_impl is not None:\n            if self._req is not request:\n                raise RuntimeError(\n                    'Response has been started with different request.')\n            return self._resp_impl\n\n        self._req = request\n\n        keep_alive = self._keep_alive\n        if keep_alive is None:\n            keep_alive = request.keep_alive\n        self._keep_alive = keep_alive\n\n        resp_impl = self._resp_impl = ResponseImpl(\n            request._writer,\n            self._status,\n            request.version,\n            not keep_alive,\n            self._reason)\n\n        self._copy_cookies()\n\n        headers = self.headers.items(getall=True)\n        for key, val in headers:\n            resp_impl.add_header(key, val)\n\n        resp_impl.send_headers()\n        return resp_impl\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)',\n                            type(data))\n\n        if self._eof_sent:\n            raise RuntimeError(\"Cannot call write() after write_eof()\")\n        if self._resp_impl is None:\n            raise RuntimeError(\"Cannot call write() before start()\")\n\n        if data:\n            return self._resp_impl.write(data)\n        else:\n            return ()\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self._resp_impl.write_eof()\n        self._eof_sent = True\n\n\nclass Response(StreamResponse):\n\n    def __init__(self, *, body=None, status=200,\n                 reason=None, headers=None,\n                 text=None, content_type=None):\n        super().__init__(status=status, reason=reason)\n\n        if headers is not None:\n            self.headers.extend(headers)\n        if content_type:\n            self.content_type = content_type\n\n        if body is not None and text is not None:\n            raise ValueError(\"body and text are not allowed together.\")\n        elif body is not None:\n            self.body = body\n        elif text is not None:\n            self.text = text\n        else:\n            self.body = None\n\n    @property\n    def body(self):\n        return self._body\n\n    @body.setter\n    def body(self, body):\n        if body is not None and not isinstance(body, bytes):\n            raise TypeError('body argument must be bytes (%r)', type(body))\n        self._body = body\n        if body is not None:\n            self.content_length = len(body)\n        else:\n            self.content_length = 0\n\n    @property\n    def text(self):\n        return self._body.decode(self.charset or 'utf-8')\n\n    @text.setter\n    def text(self, text):\n        if text is not None and not isinstance(text, str):\n            raise TypeError('text argument must be str (%r)', type(text))\n\n        if self.content_type == 'application/octet-stream':\n            self.content_type = 'text/plain'\n        if self.charset is None:\n            self.charset = 'utf-8'\n\n        self.body = text.encode(self.charset)\n\n    @asyncio.coroutine\n    def write_eof(self):\n        body = self._body\n        if body is not None:\n            self.write(body)\n        yield from super().write_eof()\n\n\n############################################################\n# HTTP Exceptions\n############################################################\n\nclass HTTPException(Response, Exception):\n\n    # You should set in subclasses:\n    # status = 200\n\n    status_code = None\n\n    def __init__(self, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        Response.__init__(self, status=self.status_code,\n                          headers=headers, reason=reason,\n                          body=body, text=text, content_type=content_type)\n        Exception.__init__(self, self.reason)\n        if self.body is None:\n            self.text = \"{}: {}\".format(self.status, self.reason)\n\n\nclass HTTPError(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 400s and 500s.\"\"\"\n\n\nclass HTTPRedirection(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 300s.\"\"\"\n\n\nclass HTTPSuccessful(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 200s.\"\"\"\n\n\nclass HTTPOk(HTTPSuccessful):\n    status_code = 200\n\n\nclass HTTPCreated(HTTPSuccessful):\n    status_code = 201\n\n\nclass HTTPAccepted(HTTPSuccessful):\n    status_code = 202\n\n\nclass HTTPNonAuthoritativeInformation(HTTPSuccessful):\n    status_code = 203\n\n\nclass HTTPNoContent(HTTPSuccessful):\n    status_code = 204\n\n\nclass HTTPResetContent(HTTPSuccessful):\n    status_code = 205\n\n\nclass HTTPPartialContent(HTTPSuccessful):\n    status_code = 206\n\n\n############################################################\n# 3xx redirection\n############################################################\n\n\nclass _HTTPMove(HTTPRedirection):\n\n    def __init__(self, location, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        if not location:\n            raise ValueError(\"HTTP redirects need a location to redirect to.\")\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Location'] = location\n        self.location = location\n\n\nclass HTTPMultipleChoices(_HTTPMove):\n    status_code = 300\n\n\nclass HTTPMovedPermanently(_HTTPMove):\n    status_code = 301\n\n\nclass HTTPFound(_HTTPMove):\n    status_code = 302\n\n\n# This one is safe after a POST (the redirected location will be\n# retrieved with GET):\nclass HTTPSeeOther(_HTTPMove):\n    status_code = 303\n\n\nclass HTTPNotModified(HTTPRedirection):\n    # FIXME: this should include a date or etag header\n    status_code = 304\n\n\nclass HTTPUseProxy(_HTTPMove):\n    # Not a move, but looks a little like one\n    status_code = 305\n\n\nclass HTTPTemporaryRedirect(_HTTPMove):\n    status_code = 307\n\n\n############################################################\n# 4xx client error\n############################################################\n\n\nclass HTTPClientError(HTTPError):\n    pass\n\n\nclass HTTPBadRequest(HTTPClientError):\n    status_code = 400\n\n\nclass HTTPUnauthorized(HTTPClientError):\n    status_code = 401\n\n\nclass HTTPPaymentRequired(HTTPClientError):\n    status_code = 402\n\n\nclass HTTPForbidden(HTTPClientError):\n    status_code = 403\n\n\nclass HTTPNotFound(HTTPClientError):\n    status_code = 404\n\n\nclass HTTPMethodNotAllowed(HTTPClientError):\n    status_code = 405\n\n    def __init__(self, method, allowed_methods, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        allow = ','.join(sorted(allowed_methods))\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Allow'] = allow\n        self.allowed_methods = allowed_methods\n        self.method = method.upper()\n\n\nclass HTTPNotAcceptable(HTTPClientError):\n    status_code = 406\n\n\nclass HTTPProxyAuthenticationRequired(HTTPClientError):\n    status_code = 407\n\n\nclass HTTPRequestTimeout(HTTPClientError):\n    status_code = 408\n\n\nclass HTTPConflict(HTTPClientError):\n    status_code = 409\n\n\nclass HTTPGone(HTTPClientError):\n    status_code = 410\n\n\nclass HTTPLengthRequired(HTTPClientError):\n    status_code = 411\n\n\nclass HTTPPreconditionFailed(HTTPClientError):\n    status_code = 412\n\n\nclass HTTPRequestEntityTooLarge(HTTPClientError):\n    status_code = 413\n\n\nclass HTTPRequestURITooLong(HTTPClientError):\n    status_code = 414\n\n\nclass HTTPUnsupportedMediaType(HTTPClientError):\n    status_code = 415\n\n\nclass HTTPRequestRangeNotSatisfiable(HTTPClientError):\n    status_code = 416\n\n\nclass HTTPExpectationFailed(HTTPClientError):\n    status_code = 417\n\n\n############################################################\n# 5xx Server Error\n############################################################\n#  Response status codes beginning with the digit \"5\" indicate cases in\n#  which the server is aware that it has erred or is incapable of\n#  performing the request. Except when responding to a HEAD request, the\n#  server SHOULD include an entity containing an explanation of the error\n#  situation, and whether it is a temporary or permanent condition. User\n#  agents SHOULD display any included entity to the user. These response\n#  codes are applicable to any request method.\n\n\nclass HTTPServerError(HTTPError):\n    pass\n\n\nclass HTTPInternalServerError(HTTPServerError):\n    status_code = 500\n\n\nclass HTTPNotImplemented(HTTPServerError):\n    status_code = 501\n\n\nclass HTTPBadGateway(HTTPServerError):\n    status_code = 502\n\n\nclass HTTPServiceUnavailable(HTTPServerError):\n    status_code = 503\n\n\nclass HTTPGatewayTimeout(HTTPServerError):\n    status_code = 504\n\n\nclass HTTPVersionNotSupported(HTTPServerError):\n    status_code = 505\n\n\n############################################################\n# UrlDispatcher implementation\n############################################################\n\n\nclass UrlMappingMatchInfo(dict, AbstractMatchInfo):\n\n    def __init__(self, match_dict, route):\n        super().__init__(match_dict)\n        self._route = route\n\n    @property\n    def handler(self):\n        return self._route.handler\n\n    @property\n    def route(self):\n        return self._route\n\n\nclass Route(metaclass=abc.ABCMeta):\n\n    def __init__(self, method, handler, name):\n        self._method = method\n        self._handler = handler\n        self._name = name\n\n    @property\n    def method(self):\n        return self._method\n\n    @property\n    def handler(self):\n        return self._handler\n\n    @property\n    def name(self):\n        return self._name\n\n    @abc.abstractmethod\n    def match(self, path):\n        \"\"\"Return dict with info for given path or\n        None if route cannot process path.\"\"\"\n\n    @abc.abstractmethod\n    def url(self, **kwargs):\n        \"\"\"Construct url for route with additional params.\"\"\"\n\n    @staticmethod\n    def _append_query(url, query):\n        if query is not None:\n            return url + \"?\" + urlencode(query)\n        else:\n            return url\n\n\nclass PlainRoute(Route):\n\n    def __init__(self, method, handler, name, path):\n        super().__init__(method, handler, name)\n        self._path = path\n\n    def match(self, path):\n        # string comparsion about 10 times faster than regexp matching\n        if self._path == path:\n            return {}\n        else:\n            return None\n\n    def url(self, *, query=None):\n        return self._append_query(self._path, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<PlainRoute {name}[{method}] {path} -> {handler!r}\".format(\n            name=name, method=self.method, path=self._path,\n            handler=self.handler)\n\n\nclass DynamicRoute(Route):\n\n    def __init__(self, method, handler, name, pattern, formatter):\n        super().__init__(method, handler, name)\n        self._pattern = pattern\n        self._formatter = formatter\n\n    def match(self, path):\n        match = self._pattern.match(path)\n        if match is None:\n            return None\n        else:\n            return match.groupdict()\n\n    def url(self, *, parts, query=None):\n        url = self._formatter.format_map(parts)\n        return self._append_query(url, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return (\"<DynamicRoute {name}[{method}] {formatter} -> {handler!r}\"\n                .format(name=name, method=self.method,\n                        formatter=self._formatter, handler=self.handler))\n\n\nclass StaticRoute(Route):\n\n    def __init__(self, name, prefix, directory):\n        assert prefix.startswith('/'), prefix\n        assert prefix.endswith('/'), prefix\n        super().__init__('GET', self.handle, name)\n        self._prefix = prefix\n        self._prefix_len = len(self._prefix)\n        self._directory = directory\n\n    def match(self, path):\n        if not path.startswith(self._prefix):\n            return None\n        return {'filename': path[self._prefix_len:]}\n\n    def url(self, *, filename, query=None):\n        while filename.startswith('/'):\n            filename = filename[1:]\n        url = self._prefix + filename\n        return self._append_query(url, query)\n\n    @asyncio.coroutine\n    def handle(self, request):\n        resp = StreamResponse()\n        filename = request.match_info['filename']\n        filepath = os.path.join(self._directory, filename)\n        if '..' in filename:\n            raise HTTPNotFound()\n        if not os.path.exists(filepath) or not os.path.isfile(filepath):\n            raise HTTPNotFound()\n\n        ct = mimetypes.guess_type(filename)[0]\n        if not ct:\n            ct = 'application/octet-stream'\n        resp.content_type = ct\n\n        resp.headers['transfer-encoding'] = 'chunked'\n        resp.start(request)\n\n        with open(filepath, 'rb') as f:\n            chunk = f.read(8192)\n            while chunk:\n                resp.write(chunk)\n                chunk = f.read(8192)\n\n        yield from resp.write_eof()\n        return resp\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<StaticRoute {name}[{method}] {path} -> {directory!r}\".format(\n            name=name, method=self.method, path=self._prefix,\n            directory=self._directory)\n\n\nclass UrlDispatcher(AbstractRouter, collections.abc.Mapping):\n\n    DYN = re.compile(r'^\\{[a-zA-Z][_a-zA-Z0-9]*\\}$')\n    GOOD = r'[^{}/]+'\n    PLAIN = re.compile('^' + GOOD + '$')\n\n    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}\n\n    def __init__(self):\n        super().__init__()\n        self._urls = []\n        self._routes = {}\n\n    @asyncio.coroutine\n    def resolve(self, request):\n        path = request.path\n        method = request.method\n        allowed_methods = set()\n        for route in self._urls:\n            match_dict = route.match(path)\n            if match_dict is None:\n                continue\n            route_method = route.method\n            if route_method != method:\n                allowed_methods.add(route_method)\n            else:\n                return UrlMappingMatchInfo(match_dict, route)\n        else:\n            if allowed_methods:\n                raise HTTPMethodNotAllowed(method, allowed_methods)\n            else:\n                raise HTTPNotFound()\n\n    def __iter__(self):\n        return iter(self._routes)\n\n    def __len__(self):\n        return len(self._routes)\n\n    def __contains__(self, name):\n        return name in self._routes\n\n    def __getitem__(self, name):\n        return self._routes[name]\n\n    def _register_endpoint(self, route):\n        name = route.name\n        if name is not None:\n            if name in self._routes:\n                raise ValueError('Duplicate {!r}, '\n                                 'already handled by {!r}'\n                                 .format(name, self._routes[name]))\n            else:\n                self._routes[name] = route\n        self._urls.append(route)\n\n    def add_route(self, method, path, handler, *, name=None):\n        assert path.startswith('/')\n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n        parts = []\n        factory = PlainRoute\n        for part in path.split('/'):\n            if not part:\n                continue\n            if self.DYN.match(part):\n                parts.append('(?P<' + part[1:-1] + '>' + self.GOOD + ')')\n                factory = DynamicRoute\n            elif self.PLAIN.match(part):\n                parts.append(re.escape(part))\n            else:\n                raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\n        if factory is PlainRoute:\n            route = PlainRoute(method, handler, name, path)\n        else:\n            pattern = '/' + '/'.join(parts)\n            if path.endswith('/') and pattern != '/':\n                pattern += '/'\n            compiled = re.compile('^' + pattern + '$')\n            route = DynamicRoute(method, handler, name, compiled, path)\n        self._register_endpoint(route)\n        return route\n\n    def add_static(self, prefix, path, *, name=None):\n        \"\"\"\n        Adds static files view\n        :param prefix - url prefix\n        :param path - folder with files\n        \"\"\"\n        assert prefix.startswith('/')\n        assert os.path.isdir(path), 'Path does not directory %s' % path\n        path = os.path.abspath(path)\n        if not prefix.endswith('/'):\n            prefix += '/'\n        route = StaticRoute(name, prefix, path)\n        self._register_endpoint(route)\n        return route\n\n\n############################################################\n# Application implementation\n############################################################\n\n\nclass RequestHandler(ServerHttpProtocol):\n\n    def __init__(self, manager, app, router, **kwargs):\n        super().__init__(**kwargs)\n\n        self._manager = manager\n        self._app = app\n        self._router = router\n\n    def connection_made(self, transport):\n        super().connection_made(transport)\n\n        self._manager.connection_made(self, transport)\n\n    def connection_lost(self, exc):\n        self._manager.connection_lost(self, exc)\n\n        super().connection_lost(exc)\n\n    @asyncio.coroutine\n    def handle_request(self, message, payload):\n        now = self._loop.time()\n\n        request = Request(self._app, message, payload,\n                          self.transport, self.writer, self.keep_alive_timeout)\n        try:\n            match_info = yield from self._router.resolve(request)\n\n            assert isinstance(match_info, AbstractMatchInfo), match_info\n\n            request._match_info = match_info\n            handler = match_info.handler\n\n            resp = yield from handler(request)\n            if not isinstance(resp, StreamResponse):\n                raise RuntimeError(\n                    (\"Handler should return response instance, got {!r}\")\n                    .format(type(resp)))\n        except HTTPException as exc:\n            resp = exc\n\n        resp_msg = resp.start(request)\n        yield from resp.write_eof()\n\n        # notify server about keep-alive\n        self.keep_alive(resp_msg.keep_alive())\n\n        # log access\n        self.log_access(message, None, resp_msg, self._loop.time() - now)\n\n\nclass RequestHandlerFactory:\n\n    def __init__(self, app, router, *,\n                 handler=RequestHandler, loop=None, **kwargs):\n        self._app = app\n        self._router = router\n        self._handler = handler\n        self._loop = loop\n        self._connections = {}\n        self._kwargs = kwargs\n        self._kwargs.setdefault('logger', app.logger)\n\n    @property\n    def connections(self):\n        return list(self._connections.keys())\n\n    def connection_made(self, handler, transport):\n        self._connections[handler] = transport\n\n    def connection_lost(self, handler, exc=None):\n        if handler in self._connections:\n            del self._connections[handler]\n\n    @asyncio.coroutine\n    def finish_connections(self, timeout=None):\n        for handler in self._connections.keys():\n            handler.closing()\n\n        def cleanup():\n            sleep = 0.05\n            while self._connections:\n                yield from asyncio.sleep(sleep, loop=self._loop)\n                if sleep < 5:\n                    sleep = sleep * 2\n\n        if timeout:\n            try:\n                yield from asyncio.wait_for(\n                    cleanup(), timeout, loop=self._loop)\n            except asyncio.TimeoutError:\n                self._app.logger.warning(\n                    \"Not all connections are closed (pending: %d)\",\n                    len(self._connections))\n\n        for transport in self._connections.values():\n            transport.close()\n\n        self._connections.clear()\n\n    def __call__(self):\n        return self._handler(\n            self, self._app, self._router, loop=self._loop, **self._kwargs)\n\n\nclass Application(dict):\n\n    def __init__(self, *, logger=web_logger, loop=None,\n                 router=None, handler_factory=RequestHandlerFactory, **kwargs):\n        # TODO: explicitly accept *debug* param\n        if loop is None:\n            loop = asyncio.get_event_loop()\n        if router is None:\n            router = UrlDispatcher()\n        assert isinstance(router, AbstractRouter), router\n\n        self._router = router\n        self._handler_factory = handler_factory\n        self._finish_callbacks = []\n        self._loop = loop\n        self.logger = logger\n\n        self.update(**kwargs)\n\n    @property\n    def router(self):\n        return self._router\n\n    @property\n    def loop(self):\n        return self._loop\n\n    def make_handler(self, **kwargs):\n        return self._handler_factory(\n            self, self.router, loop=self.loop, **kwargs)\n\n    @asyncio.coroutine\n    def finish(self):\n        callbacks = self._finish_callbacks\n        self._finish_callbacks = []\n\n        for (cb, args, kwargs) in callbacks:\n            try:\n                res = cb(self, *args, **kwargs)\n                if (asyncio.iscoroutine(res) or\n                        isinstance(res, asyncio.Future)):\n                    yield from res\n            except Exception as exc:\n                self._loop.call_exception_handler({\n                    'message': \"Error in finish callback\",\n                    'exception': exc,\n                    'application': self,\n                })\n\n    def register_on_finish(self, func, *args, **kwargs):\n        self._finish_callbacks.insert(0, (func, args, kwargs))\n",
            "header": {
                "file": {
                    "path": "aiohttp/web.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "e9ed7785",
                "new_commit_hash": "d9920173",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/aiohttp/web.py"
            },
            "plus_file": {
                "path": "b/aiohttp/web.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 517,
                            "length": 7
                        },
                        "new_range": {
                            "start": 517,
                            "length": 7
                        },
                        "section": "class StreamResponse(HeadersMixin):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        if value is None:",
                                "type": "context"
                            },
                            {
                                "content": "            self._content_dict.pop('charset', None)",
                                "type": "context"
                            },
                            {
                                "content": "        else:",
                                "type": "context"
                            },
                            {
                                "content": "            self._content_dict['charset'] = str(value)",
                                "type": "deleted"
                            },
                            {
                                "content": "            self._content_dict['charset'] = str(value).lower()",
                                "type": "added"
                            },
                            {
                                "content": "        self._generate_content_type_header()",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def _generate_content_type_header(self):",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "aiohttp/web.py",
                            "type": "class",
                            "name": "StreamResponse",
                            "content": "class StreamResponse(HeadersMixin):\n\n    def __init__(self, *, status=200, reason=None):\n        self._body = None\n        self._keep_alive = None\n        self._headers = CaseInsensitiveMutableMultiDict()\n        self._cookies = http.cookies.SimpleCookie()\n        self.set_status(status, reason)\n\n        self._req = None\n        self._resp_impl = None\n        self._eof_sent = False\n\n    def _copy_cookies(self):\n        for cookie in self._cookies.values():\n            value = cookie.output(header='')[1:]\n            self.headers.add('Set-Cookie', value)\n\n    @property\n    def status(self):\n        return self._status\n\n    @property\n    def reason(self):\n        return self._reason\n\n    def set_status(self, status, reason=None):\n        self._status = int(status)\n        if reason is None:\n            reason = ResponseImpl.calc_reason(status)\n        self._reason = reason\n\n    @property\n    def keep_alive(self):\n        return self._keep_alive\n\n    def force_close(self):\n        self._keep_alive = False\n\n    @property\n    def headers(self):\n        return self._headers\n\n    @property\n    def cookies(self):\n        return self._cookies\n\n    def set_cookie(self, name, value, *, expires=None,\n                   domain=None, max_age=None, path=None,\n                   secure=None, httponly=None, version=None):\n        \"\"\"Set or update response cookie.\n\n        Sets new cookie or updates existent with new value.\n        Also updates only those params which are not None.\n        \"\"\"\n\n        old = self._cookies.get(name)\n        if old is not None and old.coded_value == '':\n            # deleted cookie\n            self._cookies.pop(name, None)\n\n        self._cookies[name] = value\n        c = self._cookies[name]\n        if expires is not None:\n            c['expires'] = expires\n        if domain is not None:\n            c['domain'] = domain\n        if max_age is not None:\n            c['max-age'] = max_age\n        if path is not None:\n            c['path'] = path\n        if secure is not None:\n            c['secure'] = secure\n        if httponly is not None:\n            c['httponly'] = httponly\n        if version is not None:\n            c['version'] = version\n\n    def del_cookie(self, name, *, domain=None, path=None):\n        \"\"\"Delete cookie.\n\n        Creates new empty expired cookie.\n        \"\"\"\n        # TODO: do we need domain/path here?\n        self._cookies.pop(name, None)\n        self.set_cookie(name, '', max_age=0, domain=domain, path=path)\n\n    @property\n    def content_length(self):\n        # Just a placeholder for adding setter\n        return super().content_length\n\n    @content_length.setter\n    def content_length(self, value):\n        if value is not None:\n            value = int(value)\n            # TODO: raise error if chunked enabled\n            self.headers['Content-Length'] = str(value)\n        elif 'Content-Length' in self.headers:\n            del self.headers['Content-Length']\n\n    @property\n    def content_type(self):\n        # Just a placeholder for adding setter\n        return super().content_type\n\n    @content_type.setter\n    def content_type(self, value):\n        self.content_type  # read header values if needed\n        self._content_type = str(value)\n        self._generate_content_type_header()\n\n    @property\n    def charset(self):\n        # Just a placeholder for adding setter\n        return super().charset\n\n    @charset.setter\n    def charset(self, value):\n        ctype = self.content_type  # read header values if needed\n        if ctype == 'application/octet-stream':\n            raise RuntimeError(\"Setting charset for application/octet-stream \"\n                               \"doesn't make sense, setup content_type first\")\n        if value is None:\n            self._content_dict.pop('charset', None)\n        else:\n            self._content_dict['charset'] = str(value)\n        self._generate_content_type_header()\n\n    def _generate_content_type_header(self):\n        params = '; '.join(\"%s=%s\" % i for i in self._content_dict.items())\n        if params:\n            ctype = self._content_type + '; ' + params\n        else:\n            ctype = self._content_type\n        self.headers['Content-Type'] = ctype\n\n    def start(self, request):\n        if self._resp_impl is not None:\n            if self._req is not request:\n                raise RuntimeError(\n                    'Response has been started with different request.')\n            return self._resp_impl\n\n        self._req = request\n\n        keep_alive = self._keep_alive\n        if keep_alive is None:\n            keep_alive = request.keep_alive\n        self._keep_alive = keep_alive\n\n        resp_impl = self._resp_impl = ResponseImpl(\n            request._writer,\n            self._status,\n            request.version,\n            not keep_alive,\n            self._reason)\n\n        self._copy_cookies()\n\n        headers = self.headers.items(getall=True)\n        for key, val in headers:\n            resp_impl.add_header(key, val)\n\n        resp_impl.send_headers()\n        return resp_impl\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)',\n                            type(data))\n\n        if self._eof_sent:\n            raise RuntimeError(\"Cannot call write() after write_eof()\")\n        if self._resp_impl is None:\n            raise RuntimeError(\"Cannot call write() before start()\")\n\n        if data:\n            return self._resp_impl.write(data)\n        else:\n            return ()\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self._resp_impl.write_eof()\n        self._eof_sent = True",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 394,
                            "end_lineno": 584,
                            "parent": null
                        },
                        {
                            "file_name": "aiohttp/web.py",
                            "type": "method",
                            "name": "StreamResponse.charset",
                            "content": "class StreamResponse(HeadersMixin):\n\n    def __init__(self, *, status=200, reason=None):\n        self._body = None\n        self._keep_alive = None\n        self._headers = CaseInsensitiveMutableMultiDict()\n        self._cookies = http.cookies.SimpleCookie()\n        self.set_status(status, reason)\n\n        self._req = None\n        self._resp_impl = None\n        self._eof_sent = False\n\n    def _copy_cookies(self):\n        for cookie in self._cookies.values():\n            value = cookie.output(header='')[1:]\n            self.headers.add('Set-Cookie', value)\n\n    @property\n    def status(self):\n        return self._status\n\n    @property\n    def reason(self):\n        return self._reason\n\n    def set_status(self, status, reason=None):\n        self._status = int(status)\n        if reason is None:\n            reason = ResponseImpl.calc_reason(status)\n        self._reason = reason\n\n    @property\n    def keep_alive(self):\n        return self._keep_alive\n\n    def force_close(self):\n        self._keep_alive = False\n\n    @property\n    def headers(self):\n        return self._headers\n\n    @property\n    def cookies(self):\n        return self._cookies\n\n    def set_cookie(self, name, value, *, expires=None,\n                   domain=None, max_age=None, path=None,\n                   secure=None, httponly=None, version=None):\n        \"\"\"Set or update response cookie.\n\n        Sets new cookie or updates existent with new value.\n        Also updates only those params which are not None.\n        \"\"\"\n\n        old = self._cookies.get(name)\n        if old is not None and old.coded_value == '':\n            # deleted cookie\n            self._cookies.pop(name, None)\n\n        self._cookies[name] = value\n        c = self._cookies[name]\n        if expires is not None:\n            c['expires'] = expires\n        if domain is not None:\n            c['domain'] = domain\n        if max_age is not None:\n            c['max-age'] = max_age\n        if path is not None:\n            c['path'] = path\n        if secure is not None:\n            c['secure'] = secure\n        if httponly is not None:\n            c['httponly'] = httponly\n        if version is not None:\n            c['version'] = version\n\n    def del_cookie(self, name, *, domain=None, path=None):\n        \"\"\"Delete cookie.\n\n        Creates new empty expired cookie.\n        \"\"\"\n        # TODO: do we need domain/path here?\n        self._cookies.pop(name, None)\n        self.set_cookie(name, '', max_age=0, domain=domain, path=path)\n\n    @property\n    def content_length(self):\n        # Just a placeholder for adding setter\n        return super().content_length\n\n    @content_length.setter\n    def content_length(self, value):\n        if value is not None:\n            value = int(value)\n            # TODO: raise error if chunked enabled\n            self.headers['Content-Length'] = str(value)\n        elif 'Content-Length' in self.headers:\n            del self.headers['Content-Length']\n\n    @property\n    def content_type(self):\n        # Just a placeholder for adding setter\n        return super().content_type\n\n    @content_type.setter\n    def content_type(self, value):\n        self.content_type  # read header values if needed\n        self._content_type = str(value)\n        self._generate_content_type_header()\n\n    @property\n    def charset(self):\n        # Just a placeholder for adding setter\n        return super().charset\n\n    @charset.setter\n    def charset(self, value):\n        ctype = self.content_type  # read header values if needed\n        if ctype == 'application/octet-stream':\n            raise RuntimeError(\"Setting charset for application/octet-stream \"\n                               \"doesn't make sense, setup content_type first\")\n        if value is None:\n            self._content_dict.pop('charset', None)\n        else:\n            self._content_dict['charset'] = str(value)\n        self._generate_content_type_header()\n\n    def _generate_content_type_header(self):\n        params = '; '.join(\"%s=%s\" % i for i in self._content_dict.items())\n        if params:\n            ctype = self._content_type + '; ' + params\n        else:\n            ctype = self._content_type\n        self.headers['Content-Type'] = ctype\n\n    def start(self, request):\n        if self._resp_impl is not None:\n            if self._req is not request:\n                raise RuntimeError(\n                    'Response has been started with different request.')\n            return self._resp_impl\n\n        self._req = request\n\n        keep_alive = self._keep_alive\n        if keep_alive is None:\n            keep_alive = request.keep_alive\n        self._keep_alive = keep_alive\n\n        resp_impl = self._resp_impl = ResponseImpl(\n            request._writer,\n            self._status,\n            request.version,\n            not keep_alive,\n            self._reason)\n\n        self._copy_cookies()\n\n        headers = self.headers.items(getall=True)\n        for key, val in headers:\n            resp_impl.add_header(key, val)\n\n        resp_impl.send_headers()\n        return resp_impl\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)',\n                            type(data))\n\n        if self._eof_sent:\n            raise RuntimeError(\"Cannot call write() after write_eof()\")\n        if self._resp_impl is None:\n            raise RuntimeError(\"Cannot call write() before start()\")\n\n        if data:\n            return self._resp_impl.write(data)\n        else:\n            return ()\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self._resp_impl.write_eof()\n        self._eof_sent = True",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 512,
                            "end_lineno": 521,
                            "parent": {
                                "file_name": "aiohttp/web.py",
                                "type": "class",
                                "name": "StreamResponse",
                                "content": "class StreamResponse(HeadersMixin):\n\n    def __init__(self, *, status=200, reason=None):\n        self._body = None\n        self._keep_alive = None\n        self._headers = CaseInsensitiveMutableMultiDict()\n        self._cookies = http.cookies.SimpleCookie()\n        self.set_status(status, reason)\n\n        self._req = None\n        self._resp_impl = None\n        self._eof_sent = False\n\n    def _copy_cookies(self):\n        for cookie in self._cookies.values():\n            value = cookie.output(header='')[1:]\n            self.headers.add('Set-Cookie', value)\n\n    @property\n    def status(self):\n        return self._status\n\n    @property\n    def reason(self):\n        return self._reason\n\n    def set_status(self, status, reason=None):\n        self._status = int(status)\n        if reason is None:\n            reason = ResponseImpl.calc_reason(status)\n        self._reason = reason\n\n    @property\n    def keep_alive(self):\n        return self._keep_alive\n\n    def force_close(self):\n        self._keep_alive = False\n\n    @property\n    def headers(self):\n        return self._headers\n\n    @property\n    def cookies(self):\n        return self._cookies\n\n    def set_cookie(self, name, value, *, expires=None,\n                   domain=None, max_age=None, path=None,\n                   secure=None, httponly=None, version=None):\n        \"\"\"Set or update response cookie.\n\n        Sets new cookie or updates existent with new value.\n        Also updates only those params which are not None.\n        \"\"\"\n\n        old = self._cookies.get(name)\n        if old is not None and old.coded_value == '':\n            # deleted cookie\n            self._cookies.pop(name, None)\n\n        self._cookies[name] = value\n        c = self._cookies[name]\n        if expires is not None:\n            c['expires'] = expires\n        if domain is not None:\n            c['domain'] = domain\n        if max_age is not None:\n            c['max-age'] = max_age\n        if path is not None:\n            c['path'] = path\n        if secure is not None:\n            c['secure'] = secure\n        if httponly is not None:\n            c['httponly'] = httponly\n        if version is not None:\n            c['version'] = version\n\n    def del_cookie(self, name, *, domain=None, path=None):\n        \"\"\"Delete cookie.\n\n        Creates new empty expired cookie.\n        \"\"\"\n        # TODO: do we need domain/path here?\n        self._cookies.pop(name, None)\n        self.set_cookie(name, '', max_age=0, domain=domain, path=path)\n\n    @property\n    def content_length(self):\n        # Just a placeholder for adding setter\n        return super().content_length\n\n    @content_length.setter\n    def content_length(self, value):\n        if value is not None:\n            value = int(value)\n            # TODO: raise error if chunked enabled\n            self.headers['Content-Length'] = str(value)\n        elif 'Content-Length' in self.headers:\n            del self.headers['Content-Length']\n\n    @property\n    def content_type(self):\n        # Just a placeholder for adding setter\n        return super().content_type\n\n    @content_type.setter\n    def content_type(self, value):\n        self.content_type  # read header values if needed\n        self._content_type = str(value)\n        self._generate_content_type_header()\n\n    @property\n    def charset(self):\n        # Just a placeholder for adding setter\n        return super().charset\n\n    @charset.setter\n    def charset(self, value):\n        ctype = self.content_type  # read header values if needed\n        if ctype == 'application/octet-stream':\n            raise RuntimeError(\"Setting charset for application/octet-stream \"\n                               \"doesn't make sense, setup content_type first\")\n        if value is None:\n            self._content_dict.pop('charset', None)\n        else:\n            self._content_dict['charset'] = str(value)\n        self._generate_content_type_header()\n\n    def _generate_content_type_header(self):\n        params = '; '.join(\"%s=%s\" % i for i in self._content_dict.items())\n        if params:\n            ctype = self._content_type + '; ' + params\n        else:\n            ctype = self._content_type\n        self.headers['Content-Type'] = ctype\n\n    def start(self, request):\n        if self._resp_impl is not None:\n            if self._req is not request:\n                raise RuntimeError(\n                    'Response has been started with different request.')\n            return self._resp_impl\n\n        self._req = request\n\n        keep_alive = self._keep_alive\n        if keep_alive is None:\n            keep_alive = request.keep_alive\n        self._keep_alive = keep_alive\n\n        resp_impl = self._resp_impl = ResponseImpl(\n            request._writer,\n            self._status,\n            request.version,\n            not keep_alive,\n            self._reason)\n\n        self._copy_cookies()\n\n        headers = self.headers.items(getall=True)\n        for key, val in headers:\n            resp_impl.add_header(key, val)\n\n        resp_impl.send_headers()\n        return resp_impl\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)',\n                            type(data))\n\n        if self._eof_sent:\n            raise RuntimeError(\"Cannot call write() after write_eof()\")\n        if self._resp_impl is None:\n            raise RuntimeError(\"Cannot call write() before start()\")\n\n        if data:\n            return self._resp_impl.write(data)\n        else:\n            return ()\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self._resp_impl.write_eof()\n        self._eof_sent = True",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 394,
                                "end_lineno": 584,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": ".. _aiohttp-web:\n\n.. highlight:: python\n\nHigh-level HTTP Server\n======================\n\n.. module:: aiohttp.web\n\n.. versionadded:: 0.10\n\nRun a simple web server\n-----------------------\n\nFor implementing a web server, first create a :ref:`request\nhandler<aiohttp-web-handler>`.\n\nHandler is a :ref:`coroutine<coroutine>` or a regular function that\naccepts only *request* parameter of type :class:`Request`\nand returns :class:`Response` instance::\n\n   import asyncio\n   from aiohttp import web\n\n   @asyncio.coroutine\n   def hello(request):\n       return web.Response(body=b\"Hello, world\")\n\nNext, you have to create a :class:`Application` instance and register\n:ref:`handler<aiohttp-web-handler>` in the application's router pointing *HTTP\nmethod*, *path* and *handler*::\n\n   app = web.Application()\n   app.router.add_route('GET', '/', hello)\n\nAfter that, create a server and run the *asyncio loop* as usual::\n\n   loop = asyncio.get_event_loop()\n   f = loop.create_server(app.make_handler(), '0.0.0.0', 8080)\n   srv = loop.run_until_complete(f)\n   print('serving on', srv.sockets[0].getsockname())\n   try:\n       loop.run_forever()\n   except KeyboardInterrupt:\n       pass\n\nThat's it.\n\n.. _aiohttp-web-handler:\n\nHandler\n-------\n\nHandler is an any *callable* that accepts a single :class:`Request`\nargument and returns a :class:`StreamResponse` derived\n(e.g. :class:`Response`) instance.\n\nHandler **can** be a :ref:`coroutine<coroutine>`, :mod:`aiohttp.web` will\n**unyield** returned result by applying ``yield from`` to the handler.\n\nHandlers are connected to the :class:`Application` via routes::\n\n   handler = Handler()\n   app.router.add_route('GET', '/', handler)\n\n.. _aiohttp-web-variable-handler:\n\nYou can also use *variable routes*. If route contains string like\n``'/a/{name}/c'`` that means the route matches to the path like\n``'/a/b/c'`` or ``'/a/1/c'``.\n\nParsed *path part* will be available in the *request handler* as\n``request.match_info['name']``::\n\n   @asyncio.coroutine\n   def variable_handler(request):\n       return web.Response(\n           text=\"Hello, {}\".format(request.match_info['name']))\n\n   app.router.add_route('GET', '/{name}', variable_handler)\n\n\nHandlers can be first-class functions, e.g.::\n\n   @asyncio.coroutine\n   def hello(request):\n       return web.Response(body=b\"Hello, world\")\n\n   app.router.add_route('GET', '/', hello)\n\nSometimes you would like to group logically coupled handlers into a\npython class.\n\n:mod:`aiohttp.web` doesn't dictate any implementation details,\nso application developer can use classes if he wants::\n\n   class Handler:\n\n       def __init__(self):\n           pass\n\n       def handle_intro(self, request):\n           return web.Response(body=b\"Hello, world\")\n\n       @asyncio.coroutine\n       def handle_greeting(self, request):\n           name = request.match_info.get('name', \"Anonymous\")\n           txt = \"Hello, {}\".format(name)\n           return web.Response(text=txt)\n\n   handler = Handler()\n   app.router.add_route('GET', '/intro', handler.handle_intro)\n   app.router.add_route('GET', '/greet/{name}', handler.handle_greeting)\n\n\n.. _aiohttp-web-file-upload:\n\nFile Uploads\n------------\n\nThere are two steps necessary for handling file uploads. The first is\nto make sure that you have a form that has been setup correctly to accept\nfiles. This means adding *enctype* attribute to your form element with\nthe value of *multipart/form-data*. A very simple example would be a\nform that accepts a mp3 file. Notice, we have set up the form as\npreviously explained and also added the *input* element of the *file*\ntype::\n\n   <form action=\"/store_mp3\" method=\"post\" accept-charset=\"utf-8\"\n         enctype=\"multipart/form-data\">\n\n       <label for=\"mp3\">Mp3</label>\n       <input id=\"mp3\" name=\"mp3\" type=\"file\" value=\"\" />\n\n       <input type=\"submit\" value=\"submit\" />\n   </form>\n\nThe second step is handling the file upload in your :ref:`request\nhandler<aiohttp-web-handler>` (here assumed to answer on\n*/store_mp3*). The uploaded file is added to the request object as a\n:class:`FileField` object accessible through the :meth:`Request.post`\ncoroutine. The two properties we are interested in are\n:attr:`~FileField.file` and :attr:`~FileField.filename` and we will\nuse those to read a file's name and a content::\n\n    import os\n    import uuid\n    from aiohttp.web import Response\n\n    def store_mp3_view(request):\n\n        data = yield from request.post()\n\n        # ``filename`` contains the name of the file in string format.\n        filename = data['mp3'].filename\n\n        # ``input_file`` contains the actual file data which needs to be\n        # stored somewhere.\n\n        input_file = data['mp3'].file\n\n        content = input_file.read()\n\n        return Response(body=content,\n                        headers=MultiDict({'CONTENT-DISPOSITION': input_file})\n\n\n.. _aiohttp-web-request:\n\n\nRequest\n-------\n\nThe Request object contains all the information about an incoming HTTP request.\n\nEvery :ref:`handler<aiohttp-web-handler>` accepts a request instance as the\nfirst positional parameter.\n\n.. note::\n\n   You should never create the :class:`Request` instance manually --\n   :mod:`aiohttp.web` does it for you.\n\n.. class:: Request\n\n   .. attribute:: method\n\n      *HTTP method*, Read-only property.\n\n      The value is upper-cased :class:`str` like ``\"GET\"``,\n      ``\"POST\"``, ``\"PUT\"`` etc.\n\n   .. attribute:: version\n\n      *HTTP version* of request, Read-only property.\n\n      Returns :class:`aiohttp.protocol.HttpVersion` instance.\n\n   .. attribute:: host\n\n      *HOST* header of request, Read-only property.\n\n      Returns :class:`str` or ``None`` if HTTP request has no *HOST* header.\n\n   .. attribute:: path_qs\n\n      The URL including PATH_INFO and the query string. e.g, ``/app/blog?id=10``\n\n      Read-only :class:`str` property.\n\n   .. attribute:: path\n\n      The URL including *PATH INFO* without the host or scheme. e.g.,\n      ``/app/blog``\n\n      Read-only :class:`str` property.\n\n   .. attribute:: query_string\n\n      The query string in the URL, e.g., ``id=10``\n\n      Read-only :class:`str` property.\n\n   .. attribute:: GET\n\n      A multidict with all the variables in the query string.\n\n      Read-only :class:`~aiohttp.multidict.MultiDict` lazy property.\n\n   .. attribute:: POST\n\n      A multidict with all the variables in the POST parameters.\n      POST property available only after :meth:`Request.post` coroutine call.\n\n      Read-only :class:`~aiohttp.multidict.MultiDict`.\n\n      :raises RuntimeError: if :meth:`Request.post` was not called \\\n                            before accessing the property.\n\n   .. attribute:: headers\n\n      A case-insensitive multidict with all headers.\n\n      Read-only :class:`~aiohttp.multidict.CaseInsensitiveMultiDict`\n      lazy property.\n\n   .. attribute:: keep_alive\n\n      ``True`` if keep-alive connection enabled by HTTP client and\n      protocol version supports it, otherwise ``False``.\n\n      Read-only :class:`bool` property.\n\n   .. attribute:: match_info\n\n      Read-only property with :class:`~aiohttp.abc.AbstractMatchInfo`\n      instance for result of route resolving.\n\n      .. note::\n\n         Exact type of property depends on used router.  If\n         ``app.router`` is :class:`UrlDispatcher` the property contains\n         :class:`UrlMappingMatchInfo` instance.\n\n   .. attribute:: app\n\n      An :class:`Application` instance used to call :ref:`request handler\n      <aiohttp-web-handler>`, Read-only property.\n\n   .. attribute:: transport\n\n      An :ref:`transport<asyncio-transport>` used to process request,\n      Read-only property.\n\n      The property can be used, for example, for getting IP address of\n      client's peer::\n\n         peername = request.transport.get_extra('peername')\n         if peername is not None:\n             host, port = peername\n\n   .. attribute:: cookies\n\n      A multidict of all request's cookies.\n\n      Read-only :class:`~aiohttp.multidict.MultiDict` lazy property.\n\n   .. attribute:: payload\n\n      A :class:`~aiohttp.streams.FlowControlStreamReader` instance,\n      input stream for reading request's *BODY*.\n\n      Read-only property.\n\n   .. attribute:: content_type\n\n      Read-only property with *content* part of *Content-Type* header.\n\n      Returns :class:`str` like ``'text/html'``\n\n      .. note::\n\n         Returns value is ``'application/octet-stream'`` if no\n         Content-Type header present in HTTP headers according to\n         :rfc:`2616`\n\n   .. attribute:: charset\n\n      Read-only property that specifies the *encoding* for the request's BODY.\n\n      The value is parsed from the *Content-Type* HTTP header.\n\n      Returns :class:`str` like ``'utf-8'`` or ``None`` if\n      *Content-Type* has no charset information.\n\n   .. attribute:: content_length\n\n      Read-only property that returns length of the request's BODY.\n\n      The value is parsed from the *Content-Length* HTTP header.\n\n      Returns :class:`int` or ``None`` if *Content-Length* is absent.\n\n   .. method:: read()\n\n      Read request body, returns :class:`bytes` object with body content.\n\n      The method is a :ref:`coroutine <coroutine>`.\n\n      .. warning::\n\n         The method doesn't store read data internally, subsequent\n         :meth:`~Request.read` call will return empty bytes ``b''``.\n\n   .. method:: text()\n\n      Read request body, decode it using :attr:`charset` encoding or\n      ``UTF-8`` if no encoding was specified in *MIME-type*.\n\n      Returns :class:`str` with body content.\n\n      The method is a :ref:`coroutine <coroutine>`.\n\n      .. warning::\n\n         The method doesn't store read data internally, subsequent\n         :meth:`~Request.text` call will return empty string ``''``.\n\n   .. method:: json(*, loader=json.loads)\n\n      Read request body decoded as *json*.\n\n      The method is just a boilerplate :ref:`coroutine <coroutine>`\n      implemented as::\n\n         @asyncio.coroutine\n         def json(self, *, loader=json.loads):\n             body = yield from self.text()\n             return loader(body)\n\n      :param callable loader: any callable that accepts :class:`str`\n                              and returns :class:`dict` with parsed\n                              JSON (:func:`json.loads` by default).\n\n      .. warning::\n\n         The method doesn't store read data internally, subsequent\n         :meth:`~Request.json` call will raise an exception.\n\n   .. method:: post()\n\n      A :ref:`coroutine <coroutine>` that reads POST parameters from\n      request body.\n\n      Returns :class:`~aiohttp.multidict.MultiDict` instance filled\n      with parsed data.\n\n      If :attr:`method` is not *POST*, *PUT* or *PATCH* or\n      :attr:`content_type` is not empty or\n      *application/x-www-form-urlencoded* or *multipart/form-data*\n      returns empty multidict.\n\n      .. warning::\n\n         The method **does** store read data internally, subsequent\n         :meth:`~Request.post` call will return the same value.\n\n   .. method:: release()\n\n      Release request.\n\n      Eat unread part of HTTP BODY if present.\n\n      The method is a :ref:`coroutine <coroutine>`.\n\n      .. note::\n\n          User code may never call :meth:`~Request.release`, all\n          required work will be processed by :mod:`aiohttp.web`\n          internal machinery.\n\n\n.. _aiohttp-web-response:\n\n\nResponse classes\n-----------------\n\nFor now, :mod:`aiohttp.web` has two classes for the *HTTP response*:\n:class:`StreamResponse` and :class:`Response`.\n\nUsually you need to use the second one. :class:`StreamResponse` is\nintended for streaming data, while :class:`Response` contains *HTTP\nBODY* as an attribute and sends own content as single piece with the\ncorrect *Content-Length HTTP header*.\n\nFor sake of design decisions :class:`Response` is derived from\n:class:`StreamResponse` parent class.\n\nThe response supports *keep-alive* handling out-of-the-box if\n*request* supports it.\n\nYou can disable *keep-alive* by :meth:`~StreamResponse.force_close` though.\n\nThe common case for sending an answer from\n:ref:`web-handler<aiohttp-web-handler>` is returning a\n:class:`Response` instance::\n\n   def handler(request):\n       return Response(\"All right!\")\n\n\nStreamResponse\n^^^^^^^^^^^^^^\n\n.. class:: StreamResponse(*, status=200, reason=None)\n\n   The base class for the *HTTP response* handling.\n\n   Contains methods for setting *HTTP response headers*, *cookies*,\n   *response status code*, writing *HTTP response BODY* and so on.\n\n   The most important thing you should know about *response* --- it\n   is *Finite State Machine*.\n\n   That means you can do any manipulations with *headers*,\n   *cookies* and *status code* only before :meth:`start`\n   called.\n\n   Once you call :meth:`start` any change of\n   the *HTTP header* part will raise :exc:`RuntimeError` exception.\n\n   Any :meth:`write` call after :meth:`write_eof` is also forbidden.\n\n   :param int status: HTTP status code, ``200`` by default.\n\n   :param str reason: HTTP reason. If param is ``None`` reason will be\n                      calculated basing on *status*\n                      parameter. Otherwise pass :class:`str` with\n                      arbitrary *status* explanation..\n\n   .. attribute:: status\n\n      Read-only property for *HTTP response status code*, :class:`int`.\n\n      ``200`` (OK) by default.\n\n   .. attribute:: reason\n\n      Read-only property for *HTTP response reason*, :class:`str`.\n\n   .. method:: set_status(status, reason=None)\n\n      Set :attr:`status` and :attr:`reason`.\n\n      *reason* value is auto calculated if not specified (``None``).\n\n   .. attribute:: keep_alive\n\n      Read-only property, copy of :attr:`Request.keep_alive` by default.\n\n      Can be switched to ``False`` by :meth:`force_close` call.\n\n   .. method:: force_close\n\n      Disable :attr:`keep_alive` for connection. There are no ways to\n      enable it back.\n\n   .. attribute:: headers\n\n      :class:`~aiohttp.multidict.CaseInsensitiveMultiDict` instance\n      for *outgoing* *HTTP headers*.\n\n   .. attribute:: cookies\n\n      An instance of :class:`http.cookies.SimpleCookie` for *outgoing* cookies.\n\n      .. warning::\n\n         Direct setting up *Set-Cookie* header may be overwritten by\n         explicit calls to cookie manipulation.\n\n         We are encourage using of :attr:`cookies` and\n         :meth:`set_cookie`, :meth:`del_cookie` for cookie\n         manipulations.\n\n   .. method:: set_cookie(name, value, *, expires=None, \\\n                   domain=None, max_age=None, path=None, \\\n                   secure=None, httponly=None, version=None)\n\n      Convenient way for setting :attr:`cookies`, allows to specify\n      some additional properties like *max_age* in a single call.\n\n      :param str name: cookie name\n\n      :param str value: cookie value (will be converted to\n                        :class:`str` if value has another type).\n\n      :param expries: expiration date (optional)\n\n      :param str domain: cookie domain (optional)\n\n      :param int max_age: defines the lifetime of the cookie, in\n                          seconds.  The delta-seconds value is a\n                          decimal non- negative integer.  After\n                          delta-seconds seconds elapse, the client\n                          should discard the cookie.  A value of zero\n                          means the cookie should be discarded\n                          immediately.  (optional)\n\n      :param str path: specifies the subset of URLs to\n                       which this cookie applies. (optional)\n\n      :param bool secure: attribute (with no value) directs\n                          the user agent to use only (unspecified)\n                          secure means to contact the origin server\n                          whenever it sends back this cookie.\n                          The user agent (possibly under the user's\n                          control) may determine what level of\n                          security it considers appropriate for\n                          \"secure\" cookies.  The *secure* should be\n                          considered security advice from the server\n                          to the user agent, indicating that it is in\n                          the session's interest to protect the cookie\n                          contents. (optional)\n\n      :param bool httponly: ``True`` if the cookie HTTP only (optional)\n\n      :param int version: a decimal integer, identifies to which\n                          version of the state management\n                          specification the cookie\n                          conforms. (Optional, *version=1* by default)\n\n   .. method:: del_cookie(name, *, domain=None, path=None)\n\n      Deletes cookie.\n\n      :param str name: cookie name\n\n      :param str domain: optional cookie domain\n\n      :param str path: optional cookie path\n\n   .. attribute:: content_length\n\n      *Content-Length* for outgoing response.\n\n   .. attribute:: content_type\n\n      *Content* part of *Content-Type* for outgoing response.\n\n   .. attribute:: charset\n\n      *Charset* aka *encoding* part of *Content-Type* for outgoing response.\n\n   .. method:: start(request)\n\n      :param aiohttp.web.Request request: HTTP request object, that the\n                                          response answers.\n\n      Send *HTTP header*. You should not change any header data after\n      calling this method.\n\n   .. method:: write(data)\n\n      Send byte-ish data as the part of *response BODY*.\n\n      :meth:`start` must be called before.\n\n      Raises :exc:`TypeError` if data is not :class:`bytes`,\n      :class:`bytearray` or :class:`memoryview` instance.\n\n      Raises :exc:`RuntimeError` if :meth:`start` has not been called.\n\n      Raises :exc:`RuntimeError` if :meth:`write_eof` has been called.\n\n   .. method:: write_eof()\n\n      A :ref:`coroutine<coroutine>` *may* be called as a mark of the\n      *HTTP response* processing finish.\n\n      *Internal machinery* will call this method at the end of\n      the request processing if needed.\n\n      After :meth:`write_eof` call any manipulations with the *response*\n      object are forbidden.\n\n\nResponse\n^^^^^^^^\n\n.. class:: Response(*, status=200, headers=None, content_type=None, \\\n                    body=None, text=None)\n\n   The most usable response class, inherited from :class:`StreamResponse`.\n\n   Accepts *body* argument for setting the *HTTP response BODY*.\n\n   The actual :attr:`body` sending happens in overridden\n   :meth:`~StreamResponse.write_eof`.\n\n   :param bytes body: response's BODY\n\n   :param int status: HTTP status code, 200 OK by default.\n\n   :param collections.abc.Mapping headers: HTTP headers that should be added to\n                           response's ones.\n\n   :param str text: response's BODY\n\n   :param str content_type: response's content type\n\n   .. attribute:: body\n\n      Read-write attribute for storing response's content aka BODY,\n      :class:`bytes`.\n\n      Setting :attr:`body` also recalculates\n      :attr:`~StreamResponse.content_length` value.\n\n      Resetting :attr:`body` (assigning ``None``) sets\n      :attr:`~StreamResponse.content_length` to ``None`` too, dropping\n      *Content-Length* HTTP header.\n\n   .. attribute:: text\n\n      Read-write attribute for storing response's content, represented as str,\n      :class:`str`.\n\n      Setting :attr:`str` also recalculates\n      :attr:`~StreamResponse.content_length` value and\n      :attr:`~StreamResponse.body` value\n\n      Resetting :attr:`body` (assigning ``None``) sets\n      :attr:`~StreamResponse.content_length` to ``None`` too, dropping\n      *Content-Length* HTTP header.\n\n\nApplication and Router\n----------------------\n\n\nApplication\n^^^^^^^^^^^\n\nApplication is a synonym for web-server.\n\nTo get fully working example, you have to make *application*, register\nsupported urls in *router* and create a *server socket* with\n:class:`aiohttp.RequestHandlerFactory` as a *protocol factory*. *RequestHandlerFactory*\ncould be constructed with :meth:`make_handler`.\n\n*Application* contains a *router* instance and a list of callbacks that\nwill be called during application finishing.\n\n*Application* is a :class:`dict`, so you can use it as registry for\narbitrary properties for later access from\n:ref:`handler<aiohttp-web-handler>` via :attr:`Request.app` property::\n\n   app = Application(loop=loop)\n   app['database'] = yield from aiopg.create_engine(**db_config)\n\n   @asyncio.coroutine\n   def handler(request):\n       with (yield from request.app['database']) as conn:\n           conn.execute(\"DELETE * FROM table\")\n\n\n.. class:: Application(*, loop=None, router=None, logger=<default>, **kwargs)\n\n   The class inherits :class:`dict`.\n\n   :param loop: :ref:`event loop<asyncio-event-loop>` used\n                for processing HTTP requests.\n\n                If param is ``None`` :func:`asyncio.get_event_loop`\n                used for getting default event loop, but we strongly\n                recommend to use explicit loops everywhere.\n\n   :param router: :class:`aiohttp.abc.AbstractRouter` instance, the system\n                  creates :class:`UrlDispatcher` by default if\n                  *router* is ``None``.\n\n   :param logger: :class:`logging.Logger` instance for storing application logs.\n\n                  By default the value is ``logging.getLogger(\"aiohttp.web\")``\n\n   :param kwargs: optional params for initializing self dict.\n\n   .. attribute:: router\n\n      Read-only property that returns *router instance*.\n\n   .. attribute:: logger\n\n      Read-only property that returns *router instance*.\n\n   .. attribute:: loop\n\n      :class:`logging.Logger` instance for storing application logs.\n\n   .. method:: make_handler(**kwargs)\n\n      Creates HTTP protocol factory for handling requests.\n\n      :param kwargs: additional parameters for :class:`RequestHandlerFactory`\n                     constructor.\n\n      You should pass result of the method as *protocol_factory* to\n      :meth:`~BaseEventLoop.create_server`, e.g.::\n\n         loop = asyncio.get_event_loop()\n\n         app = Application(loop=loop)\n\n         # setup route table\n         # app.router.add_route(...)\n\n         yield from loop.create_server(app.make_handler(), '0.0.0.0', 8080)\n\n   .. method:: finish()\n\n      A :ref:`coroutine<coroutine>` that should be called after\n      server stopping.\n\n      This method executes functions registered by\n      :meth:`register_on_finish` in LIFO order.\n\n      If callback raises an exception, the error will be stored by\n      :meth:`~asyncio.BaseEventLoop.call_exception_handler` with keys:\n      *message*, *exception*, *application*.\n\n   .. method:: register_on_finish(self, func, *args, **kwargs):\n\n      Register *func* as a function to be executed at termination.\n      Any optional arguments that are to be passed to *func* must be\n      passed as arguments to :meth:`register_on_finish`.  It is possible to\n      register the same function and arguments more than once.\n\n      During the call of :meth:`finish` all functions registered are called in\n      last in, first out order.\n\n      *func* may be either regular function or :ref:`coroutine<coroutine>`,\n      :meth:`finish` will un-yield (`yield from`) the later.\n\n\nRequestHandlerFactory\n^^^^^^^^^^^^^^^^^^^^^\n\nRequestHandlerFactory is responsible for creating HTTP protocol objects that\ncan handle http connections.\n\n   .. attribute:: connections\n\n      List of all currently oppened connections.\n\n   .. method:: finish_connections(timeout)\n\n      A :ref:`coroutine<coroutine>` that should be called to close all opened\n      connections.\n\n\nRouter\n^^^^^^\n\nFor dispatching URLs to :ref:`handlers<aiohttp-web-handler>`\n:mod:`aiohttp.web` uses *routers*.\n\nRouter is any object that implements :class:`AbstractRouter` interface.\n\n:mod:`aiohttp.web` provides an implementation called :class:`UrlDispatcher`.\n\n:class:`Application` uses :class:`UrlDispatcher` as :meth:`router` by default.\n\n.. class:: UrlDispatcher()\n\n   Straightforward url-mathing router, implements\n   :class:`collections.abc.Mapping` for access to *named routes*.\n\n   Before running :class:`Application` you should fill *route\n   table* first by calling :meth:`add_route` and :meth:`add_static`.\n\n   :ref:`Handler<aiohttp-web-handler>` lookup is performed by iterating on\n   added *routes* in FIFO order. The first matching *route* will be used\n   to call corresponding *handler*.\n\n   If on route creation you specify *name* parameter the result is\n   *named route*.\n\n   *Named route* can be retrieved by ``app.router[name]`` call, checked for\n   existence by ``name in app.router`` etc.\n\n   .. seealso:: :ref:`Route classes <aiohttp-web-route>`\n\n   .. method:: add_route(method, path, handler, *, name=None)\n\n      Append :ref:`handler<aiohttp-web-handler>` to the end of route table.\n\n      *path* may be either *constant* string like ``'/a/b/c'`` or\n       *variable rule* like ``'/a/{var}'`` (see\n       :ref:`handling variable pathes<aiohttp-web-variable-handler>`)\n\n      Pay attention please: *handler* is converted to coroutine internally when\n      it is a regular function.\n\n      :param str path: route path\n\n      :param callable handler: route handler\n\n      :param str name: optional route name.\n\n      :returns: new :class:`PlainRoute` or :class:`DynamicRoute` instance.\n\n   .. method:: add_static(prefix, path, *, name=None)\n\n      Adds router for returning static files.\n\n      Useful for handling static content like images, javascript and css files.\n\n      .. warning::\n\n         Use :meth:`add_static` for development only. In production,\n         static content should be processed by web servers like *nginx*\n         or *apache*.\n\n      :param str prefix: URL path prefix for handled static files\n\n      :param str path: path to the folder in file system that contains\n                       handled static files.\n\n      :param str name: optional route name.\n\n      :returns: new :class:`StaticRoute` instance.\n\n   .. method:: resolve(requst)\n\n      A :ref:`coroutine<coroutine>` that returns\n      :class:`AbstractMatchInfo` for *request* or raises http\n      exception like :exc:`HTTPNotFound` if there is no registered\n      route for *request*.\n\n      Used by internal machinery, end user unlikely need to call the method.\n\n.. _aiohttp-web-route:\n\nRoute\n^^^^^\n\n.. versionadded:: 0.11\n\nDefault router :class:`UrlDispatcher` operates with *routes*.\n\nUser should not instantiate route classes by hand but can give *named\nroute instance* by ``router[name]`` if he have added route by\n:meth:`UrlDispatcher.add_route` or :meth:`UrlDispatcher.add_static`\ncalls with non-empty *name* parameter.\n\nThe main usage of *named routes* is constructing URL by route name for\npassing it into *template engine* for example::\n\n   url = app.router['route_name'].url(query={'a': 1, 'b': 2})\n\nThere are three conctrete route classes:* :class:`DynamicRoute` for\nurls with :ref:`variable pathes<aiohttp-web-variable-handler>` spec.\n\n\n* :class:`PlainRoute` for urls without :ref:`variable\n  pathes<aiohttp-web-variable-handler>`\n\n* :class:`DynamicRoute` for urls with :ref:`variable\n  pathes<aiohttp-web-variable-handler>` spec.\n\n* :class:`StaticRoute` for static file handlers.\n\n.. class:: Route\n\n   Base class for routes served by :class:`UrlDispatcher`.\n\n   .. attribute:: method\n\n   HTTP method handled by the route, e.g. *GET*, *POST* etc.\n\n   .. attribute:: handler\n\n   :ref:`handler<aiohttp-web-handler>` that processes the route.\n\n   .. attribute:: name\n\n   Name of the route.\n\n   .. method:: match(path)\n\n   Abstract method, accepts *URL path* and returns :class:`dict` with\n   parsed *path parts* for :class:`UrlMappingMatchInfo` or ``None`` if\n   the route cannot handle given *path*.\n\n   The method exists for internal usage, end user unlikely need to call it.\n\n   .. method:: url(*, query=None, **kwargs)\n\n   Abstract method for constructing url handled by the route.\n\n   *query* is a mapping or list of *(name, value)* pairs for\n   specifying *query* part of url (parameter is processed by\n   :func:`~urllib.parse.urlencode`).\n\n   Other available parameters depends on concrete route class and\n   described in descendant classes.\n\n.. class:: PlainRoute\n\n   The route class for handling plain *URL path*, e.g. ``\"/a/b/c\"``\n\n   .. method:: url(*, parts, query=None)\n\n   Construct url, doesn't accepts extra parameters::\n\n      >>> route.url(query={'d': 1, 'e': 2})\n      '/a/b/c/?d=1&e=2'``\n\n.. class:: DynamicRoute\n\n   The route class for handling :ref:`variable\n   path<aiohttp-web-variable-handler>`, e.g. ``\"/a/{name1}/{name2}\"``\n\n   .. method:: url(*, parts, query=None)\n\n   Construct url with given *dynamic parts*::\n\n       >>> route.url(parts={'name1': 'b', 'name2': 'c'}, query={'d': 1, 'e': 2})\n       '/a/b/c/?d=1&e=2'\n\n\n.. class:: StaticRoute\n\n   The route class for handling static files, created by\n   :meth:`UrlDispatcher.add_static` call.\n\n   .. method:: url(*, filename, query=None)\n\n   Construct url for given *filename*::\n\n      >>> route.url(filename='img/logo.png', query={'param': 1})\n      '/path/to/static/img/logo.png?param=1'\n\nMatchInfo\n^^^^^^^^^\n\nAfter route matching web application calls found handler if any.\n\nMatching result can be accessible from handler as\n:attr:`Request.match_info` attribute.\n\nIn general the result may be any object derived from\n:class:`AbstractMatchInfo` (:class:`UrlMappingMatchInfo` for default\n:class:`UrlDispatcher` router).\n\n.. class:: UrlMappingMatchInfo\n\n   Inherited from :class:`dict` and :class:`AbstractMatchInfo`. Dict\n   items are given from :meth:`Route.match` call return value.\n\n   .. attribute:: route\n\n   :class:`Route` instance for url matching.\n\n\n\nUtilities\n---------\n\n.. class:: FileField\n\n   A :func:`~collections.namedtuple` that is returned as multidict value\n   by :meth:`Request.POST` if field is uploaded file.\n\n   .. attribute:: name\n\n      Field name\n\n   .. attribute:: filename\n\n      File name as specified by uploading (client) side.\n\n   .. attribute:: file\n\n      An :class:`io.IOBase` instance with content of uploaded file.\n\n   .. attribute:: content_type\n\n      *MIME type* of uploaded file, ``'text/plain'`` by default.\n\n   .. seealso:: :ref:`aiohttp-web-file-upload`\n\n\nExceptions\n-----------\n\n:mod:`aiohttp.web` defines exceptions for list of *HTTP status codes*.\n\nEach class relates to a single HTTP status code.  Each class is a\nsubclass of the :class:`~HTTPException`.\n\nThose exceptions are derived from :class:`Response` too, so you can\neither return exception object from :ref:`aiohttp-web-handler` or raise it.\n\nThe following snippets are equal::\n\n    @asyncio.coroutine\n    def handler(request):\n        return aiohttp.web.HTTPFound('/redirect')\n\nand::\n\n    @asyncio.coroutine\n    def handler(request):\n        raise aiohttp.web.HTTPFound('/redirect')\n\n\nEach exception class has a status code according to :rfc:`2068`:\ncodes with 100-300 are not really errors; 400s are client errors,\nand 500s are server errors.\n\nHttp Exception hierarchy chart::\n\n   Exception\n     HTTPException\n       HTTPSuccessful\n         * 200 - HTTPOk\n         * 201 - HTTPCreated\n         * 202 - HTTPAccepted\n         * 203 - HTTPNonAuthoritativeInformation\n         * 204 - HTTPNoContent\n         * 205 - HTTPResetContent\n         * 206 - HTTPPartialContent\n       HTTPRedirection\n         * 300 - HTTPMultipleChoices\n         * 301 - HTTPMovedPermanently\n         * 302 - HTTPFound\n         * 303 - HTTPSeeOther\n         * 304 - HTTPNotModified\n         * 305 - HTTPUseProxy\n         * 307 - HTTPTemporaryRedirect\n       HTTPError\n         HTTPClientError\n           * 400 - HTTPBadRequest\n           * 401 - HTTPUnauthorized\n           * 402 - HTTPPaymentRequired\n           * 403 - HTTPForbidden\n           * 404 - HTTPNotFound\n           * 405 - HTTPMethodNotAllowed\n           * 406 - HTTPNotAcceptable\n           * 407 - HTTPProxyAuthenticationRequired\n           * 408 - HTTPRequestTimeout\n           * 409 - HTTPConflict\n           * 410 - HTTPGone\n           * 411 - HTTPLengthRequired\n           * 412 - HTTPPreconditionFailed\n           * 413 - HTTPRequestEntityTooLarge\n           * 414 - HTTPRequestURITooLong\n           * 415 - HTTPUnsupportedMediaType\n           * 416 - HTTPRequestRangeNotSatisfiable\n           * 417 - HTTPExpectationFailed\n         HTTPServerError\n           * 500 - HTTPInternalServerError\n           * 501 - HTTPNotImplemented\n           * 502 - HTTPBadGateway\n           * 503 - HTTPServiceUnavailable\n           * 504 - HTTPGatewayTimeout\n           * 505 - HTTPVersionNotSupported\n\nAll http exceptions have the same constructor::\n\n    HTTPNotFound(*, headers=None, reason=None, \\\n                 body=None, text=None, content_type=None)\n\nif other not directly specified. *headers* will be added to *default\nresponse headers*.\n\nClasses :class:`HTTPMultipleChoices`, :class:`HTTPMovedPermanently`,\n:class:`HTTPFound`, :class:`HTTPSeeOther`, :class:`HTTPUseProxy`,\n:class:`HTTPTemporaryRedirect` has constructor signature like::\n\n    HTTPFound(location, *, headers=None, reason=None, \\\n              body=None, text=None, content_type=None)\n\nwhere *location* is value for *Location HTTP header*.\n\n:class:`HTTPMethodNotAllowed` constructed with pointing trial method\nand list of allowed methods::\n\n    HTTPMethodNotAllowed(method, allowed_methods, *,\n                         headers=None, reason=None, \\\n                         body=None, text=None, content_type=None)\n",
            "new_file_content": ".. _aiohttp-web:\n\n.. highlight:: python\n\nHigh-level HTTP Server\n======================\n\n.. module:: aiohttp.web\n\n.. versionadded:: 0.10\n\nRun a simple web server\n-----------------------\n\nFor implementing a web server, first create a :ref:`request\nhandler<aiohttp-web-handler>`.\n\nHandler is a :ref:`coroutine<coroutine>` or a regular function that\naccepts only *request* parameter of type :class:`Request`\nand returns :class:`Response` instance::\n\n   import asyncio\n   from aiohttp import web\n\n   @asyncio.coroutine\n   def hello(request):\n       return web.Response(body=b\"Hello, world\")\n\nNext, you have to create a :class:`Application` instance and register\n:ref:`handler<aiohttp-web-handler>` in the application's router pointing *HTTP\nmethod*, *path* and *handler*::\n\n   app = web.Application()\n   app.router.add_route('GET', '/', hello)\n\nAfter that, create a server and run the *asyncio loop* as usual::\n\n   loop = asyncio.get_event_loop()\n   f = loop.create_server(app.make_handler(), '0.0.0.0', 8080)\n   srv = loop.run_until_complete(f)\n   print('serving on', srv.sockets[0].getsockname())\n   try:\n       loop.run_forever()\n   except KeyboardInterrupt:\n       pass\n\nThat's it.\n\n.. _aiohttp-web-handler:\n\nHandler\n-------\n\nHandler is an any *callable* that accepts a single :class:`Request`\nargument and returns a :class:`StreamResponse` derived\n(e.g. :class:`Response`) instance.\n\nHandler **can** be a :ref:`coroutine<coroutine>`, :mod:`aiohttp.web` will\n**unyield** returned result by applying ``yield from`` to the handler.\n\nHandlers are connected to the :class:`Application` via routes::\n\n   handler = Handler()\n   app.router.add_route('GET', '/', handler)\n\n.. _aiohttp-web-variable-handler:\n\nYou can also use *variable routes*. If route contains string like\n``'/a/{name}/c'`` that means the route matches to the path like\n``'/a/b/c'`` or ``'/a/1/c'``.\n\nParsed *path part* will be available in the *request handler* as\n``request.match_info['name']``::\n\n   @asyncio.coroutine\n   def variable_handler(request):\n       return web.Response(\n           text=\"Hello, {}\".format(request.match_info['name']))\n\n   app.router.add_route('GET', '/{name}', variable_handler)\n\n\nHandlers can be first-class functions, e.g.::\n\n   @asyncio.coroutine\n   def hello(request):\n       return web.Response(body=b\"Hello, world\")\n\n   app.router.add_route('GET', '/', hello)\n\nSometimes you would like to group logically coupled handlers into a\npython class.\n\n:mod:`aiohttp.web` doesn't dictate any implementation details,\nso application developer can use classes if he wants::\n\n   class Handler:\n\n       def __init__(self):\n           pass\n\n       def handle_intro(self, request):\n           return web.Response(body=b\"Hello, world\")\n\n       @asyncio.coroutine\n       def handle_greeting(self, request):\n           name = request.match_info.get('name', \"Anonymous\")\n           txt = \"Hello, {}\".format(name)\n           return web.Response(text=txt)\n\n   handler = Handler()\n   app.router.add_route('GET', '/intro', handler.handle_intro)\n   app.router.add_route('GET', '/greet/{name}', handler.handle_greeting)\n\n\n.. _aiohttp-web-file-upload:\n\nFile Uploads\n------------\n\nThere are two steps necessary for handling file uploads. The first is\nto make sure that you have a form that has been setup correctly to accept\nfiles. This means adding *enctype* attribute to your form element with\nthe value of *multipart/form-data*. A very simple example would be a\nform that accepts a mp3 file. Notice, we have set up the form as\npreviously explained and also added the *input* element of the *file*\ntype::\n\n   <form action=\"/store_mp3\" method=\"post\" accept-charset=\"utf-8\"\n         enctype=\"multipart/form-data\">\n\n       <label for=\"mp3\">Mp3</label>\n       <input id=\"mp3\" name=\"mp3\" type=\"file\" value=\"\" />\n\n       <input type=\"submit\" value=\"submit\" />\n   </form>\n\nThe second step is handling the file upload in your :ref:`request\nhandler<aiohttp-web-handler>` (here assumed to answer on\n*/store_mp3*). The uploaded file is added to the request object as a\n:class:`FileField` object accessible through the :meth:`Request.post`\ncoroutine. The two properties we are interested in are\n:attr:`~FileField.file` and :attr:`~FileField.filename` and we will\nuse those to read a file's name and a content::\n\n    import os\n    import uuid\n    from aiohttp.web import Response\n\n    def store_mp3_view(request):\n\n        data = yield from request.post()\n\n        # ``filename`` contains the name of the file in string format.\n        filename = data['mp3'].filename\n\n        # ``input_file`` contains the actual file data which needs to be\n        # stored somewhere.\n\n        input_file = data['mp3'].file\n\n        content = input_file.read()\n\n        return Response(body=content,\n                        headers=MultiDict({'CONTENT-DISPOSITION': input_file})\n\n\n.. _aiohttp-web-request:\n\n\nRequest\n-------\n\nThe Request object contains all the information about an incoming HTTP request.\n\nEvery :ref:`handler<aiohttp-web-handler>` accepts a request instance as the\nfirst positional parameter.\n\n.. note::\n\n   You should never create the :class:`Request` instance manually --\n   :mod:`aiohttp.web` does it for you.\n\n.. class:: Request\n\n   .. attribute:: method\n\n      *HTTP method*, Read-only property.\n\n      The value is upper-cased :class:`str` like ``\"GET\"``,\n      ``\"POST\"``, ``\"PUT\"`` etc.\n\n   .. attribute:: version\n\n      *HTTP version* of request, Read-only property.\n\n      Returns :class:`aiohttp.protocol.HttpVersion` instance.\n\n   .. attribute:: host\n\n      *HOST* header of request, Read-only property.\n\n      Returns :class:`str` or ``None`` if HTTP request has no *HOST* header.\n\n   .. attribute:: path_qs\n\n      The URL including PATH_INFO and the query string. e.g, ``/app/blog?id=10``\n\n      Read-only :class:`str` property.\n\n   .. attribute:: path\n\n      The URL including *PATH INFO* without the host or scheme. e.g.,\n      ``/app/blog``\n\n      Read-only :class:`str` property.\n\n   .. attribute:: query_string\n\n      The query string in the URL, e.g., ``id=10``\n\n      Read-only :class:`str` property.\n\n   .. attribute:: GET\n\n      A multidict with all the variables in the query string.\n\n      Read-only :class:`~aiohttp.multidict.MultiDict` lazy property.\n\n   .. attribute:: POST\n\n      A multidict with all the variables in the POST parameters.\n      POST property available only after :meth:`Request.post` coroutine call.\n\n      Read-only :class:`~aiohttp.multidict.MultiDict`.\n\n      :raises RuntimeError: if :meth:`Request.post` was not called \\\n                            before accessing the property.\n\n   .. attribute:: headers\n\n      A case-insensitive multidict with all headers.\n\n      Read-only :class:`~aiohttp.multidict.CaseInsensitiveMultiDict`\n      lazy property.\n\n   .. attribute:: keep_alive\n\n      ``True`` if keep-alive connection enabled by HTTP client and\n      protocol version supports it, otherwise ``False``.\n\n      Read-only :class:`bool` property.\n\n   .. attribute:: match_info\n\n      Read-only property with :class:`~aiohttp.abc.AbstractMatchInfo`\n      instance for result of route resolving.\n\n      .. note::\n\n         Exact type of property depends on used router.  If\n         ``app.router`` is :class:`UrlDispatcher` the property contains\n         :class:`UrlMappingMatchInfo` instance.\n\n   .. attribute:: app\n\n      An :class:`Application` instance used to call :ref:`request handler\n      <aiohttp-web-handler>`, Read-only property.\n\n   .. attribute:: transport\n\n      An :ref:`transport<asyncio-transport>` used to process request,\n      Read-only property.\n\n      The property can be used, for example, for getting IP address of\n      client's peer::\n\n         peername = request.transport.get_extra('peername')\n         if peername is not None:\n             host, port = peername\n\n   .. attribute:: cookies\n\n      A multidict of all request's cookies.\n\n      Read-only :class:`~aiohttp.multidict.MultiDict` lazy property.\n\n   .. attribute:: payload\n\n      A :class:`~aiohttp.streams.FlowControlStreamReader` instance,\n      input stream for reading request's *BODY*.\n\n      Read-only property.\n\n   .. attribute:: content_type\n\n      Read-only property with *content* part of *Content-Type* header.\n\n      Returns :class:`str` like ``'text/html'``\n\n      .. note::\n\n         Returns value is ``'application/octet-stream'`` if no\n         Content-Type header present in HTTP headers according to\n         :rfc:`2616`\n\n   .. attribute:: charset\n\n      Read-only property that specifies the *encoding* for the request's BODY.\n\n      The value is parsed from the *Content-Type* HTTP header.\n\n      Returns :class:`str` like ``'utf-8'`` or ``None`` if\n      *Content-Type* has no charset information.\n\n   .. attribute:: content_length\n\n      Read-only property that returns length of the request's BODY.\n\n      The value is parsed from the *Content-Length* HTTP header.\n\n      Returns :class:`int` or ``None`` if *Content-Length* is absent.\n\n   .. method:: read()\n\n      Read request body, returns :class:`bytes` object with body content.\n\n      The method is a :ref:`coroutine <coroutine>`.\n\n      .. warning::\n\n         The method doesn't store read data internally, subsequent\n         :meth:`~Request.read` call will return empty bytes ``b''``.\n\n   .. method:: text()\n\n      Read request body, decode it using :attr:`charset` encoding or\n      ``UTF-8`` if no encoding was specified in *MIME-type*.\n\n      Returns :class:`str` with body content.\n\n      The method is a :ref:`coroutine <coroutine>`.\n\n      .. warning::\n\n         The method doesn't store read data internally, subsequent\n         :meth:`~Request.text` call will return empty string ``''``.\n\n   .. method:: json(*, loader=json.loads)\n\n      Read request body decoded as *json*.\n\n      The method is just a boilerplate :ref:`coroutine <coroutine>`\n      implemented as::\n\n         @asyncio.coroutine\n         def json(self, *, loader=json.loads):\n             body = yield from self.text()\n             return loader(body)\n\n      :param callable loader: any callable that accepts :class:`str`\n                              and returns :class:`dict` with parsed\n                              JSON (:func:`json.loads` by default).\n\n      .. warning::\n\n         The method doesn't store read data internally, subsequent\n         :meth:`~Request.json` call will raise an exception.\n\n   .. method:: post()\n\n      A :ref:`coroutine <coroutine>` that reads POST parameters from\n      request body.\n\n      Returns :class:`~aiohttp.multidict.MultiDict` instance filled\n      with parsed data.\n\n      If :attr:`method` is not *POST*, *PUT* or *PATCH* or\n      :attr:`content_type` is not empty or\n      *application/x-www-form-urlencoded* or *multipart/form-data*\n      returns empty multidict.\n\n      .. warning::\n\n         The method **does** store read data internally, subsequent\n         :meth:`~Request.post` call will return the same value.\n\n   .. method:: release()\n\n      Release request.\n\n      Eat unread part of HTTP BODY if present.\n\n      The method is a :ref:`coroutine <coroutine>`.\n\n      .. note::\n\n          User code may never call :meth:`~Request.release`, all\n          required work will be processed by :mod:`aiohttp.web`\n          internal machinery.\n\n\n.. _aiohttp-web-response:\n\n\nResponse classes\n-----------------\n\nFor now, :mod:`aiohttp.web` has two classes for the *HTTP response*:\n:class:`StreamResponse` and :class:`Response`.\n\nUsually you need to use the second one. :class:`StreamResponse` is\nintended for streaming data, while :class:`Response` contains *HTTP\nBODY* as an attribute and sends own content as single piece with the\ncorrect *Content-Length HTTP header*.\n\nFor sake of design decisions :class:`Response` is derived from\n:class:`StreamResponse` parent class.\n\nThe response supports *keep-alive* handling out-of-the-box if\n*request* supports it.\n\nYou can disable *keep-alive* by :meth:`~StreamResponse.force_close` though.\n\nThe common case for sending an answer from\n:ref:`web-handler<aiohttp-web-handler>` is returning a\n:class:`Response` instance::\n\n   def handler(request):\n       return Response(\"All right!\")\n\n\nStreamResponse\n^^^^^^^^^^^^^^\n\n.. class:: StreamResponse(*, status=200, reason=None)\n\n   The base class for the *HTTP response* handling.\n\n   Contains methods for setting *HTTP response headers*, *cookies*,\n   *response status code*, writing *HTTP response BODY* and so on.\n\n   The most important thing you should know about *response* --- it\n   is *Finite State Machine*.\n\n   That means you can do any manipulations with *headers*,\n   *cookies* and *status code* only before :meth:`start`\n   called.\n\n   Once you call :meth:`start` any change of\n   the *HTTP header* part will raise :exc:`RuntimeError` exception.\n\n   Any :meth:`write` call after :meth:`write_eof` is also forbidden.\n\n   :param int status: HTTP status code, ``200`` by default.\n\n   :param str reason: HTTP reason. If param is ``None`` reason will be\n                      calculated basing on *status*\n                      parameter. Otherwise pass :class:`str` with\n                      arbitrary *status* explanation..\n\n   .. attribute:: status\n\n      Read-only property for *HTTP response status code*, :class:`int`.\n\n      ``200`` (OK) by default.\n\n   .. attribute:: reason\n\n      Read-only property for *HTTP response reason*, :class:`str`.\n\n   .. method:: set_status(status, reason=None)\n\n      Set :attr:`status` and :attr:`reason`.\n\n      *reason* value is auto calculated if not specified (``None``).\n\n   .. attribute:: keep_alive\n\n      Read-only property, copy of :attr:`Request.keep_alive` by default.\n\n      Can be switched to ``False`` by :meth:`force_close` call.\n\n   .. method:: force_close\n\n      Disable :attr:`keep_alive` for connection. There are no ways to\n      enable it back.\n\n   .. attribute:: headers\n\n      :class:`~aiohttp.multidict.CaseInsensitiveMultiDict` instance\n      for *outgoing* *HTTP headers*.\n\n   .. attribute:: cookies\n\n      An instance of :class:`http.cookies.SimpleCookie` for *outgoing* cookies.\n\n      .. warning::\n\n         Direct setting up *Set-Cookie* header may be overwritten by\n         explicit calls to cookie manipulation.\n\n         We are encourage using of :attr:`cookies` and\n         :meth:`set_cookie`, :meth:`del_cookie` for cookie\n         manipulations.\n\n   .. method:: set_cookie(name, value, *, expires=None, \\\n                   domain=None, max_age=None, path=None, \\\n                   secure=None, httponly=None, version=None)\n\n      Convenient way for setting :attr:`cookies`, allows to specify\n      some additional properties like *max_age* in a single call.\n\n      :param str name: cookie name\n\n      :param str value: cookie value (will be converted to\n                        :class:`str` if value has another type).\n\n      :param expries: expiration date (optional)\n\n      :param str domain: cookie domain (optional)\n\n      :param int max_age: defines the lifetime of the cookie, in\n                          seconds.  The delta-seconds value is a\n                          decimal non- negative integer.  After\n                          delta-seconds seconds elapse, the client\n                          should discard the cookie.  A value of zero\n                          means the cookie should be discarded\n                          immediately.  (optional)\n\n      :param str path: specifies the subset of URLs to\n                       which this cookie applies. (optional)\n\n      :param bool secure: attribute (with no value) directs\n                          the user agent to use only (unspecified)\n                          secure means to contact the origin server\n                          whenever it sends back this cookie.\n                          The user agent (possibly under the user's\n                          control) may determine what level of\n                          security it considers appropriate for\n                          \"secure\" cookies.  The *secure* should be\n                          considered security advice from the server\n                          to the user agent, indicating that it is in\n                          the session's interest to protect the cookie\n                          contents. (optional)\n\n      :param bool httponly: ``True`` if the cookie HTTP only (optional)\n\n      :param int version: a decimal integer, identifies to which\n                          version of the state management\n                          specification the cookie\n                          conforms. (Optional, *version=1* by default)\n\n   .. method:: del_cookie(name, *, domain=None, path=None)\n\n      Deletes cookie.\n\n      :param str name: cookie name\n\n      :param str domain: optional cookie domain\n\n      :param str path: optional cookie path\n\n   .. attribute:: content_length\n\n      *Content-Length* for outgoing response.\n\n   .. attribute:: content_type\n\n      *Content* part of *Content-Type* for outgoing response.\n\n   .. attribute:: charset\n\n      *Charset* aka *encoding* part of *Content-Type* for outgoing response.\n\n      The value converted to lower-case on attribute assigning.\n\n   .. method:: start(request)\n\n      :param aiohttp.web.Request request: HTTP request object, that the\n                                          response answers.\n\n      Send *HTTP header*. You should not change any header data after\n      calling this method.\n\n   .. method:: write(data)\n\n      Send byte-ish data as the part of *response BODY*.\n\n      :meth:`start` must be called before.\n\n      Raises :exc:`TypeError` if data is not :class:`bytes`,\n      :class:`bytearray` or :class:`memoryview` instance.\n\n      Raises :exc:`RuntimeError` if :meth:`start` has not been called.\n\n      Raises :exc:`RuntimeError` if :meth:`write_eof` has been called.\n\n   .. method:: write_eof()\n\n      A :ref:`coroutine<coroutine>` *may* be called as a mark of the\n      *HTTP response* processing finish.\n\n      *Internal machinery* will call this method at the end of\n      the request processing if needed.\n\n      After :meth:`write_eof` call any manipulations with the *response*\n      object are forbidden.\n\n\nResponse\n^^^^^^^^\n\n.. class:: Response(*, status=200, headers=None, content_type=None, \\\n                    body=None, text=None)\n\n   The most usable response class, inherited from :class:`StreamResponse`.\n\n   Accepts *body* argument for setting the *HTTP response BODY*.\n\n   The actual :attr:`body` sending happens in overridden\n   :meth:`~StreamResponse.write_eof`.\n\n   :param bytes body: response's BODY\n\n   :param int status: HTTP status code, 200 OK by default.\n\n   :param collections.abc.Mapping headers: HTTP headers that should be added to\n                           response's ones.\n\n   :param str text: response's BODY\n\n   :param str content_type: response's content type\n\n   .. attribute:: body\n\n      Read-write attribute for storing response's content aka BODY,\n      :class:`bytes`.\n\n      Setting :attr:`body` also recalculates\n      :attr:`~StreamResponse.content_length` value.\n\n      Resetting :attr:`body` (assigning ``None``) sets\n      :attr:`~StreamResponse.content_length` to ``None`` too, dropping\n      *Content-Length* HTTP header.\n\n   .. attribute:: text\n\n      Read-write attribute for storing response's content, represented as str,\n      :class:`str`.\n\n      Setting :attr:`str` also recalculates\n      :attr:`~StreamResponse.content_length` value and\n      :attr:`~StreamResponse.body` value\n\n      Resetting :attr:`body` (assigning ``None``) sets\n      :attr:`~StreamResponse.content_length` to ``None`` too, dropping\n      *Content-Length* HTTP header.\n\n\nApplication and Router\n----------------------\n\n\nApplication\n^^^^^^^^^^^\n\nApplication is a synonym for web-server.\n\nTo get fully working example, you have to make *application*, register\nsupported urls in *router* and create a *server socket* with\n:class:`aiohttp.RequestHandlerFactory` as a *protocol factory*. *RequestHandlerFactory*\ncould be constructed with :meth:`make_handler`.\n\n*Application* contains a *router* instance and a list of callbacks that\nwill be called during application finishing.\n\n*Application* is a :class:`dict`, so you can use it as registry for\narbitrary properties for later access from\n:ref:`handler<aiohttp-web-handler>` via :attr:`Request.app` property::\n\n   app = Application(loop=loop)\n   app['database'] = yield from aiopg.create_engine(**db_config)\n\n   @asyncio.coroutine\n   def handler(request):\n       with (yield from request.app['database']) as conn:\n           conn.execute(\"DELETE * FROM table\")\n\n\n.. class:: Application(*, loop=None, router=None, logger=<default>, **kwargs)\n\n   The class inherits :class:`dict`.\n\n   :param loop: :ref:`event loop<asyncio-event-loop>` used\n                for processing HTTP requests.\n\n                If param is ``None`` :func:`asyncio.get_event_loop`\n                used for getting default event loop, but we strongly\n                recommend to use explicit loops everywhere.\n\n   :param router: :class:`aiohttp.abc.AbstractRouter` instance, the system\n                  creates :class:`UrlDispatcher` by default if\n                  *router* is ``None``.\n\n   :param logger: :class:`logging.Logger` instance for storing application logs.\n\n                  By default the value is ``logging.getLogger(\"aiohttp.web\")``\n\n   :param kwargs: optional params for initializing self dict.\n\n   .. attribute:: router\n\n      Read-only property that returns *router instance*.\n\n   .. attribute:: logger\n\n      Read-only property that returns *router instance*.\n\n   .. attribute:: loop\n\n      :class:`logging.Logger` instance for storing application logs.\n\n   .. method:: make_handler(**kwargs)\n\n      Creates HTTP protocol factory for handling requests.\n\n      :param kwargs: additional parameters for :class:`RequestHandlerFactory`\n                     constructor.\n\n      You should pass result of the method as *protocol_factory* to\n      :meth:`~BaseEventLoop.create_server`, e.g.::\n\n         loop = asyncio.get_event_loop()\n\n         app = Application(loop=loop)\n\n         # setup route table\n         # app.router.add_route(...)\n\n         yield from loop.create_server(app.make_handler(), '0.0.0.0', 8080)\n\n   .. method:: finish()\n\n      A :ref:`coroutine<coroutine>` that should be called after\n      server stopping.\n\n      This method executes functions registered by\n      :meth:`register_on_finish` in LIFO order.\n\n      If callback raises an exception, the error will be stored by\n      :meth:`~asyncio.BaseEventLoop.call_exception_handler` with keys:\n      *message*, *exception*, *application*.\n\n   .. method:: register_on_finish(self, func, *args, **kwargs):\n\n      Register *func* as a function to be executed at termination.\n      Any optional arguments that are to be passed to *func* must be\n      passed as arguments to :meth:`register_on_finish`.  It is possible to\n      register the same function and arguments more than once.\n\n      During the call of :meth:`finish` all functions registered are called in\n      last in, first out order.\n\n      *func* may be either regular function or :ref:`coroutine<coroutine>`,\n      :meth:`finish` will un-yield (`yield from`) the later.\n\n\nRequestHandlerFactory\n^^^^^^^^^^^^^^^^^^^^^\n\nRequestHandlerFactory is responsible for creating HTTP protocol objects that\ncan handle http connections.\n\n   .. attribute:: connections\n\n      List of all currently oppened connections.\n\n   .. method:: finish_connections(timeout)\n\n      A :ref:`coroutine<coroutine>` that should be called to close all opened\n      connections.\n\n\nRouter\n^^^^^^\n\nFor dispatching URLs to :ref:`handlers<aiohttp-web-handler>`\n:mod:`aiohttp.web` uses *routers*.\n\nRouter is any object that implements :class:`AbstractRouter` interface.\n\n:mod:`aiohttp.web` provides an implementation called :class:`UrlDispatcher`.\n\n:class:`Application` uses :class:`UrlDispatcher` as :meth:`router` by default.\n\n.. class:: UrlDispatcher()\n\n   Straightforward url-mathing router, implements\n   :class:`collections.abc.Mapping` for access to *named routes*.\n\n   Before running :class:`Application` you should fill *route\n   table* first by calling :meth:`add_route` and :meth:`add_static`.\n\n   :ref:`Handler<aiohttp-web-handler>` lookup is performed by iterating on\n   added *routes* in FIFO order. The first matching *route* will be used\n   to call corresponding *handler*.\n\n   If on route creation you specify *name* parameter the result is\n   *named route*.\n\n   *Named route* can be retrieved by ``app.router[name]`` call, checked for\n   existence by ``name in app.router`` etc.\n\n   .. seealso:: :ref:`Route classes <aiohttp-web-route>`\n\n   .. method:: add_route(method, path, handler, *, name=None)\n\n      Append :ref:`handler<aiohttp-web-handler>` to the end of route table.\n\n      *path* may be either *constant* string like ``'/a/b/c'`` or\n       *variable rule* like ``'/a/{var}'`` (see\n       :ref:`handling variable pathes<aiohttp-web-variable-handler>`)\n\n      Pay attention please: *handler* is converted to coroutine internally when\n      it is a regular function.\n\n      :param str path: route path\n\n      :param callable handler: route handler\n\n      :param str name: optional route name.\n\n      :returns: new :class:`PlainRoute` or :class:`DynamicRoute` instance.\n\n   .. method:: add_static(prefix, path, *, name=None)\n\n      Adds router for returning static files.\n\n      Useful for handling static content like images, javascript and css files.\n\n      .. warning::\n\n         Use :meth:`add_static` for development only. In production,\n         static content should be processed by web servers like *nginx*\n         or *apache*.\n\n      :param str prefix: URL path prefix for handled static files\n\n      :param str path: path to the folder in file system that contains\n                       handled static files.\n\n      :param str name: optional route name.\n\n      :returns: new :class:`StaticRoute` instance.\n\n   .. method:: resolve(requst)\n\n      A :ref:`coroutine<coroutine>` that returns\n      :class:`AbstractMatchInfo` for *request* or raises http\n      exception like :exc:`HTTPNotFound` if there is no registered\n      route for *request*.\n\n      Used by internal machinery, end user unlikely need to call the method.\n\n.. _aiohttp-web-route:\n\nRoute\n^^^^^\n\n.. versionadded:: 0.11\n\nDefault router :class:`UrlDispatcher` operates with *routes*.\n\nUser should not instantiate route classes by hand but can give *named\nroute instance* by ``router[name]`` if he have added route by\n:meth:`UrlDispatcher.add_route` or :meth:`UrlDispatcher.add_static`\ncalls with non-empty *name* parameter.\n\nThe main usage of *named routes* is constructing URL by route name for\npassing it into *template engine* for example::\n\n   url = app.router['route_name'].url(query={'a': 1, 'b': 2})\n\nThere are three conctrete route classes:* :class:`DynamicRoute` for\nurls with :ref:`variable pathes<aiohttp-web-variable-handler>` spec.\n\n\n* :class:`PlainRoute` for urls without :ref:`variable\n  pathes<aiohttp-web-variable-handler>`\n\n* :class:`DynamicRoute` for urls with :ref:`variable\n  pathes<aiohttp-web-variable-handler>` spec.\n\n* :class:`StaticRoute` for static file handlers.\n\n.. class:: Route\n\n   Base class for routes served by :class:`UrlDispatcher`.\n\n   .. attribute:: method\n\n   HTTP method handled by the route, e.g. *GET*, *POST* etc.\n\n   .. attribute:: handler\n\n   :ref:`handler<aiohttp-web-handler>` that processes the route.\n\n   .. attribute:: name\n\n   Name of the route.\n\n   .. method:: match(path)\n\n   Abstract method, accepts *URL path* and returns :class:`dict` with\n   parsed *path parts* for :class:`UrlMappingMatchInfo` or ``None`` if\n   the route cannot handle given *path*.\n\n   The method exists for internal usage, end user unlikely need to call it.\n\n   .. method:: url(*, query=None, **kwargs)\n\n   Abstract method for constructing url handled by the route.\n\n   *query* is a mapping or list of *(name, value)* pairs for\n   specifying *query* part of url (parameter is processed by\n   :func:`~urllib.parse.urlencode`).\n\n   Other available parameters depends on concrete route class and\n   described in descendant classes.\n\n.. class:: PlainRoute\n\n   The route class for handling plain *URL path*, e.g. ``\"/a/b/c\"``\n\n   .. method:: url(*, parts, query=None)\n\n   Construct url, doesn't accepts extra parameters::\n\n      >>> route.url(query={'d': 1, 'e': 2})\n      '/a/b/c/?d=1&e=2'``\n\n.. class:: DynamicRoute\n\n   The route class for handling :ref:`variable\n   path<aiohttp-web-variable-handler>`, e.g. ``\"/a/{name1}/{name2}\"``\n\n   .. method:: url(*, parts, query=None)\n\n   Construct url with given *dynamic parts*::\n\n       >>> route.url(parts={'name1': 'b', 'name2': 'c'}, query={'d': 1, 'e': 2})\n       '/a/b/c/?d=1&e=2'\n\n\n.. class:: StaticRoute\n\n   The route class for handling static files, created by\n   :meth:`UrlDispatcher.add_static` call.\n\n   .. method:: url(*, filename, query=None)\n\n   Construct url for given *filename*::\n\n      >>> route.url(filename='img/logo.png', query={'param': 1})\n      '/path/to/static/img/logo.png?param=1'\n\nMatchInfo\n^^^^^^^^^\n\nAfter route matching web application calls found handler if any.\n\nMatching result can be accessible from handler as\n:attr:`Request.match_info` attribute.\n\nIn general the result may be any object derived from\n:class:`AbstractMatchInfo` (:class:`UrlMappingMatchInfo` for default\n:class:`UrlDispatcher` router).\n\n.. class:: UrlMappingMatchInfo\n\n   Inherited from :class:`dict` and :class:`AbstractMatchInfo`. Dict\n   items are given from :meth:`Route.match` call return value.\n\n   .. attribute:: route\n\n   :class:`Route` instance for url matching.\n\n\n\nUtilities\n---------\n\n.. class:: FileField\n\n   A :func:`~collections.namedtuple` that is returned as multidict value\n   by :meth:`Request.POST` if field is uploaded file.\n\n   .. attribute:: name\n\n      Field name\n\n   .. attribute:: filename\n\n      File name as specified by uploading (client) side.\n\n   .. attribute:: file\n\n      An :class:`io.IOBase` instance with content of uploaded file.\n\n   .. attribute:: content_type\n\n      *MIME type* of uploaded file, ``'text/plain'`` by default.\n\n   .. seealso:: :ref:`aiohttp-web-file-upload`\n\n\nExceptions\n-----------\n\n:mod:`aiohttp.web` defines exceptions for list of *HTTP status codes*.\n\nEach class relates to a single HTTP status code.  Each class is a\nsubclass of the :class:`~HTTPException`.\n\nThose exceptions are derived from :class:`Response` too, so you can\neither return exception object from :ref:`aiohttp-web-handler` or raise it.\n\nThe following snippets are equal::\n\n    @asyncio.coroutine\n    def handler(request):\n        return aiohttp.web.HTTPFound('/redirect')\n\nand::\n\n    @asyncio.coroutine\n    def handler(request):\n        raise aiohttp.web.HTTPFound('/redirect')\n\n\nEach exception class has a status code according to :rfc:`2068`:\ncodes with 100-300 are not really errors; 400s are client errors,\nand 500s are server errors.\n\nHttp Exception hierarchy chart::\n\n   Exception\n     HTTPException\n       HTTPSuccessful\n         * 200 - HTTPOk\n         * 201 - HTTPCreated\n         * 202 - HTTPAccepted\n         * 203 - HTTPNonAuthoritativeInformation\n         * 204 - HTTPNoContent\n         * 205 - HTTPResetContent\n         * 206 - HTTPPartialContent\n       HTTPRedirection\n         * 300 - HTTPMultipleChoices\n         * 301 - HTTPMovedPermanently\n         * 302 - HTTPFound\n         * 303 - HTTPSeeOther\n         * 304 - HTTPNotModified\n         * 305 - HTTPUseProxy\n         * 307 - HTTPTemporaryRedirect\n       HTTPError\n         HTTPClientError\n           * 400 - HTTPBadRequest\n           * 401 - HTTPUnauthorized\n           * 402 - HTTPPaymentRequired\n           * 403 - HTTPForbidden\n           * 404 - HTTPNotFound\n           * 405 - HTTPMethodNotAllowed\n           * 406 - HTTPNotAcceptable\n           * 407 - HTTPProxyAuthenticationRequired\n           * 408 - HTTPRequestTimeout\n           * 409 - HTTPConflict\n           * 410 - HTTPGone\n           * 411 - HTTPLengthRequired\n           * 412 - HTTPPreconditionFailed\n           * 413 - HTTPRequestEntityTooLarge\n           * 414 - HTTPRequestURITooLong\n           * 415 - HTTPUnsupportedMediaType\n           * 416 - HTTPRequestRangeNotSatisfiable\n           * 417 - HTTPExpectationFailed\n         HTTPServerError\n           * 500 - HTTPInternalServerError\n           * 501 - HTTPNotImplemented\n           * 502 - HTTPBadGateway\n           * 503 - HTTPServiceUnavailable\n           * 504 - HTTPGatewayTimeout\n           * 505 - HTTPVersionNotSupported\n\nAll http exceptions have the same constructor::\n\n    HTTPNotFound(*, headers=None, reason=None, \\\n                 body=None, text=None, content_type=None)\n\nif other not directly specified. *headers* will be added to *default\nresponse headers*.\n\nClasses :class:`HTTPMultipleChoices`, :class:`HTTPMovedPermanently`,\n:class:`HTTPFound`, :class:`HTTPSeeOther`, :class:`HTTPUseProxy`,\n:class:`HTTPTemporaryRedirect` has constructor signature like::\n\n    HTTPFound(location, *, headers=None, reason=None, \\\n              body=None, text=None, content_type=None)\n\nwhere *location* is value for *Location HTTP header*.\n\n:class:`HTTPMethodNotAllowed` constructed with pointing trial method\nand list of allowed methods::\n\n    HTTPMethodNotAllowed(method, allowed_methods, *,\n                         headers=None, reason=None, \\\n                         body=None, text=None, content_type=None)\n",
            "header": {
                "file": {
                    "path": "docs/web.rst"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "df0d68d5",
                "new_commit_hash": "eaa77853",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/docs/web.rst"
            },
            "plus_file": {
                "path": "b/docs/web.rst"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 573,
                            "length": 6
                        },
                        "new_range": {
                            "start": 573,
                            "length": 8
                        },
                        "section": "StreamResponse"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "      *Charset* aka *encoding* part of *Content-Type* for outgoing response.",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "      The value converted to lower-case on attribute assigning.",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "   .. method:: start(request)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "      :param aiohttp.web.Request request: HTTP request object, that the",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "import asyncio\nimport unittest\nfrom unittest import mock\nfrom aiohttp.multidict import MultiDict, CaseInsensitiveMultiDict\nfrom aiohttp.web import Request, StreamResponse, Response\nfrom aiohttp.protocol import RawRequestMessage, HttpVersion11\n\n\nclass TestStreamResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = StreamResponse()\n        self.assertEqual(200, resp.status)\n        self.assertIsNone(resp.keep_alive)\n\n    def test_content_length(self):\n        resp = StreamResponse()\n        self.assertIsNone(resp.content_length)\n\n    def test_content_length_setter(self):\n        resp = StreamResponse()\n\n        resp.content_length = 234\n        self.assertEqual(234, resp.content_length)\n\n    def test_drop_content_length_header_on_setting_len_to_None(self):\n        resp = StreamResponse()\n\n        resp.content_length = 1\n        self.assertEqual(\"1\", resp.headers['Content-Length'])\n        resp.content_length = None\n        self.assertNotIn('Content-Length', resp.headers)\n\n    def test_set_content_length_to_None_on_non_set(self):\n        resp = StreamResponse()\n\n        resp.content_length = None\n        self.assertNotIn('Content-Length', resp.headers)\n        resp.content_length = None\n        self.assertNotIn('Content-Length', resp.headers)\n\n    def test_setting_content_type(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        self.assertEqual('text/html', resp.headers['content-type'])\n\n    def test_setting_charset(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        resp.charset = 'koi8-r'\n        self.assertEqual('text/html; charset=koi8-r',\n                         resp.headers['content-type'])\n\n    def test_default_charset(self):\n        resp = StreamResponse()\n\n        self.assertIsNone(resp.charset)\n\n    def test_reset_charset(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        resp.charset = None\n        self.assertIsNone(resp.charset)\n\n    def test_reset_charset_after_setting(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        resp.charset = 'koi8-r'\n        resp.charset = None\n        self.assertIsNone(resp.charset)\n\n    def test_charset_without_content_type(self):\n        resp = StreamResponse()\n\n        with self.assertRaises(RuntimeError):\n            resp.charset = 'koi8-r'\n\n    @mock.patch('aiohttp.web.ResponseImpl')\n    def test_start(self, ResponseImpl):\n        req = self.make_request('GET', '/')\n        resp = StreamResponse()\n        self.assertIsNone(resp.keep_alive)\n\n        msg = resp.start(req)\n\n        self.assertTrue(msg.send_headers.called)\n        self.assertIs(msg, resp.start(req))\n\n        self.assertTrue(resp.keep_alive)\n\n        req2 = self.make_request('GET', '/')\n        with self.assertRaises(RuntimeError):\n            resp.start(req2)\n\n    def test_write_non_byteish(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        with self.assertRaises(TypeError):\n            resp.write(123)\n\n    def test_write_before_start(self):\n        resp = StreamResponse()\n\n        with self.assertRaises(RuntimeError):\n            resp.write(b'data')\n\n    def test_cannot_write_after_eof(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        resp.write(b'data')\n        self.writer.drain.return_value = ()\n        self.loop.run_until_complete(resp.write_eof())\n        self.writer.write.reset_mock()\n\n        with self.assertRaises(RuntimeError):\n            resp.write(b'next data')\n        self.assertFalse(self.writer.write.called)\n\n    def test_cannot_write_eof_before_headers(self):\n        resp = StreamResponse()\n\n        with self.assertRaises(RuntimeError):\n            self.loop.run_until_complete(resp.write_eof())\n\n    def test_cannot_write_eof_twice(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        resp.write(b'data')\n        self.writer.drain.return_value = ()\n        self.loop.run_until_complete(resp.write_eof())\n        self.assertTrue(self.writer.write.called)\n\n        self.writer.write.reset_mock()\n        self.loop.run_until_complete(resp.write_eof())\n        self.assertFalse(self.writer.write.called)\n\n    def test_write_returns_drain(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        self.assertEqual((), resp.write(b'data'))\n\n    def test_write_returns_empty_tuple_on_empty_data(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        self.assertEqual((), resp.write(b''))\n\n    def test_force_close(self):\n        resp = StreamResponse()\n\n        self.assertIsNone(resp.keep_alive)\n        resp.force_close()\n        self.assertFalse(resp.keep_alive)\n\n    def test_response_cookies(self):\n        resp = StreamResponse()\n\n        self.assertEqual(resp.cookies, {})\n        self.assertEqual(str(resp.cookies), '')\n\n        resp.set_cookie('name', 'value')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=value')\n        resp.set_cookie('name', 'other_value')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=other_value')\n\n        resp.cookies['name'] = 'another_other_value'\n        resp.cookies['name']['max-age'] = 10\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=another_other_value; Max-Age=10')\n\n        resp.del_cookie('name')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=; Max-Age=0')\n\n        resp.set_cookie('name', 'value', domain='local.host')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; Domain=local.host')\n\n    def test_response_cookie_path(self):\n        resp = StreamResponse()\n\n        self.assertEqual(resp.cookies, {})\n\n        resp.set_cookie('name', 'value', path='/some/path')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; Path=/some/path')\n        resp.set_cookie('name', 'value', expires='123')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; expires=123;'\n                         ' Path=/some/path')\n        resp.set_cookie('name', 'value', domain='example.com',\n                        path='/home', expires='123', max_age='10',\n                        secure=True, httponly=True, version='2.0')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; '\n                         'Domain=example.com; '\n                         'expires=123; '\n                         'httponly; '\n                         'Max-Age=10; '\n                         'Path=/home; '\n                         'secure; '\n                         'Version=2.0')\n\n    def test_response_cookie__issue_del_cookie(self):\n        resp = StreamResponse()\n\n        self.assertEqual(resp.cookies, {})\n        self.assertEqual(str(resp.cookies), '')\n\n        resp.del_cookie('name')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=; Max-Age=0')\n\n    def test_cookie_set_after_del(self):\n        resp = StreamResponse()\n\n        resp.del_cookie('name')\n        resp.set_cookie('name', 'val')\n        # check for Max-Age dropped\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=val')\n\n    def test_set_status_with_reason(self):\n        resp = StreamResponse()\n\n        resp.set_status(200, \"Everithing is fine!\")\n        self.assertEqual(200, resp.status)\n        self.assertEqual(\"Everithing is fine!\", resp.reason)\n\n\nclass TestResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = Response()\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertIsNone(resp.body)\n        self.assertEqual(0, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('CONTENT-LENGTH', '0')]),\n                         resp.headers)\n\n    def test_ctor_with_headers_and_status(self):\n        resp = Response(body=b'body', status=201, headers={'Age': '12'})\n\n        self.assertEqual(201, resp.status)\n        self.assertEqual(b'body', resp.body)\n        self.assertEqual(4, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('AGE', '12'),\n                                                   ('CONTENT-LENGTH', '4')]),\n                         resp.headers)\n\n    def test_ctor_content_type(self):\n        resp = Response(content_type='application/json')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict([('CONTENT-TYPE', 'application/json'),\n                                      ('CONTENT-LENGTH', '0')]),\n            resp.headers)\n\n    def test_ctor_text_body_combined(self):\n        with self.assertRaises(ValueError):\n            Response(body=b'123', text='test text')\n\n    def test_ctor_text(self):\n        resp = Response(text='test text')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict(\n                [('CONTENT-TYPE', 'text/plain; charset=utf-8'),\n                 ('CONTENT-LENGTH', '9')]),\n            resp.headers)\n\n        self.assertEqual(resp.body, b'test text')\n        self.assertEqual(resp.text, 'test text')\n\n    def test_assign_nonbyteish_body(self):\n        resp = Response(body=b'data')\n\n        with self.assertRaises(TypeError):\n            resp.body = 123\n        self.assertEqual(b'data', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_assign_nonstr_text(self):\n        resp = Response(text='test')\n\n        with self.assertRaises(TypeError):\n            resp.text = b'123'\n        self.assertEqual(b'test', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_send_headers_for_empty_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response()\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_render_with_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response(body=b'data')\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 4\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\ndata')\n\n    def test_send_set_cookie_header(self):\n        resp = Response()\n        resp.cookies['name'] = 'value'\n\n        req = self.make_request('GET', '/')\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'SET-COOKIE: name=value\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n",
            "new_file_content": "import asyncio\nimport unittest\nfrom unittest import mock\nfrom aiohttp.multidict import MultiDict, CaseInsensitiveMultiDict\nfrom aiohttp.web import Request, StreamResponse, Response\nfrom aiohttp.protocol import RawRequestMessage, HttpVersion11\n\n\nclass TestStreamResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = StreamResponse()\n        self.assertEqual(200, resp.status)\n        self.assertIsNone(resp.keep_alive)\n\n    def test_content_length(self):\n        resp = StreamResponse()\n        self.assertIsNone(resp.content_length)\n\n    def test_content_length_setter(self):\n        resp = StreamResponse()\n\n        resp.content_length = 234\n        self.assertEqual(234, resp.content_length)\n\n    def test_drop_content_length_header_on_setting_len_to_None(self):\n        resp = StreamResponse()\n\n        resp.content_length = 1\n        self.assertEqual(\"1\", resp.headers['Content-Length'])\n        resp.content_length = None\n        self.assertNotIn('Content-Length', resp.headers)\n\n    def test_set_content_length_to_None_on_non_set(self):\n        resp = StreamResponse()\n\n        resp.content_length = None\n        self.assertNotIn('Content-Length', resp.headers)\n        resp.content_length = None\n        self.assertNotIn('Content-Length', resp.headers)\n\n    def test_setting_content_type(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        self.assertEqual('text/html', resp.headers['content-type'])\n\n    def test_setting_charset(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        resp.charset = 'koi8-r'\n        self.assertEqual('text/html; charset=koi8-r',\n                         resp.headers['content-type'])\n\n    def test_default_charset(self):\n        resp = StreamResponse()\n\n        self.assertIsNone(resp.charset)\n\n    def test_reset_charset(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        resp.charset = None\n        self.assertIsNone(resp.charset)\n\n    def test_reset_charset_after_setting(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        resp.charset = 'koi8-r'\n        resp.charset = None\n        self.assertIsNone(resp.charset)\n\n    def test_charset_without_content_type(self):\n        resp = StreamResponse()\n\n        with self.assertRaises(RuntimeError):\n            resp.charset = 'koi8-r'\n\n    @mock.patch('aiohttp.web.ResponseImpl')\n    def test_start(self, ResponseImpl):\n        req = self.make_request('GET', '/')\n        resp = StreamResponse()\n        self.assertIsNone(resp.keep_alive)\n\n        msg = resp.start(req)\n\n        self.assertTrue(msg.send_headers.called)\n        self.assertIs(msg, resp.start(req))\n\n        self.assertTrue(resp.keep_alive)\n\n        req2 = self.make_request('GET', '/')\n        with self.assertRaises(RuntimeError):\n            resp.start(req2)\n\n    def test_write_non_byteish(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        with self.assertRaises(TypeError):\n            resp.write(123)\n\n    def test_write_before_start(self):\n        resp = StreamResponse()\n\n        with self.assertRaises(RuntimeError):\n            resp.write(b'data')\n\n    def test_cannot_write_after_eof(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        resp.write(b'data')\n        self.writer.drain.return_value = ()\n        self.loop.run_until_complete(resp.write_eof())\n        self.writer.write.reset_mock()\n\n        with self.assertRaises(RuntimeError):\n            resp.write(b'next data')\n        self.assertFalse(self.writer.write.called)\n\n    def test_cannot_write_eof_before_headers(self):\n        resp = StreamResponse()\n\n        with self.assertRaises(RuntimeError):\n            self.loop.run_until_complete(resp.write_eof())\n\n    def test_cannot_write_eof_twice(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        resp.write(b'data')\n        self.writer.drain.return_value = ()\n        self.loop.run_until_complete(resp.write_eof())\n        self.assertTrue(self.writer.write.called)\n\n        self.writer.write.reset_mock()\n        self.loop.run_until_complete(resp.write_eof())\n        self.assertFalse(self.writer.write.called)\n\n    def test_write_returns_drain(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        self.assertEqual((), resp.write(b'data'))\n\n    def test_write_returns_empty_tuple_on_empty_data(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        self.assertEqual((), resp.write(b''))\n\n    def test_force_close(self):\n        resp = StreamResponse()\n\n        self.assertIsNone(resp.keep_alive)\n        resp.force_close()\n        self.assertFalse(resp.keep_alive)\n\n    def test_response_cookies(self):\n        resp = StreamResponse()\n\n        self.assertEqual(resp.cookies, {})\n        self.assertEqual(str(resp.cookies), '')\n\n        resp.set_cookie('name', 'value')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=value')\n        resp.set_cookie('name', 'other_value')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=other_value')\n\n        resp.cookies['name'] = 'another_other_value'\n        resp.cookies['name']['max-age'] = 10\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=another_other_value; Max-Age=10')\n\n        resp.del_cookie('name')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=; Max-Age=0')\n\n        resp.set_cookie('name', 'value', domain='local.host')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; Domain=local.host')\n\n    def test_response_cookie_path(self):\n        resp = StreamResponse()\n\n        self.assertEqual(resp.cookies, {})\n\n        resp.set_cookie('name', 'value', path='/some/path')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; Path=/some/path')\n        resp.set_cookie('name', 'value', expires='123')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; expires=123;'\n                         ' Path=/some/path')\n        resp.set_cookie('name', 'value', domain='example.com',\n                        path='/home', expires='123', max_age='10',\n                        secure=True, httponly=True, version='2.0')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; '\n                         'Domain=example.com; '\n                         'expires=123; '\n                         'httponly; '\n                         'Max-Age=10; '\n                         'Path=/home; '\n                         'secure; '\n                         'Version=2.0')\n\n    def test_response_cookie__issue_del_cookie(self):\n        resp = StreamResponse()\n\n        self.assertEqual(resp.cookies, {})\n        self.assertEqual(str(resp.cookies), '')\n\n        resp.del_cookie('name')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=; Max-Age=0')\n\n    def test_cookie_set_after_del(self):\n        resp = StreamResponse()\n\n        resp.del_cookie('name')\n        resp.set_cookie('name', 'val')\n        # check for Max-Age dropped\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=val')\n\n    def test_set_status_with_reason(self):\n        resp = StreamResponse()\n\n        resp.set_status(200, \"Everithing is fine!\")\n        self.assertEqual(200, resp.status)\n        self.assertEqual(\"Everithing is fine!\", resp.reason)\n\n\nclass TestResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = Response()\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertIsNone(resp.body)\n        self.assertEqual(0, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('CONTENT-LENGTH', '0')]),\n                         resp.headers)\n\n    def test_ctor_with_headers_and_status(self):\n        resp = Response(body=b'body', status=201, headers={'Age': '12'})\n\n        self.assertEqual(201, resp.status)\n        self.assertEqual(b'body', resp.body)\n        self.assertEqual(4, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('AGE', '12'),\n                                                   ('CONTENT-LENGTH', '4')]),\n                         resp.headers)\n\n    def test_ctor_content_type(self):\n        resp = Response(content_type='application/json')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict([('CONTENT-TYPE', 'application/json'),\n                                      ('CONTENT-LENGTH', '0')]),\n            resp.headers)\n\n    def test_ctor_text_body_combined(self):\n        with self.assertRaises(ValueError):\n            Response(body=b'123', text='test text')\n\n    def test_ctor_text(self):\n        resp = Response(text='test text')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict(\n                [('CONTENT-TYPE', 'text/plain; charset=utf-8'),\n                 ('CONTENT-LENGTH', '9')]),\n            resp.headers)\n\n        self.assertEqual(resp.body, b'test text')\n        self.assertEqual(resp.text, 'test text')\n\n    def test_assign_nonbyteish_body(self):\n        resp = Response(body=b'data')\n\n        with self.assertRaises(TypeError):\n            resp.body = 123\n        self.assertEqual(b'data', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_assign_nonstr_text(self):\n        resp = Response(text='test')\n\n        with self.assertRaises(TypeError):\n            resp.text = b'123'\n        self.assertEqual(b'test', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_send_headers_for_empty_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response()\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_render_with_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response(body=b'data')\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 4\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\ndata')\n\n    def test_send_set_cookie_header(self):\n        resp = Response()\n        resp.cookies['name'] = 'value'\n\n        req = self.make_request('GET', '/')\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'SET-COOKIE: name=value\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_set_text_with_content_type(self):\n        resp = Response()\n        resp.content_type = \"text/html\"\n        resp.text = \"text\"\n\n        self.assertEqual(\"text\", resp.text)\n        self.assertEqual(b\"text\", resp.body)\n        self.assertEqual(\"text/html\", resp.content_type)\n\n    def test_set_text_with_charset(self):\n        resp = Response()\n        resp.content_type = 'text/plain'\n        resp.charset = \"KOI8-R\"\n        resp.text = \"текст\"\n\n        self.assertEqual(\"текст\", resp.text)\n        self.assertEqual(\"текст\".encode('koi8-r'), resp.body)\n        self.assertEqual(\"koi8-r\", resp.charset)\n",
            "header": {
                "file": {
                    "path": "tests/test_web_response.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "c20a6ba6",
                "new_commit_hash": "7e2202eb",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tests/test_web_response.py"
            },
            "plus_file": {
                "path": "b/tests/test_web_response.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 396,
                            "length": 3
                        },
                        "new_range": {
                            "start": 396,
                            "length": 22
                        },
                        "section": "class TestResponse(unittest.TestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                         'SET-COOKIE: name=value\\r\\n'",
                                "type": "context"
                            },
                            {
                                "content": "                         'CONNECTION: keep-alive\\r\\n'",
                                "type": "context"
                            },
                            {
                                "content": "                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_set_text_with_content_type(self):",
                                "type": "added"
                            },
                            {
                                "content": "        resp = Response()",
                                "type": "added"
                            },
                            {
                                "content": "        resp.content_type = \"text/html\"",
                                "type": "added"
                            },
                            {
                                "content": "        resp.text = \"text\"",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(\"text\", resp.text)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(b\"text\", resp.body)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(\"text/html\", resp.content_type)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_set_text_with_charset(self):",
                                "type": "added"
                            },
                            {
                                "content": "        resp = Response()",
                                "type": "added"
                            },
                            {
                                "content": "        resp.content_type = 'text/plain'",
                                "type": "added"
                            },
                            {
                                "content": "        resp.charset = \"KOI8-R\"",
                                "type": "added"
                            },
                            {
                                "content": "        resp.text = \"текст\"",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(\"текст\", resp.text)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(\"текст\".encode('koi8-r'), resp.body)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertEqual(\"koi8-r\", resp.charset)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_web_response.py",
                            "type": "class",
                            "name": "TestResponse",
                            "content": "class TestResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = Response()\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertIsNone(resp.body)\n        self.assertEqual(0, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('CONTENT-LENGTH', '0')]),\n                         resp.headers)\n\n    def test_ctor_with_headers_and_status(self):\n        resp = Response(body=b'body', status=201, headers={'Age': '12'})\n\n        self.assertEqual(201, resp.status)\n        self.assertEqual(b'body', resp.body)\n        self.assertEqual(4, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('AGE', '12'),\n                                                   ('CONTENT-LENGTH', '4')]),\n                         resp.headers)\n\n    def test_ctor_content_type(self):\n        resp = Response(content_type='application/json')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict([('CONTENT-TYPE', 'application/json'),\n                                      ('CONTENT-LENGTH', '0')]),\n            resp.headers)\n\n    def test_ctor_text_body_combined(self):\n        with self.assertRaises(ValueError):\n            Response(body=b'123', text='test text')\n\n    def test_ctor_text(self):\n        resp = Response(text='test text')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict(\n                [('CONTENT-TYPE', 'text/plain; charset=utf-8'),\n                 ('CONTENT-LENGTH', '9')]),\n            resp.headers)\n\n        self.assertEqual(resp.body, b'test text')\n        self.assertEqual(resp.text, 'test text')\n\n    def test_assign_nonbyteish_body(self):\n        resp = Response(body=b'data')\n\n        with self.assertRaises(TypeError):\n            resp.body = 123\n        self.assertEqual(b'data', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_assign_nonstr_text(self):\n        resp = Response(text='test')\n\n        with self.assertRaises(TypeError):\n            resp.text = b'123'\n        self.assertEqual(b'test', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_send_headers_for_empty_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response()\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_render_with_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response(body=b'data')\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 4\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\ndata')\n\n    def test_send_set_cookie_header(self):\n        resp = Response()\n        resp.cookies['name'] = 'value'\n\n        req = self.make_request('GET', '/')\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'SET-COOKIE: name=value\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_set_text_with_content_type(self):\n        resp = Response()\n        resp.content_type = \"text/html\"\n        resp.text = \"text\"\n\n        self.assertEqual(\"text\", resp.text)\n        self.assertEqual(b\"text\", resp.body)\n        self.assertEqual(\"text/html\", resp.content_type)\n\n    def test_set_text_with_charset(self):\n        resp = Response()\n        resp.content_type = 'text/plain'\n        resp.charset = \"KOI8-R\"\n        resp.text = \"текст\"\n\n        self.assertEqual(\"текст\", resp.text)\n        self.assertEqual(\"текст\".encode('koi8-r'), resp.body)\n        self.assertEqual(\"koi8-r\", resp.charset)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 254,
                            "end_lineno": 417,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "tests/test_web_response.py",
                            "type": "method",
                            "name": "TestResponse.test_set_text_with_content_type",
                            "content": "class TestResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = Response()\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertIsNone(resp.body)\n        self.assertEqual(0, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('CONTENT-LENGTH', '0')]),\n                         resp.headers)\n\n    def test_ctor_with_headers_and_status(self):\n        resp = Response(body=b'body', status=201, headers={'Age': '12'})\n\n        self.assertEqual(201, resp.status)\n        self.assertEqual(b'body', resp.body)\n        self.assertEqual(4, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('AGE', '12'),\n                                                   ('CONTENT-LENGTH', '4')]),\n                         resp.headers)\n\n    def test_ctor_content_type(self):\n        resp = Response(content_type='application/json')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict([('CONTENT-TYPE', 'application/json'),\n                                      ('CONTENT-LENGTH', '0')]),\n            resp.headers)\n\n    def test_ctor_text_body_combined(self):\n        with self.assertRaises(ValueError):\n            Response(body=b'123', text='test text')\n\n    def test_ctor_text(self):\n        resp = Response(text='test text')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict(\n                [('CONTENT-TYPE', 'text/plain; charset=utf-8'),\n                 ('CONTENT-LENGTH', '9')]),\n            resp.headers)\n\n        self.assertEqual(resp.body, b'test text')\n        self.assertEqual(resp.text, 'test text')\n\n    def test_assign_nonbyteish_body(self):\n        resp = Response(body=b'data')\n\n        with self.assertRaises(TypeError):\n            resp.body = 123\n        self.assertEqual(b'data', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_assign_nonstr_text(self):\n        resp = Response(text='test')\n\n        with self.assertRaises(TypeError):\n            resp.text = b'123'\n        self.assertEqual(b'test', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_send_headers_for_empty_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response()\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_render_with_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response(body=b'data')\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 4\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\ndata')\n\n    def test_send_set_cookie_header(self):\n        resp = Response()\n        resp.cookies['name'] = 'value'\n\n        req = self.make_request('GET', '/')\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'SET-COOKIE: name=value\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_set_text_with_content_type(self):\n        resp = Response()\n        resp.content_type = \"text/html\"\n        resp.text = \"text\"\n\n        self.assertEqual(\"text\", resp.text)\n        self.assertEqual(b\"text\", resp.body)\n        self.assertEqual(\"text/html\", resp.content_type)\n\n    def test_set_text_with_charset(self):\n        resp = Response()\n        resp.content_type = 'text/plain'\n        resp.charset = \"KOI8-R\"\n        resp.text = \"текст\"\n\n        self.assertEqual(\"текст\", resp.text)\n        self.assertEqual(\"текст\".encode('koi8-r'), resp.body)\n        self.assertEqual(\"koi8-r\", resp.charset)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 400,
                            "end_lineno": 407,
                            "parent": {
                                "file_name": "tests/test_web_response.py",
                                "type": "class",
                                "name": "TestResponse",
                                "content": "class TestResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = Response()\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertIsNone(resp.body)\n        self.assertEqual(0, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('CONTENT-LENGTH', '0')]),\n                         resp.headers)\n\n    def test_ctor_with_headers_and_status(self):\n        resp = Response(body=b'body', status=201, headers={'Age': '12'})\n\n        self.assertEqual(201, resp.status)\n        self.assertEqual(b'body', resp.body)\n        self.assertEqual(4, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('AGE', '12'),\n                                                   ('CONTENT-LENGTH', '4')]),\n                         resp.headers)\n\n    def test_ctor_content_type(self):\n        resp = Response(content_type='application/json')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict([('CONTENT-TYPE', 'application/json'),\n                                      ('CONTENT-LENGTH', '0')]),\n            resp.headers)\n\n    def test_ctor_text_body_combined(self):\n        with self.assertRaises(ValueError):\n            Response(body=b'123', text='test text')\n\n    def test_ctor_text(self):\n        resp = Response(text='test text')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict(\n                [('CONTENT-TYPE', 'text/plain; charset=utf-8'),\n                 ('CONTENT-LENGTH', '9')]),\n            resp.headers)\n\n        self.assertEqual(resp.body, b'test text')\n        self.assertEqual(resp.text, 'test text')\n\n    def test_assign_nonbyteish_body(self):\n        resp = Response(body=b'data')\n\n        with self.assertRaises(TypeError):\n            resp.body = 123\n        self.assertEqual(b'data', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_assign_nonstr_text(self):\n        resp = Response(text='test')\n\n        with self.assertRaises(TypeError):\n            resp.text = b'123'\n        self.assertEqual(b'test', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_send_headers_for_empty_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response()\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_render_with_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response(body=b'data')\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 4\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\ndata')\n\n    def test_send_set_cookie_header(self):\n        resp = Response()\n        resp.cookies['name'] = 'value'\n\n        req = self.make_request('GET', '/')\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'SET-COOKIE: name=value\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_set_text_with_content_type(self):\n        resp = Response()\n        resp.content_type = \"text/html\"\n        resp.text = \"text\"\n\n        self.assertEqual(\"text\", resp.text)\n        self.assertEqual(b\"text\", resp.body)\n        self.assertEqual(\"text/html\", resp.content_type)\n\n    def test_set_text_with_charset(self):\n        resp = Response()\n        resp.content_type = 'text/plain'\n        resp.charset = \"KOI8-R\"\n        resp.text = \"текст\"\n\n        self.assertEqual(\"текст\", resp.text)\n        self.assertEqual(\"текст\".encode('koi8-r'), resp.body)\n        self.assertEqual(\"koi8-r\", resp.charset)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 254,
                                "end_lineno": 417,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "tests/test_web_response.py",
                            "type": "method",
                            "name": "TestResponse.test_set_text_with_charset",
                            "content": "class TestResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = Response()\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertIsNone(resp.body)\n        self.assertEqual(0, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('CONTENT-LENGTH', '0')]),\n                         resp.headers)\n\n    def test_ctor_with_headers_and_status(self):\n        resp = Response(body=b'body', status=201, headers={'Age': '12'})\n\n        self.assertEqual(201, resp.status)\n        self.assertEqual(b'body', resp.body)\n        self.assertEqual(4, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('AGE', '12'),\n                                                   ('CONTENT-LENGTH', '4')]),\n                         resp.headers)\n\n    def test_ctor_content_type(self):\n        resp = Response(content_type='application/json')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict([('CONTENT-TYPE', 'application/json'),\n                                      ('CONTENT-LENGTH', '0')]),\n            resp.headers)\n\n    def test_ctor_text_body_combined(self):\n        with self.assertRaises(ValueError):\n            Response(body=b'123', text='test text')\n\n    def test_ctor_text(self):\n        resp = Response(text='test text')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict(\n                [('CONTENT-TYPE', 'text/plain; charset=utf-8'),\n                 ('CONTENT-LENGTH', '9')]),\n            resp.headers)\n\n        self.assertEqual(resp.body, b'test text')\n        self.assertEqual(resp.text, 'test text')\n\n    def test_assign_nonbyteish_body(self):\n        resp = Response(body=b'data')\n\n        with self.assertRaises(TypeError):\n            resp.body = 123\n        self.assertEqual(b'data', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_assign_nonstr_text(self):\n        resp = Response(text='test')\n\n        with self.assertRaises(TypeError):\n            resp.text = b'123'\n        self.assertEqual(b'test', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_send_headers_for_empty_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response()\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_render_with_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response(body=b'data')\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 4\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\ndata')\n\n    def test_send_set_cookie_header(self):\n        resp = Response()\n        resp.cookies['name'] = 'value'\n\n        req = self.make_request('GET', '/')\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'SET-COOKIE: name=value\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_set_text_with_content_type(self):\n        resp = Response()\n        resp.content_type = \"text/html\"\n        resp.text = \"text\"\n\n        self.assertEqual(\"text\", resp.text)\n        self.assertEqual(b\"text\", resp.body)\n        self.assertEqual(\"text/html\", resp.content_type)\n\n    def test_set_text_with_charset(self):\n        resp = Response()\n        resp.content_type = 'text/plain'\n        resp.charset = \"KOI8-R\"\n        resp.text = \"текст\"\n\n        self.assertEqual(\"текст\", resp.text)\n        self.assertEqual(\"текст\".encode('koi8-r'), resp.body)\n        self.assertEqual(\"koi8-r\", resp.charset)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 409,
                            "end_lineno": 417,
                            "parent": {
                                "file_name": "tests/test_web_response.py",
                                "type": "class",
                                "name": "TestResponse",
                                "content": "class TestResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = Response()\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertIsNone(resp.body)\n        self.assertEqual(0, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('CONTENT-LENGTH', '0')]),\n                         resp.headers)\n\n    def test_ctor_with_headers_and_status(self):\n        resp = Response(body=b'body', status=201, headers={'Age': '12'})\n\n        self.assertEqual(201, resp.status)\n        self.assertEqual(b'body', resp.body)\n        self.assertEqual(4, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('AGE', '12'),\n                                                   ('CONTENT-LENGTH', '4')]),\n                         resp.headers)\n\n    def test_ctor_content_type(self):\n        resp = Response(content_type='application/json')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict([('CONTENT-TYPE', 'application/json'),\n                                      ('CONTENT-LENGTH', '0')]),\n            resp.headers)\n\n    def test_ctor_text_body_combined(self):\n        with self.assertRaises(ValueError):\n            Response(body=b'123', text='test text')\n\n    def test_ctor_text(self):\n        resp = Response(text='test text')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict(\n                [('CONTENT-TYPE', 'text/plain; charset=utf-8'),\n                 ('CONTENT-LENGTH', '9')]),\n            resp.headers)\n\n        self.assertEqual(resp.body, b'test text')\n        self.assertEqual(resp.text, 'test text')\n\n    def test_assign_nonbyteish_body(self):\n        resp = Response(body=b'data')\n\n        with self.assertRaises(TypeError):\n            resp.body = 123\n        self.assertEqual(b'data', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_assign_nonstr_text(self):\n        resp = Response(text='test')\n\n        with self.assertRaises(TypeError):\n            resp.text = b'123'\n        self.assertEqual(b'test', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_send_headers_for_empty_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response()\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_render_with_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response(body=b'data')\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 4\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\ndata')\n\n    def test_send_set_cookie_header(self):\n        resp = Response()\n        resp.cookies['name'] = 'value'\n\n        req = self.make_request('GET', '/')\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'SET-COOKIE: name=value\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_set_text_with_content_type(self):\n        resp = Response()\n        resp.content_type = \"text/html\"\n        resp.text = \"text\"\n\n        self.assertEqual(\"text\", resp.text)\n        self.assertEqual(b\"text\", resp.body)\n        self.assertEqual(\"text/html\", resp.content_type)\n\n    def test_set_text_with_charset(self):\n        resp = Response()\n        resp.content_type = 'text/plain'\n        resp.charset = \"KOI8-R\"\n        resp.text = \"текст\"\n\n        self.assertEqual(\"текст\", resp.text)\n        self.assertEqual(\"текст\".encode('koi8-r'), resp.body)\n        self.assertEqual(\"koi8-r\", resp.charset)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 254,
                                "end_lineno": 417,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "fe6325e7bc23037967f23e5e814783a17e263ffc^",
    "new_commit_hash": "fe6325e7bc23037967f23e5e814783a17e263ffc",
    "commit_message": "Convert response.charset to lowercase on assigning, add tests",
    "commit_date": "2014-12-13T13:53:37+02:00",
    "metadata": {}
}
{
    "repo_name": "aiohttp",
    "new_commit_hash": "fe6325e7bc23037967f23e5e814783a17e263ffc",
    "test_file_codes": [
        "import asyncio\nimport unittest\nfrom unittest import mock\nfrom aiohttp.multidict import MultiDict, CaseInsensitiveMultiDict\nfrom aiohttp.web import Request, StreamResponse, Response\nfrom aiohttp.protocol import RawRequestMessage, HttpVersion11\n\n\nclass TestStreamResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = StreamResponse()\n        self.assertEqual(200, resp.status)\n        self.assertIsNone(resp.keep_alive)\n\n    def test_content_length(self):\n        resp = StreamResponse()\n        self.assertIsNone(resp.content_length)\n\n    def test_content_length_setter(self):\n        resp = StreamResponse()\n\n        resp.content_length = 234\n        self.assertEqual(234, resp.content_length)\n\n    def test_drop_content_length_header_on_setting_len_to_None(self):\n        resp = StreamResponse()\n\n        resp.content_length = 1\n        self.assertEqual(\"1\", resp.headers['Content-Length'])\n        resp.content_length = None\n        self.assertNotIn('Content-Length', resp.headers)\n\n    def test_set_content_length_to_None_on_non_set(self):\n        resp = StreamResponse()\n\n        resp.content_length = None\n        self.assertNotIn('Content-Length', resp.headers)\n        resp.content_length = None\n        self.assertNotIn('Content-Length', resp.headers)\n\n    def test_setting_content_type(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        self.assertEqual('text/html', resp.headers['content-type'])\n\n    def test_setting_charset(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        resp.charset = 'koi8-r'\n        self.assertEqual('text/html; charset=koi8-r',\n                         resp.headers['content-type'])\n\n    def test_default_charset(self):\n        resp = StreamResponse()\n\n        self.assertIsNone(resp.charset)\n\n    def test_reset_charset(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        resp.charset = None\n        self.assertIsNone(resp.charset)\n\n    def test_reset_charset_after_setting(self):\n        resp = StreamResponse()\n\n        resp.content_type = 'text/html'\n        resp.charset = 'koi8-r'\n        resp.charset = None\n        self.assertIsNone(resp.charset)\n\n    def test_charset_without_content_type(self):\n        resp = StreamResponse()\n\n        with self.assertRaises(RuntimeError):\n            resp.charset = 'koi8-r'\n\n    @mock.patch('aiohttp.web.ResponseImpl')\n    def test_start(self, ResponseImpl):\n        req = self.make_request('GET', '/')\n        resp = StreamResponse()\n        self.assertIsNone(resp.keep_alive)\n\n        msg = resp.start(req)\n\n        self.assertTrue(msg.send_headers.called)\n        self.assertIs(msg, resp.start(req))\n\n        self.assertTrue(resp.keep_alive)\n\n        req2 = self.make_request('GET', '/')\n        with self.assertRaises(RuntimeError):\n            resp.start(req2)\n\n    def test_write_non_byteish(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        with self.assertRaises(TypeError):\n            resp.write(123)\n\n    def test_write_before_start(self):\n        resp = StreamResponse()\n\n        with self.assertRaises(RuntimeError):\n            resp.write(b'data')\n\n    def test_cannot_write_after_eof(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        resp.write(b'data')\n        self.writer.drain.return_value = ()\n        self.loop.run_until_complete(resp.write_eof())\n        self.writer.write.reset_mock()\n\n        with self.assertRaises(RuntimeError):\n            resp.write(b'next data')\n        self.assertFalse(self.writer.write.called)\n\n    def test_cannot_write_eof_before_headers(self):\n        resp = StreamResponse()\n\n        with self.assertRaises(RuntimeError):\n            self.loop.run_until_complete(resp.write_eof())\n\n    def test_cannot_write_eof_twice(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        resp.write(b'data')\n        self.writer.drain.return_value = ()\n        self.loop.run_until_complete(resp.write_eof())\n        self.assertTrue(self.writer.write.called)\n\n        self.writer.write.reset_mock()\n        self.loop.run_until_complete(resp.write_eof())\n        self.assertFalse(self.writer.write.called)\n\n    def test_write_returns_drain(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        self.assertEqual((), resp.write(b'data'))\n\n    def test_write_returns_empty_tuple_on_empty_data(self):\n        resp = StreamResponse()\n        resp.start(self.make_request('GET', '/'))\n\n        self.assertEqual((), resp.write(b''))\n\n    def test_force_close(self):\n        resp = StreamResponse()\n\n        self.assertIsNone(resp.keep_alive)\n        resp.force_close()\n        self.assertFalse(resp.keep_alive)\n\n    def test_response_cookies(self):\n        resp = StreamResponse()\n\n        self.assertEqual(resp.cookies, {})\n        self.assertEqual(str(resp.cookies), '')\n\n        resp.set_cookie('name', 'value')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=value')\n        resp.set_cookie('name', 'other_value')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=other_value')\n\n        resp.cookies['name'] = 'another_other_value'\n        resp.cookies['name']['max-age'] = 10\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=another_other_value; Max-Age=10')\n\n        resp.del_cookie('name')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=; Max-Age=0')\n\n        resp.set_cookie('name', 'value', domain='local.host')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; Domain=local.host')\n\n    def test_response_cookie_path(self):\n        resp = StreamResponse()\n\n        self.assertEqual(resp.cookies, {})\n\n        resp.set_cookie('name', 'value', path='/some/path')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; Path=/some/path')\n        resp.set_cookie('name', 'value', expires='123')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; expires=123;'\n                         ' Path=/some/path')\n        resp.set_cookie('name', 'value', domain='example.com',\n                        path='/home', expires='123', max_age='10',\n                        secure=True, httponly=True, version='2.0')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; '\n                         'Domain=example.com; '\n                         'expires=123; '\n                         'httponly; '\n                         'Max-Age=10; '\n                         'Path=/home; '\n                         'secure; '\n                         'Version=2.0')\n\n    def test_response_cookie__issue_del_cookie(self):\n        resp = StreamResponse()\n\n        self.assertEqual(resp.cookies, {})\n        self.assertEqual(str(resp.cookies), '')\n\n        resp.del_cookie('name')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=; Max-Age=0')\n\n    def test_cookie_set_after_del(self):\n        resp = StreamResponse()\n\n        resp.del_cookie('name')\n        resp.set_cookie('name', 'val')\n        # check for Max-Age dropped\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=val')\n\n    def test_set_status_with_reason(self):\n        resp = StreamResponse()\n\n        resp.set_status(200, \"Everithing is fine!\")\n        self.assertEqual(200, resp.status)\n        self.assertEqual(\"Everithing is fine!\", resp.reason)\n\n\nclass TestResponse(unittest.TestCase):\n\n    def setUp(self):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n\n    def tearDown(self):\n        self.loop.close()\n\n    def make_request(self, method, path, headers=MultiDict()):\n        self.app = mock.Mock()\n        message = RawRequestMessage(method, path, HttpVersion11, headers,\n                                    False, False)\n        self.payload = mock.Mock()\n        self.transport = mock.Mock()\n        self.writer = mock.Mock()\n        req = Request(self.app, message, self.payload,\n                      self.transport, self.writer, 15)\n        return req\n\n    def test_ctor(self):\n        resp = Response()\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertIsNone(resp.body)\n        self.assertEqual(0, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('CONTENT-LENGTH', '0')]),\n                         resp.headers)\n\n    def test_ctor_with_headers_and_status(self):\n        resp = Response(body=b'body', status=201, headers={'Age': '12'})\n\n        self.assertEqual(201, resp.status)\n        self.assertEqual(b'body', resp.body)\n        self.assertEqual(4, resp.content_length)\n        self.assertEqual(CaseInsensitiveMultiDict([('AGE', '12'),\n                                                   ('CONTENT-LENGTH', '4')]),\n                         resp.headers)\n\n    def test_ctor_content_type(self):\n        resp = Response(content_type='application/json')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict([('CONTENT-TYPE', 'application/json'),\n                                      ('CONTENT-LENGTH', '0')]),\n            resp.headers)\n\n    def test_ctor_text_body_combined(self):\n        with self.assertRaises(ValueError):\n            Response(body=b'123', text='test text')\n\n    def test_ctor_text(self):\n        resp = Response(text='test text')\n\n        self.assertEqual(200, resp.status)\n        self.assertEqual('OK', resp.reason)\n        self.assertEqual(\n            CaseInsensitiveMultiDict(\n                [('CONTENT-TYPE', 'text/plain; charset=utf-8'),\n                 ('CONTENT-LENGTH', '9')]),\n            resp.headers)\n\n        self.assertEqual(resp.body, b'test text')\n        self.assertEqual(resp.text, 'test text')\n\n    def test_assign_nonbyteish_body(self):\n        resp = Response(body=b'data')\n\n        with self.assertRaises(TypeError):\n            resp.body = 123\n        self.assertEqual(b'data', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_assign_nonstr_text(self):\n        resp = Response(text='test')\n\n        with self.assertRaises(TypeError):\n            resp.text = b'123'\n        self.assertEqual(b'test', resp.body)\n        self.assertEqual(4, resp.content_length)\n\n    def test_send_headers_for_empty_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response()\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_render_with_body(self):\n        req = self.make_request('GET', '/')\n        resp = Response(body=b'data')\n\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 4\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\ndata')\n\n    def test_send_set_cookie_header(self):\n        resp = Response()\n        resp.cookies['name'] = 'value'\n\n        req = self.make_request('GET', '/')\n        self.writer.drain.return_value = ()\n        buf = b''\n\n        def append(data):\n            nonlocal buf\n            buf += data\n\n        self.writer.write.side_effect = append\n\n        resp.start(req)\n        self.loop.run_until_complete(resp.write_eof())\n        txt = buf.decode('utf8')\n        self.assertRegex(txt, 'HTTP/1.1 200 OK\\r\\nCONTENT-LENGTH: 0\\r\\n'\n                         'SET-COOKIE: name=value\\r\\n'\n                         'CONNECTION: keep-alive\\r\\n'\n                         'DATE: .+\\r\\nSERVER: .+\\r\\n\\r\\n')\n\n    def test_set_text_with_content_type(self):\n        resp = Response()\n        resp.content_type = \"text/html\"\n        resp.text = \"text\"\n\n        self.assertEqual(\"text\", resp.text)\n        self.assertEqual(b\"text\", resp.body)\n        self.assertEqual(\"text/html\", resp.content_type)\n\n    def test_set_text_with_charset(self):\n        resp = Response()\n        resp.content_type = 'text/plain'\n        resp.charset = \"KOI8-R\"\n        resp.text = \"текст\"\n\n        self.assertEqual(\"текст\", resp.text)\n        self.assertEqual(\"текст\".encode('koi8-r'), resp.body)\n        self.assertEqual(\"koi8-r\", resp.charset)\n"
    ],
    "test_file_names": [
        "test_1.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Transforming aiohttp/worker.py\nTransforming aiohttp/server.py\nTransforming aiohttp/client.py\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nmake: *** No rule to make target '.develop'.  Stop.\nResolved 26 packages in 25ms\nInstalled 26 packages in 38ms\n + async-generator==1.10\n + async-timeout==5.0.1\n + brotlipy==0.7.0\n + cffi==1.17.1\n + chardet==5.2.0\n + coverage==7.6.10\n + cryptography==44.0.0\n + cython==3.0.11\n + exceptiongroup==1.2.2\n + gunicorn==23.0.0\n + idna==3.10\n + iniconfig==2.0.0\n + multdict==0.0.5\n + multidict==6.1.0\n + packaging==24.2\n + pluggy==1.5.0\n + propcache==0.2.1\n + pycparser==2.22\n + pytest==8.3.4\n + pytest-asyncio==0.25.1\n + pytest-cov==6.0.0\n + pytest-mock==3.14.0\n + tomli==2.2.1\n + trustme==1.2.1\n + typing-extensions==4.12.2\n + yarl==1.18.3\n",
    "new_commit_res_code": 1,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fe6325e7bc23037967f23e5e814783a17e263ffc\nplugins: mock-3.14.0, asyncio-0.25.1, cov-6.0.0\nasyncio: mode=strict, asyncio_default_fixture_loop_scope=None\ncollected 37 items\n\nr2e_tests/test_1.py ......F......FFF.....................                [100%]\n\n=================================== FAILURES ===================================\n_________________ TestStreamResponse.test_cookie_set_after_del _________________\n\nself = <r2e_tests.test_1.TestStreamResponse testMethod=test_cookie_set_after_del>\n\n    def test_cookie_set_after_del(self):\n        resp = StreamResponse()\n    \n        resp.del_cookie('name')\n        resp.set_cookie('name', 'val')\n        # check for Max-Age dropped\n>       self.assertEqual(str(resp.cookies), 'Set-Cookie: name=val')\nE       AssertionError: 'Set-Cookie: name=val; Max-Age=0' != 'Set-Cookie: name=val'\nE       - Set-Cookie: name=val; Max-Age=0\nE       ?                     -----------\nE       + Set-Cookie: name=val\n\nr2e_tests/test_1.py:244: AssertionError\n__________ TestStreamResponse.test_response_cookie__issue_del_cookie ___________\n\nself = <r2e_tests.test_1.TestStreamResponse testMethod=test_response_cookie__issue_del_cookie>\n\n    def test_response_cookie__issue_del_cookie(self):\n        resp = StreamResponse()\n    \n        self.assertEqual(resp.cookies, {})\n        self.assertEqual(str(resp.cookies), '')\n    \n        resp.del_cookie('name')\n>       self.assertEqual(str(resp.cookies), 'Set-Cookie: name=; Max-Age=0')\nE       AssertionError: 'Set-Cookie: name=\"\"; Max-Age=0' != 'Set-Cookie: name=; Max-Age=0'\nE       - Set-Cookie: name=\"\"; Max-Age=0\nE       ?                  --\nE       + Set-Cookie: name=; Max-Age=0\n\nr2e_tests/test_1.py:236: AssertionError\n_________________ TestStreamResponse.test_response_cookie_path _________________\n\nself = <r2e_tests.test_1.TestStreamResponse testMethod=test_response_cookie_path>\n\n    def test_response_cookie_path(self):\n        resp = StreamResponse()\n    \n        self.assertEqual(resp.cookies, {})\n    \n        resp.set_cookie('name', 'value', path='/some/path')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; Path=/some/path')\n        resp.set_cookie('name', 'value', expires='123')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; expires=123;'\n                         ' Path=/some/path')\n        resp.set_cookie('name', 'value', domain='example.com',\n                        path='/home', expires='123', max_age='10',\n                        secure=True, httponly=True, version='2.0')\n>       self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; '\n                         'Domain=example.com; '\n                         'expires=123; '\n                         'httponly; '\n                         'Max-Age=10; '\n                         'Path=/home; '\n                         'secure; '\n                         'Version=2.0')\nE       AssertionError: 'Set-[44 chars]res=123; HttpOnly; Max-Age=10; Path=/home; Secure; Version=2.0' != 'Set-[44 chars]res=123; httponly; Max-Age=10; Path=/home; secure; Version=2.0'\nE       - Set-Cookie: name=value; Domain=example.com; expires=123; HttpOnly; Max-Age=10; Path=/home; Secure; Version=2.0\nE       ?                                                          ^   ^                             ^\nE       + Set-Cookie: name=value; Domain=example.com; expires=123; httponly; Max-Age=10; Path=/home; secure; Version=2.0\nE       ?                                                          ^   ^                             ^\n\nr2e_tests/test_1.py:219: AssertionError\n___________________ TestStreamResponse.test_response_cookies ___________________\n\nself = <r2e_tests.test_1.TestStreamResponse testMethod=test_response_cookies>\n\n    def test_response_cookies(self):\n        resp = StreamResponse()\n    \n        self.assertEqual(resp.cookies, {})\n        self.assertEqual(str(resp.cookies), '')\n    \n        resp.set_cookie('name', 'value')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=value')\n        resp.set_cookie('name', 'other_value')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=other_value')\n    \n        resp.cookies['name'] = 'another_other_value'\n        resp.cookies['name']['max-age'] = 10\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=another_other_value; Max-Age=10')\n    \n        resp.del_cookie('name')\n>       self.assertEqual(str(resp.cookies), 'Set-Cookie: name=; Max-Age=0')\nE       AssertionError: 'Set-Cookie: name=\"\"; Max-Age=0' != 'Set-Cookie: name=; Max-Age=0'\nE       - Set-Cookie: name=\"\"; Max-Age=0\nE       ?                  --\nE       + Set-Cookie: name=; Max-Age=0\n\nr2e_tests/test_1.py:198: AssertionError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_cannot_write_after_eof\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_cannot_write_eof_before_headers\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_cannot_write_eof_twice\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_charset_without_content_type\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_content_length\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_content_length_setter\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_ctor\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_default_charset\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_drop_content_length_header_on_setting_len_to_None\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_force_close\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_reset_charset\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_reset_charset_after_setting\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_set_content_length_to_None_on_non_set\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_set_status_with_reason\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_setting_charset\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_setting_content_type\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_start\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_write_before_start\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_write_non_byteish\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_write_returns_drain\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_write_returns_empty_tuple_on_empty_data\nPASSED r2e_tests/test_1.py::TestResponse::test_assign_nonbyteish_body\nPASSED r2e_tests/test_1.py::TestResponse::test_assign_nonstr_text\nPASSED r2e_tests/test_1.py::TestResponse::test_ctor\nPASSED r2e_tests/test_1.py::TestResponse::test_ctor_content_type\nPASSED r2e_tests/test_1.py::TestResponse::test_ctor_text\nPASSED r2e_tests/test_1.py::TestResponse::test_ctor_text_body_combined\nPASSED r2e_tests/test_1.py::TestResponse::test_ctor_with_headers_and_status\nPASSED r2e_tests/test_1.py::TestResponse::test_render_with_body\nPASSED r2e_tests/test_1.py::TestResponse::test_send_headers_for_empty_body\nPASSED r2e_tests/test_1.py::TestResponse::test_send_set_cookie_header\nPASSED r2e_tests/test_1.py::TestResponse::test_set_text_with_charset\nPASSED r2e_tests/test_1.py::TestResponse::test_set_text_with_content_type\nFAILED r2e_tests/test_1.py::TestStreamResponse::test_cookie_set_after_del - A...\nFAILED r2e_tests/test_1.py::TestStreamResponse::test_response_cookie__issue_del_cookie\nFAILED r2e_tests/test_1.py::TestStreamResponse::test_response_cookie_path - A...\nFAILED r2e_tests/test_1.py::TestStreamResponse::test_response_cookies - Asser...\n========================= 4 failed, 33 passed in 0.46s =========================\n",
    "new_commit_res_stderr": "/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fe6325e7bc23037967f23e5e814783a17e263ffc/.venv/lib/python3.9/site-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option \"asyncio_default_fixture_loop_scope\" is unset.\nThe event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: \"function\", \"class\", \"module\", \"package\", \"session\"\n\n  warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))\n",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fe6325e7bc23037967f23e5e814783a17e263ffc\nplugins: mock-3.14.0, asyncio-0.25.1, cov-6.0.0\nasyncio: mode=strict, asyncio_default_fixture_loop_scope=None\ncollected 37 items\n\nr2e_tests/test_1.py ......F......FFF...................F.                [100%]\n\n=================================== FAILURES ===================================\n_________________ TestStreamResponse.test_cookie_set_after_del _________________\n\nself = <r2e_tests.test_1.TestStreamResponse testMethod=test_cookie_set_after_del>\n\n    def test_cookie_set_after_del(self):\n        resp = StreamResponse()\n    \n        resp.del_cookie('name')\n        resp.set_cookie('name', 'val')\n        # check for Max-Age dropped\n>       self.assertEqual(str(resp.cookies), 'Set-Cookie: name=val')\nE       AssertionError: 'Set-Cookie: name=val; Max-Age=0' != 'Set-Cookie: name=val'\nE       - Set-Cookie: name=val; Max-Age=0\nE       ?                     -----------\nE       + Set-Cookie: name=val\n\nr2e_tests/test_1.py:244: AssertionError\n__________ TestStreamResponse.test_response_cookie__issue_del_cookie ___________\n\nself = <r2e_tests.test_1.TestStreamResponse testMethod=test_response_cookie__issue_del_cookie>\n\n    def test_response_cookie__issue_del_cookie(self):\n        resp = StreamResponse()\n    \n        self.assertEqual(resp.cookies, {})\n        self.assertEqual(str(resp.cookies), '')\n    \n        resp.del_cookie('name')\n>       self.assertEqual(str(resp.cookies), 'Set-Cookie: name=; Max-Age=0')\nE       AssertionError: 'Set-Cookie: name=\"\"; Max-Age=0' != 'Set-Cookie: name=; Max-Age=0'\nE       - Set-Cookie: name=\"\"; Max-Age=0\nE       ?                  --\nE       + Set-Cookie: name=; Max-Age=0\n\nr2e_tests/test_1.py:236: AssertionError\n_________________ TestStreamResponse.test_response_cookie_path _________________\n\nself = <r2e_tests.test_1.TestStreamResponse testMethod=test_response_cookie_path>\n\n    def test_response_cookie_path(self):\n        resp = StreamResponse()\n    \n        self.assertEqual(resp.cookies, {})\n    \n        resp.set_cookie('name', 'value', path='/some/path')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; Path=/some/path')\n        resp.set_cookie('name', 'value', expires='123')\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; expires=123;'\n                         ' Path=/some/path')\n        resp.set_cookie('name', 'value', domain='example.com',\n                        path='/home', expires='123', max_age='10',\n                        secure=True, httponly=True, version='2.0')\n>       self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=value; '\n                         'Domain=example.com; '\n                         'expires=123; '\n                         'httponly; '\n                         'Max-Age=10; '\n                         'Path=/home; '\n                         'secure; '\n                         'Version=2.0')\nE       AssertionError: 'Set-[44 chars]res=123; HttpOnly; Max-Age=10; Path=/home; Secure; Version=2.0' != 'Set-[44 chars]res=123; httponly; Max-Age=10; Path=/home; secure; Version=2.0'\nE       - Set-Cookie: name=value; Domain=example.com; expires=123; HttpOnly; Max-Age=10; Path=/home; Secure; Version=2.0\nE       ?                                                          ^   ^                             ^\nE       + Set-Cookie: name=value; Domain=example.com; expires=123; httponly; Max-Age=10; Path=/home; secure; Version=2.0\nE       ?                                                          ^   ^                             ^\n\nr2e_tests/test_1.py:219: AssertionError\n___________________ TestStreamResponse.test_response_cookies ___________________\n\nself = <r2e_tests.test_1.TestStreamResponse testMethod=test_response_cookies>\n\n    def test_response_cookies(self):\n        resp = StreamResponse()\n    \n        self.assertEqual(resp.cookies, {})\n        self.assertEqual(str(resp.cookies), '')\n    \n        resp.set_cookie('name', 'value')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=value')\n        resp.set_cookie('name', 'other_value')\n        self.assertEqual(str(resp.cookies), 'Set-Cookie: name=other_value')\n    \n        resp.cookies['name'] = 'another_other_value'\n        resp.cookies['name']['max-age'] = 10\n        self.assertEqual(str(resp.cookies),\n                         'Set-Cookie: name=another_other_value; Max-Age=10')\n    \n        resp.del_cookie('name')\n>       self.assertEqual(str(resp.cookies), 'Set-Cookie: name=; Max-Age=0')\nE       AssertionError: 'Set-Cookie: name=\"\"; Max-Age=0' != 'Set-Cookie: name=; Max-Age=0'\nE       - Set-Cookie: name=\"\"; Max-Age=0\nE       ?                  --\nE       + Set-Cookie: name=; Max-Age=0\n\nr2e_tests/test_1.py:198: AssertionError\n___________________ TestResponse.test_set_text_with_charset ____________________\n\nself = <r2e_tests.test_1.TestResponse testMethod=test_set_text_with_charset>\n\n    def test_set_text_with_charset(self):\n        resp = Response()\n        resp.content_type = 'text/plain'\n        resp.charset = \"KOI8-R\"\n        resp.text = \"текст\"\n    \n        self.assertEqual(\"текст\", resp.text)\n        self.assertEqual(\"текст\".encode('koi8-r'), resp.body)\n>       self.assertEqual(\"koi8-r\", resp.charset)\nE       AssertionError: 'koi8-r' != 'KOI8-R'\nE       - koi8-r\nE       + KOI8-R\n\nr2e_tests/test_1.py:417: AssertionError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_cannot_write_after_eof\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_cannot_write_eof_before_headers\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_cannot_write_eof_twice\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_charset_without_content_type\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_content_length\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_content_length_setter\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_ctor\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_default_charset\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_drop_content_length_header_on_setting_len_to_None\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_force_close\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_reset_charset\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_reset_charset_after_setting\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_set_content_length_to_None_on_non_set\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_set_status_with_reason\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_setting_charset\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_setting_content_type\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_start\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_write_before_start\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_write_non_byteish\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_write_returns_drain\nPASSED r2e_tests/test_1.py::TestStreamResponse::test_write_returns_empty_tuple_on_empty_data\nPASSED r2e_tests/test_1.py::TestResponse::test_assign_nonbyteish_body\nPASSED r2e_tests/test_1.py::TestResponse::test_assign_nonstr_text\nPASSED r2e_tests/test_1.py::TestResponse::test_ctor\nPASSED r2e_tests/test_1.py::TestResponse::test_ctor_content_type\nPASSED r2e_tests/test_1.py::TestResponse::test_ctor_text\nPASSED r2e_tests/test_1.py::TestResponse::test_ctor_text_body_combined\nPASSED r2e_tests/test_1.py::TestResponse::test_ctor_with_headers_and_status\nPASSED r2e_tests/test_1.py::TestResponse::test_render_with_body\nPASSED r2e_tests/test_1.py::TestResponse::test_send_headers_for_empty_body\nPASSED r2e_tests/test_1.py::TestResponse::test_send_set_cookie_header\nPASSED r2e_tests/test_1.py::TestResponse::test_set_text_with_content_type\nFAILED r2e_tests/test_1.py::TestStreamResponse::test_cookie_set_after_del - A...\nFAILED r2e_tests/test_1.py::TestStreamResponse::test_response_cookie__issue_del_cookie\nFAILED r2e_tests/test_1.py::TestStreamResponse::test_response_cookie_path - A...\nFAILED r2e_tests/test_1.py::TestStreamResponse::test_response_cookies - Asser...\nFAILED r2e_tests/test_1.py::TestResponse::test_set_text_with_charset - Assert...\n========================= 5 failed, 32 passed in 0.34s =========================\n",
    "old_commit_res_stderr": "/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fe6325e7bc23037967f23e5e814783a17e263ffc/.venv/lib/python3.9/site-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option \"asyncio_default_fixture_loop_scope\" is unset.\nThe event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: \"function\", \"class\", \"module\", \"package\", \"session\"\n\n  warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))\n"
}
CHANGES.txt aiohttp/web.py docs/web.rst tests/test_web_response.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Charset Case Sensitivity Causes Assertion Failures in Response Handling

**Description:**
When setting the `charset` attribute of a response, using uppercase characters leads to unexpected behavior in tests. Specifically, assigning a charset value like `"KOI8-R"` does not match the expected lowercase value `"koi8-r"`, resulting in assertion failures.

**Example Code:**
```python
def test_set_text_with_charset(self):
    resp = Response()
    resp.content_type = 'text/plain'
    resp.charset = "KOI8-R"
    resp.text = "текст"

    self.assertEqual("текст", resp.text)
    self.assertEqual("текст".encode('koi8-r'), resp.body)
    self.assertEqual("koi8-r", resp.charset)  # This assertion fails
```

**Expected Behavior:**
The `charset` attribute should be normalized to lowercase, ensuring that the assigned value `"KOI8-R"` becomes `"koi8-r"`. This normalization allows tests to reliably compare the `charset` value without case sensitivity issues.

**Actual Behavior:**
Assigning `"KOI8-R"` to `charset` retains the uppercase characters, causing the assertion `self.assertEqual("koi8-r", resp.charset)` to fail because `"KOI8-R" != "koi8-r"`.

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

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

--- BEGIN FILE ---
```
### aiohttp/web.py
import abc
import asyncio
import binascii
import collections
import mimetypes
import cgi
import http.cookies
import io
import json
import re
import os

from urllib.parse import urlsplit, parse_qsl, urlencode, unquote

from .abc import AbstractRouter, AbstractMatchInfo
from .helpers import reify
from .log import web_logger
from .multidict import (CaseInsensitiveMultiDict,
                        CaseInsensitiveMutableMultiDict,
                        MultiDict,
                        MutableMultiDict)
from .protocol import Response as ResponseImpl, HttpVersion, HttpVersion11
from .server import ServerHttpProtocol
from .streams import EOF_MARKER


__all__ = [
    'Application',
    'HttpVersion',
    'RequestHandler',
    'RequestHandlerFactory',
    'Request',
    'StreamResponse',
    'Response',
    'UrlDispatcher',
    'UrlMappingMatchInfo',
    'HTTPException',
    'HTTPError',
    'HTTPRedirection',
    'HTTPSuccessful',
    'HTTPOk',
    'HTTPCreated',
    'HTTPAccepted',
    'HTTPNonAuthoritativeInformation',
    'HTTPNoContent',
    'HTTPResetContent',
    'HTTPPartialContent',
    'HTTPMultipleChoices',
    'HTTPMovedPermanently',
    'HTTPFound',
    'HTTPSeeOther',
    'HTTPNotModified',
    'HTTPUseProxy',
    'HTTPTemporaryRedirect',
    'HTTPClientError',
    'HTTPBadRequest',
    'HTTPUnauthorized',
    'HTTPPaymentRequired',
    'HTTPForbidden',
    'HTTPNotFound',
    'HTTPMethodNotAllowed',
    'HTTPNotAcceptable',
    'HTTPProxyAuthenticationRequired',
    'HTTPRequestTimeout',
    'HTTPConflict',
    'HTTPGone',
    'HTTPLengthRequired',
    'HTTPPreconditionFailed',
    'HTTPRequestEntityTooLarge',
    'HTTPRequestURITooLong',
    'HTTPUnsupportedMediaType',
    'HTTPRequestRangeNotSatisfiable',
    'HTTPExpectationFailed',
    'HTTPServerError',
    'HTTPInternalServerError',
    'HTTPNotImplemented',
    'HTTPBadGateway',
    'HTTPServiceUnavailable',
    'HTTPGatewayTimeout',
    'HTTPVersionNotSupported',
]


sentinel = object()


class HeadersMixin:

    _content_type = None
    _content_dict = None
    _stored_content_type = sentinel

    def _parse_content_type(self, raw):
        self._stored_content_type = raw
        if raw is None:
            # default value according to RFC 2616
            self._content_type = 'application/octet-stream'
            self._content_dict = {}
        else:
            self._content_type, self._content_dict = cgi.parse_header(raw)

    @property
    def content_type(self):
        """The value of content part for Content-Type HTTP header."""
        raw = self.headers.get('Content-Type')
        if self._stored_content_type != raw:
            self._parse_content_type(raw)
        return self._content_type

    @property
    def charset(self):
        """The value of charset part for Content-Type HTTP header."""
        raw = self.headers.get('Content-Type')
        if self._stored_content_type != raw:
            self._parse_content_type(raw)
        return self._content_dict.get('charset')

    @property
    def content_length(self):
        """The value of Content-Length HTTP header."""
        l = self.headers.get('Content-Length')
        if l is None:
            return None
        else:
            return int(l)


FileField = collections.namedtuple('Field', 'name filename file content_type')


############################################################
# HTTP Request
############################################################


class Request(HeadersMixin):

    def __init__(self, app, message, payload, transport, writer,
                 keep_alive_timeout):
        self._app = app
        self._version = message.version
        self._transport = transport
        self._writer = writer
        self._method = message.method
        self._host = message.headers.get('HOST')
        self._path_qs = message.path
        res = urlsplit(message.path)
        self._path = unquote(res.path)
        self._query_string = res.query
        self._post = None
        self._post_files_cache = None
        self._headers = CaseInsensitiveMultiDict._from_uppercase_multidict(
            message.headers)

        if self._version < HttpVersion11:
            self._keep_alive = False
        elif message.should_close:
            self._keep_alive = False
        else:
            self._keep_alive = bool(keep_alive_timeout)

        # matchdict, route_name, handler
        # or information about traversal lookup
        self._match_info = None  # initialized after route resolving

        self._payload = payload
        self._cookies = None

    @property
    def method(self):
        """Read only property for getting HTTP method.

        The value is upper-cased str like 'GET', 'POST', 'PUT' etc.
        """
        return self._method

    @property
    def version(self):
        """Read only property for getting HTTP version of request.

        Returns aiohttp.protocol.HttpVersion instance.
        """
        return self._version

    @property
    def host(self):
        """Read only property for getting *HOST* header of request.

        Returns str or None if HTTP request has no HOST header.
        """
        return self._host

    @property
    def path_qs(self):
        """The URL including PATH_INFO and the query string.

        E.g, /app/blog?id=10
        """
        return self._path_qs

    @property
    def path(self):
        """The URL including *PATH INFO* without the host or scheme.

        E.g., ``/app/blog``
        """
        return self._path

    @property
    def query_string(self):
        """The query string in the URL.

        E.g., id=10
        """
        return self._query_string

    @reify
    def GET(self):
        """A multidict with all the variables in the query string.

        Lazy property.
        """
        return MultiDict(parse_qsl(self._query_string))

    @reify
    def POST(self):
        """A multidict with all the variables in the POST parameters.

        post() methods has to be called before using this attribute.
        """
        if self._post is None:
            raise RuntimeError("POST is not available before post()")
        return self._post

    @property
    def headers(self):
        """A case-insensitive multidict with all headers.

        Lazy property.
        """
        return self._headers

    @property
    def keep_alive(self):
        """Is keepalive enabled by client?"""
        return self._keep_alive

    @property
    def match_info(self):
        """Result of route resolving."""
        return self._match_info

    @property
    def app(self):
        """Application instance."""
        return self._app

    @property
    def transport(self):
        """Transport used for request processing."""
        return self._transport

    @property
    def cookies(self):
        """Return request cookies.

        A read-only dictionary-like object.
        """
        if self._cookies is None:
            raw = self.headers.get('COOKIE', '')
            parsed = http.cookies.SimpleCookie(raw)
            self._cookies = MultiDict({key: val.value
                                       for key, val in parsed.items()})
        return self._cookies

    @property
    def payload(self):
        """Return raw paiload stream."""
        return self._payload

    @asyncio.coroutine
    def release(self):
        """Release request.

        Eat unread part of HTTP BODY if present.
        """
        chunk = yield from self._payload.readany()
        while chunk is not EOF_MARKER or chunk:
            chunk = yield from self._payload.readany()

    @asyncio.coroutine
    def read(self):
        """Read request body if present.

        Returns bytes object with full request content.
        """
        body = bytearray()
        while True:
            chunk = yield from self._payload.readany()
            body.extend(chunk)
            if chunk is EOF_MARKER:
                break
        return bytes(body)

    @asyncio.coroutine
    def text(self):
        """Return BODY as text using encoding from .charset."""
        bytes_body = yield from self.read()
        encoding = self.charset or 'utf-8'
        return bytes_body.decode(encoding)

    @asyncio.coroutine
    def json(self, *, loader=json.loads):
        """Return BODY as JSON."""
        body = yield from self.text()
        return loader(body)

    @asyncio.coroutine
    def post(self):
        """Return POST parameters."""
        if self._post is not None:
            return self._post
        if self.method not in ('POST', 'PUT', 'PATCH'):
            self._post = MultiDict()
            return self._post

        content_type = self.content_type
        if (content_type not in ('',
                                 'application/x-www-form-urlencoded',
                                 'multipart/form-data')):
            self._post = MultiDict()
            return self._post

        body = yield from self.read()
        content_charset = self.charset or 'utf-8'

        environ = {'REQUEST_METHOD': self.method,
                   'CONTENT_LENGTH': str(len(body)),
                   'QUERY_STRING': '',
                   'CONTENT_TYPE': self.headers.get('CONTENT-TYPE')}

        fs = cgi.FieldStorage(fp=io.BytesIO(body),
                              environ=environ,
                              keep_blank_values=True,
                              encoding=content_charset)

        supported_tranfer_encoding = {
            'base64': binascii.a2b_base64,
            'quoted-printable': binascii.a2b_qp
        }

        out = MutableMultiDict()
        for field in fs.list or ():
            transfer_encoding = field.headers.get(
                'Content-Transfer-Encoding', None)
            if field.filename:
                ff = FileField(field.name,
                               field.filename,
                               field.file,  # N.B. file closed error
                               field.type)
                if self._post_files_cache is None:
                    self._post_files_cache = {}
                self._post_files_cache[field.name] = field
                out.add(field.name, ff)
            else:
                value = field.value
                if transfer_encoding in supported_tranfer_encoding:
                    # binascii accepts bytes
                    value = value.encode('utf-8')
                    value = supported_tranfer_encoding[
                        transfer_encoding](value)
                out.add(field.name, value)

        self._post = MultiDict(out.items(getall=True))
        return self._post

    # @asyncio.coroutine
    # def start_websocket(self):
    #     """Upgrade connection to websocket.

    #     Returns (reader, writer) pair.
    #     """

    #     upgrade = 'websocket' in message.headers.get('UPGRADE', '').lower()
    #     if not upgrade:
    #         pass


############################################################
# HTTP Response classes
############################################################


class StreamResponse(HeadersMixin):

    def __init__(self, *, status=200, reason=None):
        self._body = None
        self._keep_alive = None
        self._headers = CaseInsensitiveMutableMultiDict()
        self._cookies = http.cookies.SimpleCookie()
        self.set_status(status, reason)

        self._req = None
        self._resp_impl = None
        self._eof_sent = False

    def _copy_cookies(self):
        for cookie in self._cookies.values():
            value = cookie.output(header='')[1:]
            self.headers.add('Set-Cookie', value)

    @property
    def status(self):
        return self._status

    @property
    def reason(self):
        return self._reason

    def set_status(self, status, reason=None):
        self._status = int(status)
        if reason is None:
            reason = ResponseImpl.calc_reason(status)
        self._reason = reason

    @property
    def keep_alive(self):
        return self._keep_alive

    def force_close(self):
        self._keep_alive = False

    @property
    def headers(self):
        return self._headers

    @property
    def cookies(self):
        return self._cookies

    def set_cookie(self, name, value, *, expires=None,
                   domain=None, max_age=None, path=None,
                   secure=None, httponly=None, version=None):
        """Set or update response cookie.

        Sets new cookie or updates existent with new value.
        Also updates only those params which are not None.
        """

        old = self._cookies.get(name)
        if old is not None and old.coded_value == '':
            # deleted cookie
            self._cookies.pop(name, None)

        self._cookies[name] = value
        c = self._cookies[name]
        if expires is not None:
            c['expires'] = expires
        if domain is not None:
            c['domain'] = domain
        if max_age is not None:
            c['max-age'] = max_age
        if path is not None:
            c['path'] = path
        if secure is not None:
            c['secure'] = secure
        if httponly is not None:
            c['httponly'] = httponly
        if version is not None:
            c['version'] = version

    def del_cookie(self, name, *, domain=None, path=None):
        """Delete cookie.

        Creates new empty expired cookie.
        """
        # TODO: do we need domain/path here?
        self._cookies.pop(name, None)
        self.set_cookie(name, '', max_age=0, domain=domain, path=path)

    @property
    def content_length(self):
        # Just a placeholder for adding setter
        return super().content_length

    @content_length.setter
    def content_length(self, value):
        if value is not None:
            value = int(value)
            # TODO: raise error if chunked enabled
            self.headers['Content-Length'] = str(value)
        elif 'Content-Length' in self.headers:
            del self.headers['Content-Length']

    @property
    def content_type(self):
        # Just a placeholder for adding setter
        return super().content_type

    @content_type.setter
    def content_type(self, value):
        self.content_type  # read header values if needed
        self._content_type = str(value)
        self._generate_content_type_header()

    @property
    def charset(self):
        # Just a placeholder for adding setter
        return super().charset

    @charset.setter
    def charset(self, value):
        ctype = self.content_type  # read header values if needed
        if ctype == 'application/octet-stream':
            raise RuntimeError("Setting charset for application/octet-stream "
                               "doesn't make sense, setup content_type first")
        if value is None:
            self._content_dict.pop('charset', None)
        else:
            self._content_dict['charset'] = str(value)
        self._generate_content_type_header()

    def _generate_content_type_header(self):
        params = '; '.join("%s=%s" % i for i in self._content_dict.items())
        if params:
            ctype = self._content_type + '; ' + params
        else:
            ctype = self._content_type
        self.headers['Content-Type'] = ctype

    def start(self, request):
        if self._resp_impl is not None:
            if self._req is not request:
                raise RuntimeError(
                    'Response has been started with different request.')
            return self._resp_impl

        self._req = request

        keep_alive = self._keep_alive
        if keep_alive is None:
            keep_alive = request.keep_alive
        self._keep_alive = keep_alive

        resp_impl = self._resp_impl = ResponseImpl(
            request._writer,
            self._status,
            request.version,
            not keep_alive,
            self._reason)

        self._copy_cookies()

        headers = self.headers.items(getall=True)
        for key, val in headers:
            resp_impl.add_header(key, val)

        resp_impl.send_headers()
        return resp_impl

    def write(self, data):
        if not isinstance(data, (bytes, bytearray, memoryview)):
            raise TypeError('data argument must be byte-ish (%r)',
                            type(data))

        if self._eof_sent:
            raise RuntimeError("Cannot call write() after write_eof()")
        if self._resp_impl is None:
            raise RuntimeError("Cannot call write() before start()")

        if data:
            return self._resp_impl.write(data)
        else:
            return ()

    @asyncio.coroutine
    def write_eof(self):
        if self._eof_sent:
            return
        if self._resp_impl is None:
            raise RuntimeError("Response has not been started")

        yield from self._resp_impl.write_eof()
        self._eof_sent = True


class Response(StreamResponse):

    def __init__(self, *, body=None, status=200,
                 reason=None, headers=None,
                 text=None, content_type=None):
        super().__init__(status=status, reason=reason)

        if headers is not None:
            self.headers.extend(headers)
        if content_type:
            self.content_type = content_type

        if body is not None and text is not None:
            raise ValueError("body and text are not allowed together.")
        elif body is not None:
            self.body = body
        elif text is not None:
            self.text = text
        else:
            self.body = None

    @property
    def body(self):
        return self._body

    @body.setter
    def body(self, body):
        if body is not None and not isinstance(body, bytes):
            raise TypeError('body argument must be bytes (%r)', type(body))
        self._body = body
        if body is not None:
            self.content_length = len(body)
        else:
            self.content_length = 0

    @property
    def text(self):
        return self._body.decode(self.charset or 'utf-8')

    @text.setter
    def text(self, text):
        if text is not None and not isinstance(text, str):
            raise TypeError('text argument must be str (%r)', type(text))

        if self.content_type == 'application/octet-stream':
            self.content_type = 'text/plain'
        if self.charset is None:
            self.charset = 'utf-8'

        self.body = text.encode(self.charset)

    @asyncio.coroutine
    def write_eof(self):
        body = self._body
        if body is not None:
            self.write(body)
        yield from super().write_eof()


############################################################
# HTTP Exceptions
############################################################

class HTTPException(Response, Exception):

    # You should set in subclasses:
    # status = 200

    status_code = None

    def __init__(self, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        Response.__init__(self, status=self.status_code,
                          headers=headers, reason=reason,
                          body=body, text=text, content_type=content_type)
        Exception.__init__(self, self.reason)
        if self.body is None:
            self.text = "{}: {}".format(self.status, self.reason)


class HTTPError(HTTPException):
    """Base class for exceptions with status codes in the 400s and 500s."""


class HTTPRedirection(HTTPException):
    """Base class for exceptions with status codes in the 300s."""


class HTTPSuccessful(HTTPException):
    """Base class for exceptions with status codes in the 200s."""


class HTTPOk(HTTPSuccessful):
    status_code = 200


class HTTPCreated(HTTPSuccessful):
    status_code = 201


class HTTPAccepted(HTTPSuccessful):
    status_code = 202


class HTTPNonAuthoritativeInformation(HTTPSuccessful):
    status_code = 203


class HTTPNoContent(HTTPSuccessful):
    status_code = 204


class HTTPResetContent(HTTPSuccessful):
    status_code = 205


class HTTPPartialContent(HTTPSuccessful):
    status_code = 206


############################################################
# 3xx redirection
############################################################


class _HTTPMove(HTTPRedirection):

    def __init__(self, location, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        if not location:
            raise ValueError("HTTP redirects need a location to redirect to.")
        super().__init__(headers=headers, reason=reason,
                         body=body, text=text, content_type=content_type)
        self.headers['Location'] = location
        self.location = location


class HTTPMultipleChoices(_HTTPMove):
    status_code = 300


class HTTPMovedPermanently(_HTTPMove):
    status_code = 301


class HTTPFound(_HTTPMove):
    status_code = 302


# This one is safe after a POST (the redirected location will be
# retrieved with GET):
class HTTPSeeOther(_HTTPMove):
    status_code = 303


class HTTPNotModified(HTTPRedirection):
    # FIXME: this should include a date or etag header
    status_code = 304


class HTTPUseProxy(_HTTPMove):
    # Not a move, but looks a little like one
    status_code = 305


class HTTPTemporaryRedirect(_HTTPMove):
    status_code = 307


############################################################
# 4xx client error
############################################################


class HTTPClientError(HTTPError):
    pass


class HTTPBadRequest(HTTPClientError):
    status_code = 400


class HTTPUnauthorized(HTTPClientError):
    status_code = 401


class HTTPPaymentRequired(HTTPClientError):
    status_code = 402


class HTTPForbidden(HTTPClientError):
    status_code = 403


class HTTPNotFound(HTTPClientError):
    status_code = 404


class HTTPMethodNotAllowed(HTTPClientError):
    status_code = 405

    def __init__(self, method, allowed_methods, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        allow = ','.join(sorted(allowed_methods))
        super().__init__(headers=headers, reason=reason,
                         body=body, text=text, content_type=content_type)
        self.headers['Allow'] = allow
        self.allowed_methods = allowed_methods
        self.method = method.upper()


class HTTPNotAcceptable(HTTPClientError):
    status_code = 406


class HTTPProxyAuthenticationRequired(HTTPClientError):
    status_code = 407


class HTTPRequestTimeout(HTTPClientError):
    status_code = 408


class HTTPConflict(HTTPClientError):
    status_code = 409


class HTTPGone(HTTPClientError):
    status_code = 410


class HTTPLengthRequired(HTTPClientError):
    status_code = 411


class HTTPPreconditionFailed(HTTPClientError):
    status_code = 412


class HTTPRequestEntityTooLarge(HTTPClientError):
    status_code = 413


class HTTPRequestURITooLong(HTTPClientError):
    status_code = 414


class HTTPUnsupportedMediaType(HTTPClientError):
    status_code = 415


class HTTPRequestRangeNotSatisfiable(HTTPClientError):
    status_code = 416


class HTTPExpectationFailed(HTTPClientError):
    status_code = 417


############################################################
# 5xx Server Error
############################################################
#  Response status codes beginning with the digit "5" indicate cases in
#  which the server is aware that it has erred or is incapable of
#  performing the request. Except when responding to a HEAD request, the
#  server SHOULD include an entity containing an explanation of the error
#  situation, and whether it is a temporary or permanent condition. User
#  agents SHOULD display any included entity to the user. These response
#  codes are applicable to any request method.


class HTTPServerError(HTTPError):
    pass


class HTTPInternalServerError(HTTPServerError):
    status_code = 500


class HTTPNotImplemented(HTTPServerError):
    status_code = 501


class HTTPBadGateway(HTTPServerError):
    status_code = 502


class HTTPServiceUnavailable(HTTPServerError):
    status_code = 503


class HTTPGatewayTimeout(HTTPServerError):
    status_code = 504


class HTTPVersionNotSupported(HTTPServerError):
    status_code = 505


############################################################
# UrlDispatcher implementation
############################################################


class UrlMappingMatchInfo(dict, AbstractMatchInfo):

    def __init__(self, match_dict, route):
        super().__init__(match_dict)
        self._route = route

    @property
    def handler(self):
        return self._route.handler

    @property
    def route(self):
        return self._route


class Route(metaclass=abc.ABCMeta):

    def __init__(self, method, handler, name):
        self._method = method
        self._handler = handler
        self._name = name

    @property
    def method(self):
        return self._method

    @property
    def handler(self):
        return self._handler

    @property
    def name(self):
        return self._name

    @abc.abstractmethod
    def match(self, path):
        """Return dict with info for given path or
        None if route cannot process path."""

    @abc.abstractmethod
    def url(self, **kwargs):
        """Construct url for route with additional params."""

    @staticmethod
    def _append_query(url, query):
        if query is not None:
            return url + "?" + urlencode(query)
        else:
            return url


class PlainRoute(Route):

    def __init__(self, method, handler, name, path):
        super().__init__(method, handler, name)
        self._path = path

    def match(self, path):
        # string comparsion about 10 times faster than regexp matching
        if self._path == path:
            return {}
        else:
            return None

    def url(self, *, query=None):
        return self._append_query(self._path, query)

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return "<PlainRoute {name}[{method}] {path} -> {handler!r}".format(
            name=name, method=self.method, path=self._path,
            handler=self.handler)


class DynamicRoute(Route):

    def __init__(self, method, handler, name, pattern, formatter):
        super().__init__(method, handler, name)
        self._pattern = pattern
        self._formatter = formatter

    def match(self, path):
        match = self._pattern.match(path)
        if match is None:
            return None
        else:
            return match.groupdict()

    def url(self, *, parts, query=None):
        url = self._formatter.format_map(parts)
        return self._append_query(url, query)

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return ("<DynamicRoute {name}[{method}] {formatter} -> {handler!r}"
                .format(name=name, method=self.method,
                        formatter=self._formatter, handler=self.handler))


class StaticRoute(Route):

    def __init__(self, name, prefix, directory):
        assert prefix.startswith('/'), prefix
        assert prefix.endswith('/'), prefix
        super().__init__('GET', self.handle, name)
        self._prefix = prefix
        self._prefix_len = len(self._prefix)
        self._directory = directory

    def match(self, path):
        if not path.startswith(self._prefix):
            return None
        return {'filename': path[self._prefix_len:]}

    def url(self, *, filename, query=None):
        while filename.startswith('/'):
            filename = filename[1:]
        url = self._prefix + filename
        return self._append_query(url, query)

    @asyncio.coroutine
    def handle(self, request):
        resp = StreamResponse()
        filename = request.match_info['filename']
        filepath = os.path.join(self._directory, filename)
        if '..' in filename:
            raise HTTPNotFound()
        if not os.path.exists(filepath) or not os.path.isfile(filepath):
            raise HTTPNotFound()

        ct = mimetypes.guess_type(filename)[0]
        if not ct:
            ct = 'application/octet-stream'
        resp.content_type = ct

        resp.headers['transfer-encoding'] = 'chunked'
        resp.start(request)

        with open(filepath, 'rb') as f:
            chunk = f.read(8192)
            while chunk:
                resp.write(chunk)
                chunk = f.read(8192)

        yield from resp.write_eof()
        return resp

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return "<StaticRoute {name}[{method}] {path} -> {directory!r}".format(
            name=name, method=self.method, path=self._prefix,
            directory=self._directory)


class UrlDispatcher(AbstractRouter, collections.abc.Mapping):

    DYN = re.compile(r'^\{[a-zA-Z][_a-zA-Z0-9]*\}$')
    GOOD = r'[^{}/]+'
    PLAIN = re.compile('^' + GOOD + '$')

    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}

    def __init__(self):
        super().__init__()
        self._urls = []
        self._routes = {}

    @asyncio.coroutine
    def resolve(self, request):
        path = request.path
        method = request.method
        allowed_methods = set()
        for route in self._urls:
            match_dict = route.match(path)
            if match_dict is None:
                continue
            route_method = route.method
            if route_method != method:
                allowed_methods.add(route_method)
            else:
                return UrlMappingMatchInfo(match_dict, route)
        else:
            if allowed_methods:
                raise HTTPMethodNotAllowed(method, allowed_methods)
            else:
                raise HTTPNotFound()

    def __iter__(self):
        return iter(self._routes)

    def __len__(self):
        return len(self._routes)

    def __contains__(self, name):
        return name in self._routes

    def __getitem__(self, name):
        return self._routes[name]

    def _register_endpoint(self, route):
        name = route.name
        if name is not None:
            if name in self._routes:
                raise ValueError('Duplicate {!r}, '
                                 'already handled by {!r}'
                                 .format(name, self._routes[name]))
            else:
                self._routes[name] = route
        self._urls.append(route)

    def add_route(self, method, path, handler, *, name=None):
        assert path.startswith('/')
        assert callable(handler), handler
        if not asyncio.iscoroutinefunction(handler):
            handler = asyncio.coroutine(handler)
        method = method.upper()
        assert method in self.METHODS, method
        parts = []
        factory = PlainRoute
        for part in path.split('/'):
            if not part:
                continue
            if self.DYN.match(part):
                parts.append('(?P<' + part[1:-1] + '>' + self.GOOD + ')')
                factory = DynamicRoute
            elif self.PLAIN.match(part):
                parts.append(re.escape(part))
            else:
                raise ValueError("Invalid path '{}'['{}']".format(path, part))
        if factory is PlainRoute:
            route = PlainRoute(method, handler, name, path)
        else:
            pattern = '/' + '/'.join(parts)
            if path.endswith('/') and pattern != '/':
                pattern += '/'
            compiled = re.compile('^' + pattern + '$')
            route = DynamicRoute(method, handler, name, compiled, path)
        self._register_endpoint(route)
        return route

    def add_static(self, prefix, path, *, name=None):
        """
        Adds static files view
        :param prefix - url prefix
        :param path - folder with files
        """
        assert prefix.startswith('/')
        assert os.path.isdir(path), 'Path does not directory %s' % path
        path = os.path.abspath(path)
        if not prefix.endswith('/'):
            prefix += '/'
        route = StaticRoute(name, prefix, path)
        self._register_endpoint(route)
        return route


############################################################
# Application implementation
############################################################


class RequestHandler(ServerHttpProtocol):

    def __init__(self, manager, app, router, **kwargs):
        super().__init__(**kwargs)

        self._manager = manager
        self._app = app
        self._router = router

    def connection_made(self, transport):
        super().connection_made(transport)

        self._manager.connection_made(self, transport)

    def connection_lost(self, exc):
        self._manager.connection_lost(self, exc)

        super().connection_lost(exc)

    @asyncio.coroutine
    def handle_request(self, message, payload):
        now = self._loop.time()

        request = Request(self._app, message, payload,
                          self.transport, self.writer, self.keep_alive_timeout)
        try:
            match_info = yield from self._router.resolve(request)

            assert isinstance(match_info, AbstractMatchInfo), match_info

            request._match_info = match_info
            handler = match_info.handler

            resp = yield from handler(request)
            if not isinstance(resp, StreamResponse):
                raise RuntimeError(
                    ("Handler should return response instance, got {!r}")
                    .format(type(resp)))
        except HTTPException as exc:
            resp = exc

        resp_msg = resp.start(request)
        yield from resp.write_eof()

        # notify server about keep-alive
        self.keep_alive(resp_msg.keep_alive())

        # log access
        self.log_access(message, None, resp_msg, self._loop.time() - now)


class RequestHandlerFactory:

    def __init__(self, app, router, *,
                 handler=RequestHandler, loop=None, **kwargs):
        self._app = app
        self._router = router
        self._handler = handler
        self._loop = loop
        self._connections = {}
        self._kwargs = kwargs
        self._kwargs.setdefault('logger', app.logger)

    @property
    def connections(self):
        return list(self._connections.keys())

    def connection_made(self, handler, transport):
        self._connections[handler] = transport

    def connection_lost(self, handler, exc=None):
        if handler in self._connections:
            del self._connections[handler]

    @asyncio.coroutine
    def finish_connections(self, timeout=None):
        for handler in self._connections.keys():
            handler.closing()

        def cleanup():
            sleep = 0.05
            while self._connections:
                yield from asyncio.sleep(sleep, loop=self._loop)
                if sleep < 5:
                    sleep = sleep * 2

        if timeout:
            try:
                yield from asyncio.wait_for(
                    cleanup(), timeout, loop=self._loop)
            except asyncio.TimeoutError:
                self._app.logger.warning(
                    "Not all connections are closed (pending: %d)",
                    len(self._connections))

        for transport in self._connections.values():
            transport.close()

        self._connections.clear()

    def __call__(self):
        return self._handler(
            self, self._app, self._router, loop=self._loop, **self._kwargs)


class Application(dict):

    def __init__(self, *, logger=web_logger, loop=None,
                 router=None, handler_factory=RequestHandlerFactory, **kwargs):
        # TODO: explicitly accept *debug* param
        if loop is None:
            loop = asyncio.get_event_loop()
        if router is None:
            router = UrlDispatcher()
        assert isinstance(router, AbstractRouter), router

        self._router = router
        self._handler_factory = handler_factory
        self._finish_callbacks = []
        self._loop = loop
        self.logger = logger

        self.update(**kwargs)

    @property
    def router(self):
        return self._router

    @property
    def loop(self):
        return self._loop

    def make_handler(self, **kwargs):
        return self._handler_factory(
            self, self.router, loop=self.loop, **kwargs)

    @asyncio.coroutine
    def finish(self):
        callbacks = self._finish_callbacks
        self._finish_callbacks = []

        for (cb, args, kwargs) in callbacks:
            try:
                res = cb(self, *args, **kwargs)
                if (asyncio.iscoroutine(res) or
                        isinstance(res, asyncio.Future)):
                    yield from res
            except Exception as exc:
                self._loop.call_exception_handler({
                    'message': "Error in finish callback",
                    'exception': exc,
                    'application': self,
                })

    def register_on_finish(self, func, *args, **kwargs):
        self._finish_callbacks.insert(0, (func, args, kwargs))

```
--- 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:** Charset Case Sensitivity Causes Assertion Failures in Response Handling

**Description:**
When setting the `charset` attribute of a response, using uppercase characters leads to unexpected behavior in tests. Specifically, assigning a charset value like `"KOI8-R"` does not match the expected lowercase value `"koi8-r"`, resulting in assertion failures.

**Example Code:**
```python
def test_set_text_with_charset(self):
    resp = Response()
    resp.content_type = 'text/plain'
    resp.charset = "KOI8-R"
    resp.text = "текст"

    self.assertEqual("текст", resp.text)
    self.assertEqual("текст".encode('koi8-r'), resp.body)
    self.assertEqual("koi8-r", resp.charset)  # This assertion fails
```

**Expected Behavior:**
The `charset` attribute should be normalized to lowercase, ensuring that the assigned value `"KOI8-R"` becomes `"koi8-r"`. This normalization allows tests to reliably compare the `charset` value without case sensitivity issues.

**Actual Behavior:**
Assigning `"KOI8-R"` to `charset` retains the uppercase characters, causing the assertion `self.assertEqual("koi8-r", resp.charset)` to fail because `"KOI8-R" != "koi8-r"`.

[/ISSUE]
{
    "TestStreamResponse.test_cannot_write_after_eof": "PASSED",
    "TestStreamResponse.test_cannot_write_eof_before_headers": "PASSED",
    "TestStreamResponse.test_cannot_write_eof_twice": "PASSED",
    "TestStreamResponse.test_charset_without_content_type": "PASSED",
    "TestStreamResponse.test_content_length": "PASSED",
    "TestStreamResponse.test_content_length_setter": "PASSED",
    "TestStreamResponse.test_ctor": "PASSED",
    "TestStreamResponse.test_default_charset": "PASSED",
    "TestStreamResponse.test_drop_content_length_header_on_setting_len_to_None": "PASSED",
    "TestStreamResponse.test_force_close": "PASSED",
    "TestStreamResponse.test_reset_charset": "PASSED",
    "TestStreamResponse.test_reset_charset_after_setting": "PASSED",
    "TestStreamResponse.test_set_content_length_to_None_on_non_set": "PASSED",
    "TestStreamResponse.test_set_status_with_reason": "PASSED",
    "TestStreamResponse.test_setting_charset": "PASSED",
    "TestStreamResponse.test_setting_content_type": "PASSED",
    "TestStreamResponse.test_start": "PASSED",
    "TestStreamResponse.test_write_before_start": "PASSED",
    "TestStreamResponse.test_write_non_byteish": "PASSED",
    "TestStreamResponse.test_write_returns_drain": "PASSED",
    "TestStreamResponse.test_write_returns_empty_tuple_on_empty_data": "PASSED",
    "TestResponse.test_assign_nonbyteish_body": "PASSED",
    "TestResponse.test_assign_nonstr_text": "PASSED",
    "TestResponse.test_ctor": "PASSED",
    "TestResponse.test_ctor_content_type": "PASSED",
    "TestResponse.test_ctor_text": "PASSED",
    "TestResponse.test_ctor_text_body_combined": "PASSED",
    "TestResponse.test_ctor_with_headers_and_status": "PASSED",
    "TestResponse.test_render_with_body": "PASSED",
    "TestResponse.test_send_headers_for_empty_body": "PASSED",
    "TestResponse.test_send_set_cookie_header": "PASSED",
    "TestResponse.test_set_text_with_charset": "PASSED",
    "TestResponse.test_set_text_with_content_type": "PASSED",
    "TestStreamResponse.test_cookie_set_after_del": "FAILED",
    "TestStreamResponse.test_response_cookie__issue_del_cookie": "FAILED",
    "TestStreamResponse.test_response_cookie_path": "FAILED",
    "TestStreamResponse.test_response_cookies": "FAILED"
}
diff --git a/aiohttp/web.py b/aiohttp/web.py
index e9ed7785..d9920173 100644
--- a/aiohttp/web.py
+++ b/aiohttp/web.py
@@ -517,7 +517,7 @@ class StreamResponse(HeadersMixin):
         if value is None:
             self._content_dict.pop('charset', None)
         else:
-            self._content_dict['charset'] = str(value)
+            self._content_dict['charset'] = str(value).lower()
         self._generate_content_type_header()
 
     def _generate_content_type_header(self):

{"aiohttp/web.py": "import abc\nimport asyncio\nimport binascii\nimport collections\nimport mimetypes\nimport cgi\nimport http.cookies\nimport io\nimport json\nimport re\nimport os\n\nfrom urllib.parse import urlsplit, parse_qsl, urlencode, unquote\n\nfrom .abc import AbstractRouter, AbstractMatchInfo\nfrom .helpers import reify\nfrom .log import web_logger\nfrom .multidict import (CaseInsensitiveMultiDict,\n                        CaseInsensitiveMutableMultiDict,\n                        MultiDict,\n                        MutableMultiDict)\nfrom .protocol import Response as ResponseImpl, HttpVersion, HttpVersion11\nfrom .server import ServerHttpProtocol\nfrom .streams import EOF_MARKER\n\n\n__all__ = [\n    'Application',\n    'HttpVersion',\n    'RequestHandler',\n    'RequestHandlerFactory',\n    'Request',\n    'StreamResponse',\n    'Response',\n    'UrlDispatcher',\n    'UrlMappingMatchInfo',\n    'HTTPException',\n    'HTTPError',\n    'HTTPRedirection',\n    'HTTPSuccessful',\n    'HTTPOk',\n    'HTTPCreated',\n    'HTTPAccepted',\n    'HTTPNonAuthoritativeInformation',\n    'HTTPNoContent',\n    'HTTPResetContent',\n    'HTTPPartialContent',\n    'HTTPMultipleChoices',\n    'HTTPMovedPermanently',\n    'HTTPFound',\n    'HTTPSeeOther',\n    'HTTPNotModified',\n    'HTTPUseProxy',\n    'HTTPTemporaryRedirect',\n    'HTTPClientError',\n    'HTTPBadRequest',\n    'HTTPUnauthorized',\n    'HTTPPaymentRequired',\n    'HTTPForbidden',\n    'HTTPNotFound',\n    'HTTPMethodNotAllowed',\n    'HTTPNotAcceptable',\n    'HTTPProxyAuthenticationRequired',\n    'HTTPRequestTimeout',\n    'HTTPConflict',\n    'HTTPGone',\n    'HTTPLengthRequired',\n    'HTTPPreconditionFailed',\n    'HTTPRequestEntityTooLarge',\n    'HTTPRequestURITooLong',\n    'HTTPUnsupportedMediaType',\n    'HTTPRequestRangeNotSatisfiable',\n    'HTTPExpectationFailed',\n    'HTTPServerError',\n    'HTTPInternalServerError',\n    'HTTPNotImplemented',\n    'HTTPBadGateway',\n    'HTTPServiceUnavailable',\n    'HTTPGatewayTimeout',\n    'HTTPVersionNotSupported',\n]\n\n\nsentinel = object()\n\n\nclass HeadersMixin:\n\n    _content_type = None\n    _content_dict = None\n    _stored_content_type = sentinel\n\n    def _parse_content_type(self, raw):\n        self._stored_content_type = raw\n        if raw is None:\n            # default value according to RFC 2616\n            self._content_type = 'application/octet-stream'\n            self._content_dict = {}\n        else:\n            self._content_type, self._content_dict = cgi.parse_header(raw)\n\n    @property\n    def content_type(self):\n        \"\"\"The value of content part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get('Content-Type')\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_type\n\n    @property\n    def charset(self):\n        \"\"\"The value of charset part for Content-Type HTTP header.\"\"\"\n        raw = self.headers.get('Content-Type')\n        if self._stored_content_type != raw:\n            self._parse_content_type(raw)\n        return self._content_dict.get('charset')\n\n    @property\n    def content_length(self):\n        \"\"\"The value of Content-Length HTTP header.\"\"\"\n        l = self.headers.get('Content-Length')\n        if l is None:\n            return None\n        else:\n            return int(l)\n\n\nFileField = collections.namedtuple('Field', 'name filename file content_type')\n\n\n############################################################\n# HTTP Request\n############################################################\n\n\nclass Request(HeadersMixin):\n\n    def __init__(self, app, message, payload, transport, writer,\n                 keep_alive_timeout):\n        self._app = app\n        self._version = message.version\n        self._transport = transport\n        self._writer = writer\n        self._method = message.method\n        self._host = message.headers.get('HOST')\n        self._path_qs = message.path\n        res = urlsplit(message.path)\n        self._path = unquote(res.path)\n        self._query_string = res.query\n        self._post = None\n        self._post_files_cache = None\n        self._headers = CaseInsensitiveMultiDict._from_uppercase_multidict(\n            message.headers)\n\n        if self._version < HttpVersion11:\n            self._keep_alive = False\n        elif message.should_close:\n            self._keep_alive = False\n        else:\n            self._keep_alive = bool(keep_alive_timeout)\n\n        # matchdict, route_name, handler\n        # or information about traversal lookup\n        self._match_info = None  # initialized after route resolving\n\n        self._payload = payload\n        self._cookies = None\n\n    @property\n    def method(self):\n        \"\"\"Read only property for getting HTTP method.\n\n        The value is upper-cased str like 'GET', 'POST', 'PUT' etc.\n        \"\"\"\n        return self._method\n\n    @property\n    def version(self):\n        \"\"\"Read only property for getting HTTP version of request.\n\n        Returns aiohttp.protocol.HttpVersion instance.\n        \"\"\"\n        return self._version\n\n    @property\n    def host(self):\n        \"\"\"Read only property for getting *HOST* header of request.\n\n        Returns str or None if HTTP request has no HOST header.\n        \"\"\"\n        return self._host\n\n    @property\n    def path_qs(self):\n        \"\"\"The URL including PATH_INFO and the query string.\n\n        E.g, /app/blog?id=10\n        \"\"\"\n        return self._path_qs\n\n    @property\n    def path(self):\n        \"\"\"The URL including *PATH INFO* without the host or scheme.\n\n        E.g., ``/app/blog``\n        \"\"\"\n        return self._path\n\n    @property\n    def query_string(self):\n        \"\"\"The query string in the URL.\n\n        E.g., id=10\n        \"\"\"\n        return self._query_string\n\n    @reify\n    def GET(self):\n        \"\"\"A multidict with all the variables in the query string.\n\n        Lazy property.\n        \"\"\"\n        return MultiDict(parse_qsl(self._query_string))\n\n    @reify\n    def POST(self):\n        \"\"\"A multidict with all the variables in the POST parameters.\n\n        post() methods has to be called before using this attribute.\n        \"\"\"\n        if self._post is None:\n            raise RuntimeError(\"POST is not available before post()\")\n        return self._post\n\n    @property\n    def headers(self):\n        \"\"\"A case-insensitive multidict with all headers.\n\n        Lazy property.\n        \"\"\"\n        return self._headers\n\n    @property\n    def keep_alive(self):\n        \"\"\"Is keepalive enabled by client?\"\"\"\n        return self._keep_alive\n\n    @property\n    def match_info(self):\n        \"\"\"Result of route resolving.\"\"\"\n        return self._match_info\n\n    @property\n    def app(self):\n        \"\"\"Application instance.\"\"\"\n        return self._app\n\n    @property\n    def transport(self):\n        \"\"\"Transport used for request processing.\"\"\"\n        return self._transport\n\n    @property\n    def cookies(self):\n        \"\"\"Return request cookies.\n\n        A read-only dictionary-like object.\n        \"\"\"\n        if self._cookies is None:\n            raw = self.headers.get('COOKIE', '')\n            parsed = http.cookies.SimpleCookie(raw)\n            self._cookies = MultiDict({key: val.value\n                                       for key, val in parsed.items()})\n        return self._cookies\n\n    @property\n    def payload(self):\n        \"\"\"Return raw paiload stream.\"\"\"\n        return self._payload\n\n    @asyncio.coroutine\n    def release(self):\n        \"\"\"Release request.\n\n        Eat unread part of HTTP BODY if present.\n        \"\"\"\n        chunk = yield from self._payload.readany()\n        while chunk is not EOF_MARKER or chunk:\n            chunk = yield from self._payload.readany()\n\n    @asyncio.coroutine\n    def read(self):\n        \"\"\"Read request body if present.\n\n        Returns bytes object with full request content.\n        \"\"\"\n        body = bytearray()\n        while True:\n            chunk = yield from self._payload.readany()\n            body.extend(chunk)\n            if chunk is EOF_MARKER:\n                break\n        return bytes(body)\n\n    @asyncio.coroutine\n    def text(self):\n        \"\"\"Return BODY as text using encoding from .charset.\"\"\"\n        bytes_body = yield from self.read()\n        encoding = self.charset or 'utf-8'\n        return bytes_body.decode(encoding)\n\n    @asyncio.coroutine\n    def json(self, *, loader=json.loads):\n        \"\"\"Return BODY as JSON.\"\"\"\n        body = yield from self.text()\n        return loader(body)\n\n    @asyncio.coroutine\n    def post(self):\n        \"\"\"Return POST parameters.\"\"\"\n        if self._post is not None:\n            return self._post\n        if self.method not in ('POST', 'PUT', 'PATCH'):\n            self._post = MultiDict()\n            return self._post\n\n        content_type = self.content_type\n        if (content_type not in ('',\n                                 'application/x-www-form-urlencoded',\n                                 'multipart/form-data')):\n            self._post = MultiDict()\n            return self._post\n\n        body = yield from self.read()\n        content_charset = self.charset or 'utf-8'\n\n        environ = {'REQUEST_METHOD': self.method,\n                   'CONTENT_LENGTH': str(len(body)),\n                   'QUERY_STRING': '',\n                   'CONTENT_TYPE': self.headers.get('CONTENT-TYPE')}\n\n        fs = cgi.FieldStorage(fp=io.BytesIO(body),\n                              environ=environ,\n                              keep_blank_values=True,\n                              encoding=content_charset)\n\n        supported_tranfer_encoding = {\n            'base64': binascii.a2b_base64,\n            'quoted-printable': binascii.a2b_qp\n        }\n\n        out = MutableMultiDict()\n        for field in fs.list or ():\n            transfer_encoding = field.headers.get(\n                'Content-Transfer-Encoding', None)\n            if field.filename:\n                ff = FileField(field.name,\n                               field.filename,\n                               field.file,  # N.B. file closed error\n                               field.type)\n                if self._post_files_cache is None:\n                    self._post_files_cache = {}\n                self._post_files_cache[field.name] = field\n                out.add(field.name, ff)\n            else:\n                value = field.value\n                if transfer_encoding in supported_tranfer_encoding:\n                    # binascii accepts bytes\n                    value = value.encode('utf-8')\n                    value = supported_tranfer_encoding[\n                        transfer_encoding](value)\n                out.add(field.name, value)\n\n        self._post = MultiDict(out.items(getall=True))\n        return self._post\n\n    # @asyncio.coroutine\n    # def start_websocket(self):\n    #     \"\"\"Upgrade connection to websocket.\n\n    #     Returns (reader, writer) pair.\n    #     \"\"\"\n\n    #     upgrade = 'websocket' in message.headers.get('UPGRADE', '').lower()\n    #     if not upgrade:\n    #         pass\n\n\n############################################################\n# HTTP Response classes\n############################################################\n\n\nclass StreamResponse(HeadersMixin):\n\n    def __init__(self, *, status=200, reason=None):\n        self._body = None\n        self._keep_alive = None\n        self._headers = CaseInsensitiveMutableMultiDict()\n        self._cookies = http.cookies.SimpleCookie()\n        self.set_status(status, reason)\n\n        self._req = None\n        self._resp_impl = None\n        self._eof_sent = False\n\n    def _copy_cookies(self):\n        for cookie in self._cookies.values():\n            value = cookie.output(header='')[1:]\n            self.headers.add('Set-Cookie', value)\n\n    @property\n    def status(self):\n        return self._status\n\n    @property\n    def reason(self):\n        return self._reason\n\n    def set_status(self, status, reason=None):\n        self._status = int(status)\n        if reason is None:\n            reason = ResponseImpl.calc_reason(status)\n        self._reason = reason\n\n    @property\n    def keep_alive(self):\n        return self._keep_alive\n\n    def force_close(self):\n        self._keep_alive = False\n\n    @property\n    def headers(self):\n        return self._headers\n\n    @property\n    def cookies(self):\n        return self._cookies\n\n    def set_cookie(self, name, value, *, expires=None,\n                   domain=None, max_age=None, path=None,\n                   secure=None, httponly=None, version=None):\n        \"\"\"Set or update response cookie.\n\n        Sets new cookie or updates existent with new value.\n        Also updates only those params which are not None.\n        \"\"\"\n\n        old = self._cookies.get(name)\n        if old is not None and old.coded_value == '':\n            # deleted cookie\n            self._cookies.pop(name, None)\n\n        self._cookies[name] = value\n        c = self._cookies[name]\n        if expires is not None:\n            c['expires'] = expires\n        if domain is not None:\n            c['domain'] = domain\n        if max_age is not None:\n            c['max-age'] = max_age\n        if path is not None:\n            c['path'] = path\n        if secure is not None:\n            c['secure'] = secure\n        if httponly is not None:\n            c['httponly'] = httponly\n        if version is not None:\n            c['version'] = version\n\n    def del_cookie(self, name, *, domain=None, path=None):\n        \"\"\"Delete cookie.\n\n        Creates new empty expired cookie.\n        \"\"\"\n        # TODO: do we need domain/path here?\n        self._cookies.pop(name, None)\n        self.set_cookie(name, '', max_age=0, domain=domain, path=path)\n\n    @property\n    def content_length(self):\n        # Just a placeholder for adding setter\n        return super().content_length\n\n    @content_length.setter\n    def content_length(self, value):\n        if value is not None:\n            value = int(value)\n            # TODO: raise error if chunked enabled\n            self.headers['Content-Length'] = str(value)\n        elif 'Content-Length' in self.headers:\n            del self.headers['Content-Length']\n\n    @property\n    def content_type(self):\n        # Just a placeholder for adding setter\n        return super().content_type\n\n    @content_type.setter\n    def content_type(self, value):\n        self.content_type  # read header values if needed\n        self._content_type = str(value)\n        self._generate_content_type_header()\n\n    @property\n    def charset(self):\n        # Just a placeholder for adding setter\n        return super().charset\n\n    @charset.setter\n    def charset(self, value):\n        ctype = self.content_type  # read header values if needed\n        if ctype == 'application/octet-stream':\n            raise RuntimeError(\"Setting charset for application/octet-stream \"\n                               \"doesn't make sense, setup content_type first\")\n        if value is None:\n            self._content_dict.pop('charset', None)\n        else:\n            self._content_dict['charset'] = str(value)\n        self._generate_content_type_header()\n\n    def _generate_content_type_header(self):\n        params = '; '.join(\"%s=%s\" % i for i in self._content_dict.items())\n        if params:\n            ctype = self._content_type + '; ' + params\n        else:\n            ctype = self._content_type\n        self.headers['Content-Type'] = ctype\n\n    def start(self, request):\n        if self._resp_impl is not None:\n            if self._req is not request:\n                raise RuntimeError(\n                    'Response has been started with different request.')\n            return self._resp_impl\n\n        self._req = request\n\n        keep_alive = self._keep_alive\n        if keep_alive is None:\n            keep_alive = request.keep_alive\n        self._keep_alive = keep_alive\n\n        resp_impl = self._resp_impl = ResponseImpl(\n            request._writer,\n            self._status,\n            request.version,\n            not keep_alive,\n            self._reason)\n\n        self._copy_cookies()\n\n        headers = self.headers.items(getall=True)\n        for key, val in headers:\n            resp_impl.add_header(key, val)\n\n        resp_impl.send_headers()\n        return resp_impl\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError('data argument must be byte-ish (%r)',\n                            type(data))\n\n        if self._eof_sent:\n            raise RuntimeError(\"Cannot call write() after write_eof()\")\n        if self._resp_impl is None:\n            raise RuntimeError(\"Cannot call write() before start()\")\n\n        if data:\n            return self._resp_impl.write(data)\n        else:\n            return ()\n\n    @asyncio.coroutine\n    def write_eof(self):\n        if self._eof_sent:\n            return\n        if self._resp_impl is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        yield from self._resp_impl.write_eof()\n        self._eof_sent = True\n\n\nclass Response(StreamResponse):\n\n    def __init__(self, *, body=None, status=200,\n                 reason=None, headers=None,\n                 text=None, content_type=None):\n        super().__init__(status=status, reason=reason)\n\n        if headers is not None:\n            self.headers.extend(headers)\n        if content_type:\n            self.content_type = content_type\n\n        if body is not None and text is not None:\n            raise ValueError(\"body and text are not allowed together.\")\n        elif body is not None:\n            self.body = body\n        elif text is not None:\n            self.text = text\n        else:\n            self.body = None\n\n    @property\n    def body(self):\n        return self._body\n\n    @body.setter\n    def body(self, body):\n        if body is not None and not isinstance(body, bytes):\n            raise TypeError('body argument must be bytes (%r)', type(body))\n        self._body = body\n        if body is not None:\n            self.content_length = len(body)\n        else:\n            self.content_length = 0\n\n    @property\n    def text(self):\n        return self._body.decode(self.charset or 'utf-8')\n\n    @text.setter\n    def text(self, text):\n        if text is not None and not isinstance(text, str):\n            raise TypeError('text argument must be str (%r)', type(text))\n\n        if self.content_type == 'application/octet-stream':\n            self.content_type = 'text/plain'\n        if self.charset is None:\n            self.charset = 'utf-8'\n\n        self.body = text.encode(self.charset)\n\n    @asyncio.coroutine\n    def write_eof(self):\n        body = self._body\n        if body is not None:\n            self.write(body)\n        yield from super().write_eof()\n\n\n############################################################\n# HTTP Exceptions\n############################################################\n\nclass HTTPException(Response, Exception):\n\n    # You should set in subclasses:\n    # status = 200\n\n    status_code = None\n\n    def __init__(self, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        Response.__init__(self, status=self.status_code,\n                          headers=headers, reason=reason,\n                          body=body, text=text, content_type=content_type)\n        Exception.__init__(self, self.reason)\n        if self.body is None:\n            self.text = \"{}: {}\".format(self.status, self.reason)\n\n\nclass HTTPError(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 400s and 500s.\"\"\"\n\n\nclass HTTPRedirection(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 300s.\"\"\"\n\n\nclass HTTPSuccessful(HTTPException):\n    \"\"\"Base class for exceptions with status codes in the 200s.\"\"\"\n\n\nclass HTTPOk(HTTPSuccessful):\n    status_code = 200\n\n\nclass HTTPCreated(HTTPSuccessful):\n    status_code = 201\n\n\nclass HTTPAccepted(HTTPSuccessful):\n    status_code = 202\n\n\nclass HTTPNonAuthoritativeInformation(HTTPSuccessful):\n    status_code = 203\n\n\nclass HTTPNoContent(HTTPSuccessful):\n    status_code = 204\n\n\nclass HTTPResetContent(HTTPSuccessful):\n    status_code = 205\n\n\nclass HTTPPartialContent(HTTPSuccessful):\n    status_code = 206\n\n\n############################################################\n# 3xx redirection\n############################################################\n\n\nclass _HTTPMove(HTTPRedirection):\n\n    def __init__(self, location, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        if not location:\n            raise ValueError(\"HTTP redirects need a location to redirect to.\")\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Location'] = location\n        self.location = location\n\n\nclass HTTPMultipleChoices(_HTTPMove):\n    status_code = 300\n\n\nclass HTTPMovedPermanently(_HTTPMove):\n    status_code = 301\n\n\nclass HTTPFound(_HTTPMove):\n    status_code = 302\n\n\n# This one is safe after a POST (the redirected location will be\n# retrieved with GET):\nclass HTTPSeeOther(_HTTPMove):\n    status_code = 303\n\n\nclass HTTPNotModified(HTTPRedirection):\n    # FIXME: this should include a date or etag header\n    status_code = 304\n\n\nclass HTTPUseProxy(_HTTPMove):\n    # Not a move, but looks a little like one\n    status_code = 305\n\n\nclass HTTPTemporaryRedirect(_HTTPMove):\n    status_code = 307\n\n\n############################################################\n# 4xx client error\n############################################################\n\n\nclass HTTPClientError(HTTPError):\n    pass\n\n\nclass HTTPBadRequest(HTTPClientError):\n    status_code = 400\n\n\nclass HTTPUnauthorized(HTTPClientError):\n    status_code = 401\n\n\nclass HTTPPaymentRequired(HTTPClientError):\n    status_code = 402\n\n\nclass HTTPForbidden(HTTPClientError):\n    status_code = 403\n\n\nclass HTTPNotFound(HTTPClientError):\n    status_code = 404\n\n\nclass HTTPMethodNotAllowed(HTTPClientError):\n    status_code = 405\n\n    def __init__(self, method, allowed_methods, *, headers=None, reason=None,\n                 body=None, text=None, content_type=None):\n        allow = ','.join(sorted(allowed_methods))\n        super().__init__(headers=headers, reason=reason,\n                         body=body, text=text, content_type=content_type)\n        self.headers['Allow'] = allow\n        self.allowed_methods = allowed_methods\n        self.method = method.upper()\n\n\nclass HTTPNotAcceptable(HTTPClientError):\n    status_code = 406\n\n\nclass HTTPProxyAuthenticationRequired(HTTPClientError):\n    status_code = 407\n\n\nclass HTTPRequestTimeout(HTTPClientError):\n    status_code = 408\n\n\nclass HTTPConflict(HTTPClientError):\n    status_code = 409\n\n\nclass HTTPGone(HTTPClientError):\n    status_code = 410\n\n\nclass HTTPLengthRequired(HTTPClientError):\n    status_code = 411\n\n\nclass HTTPPreconditionFailed(HTTPClientError):\n    status_code = 412\n\n\nclass HTTPRequestEntityTooLarge(HTTPClientError):\n    status_code = 413\n\n\nclass HTTPRequestURITooLong(HTTPClientError):\n    status_code = 414\n\n\nclass HTTPUnsupportedMediaType(HTTPClientError):\n    status_code = 415\n\n\nclass HTTPRequestRangeNotSatisfiable(HTTPClientError):\n    status_code = 416\n\n\nclass HTTPExpectationFailed(HTTPClientError):\n    status_code = 417\n\n\n############################################################\n# 5xx Server Error\n############################################################\n#  Response status codes beginning with the digit \"5\" indicate cases in\n#  which the server is aware that it has erred or is incapable of\n#  performing the request. Except when responding to a HEAD request, the\n#  server SHOULD include an entity containing an explanation of the error\n#  situation, and whether it is a temporary or permanent condition. User\n#  agents SHOULD display any included entity to the user. These response\n#  codes are applicable to any request method.\n\n\nclass HTTPServerError(HTTPError):\n    pass\n\n\nclass HTTPInternalServerError(HTTPServerError):\n    status_code = 500\n\n\nclass HTTPNotImplemented(HTTPServerError):\n    status_code = 501\n\n\nclass HTTPBadGateway(HTTPServerError):\n    status_code = 502\n\n\nclass HTTPServiceUnavailable(HTTPServerError):\n    status_code = 503\n\n\nclass HTTPGatewayTimeout(HTTPServerError):\n    status_code = 504\n\n\nclass HTTPVersionNotSupported(HTTPServerError):\n    status_code = 505\n\n\n############################################################\n# UrlDispatcher implementation\n############################################################\n\n\nclass UrlMappingMatchInfo(dict, AbstractMatchInfo):\n\n    def __init__(self, match_dict, route):\n        super().__init__(match_dict)\n        self._route = route\n\n    @property\n    def handler(self):\n        return self._route.handler\n\n    @property\n    def route(self):\n        return self._route\n\n\nclass Route(metaclass=abc.ABCMeta):\n\n    def __init__(self, method, handler, name):\n        self._method = method\n        self._handler = handler\n        self._name = name\n\n    @property\n    def method(self):\n        return self._method\n\n    @property\n    def handler(self):\n        return self._handler\n\n    @property\n    def name(self):\n        return self._name\n\n    @abc.abstractmethod\n    def match(self, path):\n        \"\"\"Return dict with info for given path or\n        None if route cannot process path.\"\"\"\n\n    @abc.abstractmethod\n    def url(self, **kwargs):\n        \"\"\"Construct url for route with additional params.\"\"\"\n\n    @staticmethod\n    def _append_query(url, query):\n        if query is not None:\n            return url + \"?\" + urlencode(query)\n        else:\n            return url\n\n\nclass PlainRoute(Route):\n\n    def __init__(self, method, handler, name, path):\n        super().__init__(method, handler, name)\n        self._path = path\n\n    def match(self, path):\n        # string comparsion about 10 times faster than regexp matching\n        if self._path == path:\n            return {}\n        else:\n            return None\n\n    def url(self, *, query=None):\n        return self._append_query(self._path, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<PlainRoute {name}[{method}] {path} -> {handler!r}\".format(\n            name=name, method=self.method, path=self._path,\n            handler=self.handler)\n\n\nclass DynamicRoute(Route):\n\n    def __init__(self, method, handler, name, pattern, formatter):\n        super().__init__(method, handler, name)\n        self._pattern = pattern\n        self._formatter = formatter\n\n    def match(self, path):\n        match = self._pattern.match(path)\n        if match is None:\n            return None\n        else:\n            return match.groupdict()\n\n    def url(self, *, parts, query=None):\n        url = self._formatter.format_map(parts)\n        return self._append_query(url, query)\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return (\"<DynamicRoute {name}[{method}] {formatter} -> {handler!r}\"\n                .format(name=name, method=self.method,\n                        formatter=self._formatter, handler=self.handler))\n\n\nclass StaticRoute(Route):\n\n    def __init__(self, name, prefix, directory):\n        assert prefix.startswith('/'), prefix\n        assert prefix.endswith('/'), prefix\n        super().__init__('GET', self.handle, name)\n        self._prefix = prefix\n        self._prefix_len = len(self._prefix)\n        self._directory = directory\n\n    def match(self, path):\n        if not path.startswith(self._prefix):\n            return None\n        return {'filename': path[self._prefix_len:]}\n\n    def url(self, *, filename, query=None):\n        while filename.startswith('/'):\n            filename = filename[1:]\n        url = self._prefix + filename\n        return self._append_query(url, query)\n\n    @asyncio.coroutine\n    def handle(self, request):\n        resp = StreamResponse()\n        filename = request.match_info['filename']\n        filepath = os.path.join(self._directory, filename)\n        if '..' in filename:\n            raise HTTPNotFound()\n        if not os.path.exists(filepath) or not os.path.isfile(filepath):\n            raise HTTPNotFound()\n\n        ct = mimetypes.guess_type(filename)[0]\n        if not ct:\n            ct = 'application/octet-stream'\n        resp.content_type = ct\n\n        resp.headers['transfer-encoding'] = 'chunked'\n        resp.start(request)\n\n        with open(filepath, 'rb') as f:\n            chunk = f.read(8192)\n            while chunk:\n                resp.write(chunk)\n                chunk = f.read(8192)\n\n        yield from resp.write_eof()\n        return resp\n\n    def __repr__(self):\n        name = \"'\" + self.name + \"' \" if self.name is not None else \"\"\n        return \"<StaticRoute {name}[{method}] {path} -> {directory!r}\".format(\n            name=name, method=self.method, path=self._prefix,\n            directory=self._directory)\n\n\nclass UrlDispatcher(AbstractRouter, collections.abc.Mapping):\n\n    DYN = re.compile(r'^\\{[a-zA-Z][_a-zA-Z0-9]*\\}$')\n    GOOD = r'[^{}/]+'\n    PLAIN = re.compile('^' + GOOD + '$')\n\n    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}\n\n    def __init__(self):\n        super().__init__()\n        self._urls = []\n        self._routes = {}\n\n    @asyncio.coroutine\n    def resolve(self, request):\n        path = request.path\n        method = request.method\n        allowed_methods = set()\n        for route in self._urls:\n            match_dict = route.match(path)\n            if match_dict is None:\n                continue\n            route_method = route.method\n            if route_method != method:\n                allowed_methods.add(route_method)\n            else:\n                return UrlMappingMatchInfo(match_dict, route)\n        else:\n            if allowed_methods:\n                raise HTTPMethodNotAllowed(method, allowed_methods)\n            else:\n                raise HTTPNotFound()\n\n    def __iter__(self):\n        return iter(self._routes)\n\n    def __len__(self):\n        return len(self._routes)\n\n    def __contains__(self, name):\n        return name in self._routes\n\n    def __getitem__(self, name):\n        return self._routes[name]\n\n    def _register_endpoint(self, route):\n        name = route.name\n        if name is not None:\n            if name in self._routes:\n                raise ValueError('Duplicate {!r}, '\n                                 'already handled by {!r}'\n                                 .format(name, self._routes[name]))\n            else:\n                self._routes[name] = route\n        self._urls.append(route)\n\n    def add_route(self, method, path, handler, *, name=None):\n        assert path.startswith('/')\n        assert callable(handler), handler\n        if not asyncio.iscoroutinefunction(handler):\n            handler = asyncio.coroutine(handler)\n        method = method.upper()\n        assert method in self.METHODS, method\n        parts = []\n        factory = PlainRoute\n        for part in path.split('/'):\n            if not part:\n                continue\n            if self.DYN.match(part):\n                parts.append('(?P<' + part[1:-1] + '>' + self.GOOD + ')')\n                factory = DynamicRoute\n            elif self.PLAIN.match(part):\n                parts.append(re.escape(part))\n            else:\n                raise ValueError(\"Invalid path '{}'['{}']\".format(path, part))\n        if factory is PlainRoute:\n            route = PlainRoute(method, handler, name, path)\n        else:\n            pattern = '/' + '/'.join(parts)\n            if path.endswith('/') and pattern != '/':\n                pattern += '/'\n            compiled = re.compile('^' + pattern + '$')\n            route = DynamicRoute(method, handler, name, compiled, path)\n        self._register_endpoint(route)\n        return route\n\n    def add_static(self, prefix, path, *, name=None):\n        \"\"\"\n        Adds static files view\n        :param prefix - url prefix\n        :param path - folder with files\n        \"\"\"\n        assert prefix.startswith('/')\n        assert os.path.isdir(path), 'Path does not directory %s' % path\n        path = os.path.abspath(path)\n        if not prefix.endswith('/'):\n            prefix += '/'\n        route = StaticRoute(name, prefix, path)\n        self._register_endpoint(route)\n        return route\n\n\n############################################################\n# Application implementation\n############################################################\n\n\nclass RequestHandler(ServerHttpProtocol):\n\n    def __init__(self, manager, app, router, **kwargs):\n        super().__init__(**kwargs)\n\n        self._manager = manager\n        self._app = app\n        self._router = router\n\n    def connection_made(self, transport):\n        super().connection_made(transport)\n\n        self._manager.connection_made(self, transport)\n\n    def connection_lost(self, exc):\n        self._manager.connection_lost(self, exc)\n\n        super().connection_lost(exc)\n\n    @asyncio.coroutine\n    def handle_request(self, message, payload):\n        now = self._loop.time()\n\n        request = Request(self._app, message, payload,\n                          self.transport, self.writer, self.keep_alive_timeout)\n        try:\n            match_info = yield from self._router.resolve(request)\n\n            assert isinstance(match_info, AbstractMatchInfo), match_info\n\n            request._match_info = match_info\n            handler = match_info.handler\n\n            resp = yield from handler(request)\n            if not isinstance(resp, StreamResponse):\n                raise RuntimeError(\n                    (\"Handler should return response instance, got {!r}\")\n                    .format(type(resp)))\n        except HTTPException as exc:\n            resp = exc\n\n        resp_msg = resp.start(request)\n        yield from resp.write_eof()\n\n        # notify server about keep-alive\n        self.keep_alive(resp_msg.keep_alive())\n\n        # log access\n        self.log_access(message, None, resp_msg, self._loop.time() - now)\n\n\nclass RequestHandlerFactory:\n\n    def __init__(self, app, router, *,\n                 handler=RequestHandler, loop=None, **kwargs):\n        self._app = app\n        self._router = router\n        self._handler = handler\n        self._loop = loop\n        self._connections = {}\n        self._kwargs = kwargs\n        self._kwargs.setdefault('logger', app.logger)\n\n    @property\n    def connections(self):\n        return list(self._connections.keys())\n\n    def connection_made(self, handler, transport):\n        self._connections[handler] = transport\n\n    def connection_lost(self, handler, exc=None):\n        if handler in self._connections:\n            del self._connections[handler]\n\n    @asyncio.coroutine\n    def finish_connections(self, timeout=None):\n        for handler in self._connections.keys():\n            handler.closing()\n\n        def cleanup():\n            sleep = 0.05\n            while self._connections:\n                yield from asyncio.sleep(sleep, loop=self._loop)\n                if sleep < 5:\n                    sleep = sleep * 2\n\n        if timeout:\n            try:\n                yield from asyncio.wait_for(\n                    cleanup(), timeout, loop=self._loop)\n            except asyncio.TimeoutError:\n                self._app.logger.warning(\n                    \"Not all connections are closed (pending: %d)\",\n                    len(self._connections))\n\n        for transport in self._connections.values():\n            transport.close()\n\n        self._connections.clear()\n\n    def __call__(self):\n        return self._handler(\n            self, self._app, self._router, loop=self._loop, **self._kwargs)\n\n\nclass Application(dict):\n\n    def __init__(self, *, logger=web_logger, loop=None,\n                 router=None, handler_factory=RequestHandlerFactory, **kwargs):\n        # TODO: explicitly accept *debug* param\n        if loop is None:\n            loop = asyncio.get_event_loop()\n        if router is None:\n            router = UrlDispatcher()\n        assert isinstance(router, AbstractRouter), router\n\n        self._router = router\n        self._handler_factory = handler_factory\n        self._finish_callbacks = []\n        self._loop = loop\n        self.logger = logger\n\n        self.update(**kwargs)\n\n    @property\n    def router(self):\n        return self._router\n\n    @property\n    def loop(self):\n        return self._loop\n\n    def make_handler(self, **kwargs):\n        return self._handler_factory(\n            self, self.router, loop=self.loop, **kwargs)\n\n    @asyncio.coroutine\n    def finish(self):\n        callbacks = self._finish_callbacks\n        self._finish_callbacks = []\n\n        for (cb, args, kwargs) in callbacks:\n            try:\n                res = cb(self, *args, **kwargs)\n                if (asyncio.iscoroutine(res) or\n                        isinstance(res, asyncio.Future)):\n                    yield from res\n            except Exception as exc:\n                self._loop.call_exception_handler({\n                    'message': \"Error in finish callback\",\n                    'exception': exc,\n                    'application': self,\n                })\n\n    def register_on_finish(self, func, *args, **kwargs):\n        self._finish_callbacks.insert(0, (func, args, kwargs))\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:** Charset Case Sensitivity Causes Assertion Failures in Response Handling

**Description:**
When setting the `charset` attribute of a response, using uppercase characters leads to unexpected behavior in tests. Specifically, assigning a charset value like `"KOI8-R"` does not match the expected lowercase value `"koi8-r"`, resulting in assertion failures.

**Example Code:**
```python
def test_set_text_with_charset(self):
    resp = Response()
    resp.content_type = 'text/plain'
    resp.charset = "KOI8-R"
    resp.text = "текст"

    self.assertEqual("текст", resp.text)
    self.assertEqual("текст".encode('koi8-r'), resp.body)
    self.assertEqual("koi8-r", resp.charset)  # This assertion fails
```

**Expected Behavior:**
The `charset` attribute should be normalized to lowercase, ensuring that the assigned value `"KOI8-R"` becomes `"koi8-r"`. This normalization allows tests to reliably compare the `charset` value without case sensitivity issues.

**Actual Behavior:**
Assigning `"KOI8-R"` to `charset` retains the uppercase characters, causing the assertion `self.assertEqual("koi8-r", resp.charset)` to fail because `"KOI8-R" != "koi8-r"`.

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

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

--- BEGIN FILE ---
```
### aiohttp/web.py
import abc
import asyncio
import binascii
import collections
import mimetypes
import cgi
import http.cookies
import io
import json
import re
import os

from urllib.parse import urlsplit, parse_qsl, urlencode, unquote

from .abc import AbstractRouter, AbstractMatchInfo
from .helpers import reify
from .log import web_logger
from .multidict import (CaseInsensitiveMultiDict,
                        CaseInsensitiveMutableMultiDict,
                        MultiDict,
                        MutableMultiDict)
from .protocol import Response as ResponseImpl, HttpVersion, HttpVersion11
from .server import ServerHttpProtocol
from .streams import EOF_MARKER


__all__ = [
    'Application',
    'HttpVersion',
    'RequestHandler',
    'RequestHandlerFactory',
    'Request',
    'StreamResponse',
    'Response',
    'UrlDispatcher',
    'UrlMappingMatchInfo',
    'HTTPException',
    'HTTPError',
    'HTTPRedirection',
    'HTTPSuccessful',
    'HTTPOk',
    'HTTPCreated',
    'HTTPAccepted',
    'HTTPNonAuthoritativeInformation',
    'HTTPNoContent',
    'HTTPResetContent',
    'HTTPPartialContent',
    'HTTPMultipleChoices',
    'HTTPMovedPermanently',
    'HTTPFound',
    'HTTPSeeOther',
    'HTTPNotModified',
    'HTTPUseProxy',
    'HTTPTemporaryRedirect',
    'HTTPClientError',
    'HTTPBadRequest',
    'HTTPUnauthorized',
    'HTTPPaymentRequired',
    'HTTPForbidden',
    'HTTPNotFound',
    'HTTPMethodNotAllowed',
    'HTTPNotAcceptable',
    'HTTPProxyAuthenticationRequired',
    'HTTPRequestTimeout',
    'HTTPConflict',
    'HTTPGone',
    'HTTPLengthRequired',
    'HTTPPreconditionFailed',
    'HTTPRequestEntityTooLarge',
    'HTTPRequestURITooLong',
    'HTTPUnsupportedMediaType',
    'HTTPRequestRangeNotSatisfiable',
    'HTTPExpectationFailed',
    'HTTPServerError',
    'HTTPInternalServerError',
    'HTTPNotImplemented',
    'HTTPBadGateway',
    'HTTPServiceUnavailable',
    'HTTPGatewayTimeout',
    'HTTPVersionNotSupported',
]


sentinel = object()


class HeadersMixin:

    _content_type = None
    _content_dict = None
    _stored_content_type = sentinel

    def _parse_content_type(self, raw):
        self._stored_content_type = raw
        if raw is None:
            # default value according to RFC 2616
            self._content_type = 'application/octet-stream'
            self._content_dict = {}
        else:
            self._content_type, self._content_dict = cgi.parse_header(raw)

    @property
    def content_type(self):
        """The value of content part for Content-Type HTTP header."""
        raw = self.headers.get('Content-Type')
        if self._stored_content_type != raw:
            self._parse_content_type(raw)
        return self._content_type

    @property
    def charset(self):
        """The value of charset part for Content-Type HTTP header."""
        raw = self.headers.get('Content-Type')
        if self._stored_content_type != raw:
            self._parse_content_type(raw)
        return self._content_dict.get('charset')

    @property
    def content_length(self):
        """The value of Content-Length HTTP header."""
        l = self.headers.get('Content-Length')
        if l is None:
            return None
        else:
            return int(l)


FileField = collections.namedtuple('Field', 'name filename file content_type')


############################################################
# HTTP Request
############################################################


class Request(HeadersMixin):

    def __init__(self, app, message, payload, transport, writer,
                 keep_alive_timeout):
        self._app = app
        self._version = message.version
        self._transport = transport
        self._writer = writer
        self._method = message.method
        self._host = message.headers.get('HOST')
        self._path_qs = message.path
        res = urlsplit(message.path)
        self._path = unquote(res.path)
        self._query_string = res.query
        self._post = None
        self._post_files_cache = None
        self._headers = CaseInsensitiveMultiDict._from_uppercase_multidict(
            message.headers)

        if self._version < HttpVersion11:
            self._keep_alive = False
        elif message.should_close:
            self._keep_alive = False
        else:
            self._keep_alive = bool(keep_alive_timeout)

        # matchdict, route_name, handler
        # or information about traversal lookup
        self._match_info = None  # initialized after route resolving

        self._payload = payload
        self._cookies = None

    @property
    def method(self):
        """Read only property for getting HTTP method.

        The value is upper-cased str like 'GET', 'POST', 'PUT' etc.
        """
        return self._method

    @property
    def version(self):
        """Read only property for getting HTTP version of request.

        Returns aiohttp.protocol.HttpVersion instance.
        """
        return self._version

    @property
    def host(self):
        """Read only property for getting *HOST* header of request.

        Returns str or None if HTTP request has no HOST header.
        """
        return self._host

    @property
    def path_qs(self):
        """The URL including PATH_INFO and the query string.

        E.g, /app/blog?id=10
        """
        return self._path_qs

    @property
    def path(self):
        """The URL including *PATH INFO* without the host or scheme.

        E.g., ``/app/blog``
        """
        return self._path

    @property
    def query_string(self):
        """The query string in the URL.

        E.g., id=10
        """
        return self._query_string

    @reify
    def GET(self):
        """A multidict with all the variables in the query string.

        Lazy property.
        """
        return MultiDict(parse_qsl(self._query_string))

    @reify
    def POST(self):
        """A multidict with all the variables in the POST parameters.

        post() methods has to be called before using this attribute.
        """
        if self._post is None:
            raise RuntimeError("POST is not available before post()")
        return self._post

    @property
    def headers(self):
        """A case-insensitive multidict with all headers.

        Lazy property.
        """
        return self._headers

    @property
    def keep_alive(self):
        """Is keepalive enabled by client?"""
        return self._keep_alive

    @property
    def match_info(self):
        """Result of route resolving."""
        return self._match_info

    @property
    def app(self):
        """Application instance."""
        return self._app

    @property
    def transport(self):
        """Transport used for request processing."""
        return self._transport

    @property
    def cookies(self):
        """Return request cookies.

        A read-only dictionary-like object.
        """
        if self._cookies is None:
            raw = self.headers.get('COOKIE', '')
            parsed = http.cookies.SimpleCookie(raw)
            self._cookies = MultiDict({key: val.value
                                       for key, val in parsed.items()})
        return self._cookies

    @property
    def payload(self):
        """Return raw paiload stream."""
        return self._payload

    @asyncio.coroutine
    def release(self):
        """Release request.

        Eat unread part of HTTP BODY if present.
        """
        chunk = yield from self._payload.readany()
        while chunk is not EOF_MARKER or chunk:
            chunk = yield from self._payload.readany()

    @asyncio.coroutine
    def read(self):
        """Read request body if present.

        Returns bytes object with full request content.
        """
        body = bytearray()
        while True:
            chunk = yield from self._payload.readany()
            body.extend(chunk)
            if chunk is EOF_MARKER:
                break
        return bytes(body)

    @asyncio.coroutine
    def text(self):
        """Return BODY as text using encoding from .charset."""
        bytes_body = yield from self.read()
        encoding = self.charset or 'utf-8'
        return bytes_body.decode(encoding)

    @asyncio.coroutine
    def json(self, *, loader=json.loads):
        """Return BODY as JSON."""
        body = yield from self.text()
        return loader(body)

    @asyncio.coroutine
    def post(self):
        """Return POST parameters."""
        if self._post is not None:
            return self._post
        if self.method not in ('POST', 'PUT', 'PATCH'):
            self._post = MultiDict()
            return self._post

        content_type = self.content_type
        if (content_type not in ('',
                                 'application/x-www-form-urlencoded',
                                 'multipart/form-data')):
            self._post = MultiDict()
            return self._post

        body = yield from self.read()
        content_charset = self.charset or 'utf-8'

        environ = {'REQUEST_METHOD': self.method,
                   'CONTENT_LENGTH': str(len(body)),
                   'QUERY_STRING': '',
                   'CONTENT_TYPE': self.headers.get('CONTENT-TYPE')}

        fs = cgi.FieldStorage(fp=io.BytesIO(body),
                              environ=environ,
                              keep_blank_values=True,
                              encoding=content_charset)

        supported_tranfer_encoding = {
            'base64': binascii.a2b_base64,
            'quoted-printable': binascii.a2b_qp
        }

        out = MutableMultiDict()
        for field in fs.list or ():
            transfer_encoding = field.headers.get(
                'Content-Transfer-Encoding', None)
            if field.filename:
                ff = FileField(field.name,
                               field.filename,
                               field.file,  # N.B. file closed error
                               field.type)
                if self._post_files_cache is None:
                    self._post_files_cache = {}
                self._post_files_cache[field.name] = field
                out.add(field.name, ff)
            else:
                value = field.value
                if transfer_encoding in supported_tranfer_encoding:
                    # binascii accepts bytes
                    value = value.encode('utf-8')
                    value = supported_tranfer_encoding[
                        transfer_encoding](value)
                out.add(field.name, value)

        self._post = MultiDict(out.items(getall=True))
        return self._post

    # @asyncio.coroutine
    # def start_websocket(self):
    #     """Upgrade connection to websocket.

    #     Returns (reader, writer) pair.
    #     """

    #     upgrade = 'websocket' in message.headers.get('UPGRADE', '').lower()
    #     if not upgrade:
    #         pass


############################################################
# HTTP Response classes
############################################################


class StreamResponse(HeadersMixin):

    def __init__(self, *, status=200, reason=None):
        self._body = None
        self._keep_alive = None
        self._headers = CaseInsensitiveMutableMultiDict()
        self._cookies = http.cookies.SimpleCookie()
        self.set_status(status, reason)

        self._req = None
        self._resp_impl = None
        self._eof_sent = False

    def _copy_cookies(self):
        for cookie in self._cookies.values():
            value = cookie.output(header='')[1:]
            self.headers.add('Set-Cookie', value)

    @property
    def status(self):
        return self._status

    @property
    def reason(self):
        return self._reason

    def set_status(self, status, reason=None):
        self._status = int(status)
        if reason is None:
            reason = ResponseImpl.calc_reason(status)
        self._reason = reason

    @property
    def keep_alive(self):
        return self._keep_alive

    def force_close(self):
        self._keep_alive = False

    @property
    def headers(self):
        return self._headers

    @property
    def cookies(self):
        return self._cookies

    def set_cookie(self, name, value, *, expires=None,
                   domain=None, max_age=None, path=None,
                   secure=None, httponly=None, version=None):
        """Set or update response cookie.

        Sets new cookie or updates existent with new value.
        Also updates only those params which are not None.
        """

        old = self._cookies.get(name)
        if old is not None and old.coded_value == '':
            # deleted cookie
            self._cookies.pop(name, None)

        self._cookies[name] = value
        c = self._cookies[name]
        if expires is not None:
            c['expires'] = expires
        if domain is not None:
            c['domain'] = domain
        if max_age is not None:
            c['max-age'] = max_age
        if path is not None:
            c['path'] = path
        if secure is not None:
            c['secure'] = secure
        if httponly is not None:
            c['httponly'] = httponly
        if version is not None:
            c['version'] = version

    def del_cookie(self, name, *, domain=None, path=None):
        """Delete cookie.

        Creates new empty expired cookie.
        """
        # TODO: do we need domain/path here?
        self._cookies.pop(name, None)
        self.set_cookie(name, '', max_age=0, domain=domain, path=path)

    @property
    def content_length(self):
        # Just a placeholder for adding setter
        return super().content_length

    @content_length.setter
    def content_length(self, value):
        if value is not None:
            value = int(value)
            # TODO: raise error if chunked enabled
            self.headers['Content-Length'] = str(value)
        elif 'Content-Length' in self.headers:
            del self.headers['Content-Length']

    @property
    def content_type(self):
        # Just a placeholder for adding setter
        return super().content_type

    @content_type.setter
    def content_type(self, value):
        self.content_type  # read header values if needed
        self._content_type = str(value)
        self._generate_content_type_header()

    @property
    def charset(self):
        # Just a placeholder for adding setter
        return super().charset

    @charset.setter
    def charset(self, value):
        ctype = self.content_type  # read header values if needed
        if ctype == 'application/octet-stream':
            raise RuntimeError("Setting charset for application/octet-stream "
                               "doesn't make sense, setup content_type first")
        if value is None:
            self._content_dict.pop('charset', None)
        else:
            self._content_dict['charset'] = str(value)
        self._generate_content_type_header()

    def _generate_content_type_header(self):
        params = '; '.join("%s=%s" % i for i in self._content_dict.items())
        if params:
            ctype = self._content_type + '; ' + params
        else:
            ctype = self._content_type
        self.headers['Content-Type'] = ctype

    def start(self, request):
        if self._resp_impl is not None:
            if self._req is not request:
                raise RuntimeError(
                    'Response has been started with different request.')
            return self._resp_impl

        self._req = request

        keep_alive = self._keep_alive
        if keep_alive is None:
            keep_alive = request.keep_alive
        self._keep_alive = keep_alive

        resp_impl = self._resp_impl = ResponseImpl(
            request._writer,
            self._status,
            request.version,
            not keep_alive,
            self._reason)

        self._copy_cookies()

        headers = self.headers.items(getall=True)
        for key, val in headers:
            resp_impl.add_header(key, val)

        resp_impl.send_headers()
        return resp_impl

    def write(self, data):
        if not isinstance(data, (bytes, bytearray, memoryview)):
            raise TypeError('data argument must be byte-ish (%r)',
                            type(data))

        if self._eof_sent:
            raise RuntimeError("Cannot call write() after write_eof()")
        if self._resp_impl is None:
            raise RuntimeError("Cannot call write() before start()")

        if data:
            return self._resp_impl.write(data)
        else:
            return ()

    @asyncio.coroutine
    def write_eof(self):
        if self._eof_sent:
            return
        if self._resp_impl is None:
            raise RuntimeError("Response has not been started")

        yield from self._resp_impl.write_eof()
        self._eof_sent = True


class Response(StreamResponse):

    def __init__(self, *, body=None, status=200,
                 reason=None, headers=None,
                 text=None, content_type=None):
        super().__init__(status=status, reason=reason)

        if headers is not None:
            self.headers.extend(headers)
        if content_type:
            self.content_type = content_type

        if body is not None and text is not None:
            raise ValueError("body and text are not allowed together.")
        elif body is not None:
            self.body = body
        elif text is not None:
            self.text = text
        else:
            self.body = None

    @property
    def body(self):
        return self._body

    @body.setter
    def body(self, body):
        if body is not None and not isinstance(body, bytes):
            raise TypeError('body argument must be bytes (%r)', type(body))
        self._body = body
        if body is not None:
            self.content_length = len(body)
        else:
            self.content_length = 0

    @property
    def text(self):
        return self._body.decode(self.charset or 'utf-8')

    @text.setter
    def text(self, text):
        if text is not None and not isinstance(text, str):
            raise TypeError('text argument must be str (%r)', type(text))

        if self.content_type == 'application/octet-stream':
            self.content_type = 'text/plain'
        if self.charset is None:
            self.charset = 'utf-8'

        self.body = text.encode(self.charset)

    @asyncio.coroutine
    def write_eof(self):
        body = self._body
        if body is not None:
            self.write(body)
        yield from super().write_eof()


############################################################
# HTTP Exceptions
############################################################

class HTTPException(Response, Exception):

    # You should set in subclasses:
    # status = 200

    status_code = None

    def __init__(self, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        Response.__init__(self, status=self.status_code,
                          headers=headers, reason=reason,
                          body=body, text=text, content_type=content_type)
        Exception.__init__(self, self.reason)
        if self.body is None:
            self.text = "{}: {}".format(self.status, self.reason)


class HTTPError(HTTPException):
    """Base class for exceptions with status codes in the 400s and 500s."""


class HTTPRedirection(HTTPException):
    """Base class for exceptions with status codes in the 300s."""


class HTTPSuccessful(HTTPException):
    """Base class for exceptions with status codes in the 200s."""


class HTTPOk(HTTPSuccessful):
    status_code = 200


class HTTPCreated(HTTPSuccessful):
    status_code = 201


class HTTPAccepted(HTTPSuccessful):
    status_code = 202


class HTTPNonAuthoritativeInformation(HTTPSuccessful):
    status_code = 203


class HTTPNoContent(HTTPSuccessful):
    status_code = 204


class HTTPResetContent(HTTPSuccessful):
    status_code = 205


class HTTPPartialContent(HTTPSuccessful):
    status_code = 206


############################################################
# 3xx redirection
############################################################


class _HTTPMove(HTTPRedirection):

    def __init__(self, location, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        if not location:
            raise ValueError("HTTP redirects need a location to redirect to.")
        super().__init__(headers=headers, reason=reason,
                         body=body, text=text, content_type=content_type)
        self.headers['Location'] = location
        self.location = location


class HTTPMultipleChoices(_HTTPMove):
    status_code = 300


class HTTPMovedPermanently(_HTTPMove):
    status_code = 301


class HTTPFound(_HTTPMove):
    status_code = 302


# This one is safe after a POST (the redirected location will be
# retrieved with GET):
class HTTPSeeOther(_HTTPMove):
    status_code = 303


class HTTPNotModified(HTTPRedirection):
    # FIXME: this should include a date or etag header
    status_code = 304


class HTTPUseProxy(_HTTPMove):
    # Not a move, but looks a little like one
    status_code = 305


class HTTPTemporaryRedirect(_HTTPMove):
    status_code = 307


############################################################
# 4xx client error
############################################################


class HTTPClientError(HTTPError):
    pass


class HTTPBadRequest(HTTPClientError):
    status_code = 400


class HTTPUnauthorized(HTTPClientError):
    status_code = 401


class HTTPPaymentRequired(HTTPClientError):
    status_code = 402


class HTTPForbidden(HTTPClientError):
    status_code = 403


class HTTPNotFound(HTTPClientError):
    status_code = 404


class HTTPMethodNotAllowed(HTTPClientError):
    status_code = 405

    def __init__(self, method, allowed_methods, *, headers=None, reason=None,
                 body=None, text=None, content_type=None):
        allow = ','.join(sorted(allowed_methods))
        super().__init__(headers=headers, reason=reason,
                         body=body, text=text, content_type=content_type)
        self.headers['Allow'] = allow
        self.allowed_methods = allowed_methods
        self.method = method.upper()


class HTTPNotAcceptable(HTTPClientError):
    status_code = 406


class HTTPProxyAuthenticationRequired(HTTPClientError):
    status_code = 407


class HTTPRequestTimeout(HTTPClientError):
    status_code = 408


class HTTPConflict(HTTPClientError):
    status_code = 409


class HTTPGone(HTTPClientError):
    status_code = 410


class HTTPLengthRequired(HTTPClientError):
    status_code = 411


class HTTPPreconditionFailed(HTTPClientError):
    status_code = 412


class HTTPRequestEntityTooLarge(HTTPClientError):
    status_code = 413


class HTTPRequestURITooLong(HTTPClientError):
    status_code = 414


class HTTPUnsupportedMediaType(HTTPClientError):
    status_code = 415


class HTTPRequestRangeNotSatisfiable(HTTPClientError):
    status_code = 416


class HTTPExpectationFailed(HTTPClientError):
    status_code = 417


############################################################
# 5xx Server Error
############################################################
#  Response status codes beginning with the digit "5" indicate cases in
#  which the server is aware that it has erred or is incapable of
#  performing the request. Except when responding to a HEAD request, the
#  server SHOULD include an entity containing an explanation of the error
#  situation, and whether it is a temporary or permanent condition. User
#  agents SHOULD display any included entity to the user. These response
#  codes are applicable to any request method.


class HTTPServerError(HTTPError):
    pass


class HTTPInternalServerError(HTTPServerError):
    status_code = 500


class HTTPNotImplemented(HTTPServerError):
    status_code = 501


class HTTPBadGateway(HTTPServerError):
    status_code = 502


class HTTPServiceUnavailable(HTTPServerError):
    status_code = 503


class HTTPGatewayTimeout(HTTPServerError):
    status_code = 504


class HTTPVersionNotSupported(HTTPServerError):
    status_code = 505


############################################################
# UrlDispatcher implementation
############################################################


class UrlMappingMatchInfo(dict, AbstractMatchInfo):

    def __init__(self, match_dict, route):
        super().__init__(match_dict)
        self._route = route

    @property
    def handler(self):
        return self._route.handler

    @property
    def route(self):
        return self._route


class Route(metaclass=abc.ABCMeta):

    def __init__(self, method, handler, name):
        self._method = method
        self._handler = handler
        self._name = name

    @property
    def method(self):
        return self._method

    @property
    def handler(self):
        return self._handler

    @property
    def name(self):
        return self._name

    @abc.abstractmethod
    def match(self, path):
        """Return dict with info for given path or
        None if route cannot process path."""

    @abc.abstractmethod
    def url(self, **kwargs):
        """Construct url for route with additional params."""

    @staticmethod
    def _append_query(url, query):
        if query is not None:
            return url + "?" + urlencode(query)
        else:
            return url


class PlainRoute(Route):

    def __init__(self, method, handler, name, path):
        super().__init__(method, handler, name)
        self._path = path

    def match(self, path):
        # string comparsion about 10 times faster than regexp matching
        if self._path == path:
            return {}
        else:
            return None

    def url(self, *, query=None):
        return self._append_query(self._path, query)

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return "<PlainRoute {name}[{method}] {path} -> {handler!r}".format(
            name=name, method=self.method, path=self._path,
            handler=self.handler)


class DynamicRoute(Route):

    def __init__(self, method, handler, name, pattern, formatter):
        super().__init__(method, handler, name)
        self._pattern = pattern
        self._formatter = formatter

    def match(self, path):
        match = self._pattern.match(path)
        if match is None:
            return None
        else:
            return match.groupdict()

    def url(self, *, parts, query=None):
        url = self._formatter.format_map(parts)
        return self._append_query(url, query)

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return ("<DynamicRoute {name}[{method}] {formatter} -> {handler!r}"
                .format(name=name, method=self.method,
                        formatter=self._formatter, handler=self.handler))


class StaticRoute(Route):

    def __init__(self, name, prefix, directory):
        assert prefix.startswith('/'), prefix
        assert prefix.endswith('/'), prefix
        super().__init__('GET', self.handle, name)
        self._prefix = prefix
        self._prefix_len = len(self._prefix)
        self._directory = directory

    def match(self, path):
        if not path.startswith(self._prefix):
            return None
        return {'filename': path[self._prefix_len:]}

    def url(self, *, filename, query=None):
        while filename.startswith('/'):
            filename = filename[1:]
        url = self._prefix + filename
        return self._append_query(url, query)

    @asyncio.coroutine
    def handle(self, request):
        resp = StreamResponse()
        filename = request.match_info['filename']
        filepath = os.path.join(self._directory, filename)
        if '..' in filename:
            raise HTTPNotFound()
        if not os.path.exists(filepath) or not os.path.isfile(filepath):
            raise HTTPNotFound()

        ct = mimetypes.guess_type(filename)[0]
        if not ct:
            ct = 'application/octet-stream'
        resp.content_type = ct

        resp.headers['transfer-encoding'] = 'chunked'
        resp.start(request)

        with open(filepath, 'rb') as f:
            chunk = f.read(8192)
            while chunk:
                resp.write(chunk)
                chunk = f.read(8192)

        yield from resp.write_eof()
        return resp

    def __repr__(self):
        name = "'" + self.name + "' " if self.name is not None else ""
        return "<StaticRoute {name}[{method}] {path} -> {directory!r}".format(
            name=name, method=self.method, path=self._prefix,
            directory=self._directory)


class UrlDispatcher(AbstractRouter, collections.abc.Mapping):

    DYN = re.compile(r'^\{[a-zA-Z][_a-zA-Z0-9]*\}$')
    GOOD = r'[^{}/]+'
    PLAIN = re.compile('^' + GOOD + '$')

    METHODS = {'POST', 'GET', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}

    def __init__(self):
        super().__init__()
        self._urls = []
        self._routes = {}

    @asyncio.coroutine
    def resolve(self, request):
        path = request.path
        method = request.method
        allowed_methods = set()
        for route in self._urls:
            match_dict = route.match(path)
            if match_dict is None:
                continue
            route_method = route.method
            if route_method != method:
                allowed_methods.add(route_method)
            else:
                return UrlMappingMatchInfo(match_dict, route)
        else:
            if allowed_methods:
                raise HTTPMethodNotAllowed(method, allowed_methods)
            else:
                raise HTTPNotFound()

    def __iter__(self):
        return iter(self._routes)

    def __len__(self):
        return len(self._routes)

    def __contains__(self, name):
        return name in self._routes

    def __getitem__(self, name):
        return self._routes[name]

    def _register_endpoint(self, route):
        name = route.name
        if name is not None:
            if name in self._routes:
                raise ValueError('Duplicate {!r}, '
                                 'already handled by {!r}'
                                 .format(name, self._routes[name]))
            else:
                self._routes[name] = route
        self._urls.append(route)

    def add_route(self, method, path, handler, *, name=None):
        assert path.startswith('/')
        assert callable(handler), handler
        if not asyncio.iscoroutinefunction(handler):
            handler = asyncio.coroutine(handler)
        method = method.upper()
        assert method in self.METHODS, method
        parts = []
        factory = PlainRoute
        for part in path.split('/'):
            if not part:
                continue
            if self.DYN.match(part):
                parts.append('(?P<' + part[1:-1] + '>' + self.GOOD + ')')
                factory = DynamicRoute
            elif self.PLAIN.match(part):
                parts.append(re.escape(part))
            else:
                raise ValueError("Invalid path '{}'['{}']".format(path, part))
        if factory is PlainRoute:
            route = PlainRoute(method, handler, name, path)
        else:
            pattern = '/' + '/'.join(parts)
            if path.endswith('/') and pattern != '/':
                pattern += '/'
            compiled = re.compile('^' + pattern + '$')
            route = DynamicRoute(method, handler, name, compiled, path)
        self._register_endpoint(route)
        return route

    def add_static(self, prefix, path, *, name=None):
        """
        Adds static files view
        :param prefix - url prefix
        :param path - folder with files
        """
        assert prefix.startswith('/')
        assert os.path.isdir(path), 'Path does not directory %s' % path
        path = os.path.abspath(path)
        if not prefix.endswith('/'):
            prefix += '/'
        route = StaticRoute(name, prefix, path)
        self._register_endpoint(route)
        return route


############################################################
# Application implementation
############################################################


class RequestHandler(ServerHttpProtocol):

    def __init__(self, manager, app, router, **kwargs):
        super().__init__(**kwargs)

        self._manager = manager
        self._app = app
        self._router = router

    def connection_made(self, transport):
        super().connection_made(transport)

        self._manager.connection_made(self, transport)

    def connection_lost(self, exc):
        self._manager.connection_lost(self, exc)

        super().connection_lost(exc)

    @asyncio.coroutine
    def handle_request(self, message, payload):
        now = self._loop.time()

        request = Request(self._app, message, payload,
                          self.transport, self.writer, self.keep_alive_timeout)
        try:
            match_info = yield from self._router.resolve(request)

            assert isinstance(match_info, AbstractMatchInfo), match_info

            request._match_info = match_info
            handler = match_info.handler

            resp = yield from handler(request)
            if not isinstance(resp, StreamResponse):
                raise RuntimeError(
                    ("Handler should return response instance, got {!r}")
                    .format(type(resp)))
        except HTTPException as exc:
            resp = exc

        resp_msg = resp.start(request)
        yield from resp.write_eof()

        # notify server about keep-alive
        self.keep_alive(resp_msg.keep_alive())

        # log access
        self.log_access(message, None, resp_msg, self._loop.time() - now)


class RequestHandlerFactory:

    def __init__(self, app, router, *,
                 handler=RequestHandler, loop=None, **kwargs):
        self._app = app
        self._router = router
        self._handler = handler
        self._loop = loop
        self._connections = {}
        self._kwargs = kwargs
        self._kwargs.setdefault('logger', app.logger)

    @property
    def connections(self):
        return list(self._connections.keys())

    def connection_made(self, handler, transport):
        self._connections[handler] = transport

    def connection_lost(self, handler, exc=None):
        if handler in self._connections:
            del self._connections[handler]

    @asyncio.coroutine
    def finish_connections(self, timeout=None):
        for handler in self._connections.keys():
            handler.closing()

        def cleanup():
            sleep = 0.05
            while self._connections:
                yield from asyncio.sleep(sleep, loop=self._loop)
                if sleep < 5:
                    sleep = sleep * 2

        if timeout:
            try:
                yield from asyncio.wait_for(
                    cleanup(), timeout, loop=self._loop)
            except asyncio.TimeoutError:
                self._app.logger.warning(
                    "Not all connections are closed (pending: %d)",
                    len(self._connections))

        for transport in self._connections.values():
            transport.close()

        self._connections.clear()

    def __call__(self):
        return self._handler(
            self, self._app, self._router, loop=self._loop, **self._kwargs)


class Application(dict):

    def __init__(self, *, logger=web_logger, loop=None,
                 router=None, handler_factory=RequestHandlerFactory, **kwargs):
        # TODO: explicitly accept *debug* param
        if loop is None:
            loop = asyncio.get_event_loop()
        if router is None:
            router = UrlDispatcher()
        assert isinstance(router, AbstractRouter), router

        self._router = router
        self._handler_factory = handler_factory
        self._finish_callbacks = []
        self._loop = loop
        self.logger = logger

        self.update(**kwargs)

    @property
    def router(self):
        return self._router

    @property
    def loop(self):
        return self._loop

    def make_handler(self, **kwargs):
        return self._handler_factory(
            self, self.router, loop=self.loop, **kwargs)

    @asyncio.coroutine
    def finish(self):
        callbacks = self._finish_callbacks
        self._finish_callbacks = []

        for (cb, args, kwargs) in callbacks:
            try:
                res = cb(self, *args, **kwargs)
                if (asyncio.iscoroutine(res) or
                        isinstance(res, asyncio.Future)):
                    yield from res
            except Exception as exc:
                self._loop.call_exception_handler({
                    'message': "Error in finish callback",
                    'exception': exc,
                    'application': self,
                })

    def register_on_finish(self, func, *args, **kwargs):
        self._finish_callbacks.insert(0, (func, args, kwargs))

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