We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
HTTPConnection::__get_item__ has incorrect type signature
### Checklist

<!-- Please make sure you check all these items before submitting your bug report. -->

- [x] The bug is reproducible against the latest release and/or `master`.
- [x] There are no similar issues or pull requests to fix it yet.

### Describe the bug

`HTTPConnection`'s `__get_item__` has the return type `str`. However, it returns the value for the provided key in the scope. Not all values in the scope are strings, (See documentation here: https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope

Since `WebSocket` inherits this method it also has the wrong return type.

### To reproduce
This example uses WebSocket because that's how I originally found the issue.

Run mypy
```bash
mypy --pretty main.py
```
On the following code
```python
from starlette.types import Scope, Receive, Send
from starlette.websockets import WebSocket


async def app(scope: Scope, receive: Receive, send: Send) -> None:
    websocket = WebSocket(scope=scope, receive=receive, send=send)
    await websocket.accept()
    ip, port = websocket['client']  # mypy fails to typecheck, because websocket['client'] typed as a str
    # Succeeds at runtime, because websocket['client'] is not a string
    assert isinstance(ip, str)
    assert isinstance(port, int)
```

### Expected behavior

mypy typecheck should pass for valid code

### Actual behavior

Mypy fails to typecheck with the following error:

```
main.py:8: error: Unpacking a string is disallowed
        ip, port = websocket['client']  # mypy fails to typecheck, because webso...
                   ^
```


### Environment

- OS: linux/windows/mac
- Python version: 3.9
- Starlette version: 0.14.1

### Additional context

Changing the return type of `__get_item__` to `Any` is likely the right thing to do here, which would be backwards-compatible for those currently using mypy. 

