We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Session cookie triggers failure on websocket connection upgrade
Hi,

When enabling the session cookie on an app, if a websocket connection upgrade is made, then we seem to fall into a an assertion failuire:

```
Traceback (most recent call last):
  File ".venv/lib/python3.7/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 146, in run_asgi
    asgi = self.app(self.scope)
  File ".venv/lib/python3.7/site-packages/starlette/applications.py", line 134, in __call__
    return self.error_middleware(scope)
  File ".venv/lib/python3.7/site-packages/starlette/middleware/errors.py", line 88, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 37, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/middleware/base.py", line 22, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/middleware/base.py", line 22, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/exceptions.py", line 49, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/routing.py", line 587, in __call__
    return route(scope)
  File ".venv/lib/python3.7/site-packages/starlette/routing.py", line 356, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/applications.py", line 134, in __call__
    return self.error_middleware(scope)
  File ".venv/lib/python3.7/site-packages/starlette/middleware/errors.py", line 88, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/middleware/sessions.py", line 34, in __call__
    request = Request(scope)
  File ".venv/lib/python3.7/site-packages/starlette/requests.py", line 128, in __init__
    assert scope["type"] == "http"
```

I think the issue is that the session middleware doesn't coimplain when the request type is both http and websocket: https://github.com/encode/starlette/blob/master/starlette/middleware/sessions.py#L32

But right after, creates a Request from the scope when, itself assert only http: https://github.com/encode/starlette/blob/master/starlette/requests.py#L128


Session cookie triggers failure on websocket connection upgrade
Hi,

When enabling the session cookie on an app, if a websocket connection upgrade is made, then we seem to fall into a an assertion failuire:

```
Traceback (most recent call last):
  File ".venv/lib/python3.7/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 146, in run_asgi
    asgi = self.app(self.scope)
  File ".venv/lib/python3.7/site-packages/starlette/applications.py", line 134, in __call__
    return self.error_middleware(scope)
  File ".venv/lib/python3.7/site-packages/starlette/middleware/errors.py", line 88, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 37, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/middleware/base.py", line 22, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/middleware/base.py", line 22, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/exceptions.py", line 49, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/routing.py", line 587, in __call__
    return route(scope)
  File ".venv/lib/python3.7/site-packages/starlette/routing.py", line 356, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/applications.py", line 134, in __call__
    return self.error_middleware(scope)
  File ".venv/lib/python3.7/site-packages/starlette/middleware/errors.py", line 88, in __call__
    return self.app(scope)
  File ".venv/lib/python3.7/site-packages/starlette/middleware/sessions.py", line 34, in __call__
    request = Request(scope)
  File ".venv/lib/python3.7/site-packages/starlette/requests.py", line 128, in __init__
    assert scope["type"] == "http"
```

I think the issue is that the session middleware doesn't coimplain when the request type is both http and websocket: https://github.com/encode/starlette/blob/master/starlette/middleware/sessions.py#L32

But right after, creates a Request from the scope when, itself assert only http: https://github.com/encode/starlette/blob/master/starlette/requests.py#L128