As an extra solution to this problem, it would be great to expose the correct types for the scope using a TypedDict (couldn't be done for `__getitem__`, but could be done for the scope attribute on `WebSocket` and `HTTPConnection`), although that would be a breaking change for MyPy users, and also complicate the middleware story.

I'd be happy to work on either/both of these solutions if there's a consensus on what the right direction is.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `starlette/requests.py`
Content:
```
1 import json
2 import typing
3 from collections.abc import Mapping
4 from http import cookies as http_cookies
5 
6 import anyio
7 
8 from starlette.datastructures import URL, Address, FormData, Headers, QueryParams, State
9 from starlette.formparsers import FormParser, MultiPartParser
10 from starlette.types import Message, Receive, Scope, Send
11 
12 try:
13     from multipart.multipart import parse_options_header
14 except ImportError:  # pragma: nocover
15     parse_options_header = None
16 
17 
18 SERVER_PUSH_HEADERS_TO_COPY = {
19     "accept",
20     "accept-encoding",
21     "accept-language",
22     "cache-control",
23     "user-agent",
24 }
25 
26 
27 def cookie_parser(cookie_string: str) -> typing.Dict[str, str]:
28     """
29     This function parses a ``Cookie`` HTTP header into a dict of key/value pairs.
30 
31     It attempts to mimic browser cookie parsing behavior: browsers and web servers
32     frequently disregard the spec (RFC 6265) when setting and reading cookies,
33     so we attempt to suit the common scenarios here.
34 
35     This function has been adapted from Django 3.1.0.
36     Note: we are explicitly _NOT_ using `SimpleCookie.load` because it is based
37     on an outdated spec and will fail on lots of input we want to support
38     """
39     cookie_dict: typing.Dict[str, str] = {}
40     for chunk in cookie_string.split(";"):
41         if "=" in chunk:
42             key, val = chunk.split("=", 1)
43         else:
44             # Assume an empty name per
45             # https://bugzilla.mozilla.org/show_bug.cgi?id=169091
46             key, val = "", chunk
47         key, val = key.strip(), val.strip()
48         if key or val:
49             # unquote using Python's algorithm.
50             cookie_dict[key] = http_cookies._unquote(val)  # type: ignore
51     return cookie_dict
52 
53 
54 class ClientDisconnect(Exception):
55     pass
56 
57 
58 class HTTPConnection(Mapping):
59     """
60     A base class for incoming HTTP connections, that is used to provide
61     any functionality that is common to both `Request` and `WebSocket`.
62     """
63 
64     def __init__(self, scope: Scope, receive: Receive = None) -> None:
65         assert scope["type"] in ("http", "websocket")
66         self.scope = scope
67 
68     def __getitem__(self, key: str) -> str:
69         return self.scope[key]
70 
71     def __iter__(self) -> typing.Iterator[str]:
72         return iter(self.scope)
73 
74     def __len__(self) -> int:
75         return len(self.scope)
76 
77     # Don't use the `abc.Mapping.__eq__` implementation.
78     # Connection instances should never be considered equal
79     # unless `self is other`.
80     __eq__ = object.__eq__
81     __hash__ = object.__hash__
82 
83     @property
84     def app(self) -> typing.Any:
85         return self.scope["app"]
86 
87     @property
88     def url(self) -> URL:
89         if not hasattr(self, "_url"):
90             self._url = URL(scope=self.scope)
91         return self._url
92 
93     @property
94     def base_url(self) -> URL:
95         if not hasattr(self, "_base_url"):
96             base_url_scope = dict(self.scope)
97             base_url_scope["path"] = "/"
98             base_url_scope["query_string"] = b""
99             base_url_scope["root_path"] = base_url_scope.get(
100                 "app_root_path", base_url_scope.get("root_path", "")
101             )
102             self._base_url = URL(scope=base_url_scope)
103         return self._base_url
104 
105     @property
106     def headers(self) -> Headers:
107         if not hasattr(self, "_headers"):
108             self._headers = Headers(scope=self.scope)
109         return self._headers
110 
111     @property
112     def query_params(self) -> QueryParams:
113         if not hasattr(self, "_query_params"):
114             self._query_params = QueryParams(self.scope["query_string"])
115         return self._query_params
116 
117     @property
118     def path_params(self) -> dict:
119         return self.scope.get("path_params", {})
120 
121     @property
122     def cookies(self) -> typing.Dict[str, str]:
123         if not hasattr(self, "_cookies"):
124             cookies: typing.Dict[str, str] = {}
125             cookie_header = self.headers.get("cookie")
126 
127             if cookie_header:
128                 cookies = cookie_parser(cookie_header)
129             self._cookies = cookies
130         return self._cookies
131 
132     @property
133     def client(self) -> Address:
134         host, port = self.scope.get("client") or (None, None)
135         return Address(host=host, port=port)
136 
137     @property
138     def session(self) -> dict:
139         assert (
140             "session" in self.scope
141         ), "SessionMiddleware must be installed to access request.session"
142         return self.scope["session"]
143 
144     @property
145     def auth(self) -> typing.Any:
146         assert (
147             "auth" in self.scope
148         ), "AuthenticationMiddleware must be installed to access request.auth"
149         return self.scope["auth"]
150 
151     @property
152     def user(self) -> typing.Any:
153         assert (
154             "user" in self.scope
155         ), "AuthenticationMiddleware must be installed to access request.user"
156         return self.scope["user"]
157 
158     @property
159     def state(self) -> State:
160         if not hasattr(self, "_state"):
161             # Ensure 'state' has an empty dict if it's not already populated.
162             self.scope.setdefault("state", {})
163             # Create a state instance with a reference to the dict in which it should
164             # store info
165             self._state = State(self.scope["state"])
166         return self._state
167 
168     def url_for(self, name: str, **path_params: typing.Any) -> str:
169         router = self.scope["router"]
170         url_path = router.url_path_for(name, **path_params)
171         return url_path.make_absolute_url(base_url=self.base_url)
172 
173 
174 async def empty_receive() -> Message:
175     raise RuntimeError("Receive channel has not been made available")
176 
177 
178 async def empty_send(message: Message) -> None:
179     raise RuntimeError("Send channel has not been made available")
180 
181 
182 class Request(HTTPConnection):
183     def __init__(
184         self, scope: Scope, receive: Receive = empty_receive, send: Send = empty_send
185     ):
186         super().__init__(scope)
187         assert scope["type"] == "http"
188         self._receive = receive
189         self._send = send
190         self._stream_consumed = False
191         self._is_disconnected = False
192 
193     @property
194     def method(self) -> str:
195         return self.scope["method"]
196 
197     @property
198     def receive(self) -> Receive:
199         return self._receive
200 
201     async def stream(self) -> typing.AsyncGenerator[bytes, None]:
202         if hasattr(self, "_body"):
203             yield self._body
204             yield b""
205             return
206 
207         if self._stream_consumed:
208             raise RuntimeError("Stream consumed")
209 
210         self._stream_consumed = True
211         while True:
212             message = await self._receive()
213             if message["type"] == "http.request":
214                 body = message.get("body", b"")
215                 if body:
216                     yield body
217                 if not message.get("more_body", False):
218                     break
219             elif message["type"] == "http.disconnect":
220                 self._is_disconnected = True
221                 raise ClientDisconnect()
222         yield b""
223 
224     async def body(self) -> bytes:
225         if not hasattr(self, "_body"):
226             chunks = []
227             async for chunk in self.stream():
228                 chunks.append(chunk)
229             self._body = b"".join(chunks)
230         return self._body
231 
232     async def json(self) -> typing.Any:
233         if not hasattr(self, "_json"):
234             body = await self.body()
235             self._json = json.loads(body)
236         return self._json
237 
238     async def form(self) -> FormData:
239         if not hasattr(self, "_form"):
240             assert (
241                 parse_options_header is not None
242             ), "The `python-multipart` library must be installed to use form parsing."
243             content_type_header = self.headers.get("Content-Type")
244             content_type, options = parse_options_header(content_type_header)
245             if content_type == b"multipart/form-data":
246                 multipart_parser = MultiPartParser(self.headers, self.stream())
247                 self._form = await multipart_parser.parse()
248             elif content_type == b"application/x-www-form-urlencoded":
249                 form_parser = FormParser(self.headers, self.stream())
250                 self._form = await form_parser.parse()
251             else:
252                 self._form = FormData()
253         return self._form
254 
255     async def close(self) -> None:
256         if hasattr(self, "_form"):
257             await self._form.close()
258 
259     async def is_disconnected(self) -> bool:
260         if not self._is_disconnected:
261             message: Message = {}
262 
263             # If message isn't immediately available, move on
264             with anyio.CancelScope() as cs:
265                 cs.cancel()
266                 message = await self._receive()
267 
268             if message.get("type") == "http.disconnect":
269                 self._is_disconnected = True
270 
271         return self._is_disconnected
272 
273     async def send_push_promise(self, path: str) -> None:
274         if "http.response.push" in self.scope.get("extensions", {}):
275             raw_headers = []
276             for name in SERVER_PUSH_HEADERS_TO_COPY:
277                 for value in self.headers.getlist(name):
278                     raw_headers.append(
279                         (name.encode("latin-1"), value.encode("latin-1"))
280                     )
281             await self._send(
282                 {"type": "http.response.push", "path": path, "headers": raw_headers}
283             )
284
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/starlette/requests.py b/starlette/requests.py
--- a/starlette/requests.py
+++ b/starlette/requests.py
@@ -65,7 +65,7 @@
         assert scope["type"] in ("http", "websocket")
         self.scope = scope
 
-    def __getitem__(self, key: str) -> str:
+    def __getitem__(self, key: str) -> typing.Any:
         return self.scope[key]
 
     def __iter__(self) -> typing.Iterator[str]:

{"golden_diff": "diff --git a/starlette/requests.py b/starlette/requests.py\n--- a/starlette/requests.py\n+++ b/starlette/requests.py\n@@ -65,7 +65,7 @@\n         assert scope[\"type\"] in (\"http\", \"websocket\")\n         self.scope = scope\n \n-    def __getitem__(self, key: str) -> str:\n+    def __getitem__(self, key: str) -> typing.Any:\n         return self.scope[key]\n \n     def __iter__(self) -> typing.Iterator[str]:\n", "issue": "HTTPConnection::__get_item__ has incorrect type signature\n### Checklist\r\n\r\n<!-- Please make sure you check all these items before submitting your bug report. -->\r\n\r\n- [x] The bug is reproducible against the latest release and/or `master`.\r\n- [x] There are no similar issues or pull requests to fix it yet.\r\n\r\n### Describe the bug\r\n\r\n`HTTPConnection`'s `__get_item__` has the return type `str`. However, it returns the value for the provided key in the scope. Not all values in the scope are strings, (See documentation here: https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope\r\n\r\nSince `WebSocket` inherits this method it also has the wrong return type.\r\n\r\n### To reproduce\r\nThis example uses WebSocket because that's how I originally found the issue.\r\n\r\nRun mypy\r\n```bash\r\nmypy --pretty main.py\r\n```\r\nOn the following code\r\n```python\r\nfrom starlette.types import Scope, Receive, Send\r\nfrom starlette.websockets import WebSocket\r\n\r\n\r\nasync def app(scope: Scope, receive: Receive, send: Send) -> None:\r\n    websocket = WebSocket(scope=scope, receive=receive, send=send)\r\n    await websocket.accept()\r\n    ip, port = websocket['client']  # mypy fails to typecheck, because websocket['client'] typed as a str\r\n    # Succeeds at runtime, because websocket['client'] is not a string\r\n    assert isinstance(ip, str)\r\n    assert isinstance(port, int)\r\n```\r\n\r\n### Expected behavior\r\n\r\nmypy typecheck should pass for valid code\r\n\r\n### Actual behavior\r\n\r\nMypy fails to typecheck with the following error:\r\n\r\n```\r\nmain.py:8: error: Unpacking a string is disallowed\r\n        ip, port = websocket['client']  # mypy fails to typecheck, because webso...\r\n                   ^\r\n```\r\n\r\n\r\n### Environment\r\n\r\n- OS: linux/windows/mac\r\n- Python version: 3.9\r\n- Starlette version: 0.14.1\r\n\r\n### Additional context\r\n\r\nChanging the return type of `__get_item__` to `Any` is likely the right thing to do here, which would be backwards-compatible for those currently using mypy. \r\n\r\nAs an extra solution to this problem, it would be great to expose the correct types for the scope using a TypedDict (couldn't be done for `__getitem__`, but could be done for the scope attribute on `WebSocket` and `HTTPConnection`), although that would be a breaking change for MyPy users, and also complicate the middleware story.\r\n\r\nI'd be happy to work on either/both of these solutions if there's a consensus on what the right direction is.\n", "before_files": [{"content": "import json\nimport typing\nfrom collections.abc import Mapping\nfrom http import cookies as http_cookies\n\nimport anyio\n\nfrom starlette.datastructures import URL, Address, FormData, Headers, QueryParams, State\nfrom starlette.formparsers import FormParser, MultiPartParser\nfrom starlette.types import Message, Receive, Scope, Send\n\ntry:\n    from multipart.multipart import parse_options_header\nexcept ImportError:  # pragma: nocover\n    parse_options_header = None\n\n\nSERVER_PUSH_HEADERS_TO_COPY = {\n    \"accept\",\n    \"accept-encoding\",\n    \"accept-language\",\n    \"cache-control\",\n    \"user-agent\",\n}\n\n\ndef cookie_parser(cookie_string: str) -> typing.Dict[str, str]:\n    \"\"\"\n    This function parses a ``Cookie`` HTTP header into a dict of key/value pairs.\n\n    It attempts to mimic browser cookie parsing behavior: browsers and web servers\n    frequently disregard the spec (RFC 6265) when setting and reading cookies,\n    so we attempt to suit the common scenarios here.\n\n    This function has been adapted from Django 3.1.0.\n    Note: we are explicitly _NOT_ using `SimpleCookie.load` because it is based\n    on an outdated spec and will fail on lots of input we want to support\n    \"\"\"\n    cookie_dict: typing.Dict[str, str] = {}\n    for chunk in cookie_string.split(\";\"):\n        if \"=\" in chunk:\n            key, val = chunk.split(\"=\", 1)\n        else:\n            # Assume an empty name per\n            # https://bugzilla.mozilla.org/show_bug.cgi?id=169091\n            key, val = \"\", chunk\n        key, val = key.strip(), val.strip()\n        if key or val:\n            # unquote using Python's algorithm.\n            cookie_dict[key] = http_cookies._unquote(val)  # type: ignore\n    return cookie_dict\n\n\nclass ClientDisconnect(Exception):\n    pass\n\n\nclass HTTPConnection(Mapping):\n    \"\"\"\n    A base class for incoming HTTP connections, that is used to provide\n    any functionality that is common to both `Request` and `WebSocket`.\n    \"\"\"\n\n    def __init__(self, scope: Scope, receive: Receive = None) -> None:\n        assert scope[\"type\"] in (\"http\", \"websocket\")\n        self.scope = scope\n\n    def __getitem__(self, key: str) -> str:\n        return self.scope[key]\n\n    def __iter__(self) -> typing.Iterator[str]:\n        return iter(self.scope)\n\n    def __len__(self) -> int:\n        return len(self.scope)\n\n    # Don't use the `abc.Mapping.__eq__` implementation.\n    # Connection instances should never be considered equal\n    # unless `self is other`.\n    __eq__ = object.__eq__\n    __hash__ = object.__hash__\n\n    @property\n    def app(self) -> typing.Any:\n        return self.scope[\"app\"]\n\n    @property\n    def url(self) -> URL:\n        if not hasattr(self, \"_url\"):\n            self._url = URL(scope=self.scope)\n        return self._url\n\n    @property\n    def base_url(self) -> URL:\n        if not hasattr(self, \"_base_url\"):\n            base_url_scope = dict(self.scope)\n            base_url_scope[\"path\"] = \"/\"\n            base_url_scope[\"query_string\"] = b\"\"\n            base_url_scope[\"root_path\"] = base_url_scope.get(\n                \"app_root_path\", base_url_scope.get(\"root_path\", \"\")\n            )\n            self._base_url = URL(scope=base_url_scope)\n        return self._base_url\n\n    @property\n    def headers(self) -> Headers:\n        if not hasattr(self, \"_headers\"):\n            self._headers = Headers(scope=self.scope)\n        return self._headers\n\n    @property\n    def query_params(self) -> QueryParams:\n        if not hasattr(self, \"_query_params\"):\n            self._query_params = QueryParams(self.scope[\"query_string\"])\n        return self._query_params\n\n    @property\n    def path_params(self) -> dict:\n        return self.scope.get(\"path_params\", {})\n\n    @property\n    def cookies(self) -> typing.Dict[str, str]:\n        if not hasattr(self, \"_cookies\"):\n            cookies: typing.Dict[str, str] = {}\n            cookie_header = self.headers.get(\"cookie\")\n\n            if cookie_header:\n                cookies = cookie_parser(cookie_header)\n            self._cookies = cookies\n        return self._cookies\n\n    @property\n    def client(self) -> Address:\n        host, port = self.scope.get(\"client\") or (None, None)\n        return Address(host=host, port=port)\n\n    @property\n    def session(self) -> dict:\n        assert (\n            \"session\" in self.scope\n        ), \"SessionMiddleware must be installed to access request.session\"\n        return self.scope[\"session\"]\n\n    @property\n    def auth(self) -> typing.Any:\n        assert (\n            \"auth\" in self.scope\n        ), \"AuthenticationMiddleware must be installed to access request.auth\"\n        return self.scope[\"auth\"]\n\n    @property\n    def user(self) -> typing.Any:\n        assert (\n            \"user\" in self.scope\n        ), \"AuthenticationMiddleware must be installed to access request.user\"\n        return self.scope[\"user\"]\n\n    @property\n    def state(self) -> State:\n        if not hasattr(self, \"_state\"):\n            # Ensure 'state' has an empty dict if it's not already populated.\n            self.scope.setdefault(\"state\", {})\n            # Create a state instance with a reference to the dict in which it should\n            # store info\n            self._state = State(self.scope[\"state\"])\n        return self._state\n\n    def url_for(self, name: str, **path_params: typing.Any) -> str:\n        router = self.scope[\"router\"]\n        url_path = router.url_path_for(name, **path_params)\n        return url_path.make_absolute_url(base_url=self.base_url)\n\n\nasync def empty_receive() -> Message:\n    raise RuntimeError(\"Receive channel has not been made available\")\n\n\nasync def empty_send(message: Message) -> None:\n    raise RuntimeError(\"Send channel has not been made available\")\n\n\nclass Request(HTTPConnection):\n    def __init__(\n        self, scope: Scope, receive: Receive = empty_receive, send: Send = empty_send\n    ):\n        super().__init__(scope)\n        assert scope[\"type\"] == \"http\"\n        self._receive = receive\n        self._send = send\n        self._stream_consumed = False\n        self._is_disconnected = False\n\n    @property\n    def method(self) -> str:\n        return self.scope[\"method\"]\n\n    @property\n    def receive(self) -> Receive:\n        return self._receive\n\n    async def stream(self) -> typing.AsyncGenerator[bytes, None]:\n        if hasattr(self, \"_body\"):\n            yield self._body\n            yield b\"\"\n            return\n\n        if self._stream_consumed:\n            raise RuntimeError(\"Stream consumed\")\n\n        self._stream_consumed = True\n        while True:\n            message = await self._receive()\n            if message[\"type\"] == \"http.request\":\n                body = message.get(\"body\", b\"\")\n                if body:\n                    yield body\n                if not message.get(\"more_body\", False):\n                    break\n            elif message[\"type\"] == \"http.disconnect\":\n                self._is_disconnected = True\n                raise ClientDisconnect()\n        yield b\"\"\n\n    async def body(self) -> bytes:\n        if not hasattr(self, \"_body\"):\n            chunks = []\n            async for chunk in self.stream():\n                chunks.append(chunk)\n            self._body = b\"\".join(chunks)\n        return self._body\n\n    async def json(self) -> typing.Any:\n        if not hasattr(self, \"_json\"):\n            body = await self.body()\n            self._json = json.loads(body)\n        return self._json\n\n    async def form(self) -> FormData:\n        if not hasattr(self, \"_form\"):\n            assert (\n                parse_options_header is not None\n            ), \"The `python-multipart` library must be installed to use form parsing.\"\n            content_type_header = self.headers.get(\"Content-Type\")\n            content_type, options = parse_options_header(content_type_header)\n            if content_type == b\"multipart/form-data\":\n                multipart_parser = MultiPartParser(self.headers, self.stream())\n                self._form = await multipart_parser.parse()\n            elif content_type == b\"application/x-www-form-urlencoded\":\n                form_parser = FormParser(self.headers, self.stream())\n                self._form = await form_parser.parse()\n            else:\n                self._form = FormData()\n        return self._form\n\n    async def close(self) -> None:\n        if hasattr(self, \"_form\"):\n            await self._form.close()\n\n    async def is_disconnected(self) -> bool:\n        if not self._is_disconnected:\n            message: Message = {}\n\n            # If message isn't immediately available, move on\n            with anyio.CancelScope() as cs:\n                cs.cancel()\n                message = await self._receive()\n\n            if message.get(\"type\") == \"http.disconnect\":\n                self._is_disconnected = True\n\n        return self._is_disconnected\n\n    async def send_push_promise(self, path: str) -> None:\n        if \"http.response.push\" in self.scope.get(\"extensions\", {}):\n            raw_headers = []\n            for name in SERVER_PUSH_HEADERS_TO_COPY:\n                for value in self.headers.getlist(name):\n                    raw_headers.append(\n                        (name.encode(\"latin-1\"), value.encode(\"latin-1\"))\n                    )\n            await self._send(\n                {\"type\": \"http.response.push\", \"path\": path, \"headers\": raw_headers}\n            )\n", "path": "starlette/requests.py"}], "after_files": [{"content": "import json\nimport typing\nfrom collections.abc import Mapping\nfrom http import cookies as http_cookies\n\nimport anyio\n\nfrom starlette.datastructures import URL, Address, FormData, Headers, QueryParams, State\nfrom starlette.formparsers import FormParser, MultiPartParser\nfrom starlette.types import Message, Receive, Scope, Send\n\ntry:\n    from multipart.multipart import parse_options_header\nexcept ImportError:  # pragma: nocover\n    parse_options_header = None\n\n\nSERVER_PUSH_HEADERS_TO_COPY = {\n    \"accept\",\n    \"accept-encoding\",\n    \"accept-language\",\n    \"cache-control\",\n    \"user-agent\",\n}\n\n\ndef cookie_parser(cookie_string: str) -> typing.Dict[str, str]:\n    \"\"\"\n    This function parses a ``Cookie`` HTTP header into a dict of key/value pairs.\n\n    It attempts to mimic browser cookie parsing behavior: browsers and web servers\n    frequently disregard the spec (RFC 6265) when setting and reading cookies,\n    so we attempt to suit the common scenarios here.\n\n    This function has been adapted from Django 3.1.0.\n    Note: we are explicitly _NOT_ using `SimpleCookie.load` because it is based\n    on an outdated spec and will fail on lots of input we want to support\n    \"\"\"\n    cookie_dict: typing.Dict[str, str] = {}\n    for chunk in cookie_string.split(\";\"):\n        if \"=\" in chunk:\n            key, val = chunk.split(\"=\", 1)\n        else:\n            # Assume an empty name per\n            # https://bugzilla.mozilla.org/show_bug.cgi?id=169091\n            key, val = \"\", chunk\n        key, val = key.strip(), val.strip()\n        if key or val:\n            # unquote using Python's algorithm.\n            cookie_dict[key] = http_cookies._unquote(val)  # type: ignore\n    return cookie_dict\n\n\nclass ClientDisconnect(Exception):\n    pass\n\n\nclass HTTPConnection(Mapping):\n    \"\"\"\n    A base class for incoming HTTP connections, that is used to provide\n    any functionality that is common to both `Request` and `WebSocket`.\n    \"\"\"\n\n    def __init__(self, scope: Scope, receive: Receive = None) -> None:\n        assert scope[\"type\"] in (\"http\", \"websocket\")\n        self.scope = scope\n\n    def __getitem__(self, key: str) -> typing.Any:\n        return self.scope[key]\n\n    def __iter__(self) -> typing.Iterator[str]:\n        return iter(self.scope)\n\n    def __len__(self) -> int:\n        return len(self.scope)\n\n    # Don't use the `abc.Mapping.__eq__` implementation.\n    # Connection instances should never be considered equal\n    # unless `self is other`.\n    __eq__ = object.__eq__\n    __hash__ = object.__hash__\n\n    @property\n    def app(self) -> typing.Any:\n        return self.scope[\"app\"]\n\n    @property\n    def url(self) -> URL:\n        if not hasattr(self, \"_url\"):\n            self._url = URL(scope=self.scope)\n        return self._url\n\n    @property\n    def base_url(self) -> URL:\n        if not hasattr(self, \"_base_url\"):\n            base_url_scope = dict(self.scope)\n            base_url_scope[\"path\"] = \"/\"\n            base_url_scope[\"query_string\"] = b\"\"\n            base_url_scope[\"root_path\"] = base_url_scope.get(\n                \"app_root_path\", base_url_scope.get(\"root_path\", \"\")\n            )\n            self._base_url = URL(scope=base_url_scope)\n        return self._base_url\n\n    @property\n    def headers(self) -> Headers:\n        if not hasattr(self, \"_headers\"):\n            self._headers = Headers(scope=self.scope)\n        return self._headers\n\n    @property\n    def query_params(self) -> QueryParams:\n        if not hasattr(self, \"_query_params\"):\n            self._query_params = QueryParams(self.scope[\"query_string\"])\n        return self._query_params\n\n    @property\n    def path_params(self) -> dict:\n        return self.scope.get(\"path_params\", {})\n\n    @property\n    def cookies(self) -> typing.Dict[str, str]:\n        if not hasattr(self, \"_cookies\"):\n            cookies: typing.Dict[str, str] = {}\n            cookie_header = self.headers.get(\"cookie\")\n\n            if cookie_header:\n                cookies = cookie_parser(cookie_header)\n            self._cookies = cookies\n        return self._cookies\n\n    @property\n    def client(self) -> Address:\n        host, port = self.scope.get(\"client\") or (None, None)\n        return Address(host=host, port=port)\n\n    @property\n    def session(self) -> dict:\n        assert (\n            \"session\" in self.scope\n        ), \"SessionMiddleware must be installed to access request.session\"\n        return self.scope[\"session\"]\n\n    @property\n    def auth(self) -> typing.Any:\n        assert (\n            \"auth\" in self.scope\n        ), \"AuthenticationMiddleware must be installed to access request.auth\"\n        return self.scope[\"auth\"]\n\n    @property\n    def user(self) -> typing.Any:\n        assert (\n            \"user\" in self.scope\n        ), \"AuthenticationMiddleware must be installed to access request.user\"\n        return self.scope[\"user\"]\n\n    @property\n    def state(self) -> State:\n        if not hasattr(self, \"_state\"):\n            # Ensure 'state' has an empty dict if it's not already populated.\n            self.scope.setdefault(\"state\", {})\n            # Create a state instance with a reference to the dict in which it should\n            # store info\n            self._state = State(self.scope[\"state\"])\n        return self._state\n\n    def url_for(self, name: str, **path_params: typing.Any) -> str:\n        router = self.scope[\"router\"]\n        url_path = router.url_path_for(name, **path_params)\n        return url_path.make_absolute_url(base_url=self.base_url)\n\n\nasync def empty_receive() -> Message:\n    raise RuntimeError(\"Receive channel has not been made available\")\n\n\nasync def empty_send(message: Message) -> None:\n    raise RuntimeError(\"Send channel has not been made available\")\n\n\nclass Request(HTTPConnection):\n    def __init__(\n        self, scope: Scope, receive: Receive = empty_receive, send: Send = empty_send\n    ):\n        super().__init__(scope)\n        assert scope[\"type\"] == \"http\"\n        self._receive = receive\n        self._send = send\n        self._stream_consumed = False\n        self._is_disconnected = False\n\n    @property\n    def method(self) -> str:\n        return self.scope[\"method\"]\n\n    @property\n    def receive(self) -> Receive:\n        return self._receive\n\n    async def stream(self) -> typing.AsyncGenerator[bytes, None]:\n        if hasattr(self, \"_body\"):\n            yield self._body\n            yield b\"\"\n            return\n\n        if self._stream_consumed:\n            raise RuntimeError(\"Stream consumed\")\n\n        self._stream_consumed = True\n        while True:\n            message = await self._receive()\n            if message[\"type\"] == \"http.request\":\n                body = message.get(\"body\", b\"\")\n                if body:\n                    yield body\n                if not message.get(\"more_body\", False):\n                    break\n            elif message[\"type\"] == \"http.disconnect\":\n                self._is_disconnected = True\n                raise ClientDisconnect()\n        yield b\"\"\n\n    async def body(self) -> bytes:\n        if not hasattr(self, \"_body\"):\n            chunks = []\n            async for chunk in self.stream():\n                chunks.append(chunk)\n            self._body = b\"\".join(chunks)\n        return self._body\n\n    async def json(self) -> typing.Any:\n        if not hasattr(self, \"_json\"):\n            body = await self.body()\n            self._json = json.loads(body)\n        return self._json\n\n    async def form(self) -> FormData:\n        if not hasattr(self, \"_form\"):\n            assert (\n                parse_options_header is not None\n            ), \"The `python-multipart` library must be installed to use form parsing.\"\n            content_type_header = self.headers.get(\"Content-Type\")\n            content_type, options = parse_options_header(content_type_header)\n            if content_type == b\"multipart/form-data\":\n                multipart_parser = MultiPartParser(self.headers, self.stream())\n                self._form = await multipart_parser.parse()\n            elif content_type == b\"application/x-www-form-urlencoded\":\n                form_parser = FormParser(self.headers, self.stream())\n                self._form = await form_parser.parse()\n            else:\n                self._form = FormData()\n        return self._form\n\n    async def close(self) -> None:\n        if hasattr(self, \"_form\"):\n            await self._form.close()\n\n    async def is_disconnected(self) -> bool:\n        if not self._is_disconnected:\n            message: Message = {}\n\n            # If message isn't immediately available, move on\n            with anyio.CancelScope() as cs:\n                cs.cancel()\n                message = await self._receive()\n\n            if message.get(\"type\") == \"http.disconnect\":\n                self._is_disconnected = True\n\n        return self._is_disconnected\n\n    async def send_push_promise(self, path: str) -> None:\n        if \"http.response.push\" in self.scope.get(\"extensions\", {}):\n            raw_headers = []\n            for name in SERVER_PUSH_HEADERS_TO_COPY:\n                for value in self.headers.getlist(name):\n                    raw_headers.append(\n                        (name.encode(\"latin-1\"), value.encode(\"latin-1\"))\n                    )\n            await self._send(\n                {\"type\": \"http.response.push\", \"path\": path, \"headers\": raw_headers}\n            )\n", "path": "starlette/requests.py"}]}