--- 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/middleware/sessions.py`
Content:
```
1 import json
2 import typing
3 from base64 import b64decode, b64encode
4 
5 import itsdangerous
6 from itsdangerous.exc import BadTimeSignature, SignatureExpired
7 
8 from starlette.datastructures import MutableHeaders, Secret
9 from starlette.requests import Request
10 from starlette.types import ASGIApp, Message, Receive, Scope, Send
11 
12 
13 class SessionMiddleware:
14     def __init__(
15         self,
16         app: ASGIApp,
17         secret_key: typing.Union[str, Secret],
18         session_cookie: str = "session",
19         max_age: int = 14 * 24 * 60 * 60,  # 14 days, in seconds
20         same_site: str = "lax",
21         https_only: bool = False,
22     ) -> None:
23         self.app = app
24         self.signer = itsdangerous.TimestampSigner(str(secret_key))
25         self.session_cookie = session_cookie
26         self.max_age = max_age
27         self.security_flags = "httponly; samesite=" + same_site
28         if https_only:  # Secure flag can be used with HTTPS only
29             self.security_flags += "; secure"
30 
31     async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
32         if scope["type"] not in ("http", "websocket"):  # pragma: no cover
33             await self.app(scope, receive, send)
34             return
35 
36         request = Request(scope)
37         initial_session_was_empty = True
38 
39         if self.session_cookie in request.cookies:
40             data = request.cookies[self.session_cookie].encode("utf-8")
41             try:
42                 data = self.signer.unsign(data, max_age=self.max_age)
43                 scope["session"] = json.loads(b64decode(data))
44                 initial_session_was_empty = False
45             except (BadTimeSignature, SignatureExpired):
46                 scope["session"] = {}
47         else:
48             scope["session"] = {}
49 
50         async def send_wrapper(message: Message) -> None:
51             if message["type"] == "http.response.start":
52                 if scope["session"]:
53                     # We have session data to persist.
54                     data = b64encode(json.dumps(scope["session"]).encode("utf-8"))
55                     data = self.signer.sign(data)
56                     headers = MutableHeaders(scope=message)
57                     header_value = "%s=%s; path=/; Max-Age=%d; %s" % (
58                         self.session_cookie,
59                         data.decode("utf-8"),
60                         self.max_age,
61                         self.security_flags,
62                     )
63                     headers.append("Set-Cookie", header_value)
64                 elif not initial_session_was_empty:
65                     # The session has been cleared.
66                     headers = MutableHeaders(scope=message)
67                     header_value = "%s=%s; %s" % (
68                         self.session_cookie,
69                         "null; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT;",
70                         self.security_flags,
71                     )
72                     headers.append("Set-Cookie", header_value)
73             await send(message)
74 
75         await self.app(scope, receive, send_wrapper)
76
```
--- 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/middleware/sessions.py b/starlette/middleware/sessions.py
--- a/starlette/middleware/sessions.py
+++ b/starlette/middleware/sessions.py
@@ -6,7 +6,7 @@
 from itsdangerous.exc import BadTimeSignature, SignatureExpired
 
 from starlette.datastructures import MutableHeaders, Secret
-from starlette.requests import Request
+from starlette.requests import HTTPConnection
 from starlette.types import ASGIApp, Message, Receive, Scope, Send
 
 
@@ -33,11 +33,11 @@
             await self.app(scope, receive, send)
             return
 
-        request = Request(scope)
+        connection = HTTPConnection(scope)
         initial_session_was_empty = True
 
-        if self.session_cookie in request.cookies:
-            data = request.cookies[self.session_cookie].encode("utf-8")
+        if self.session_cookie in connection.cookies:
+            data = connection.cookies[self.session_cookie].encode("utf-8")
             try:
                 data = self.signer.unsign(data, max_age=self.max_age)
                 scope["session"] = json.loads(b64decode(data))

{"golden_diff": "diff --git a/starlette/middleware/sessions.py b/starlette/middleware/sessions.py\n--- a/starlette/middleware/sessions.py\n+++ b/starlette/middleware/sessions.py\n@@ -6,7 +6,7 @@\n from itsdangerous.exc import BadTimeSignature, SignatureExpired\n \n from starlette.datastructures import MutableHeaders, Secret\n-from starlette.requests import Request\n+from starlette.requests import HTTPConnection\n from starlette.types import ASGIApp, Message, Receive, Scope, Send\n \n \n@@ -33,11 +33,11 @@\n             await self.app(scope, receive, send)\n             return\n \n-        request = Request(scope)\n+        connection = HTTPConnection(scope)\n         initial_session_was_empty = True\n \n-        if self.session_cookie in request.cookies:\n-            data = request.cookies[self.session_cookie].encode(\"utf-8\")\n+        if self.session_cookie in connection.cookies:\n+            data = connection.cookies[self.session_cookie].encode(\"utf-8\")\n             try:\n                 data = self.signer.unsign(data, max_age=self.max_age)\n                 scope[\"session\"] = json.loads(b64decode(data))\n", "issue": "Session cookie triggers failure on websocket connection upgrade\nHi,\r\n\r\nWhen enabling the session cookie on an app, if a websocket connection upgrade is made, then we seem to fall into a an assertion failuire:\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \".venv/lib/python3.7/site-packages/uvicorn/protocols/websockets/websockets_impl.py\", line 146, in run_asgi\r\n    asgi = self.app(self.scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/applications.py\", line 134, in __call__\r\n    return self.error_middleware(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/middleware/errors.py\", line 88, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py\", line 37, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/middleware/base.py\", line 22, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/middleware/base.py\", line 22, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/exceptions.py\", line 49, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/routing.py\", line 587, in __call__\r\n    return route(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/routing.py\", line 356, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/applications.py\", line 134, in __call__\r\n    return self.error_middleware(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/middleware/errors.py\", line 88, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/middleware/sessions.py\", line 34, in __call__\r\n    request = Request(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/requests.py\", line 128, in __init__\r\n    assert scope[\"type\"] == \"http\"\r\n```\r\n\r\nI think the issue is that the session middleware doesn't coimplain when the request type is both http and websocket: https://github.com/encode/starlette/blob/master/starlette/middleware/sessions.py#L32\r\n\r\nBut right after, creates a Request from the scope when, itself assert only http: https://github.com/encode/starlette/blob/master/starlette/requests.py#L128\r\n\r\n\nSession cookie triggers failure on websocket connection upgrade\nHi,\r\n\r\nWhen enabling the session cookie on an app, if a websocket connection upgrade is made, then we seem to fall into a an assertion failuire:\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \".venv/lib/python3.7/site-packages/uvicorn/protocols/websockets/websockets_impl.py\", line 146, in run_asgi\r\n    asgi = self.app(self.scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/applications.py\", line 134, in __call__\r\n    return self.error_middleware(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/middleware/errors.py\", line 88, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py\", line 37, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/middleware/base.py\", line 22, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/middleware/base.py\", line 22, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/exceptions.py\", line 49, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/routing.py\", line 587, in __call__\r\n    return route(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/routing.py\", line 356, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/applications.py\", line 134, in __call__\r\n    return self.error_middleware(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/middleware/errors.py\", line 88, in __call__\r\n    return self.app(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/middleware/sessions.py\", line 34, in __call__\r\n    request = Request(scope)\r\n  File \".venv/lib/python3.7/site-packages/starlette/requests.py\", line 128, in __init__\r\n    assert scope[\"type\"] == \"http\"\r\n```\r\n\r\nI think the issue is that the session middleware doesn't coimplain when the request type is both http and websocket: https://github.com/encode/starlette/blob/master/starlette/middleware/sessions.py#L32\r\n\r\nBut right after, creates a Request from the scope when, itself assert only http: https://github.com/encode/starlette/blob/master/starlette/requests.py#L128\r\n\r\n\n", "before_files": [{"content": "import json\nimport typing\nfrom base64 import b64decode, b64encode\n\nimport itsdangerous\nfrom itsdangerous.exc import BadTimeSignature, SignatureExpired\n\nfrom starlette.datastructures import MutableHeaders, Secret\nfrom starlette.requests import Request\nfrom starlette.types import ASGIApp, Message, Receive, Scope, Send\n\n\nclass SessionMiddleware:\n    def __init__(\n        self,\n        app: ASGIApp,\n        secret_key: typing.Union[str, Secret],\n        session_cookie: str = \"session\",\n        max_age: int = 14 * 24 * 60 * 60,  # 14 days, in seconds\n        same_site: str = \"lax\",\n        https_only: bool = False,\n    ) -> None:\n        self.app = app\n        self.signer = itsdangerous.TimestampSigner(str(secret_key))\n        self.session_cookie = session_cookie\n        self.max_age = max_age\n        self.security_flags = \"httponly; samesite=\" + same_site\n        if https_only:  # Secure flag can be used with HTTPS only\n            self.security_flags += \"; secure\"\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        if scope[\"type\"] not in (\"http\", \"websocket\"):  # pragma: no cover\n            await self.app(scope, receive, send)\n            return\n\n        request = Request(scope)\n        initial_session_was_empty = True\n\n        if self.session_cookie in request.cookies:\n            data = request.cookies[self.session_cookie].encode(\"utf-8\")\n            try:\n                data = self.signer.unsign(data, max_age=self.max_age)\n                scope[\"session\"] = json.loads(b64decode(data))\n                initial_session_was_empty = False\n            except (BadTimeSignature, SignatureExpired):\n                scope[\"session\"] = {}\n        else:\n            scope[\"session\"] = {}\n\n        async def send_wrapper(message: Message) -> None:\n            if message[\"type\"] == \"http.response.start\":\n                if scope[\"session\"]:\n                    # We have session data to persist.\n                    data = b64encode(json.dumps(scope[\"session\"]).encode(\"utf-8\"))\n                    data = self.signer.sign(data)\n                    headers = MutableHeaders(scope=message)\n                    header_value = \"%s=%s; path=/; Max-Age=%d; %s\" % (\n                        self.session_cookie,\n                        data.decode(\"utf-8\"),\n                        self.max_age,\n                        self.security_flags,\n                    )\n                    headers.append(\"Set-Cookie\", header_value)\n                elif not initial_session_was_empty:\n                    # The session has been cleared.\n                    headers = MutableHeaders(scope=message)\n                    header_value = \"%s=%s; %s\" % (\n                        self.session_cookie,\n                        \"null; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT;\",\n                        self.security_flags,\n                    )\n                    headers.append(\"Set-Cookie\", header_value)\n            await send(message)\n\n        await self.app(scope, receive, send_wrapper)\n", "path": "starlette/middleware/sessions.py"}], "after_files": [{"content": "import json\nimport typing\nfrom base64 import b64decode, b64encode\n\nimport itsdangerous\nfrom itsdangerous.exc import BadTimeSignature, SignatureExpired\n\nfrom starlette.datastructures import MutableHeaders, Secret\nfrom starlette.requests import HTTPConnection\nfrom starlette.types import ASGIApp, Message, Receive, Scope, Send\n\n\nclass SessionMiddleware:\n    def __init__(\n        self,\n        app: ASGIApp,\n        secret_key: typing.Union[str, Secret],\n        session_cookie: str = \"session\",\n        max_age: int = 14 * 24 * 60 * 60,  # 14 days, in seconds\n        same_site: str = \"lax\",\n        https_only: bool = False,\n    ) -> None:\n        self.app = app\n        self.signer = itsdangerous.TimestampSigner(str(secret_key))\n        self.session_cookie = session_cookie\n        self.max_age = max_age\n        self.security_flags = \"httponly; samesite=\" + same_site\n        if https_only:  # Secure flag can be used with HTTPS only\n            self.security_flags += \"; secure\"\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:\n        if scope[\"type\"] not in (\"http\", \"websocket\"):  # pragma: no cover\n            await self.app(scope, receive, send)\n            return\n\n        connection = HTTPConnection(scope)\n        initial_session_was_empty = True\n\n        if self.session_cookie in connection.cookies:\n            data = connection.cookies[self.session_cookie].encode(\"utf-8\")\n            try:\n                data = self.signer.unsign(data, max_age=self.max_age)\n                scope[\"session\"] = json.loads(b64decode(data))\n                initial_session_was_empty = False\n            except (BadTimeSignature, SignatureExpired):\n                scope[\"session\"] = {}\n        else:\n            scope[\"session\"] = {}\n\n        async def send_wrapper(message: Message) -> None:\n            if message[\"type\"] == \"http.response.start\":\n                if scope[\"session\"]:\n                    # We have session data to persist.\n                    data = b64encode(json.dumps(scope[\"session\"]).encode(\"utf-8\"))\n                    data = self.signer.sign(data)\n                    headers = MutableHeaders(scope=message)\n                    header_value = \"%s=%s; path=/; Max-Age=%d; %s\" % (\n                        self.session_cookie,\n                        data.decode(\"utf-8\"),\n                        self.max_age,\n                        self.security_flags,\n                    )\n                    headers.append(\"Set-Cookie\", header_value)\n                elif not initial_session_was_empty:\n                    # The session has been cleared.\n                    headers = MutableHeaders(scope=message)\n                    header_value = \"%s=%s; %s\" % (\n                        self.session_cookie,\n                        \"null; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT;\",\n                        self.security_flags,\n                    )\n                    headers.append(\"Set-Cookie\", header_value)\n            await send(message)\n\n        await self.app(scope, receive, send_wrapper)\n", "path": "starlette/middleware/sessions.py"}]}