We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
WSGI middleware should stream request body, rather than loading it all at once.
Hi! I'm deploying a Django app with uvicorn, running on k8s. Our containers were being killed, and I've found that when users upload large files, uvicorn increases memory usage, and slows to a crawl. Eventually causing an OOM.

I'm not sure where this is happening yet, and I suspect could be related to https://github.com/django/channels/pull/1251, but I did a bit more digging and I'm not totally sure. I've tried running uvicorn in wsgi mode, and completely removed channels from the django install, and I'm getting the same behavior. File uploads are being loaded into memory (rather than streaming to disk, as they should be), and upload speeds slow to a crawl. The same app running on gunicorn works fine.

The file I'm testing with is about 470mb.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `uvicorn/middleware/wsgi.py`
Content:
```
1 import asyncio
2 import concurrent.futures
3 import io
4 import sys
5 from collections import deque
6 from typing import TYPE_CHECKING, Deque, Iterable, Optional, Tuple
7 
8 if TYPE_CHECKING:
9     from asgiref.typing import (
10         ASGIReceiveCallable,
11         ASGIReceiveEvent,
12         ASGISendCallable,
13         ASGISendEvent,
14         HTTPRequestEvent,
15         HTTPResponseBodyEvent,
16         HTTPResponseStartEvent,
17         HTTPScope,
18     )
19 
20 from uvicorn._types import Environ, ExcInfo, StartResponse, WSGIApp
21 
22 
23 def build_environ(
24     scope: "HTTPScope", message: "ASGIReceiveEvent", body: io.BytesIO
25 ) -> Environ:
26     """
27     Builds a scope and request message into a WSGI environ object.
28     """
29     environ = {
30         "REQUEST_METHOD": scope["method"],
31         "SCRIPT_NAME": "",
32         "PATH_INFO": scope["path"].encode("utf8").decode("latin1"),
33         "QUERY_STRING": scope["query_string"].decode("ascii"),
34         "SERVER_PROTOCOL": "HTTP/%s" % scope["http_version"],
35         "wsgi.version": (1, 0),
36         "wsgi.url_scheme": scope.get("scheme", "http"),
37         "wsgi.input": body,
38         "wsgi.errors": sys.stdout,
39         "wsgi.multithread": True,
40         "wsgi.multiprocess": True,
41         "wsgi.run_once": False,
42     }
43 
44     # Get server name and port - required in WSGI, not in ASGI
45     server = scope.get("server")
46     if server is None:
47         server = ("localhost", 80)
48     environ["SERVER_NAME"] = server[0]
49     environ["SERVER_PORT"] = server[1]
50 
51     # Get client IP address
52     client = scope.get("client")
53     if client is not None:
54         environ["REMOTE_ADDR"] = client[0]
55 
56     # Go through headers and make them into environ entries
57     for name, value in scope.get("headers", []):
58         name_str: str = name.decode("latin1")
59         if name_str == "content-length":
60             corrected_name = "CONTENT_LENGTH"
61         elif name_str == "content-type":
62             corrected_name = "CONTENT_TYPE"
63         else:
64             corrected_name = "HTTP_%s" % name_str.upper().replace("-", "_")
65         # HTTPbis say only ASCII chars are allowed in headers, but we latin1
66         # just in case
67         value_str: str = value.decode("latin1")
68         if corrected_name in environ:
69             corrected_name_environ = environ[corrected_name]
70             assert isinstance(corrected_name_environ, str)
71             value_str = corrected_name_environ + "," + value_str
72         environ[corrected_name] = value_str
73     return environ
74 
75 
76 class WSGIMiddleware:
77     def __init__(self, app: WSGIApp, workers: int = 10):
78         self.app = app
79         self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=workers)
80 
81     async def __call__(
82         self,
83         scope: "HTTPScope",
84         receive: "ASGIReceiveCallable",
85         send: "ASGISendCallable",
86     ) -> None:
87         assert scope["type"] == "http"
88         instance = WSGIResponder(self.app, self.executor, scope)
89         await instance(receive, send)
90 
91 
92 class WSGIResponder:
93     def __init__(
94         self,
95         app: WSGIApp,
96         executor: concurrent.futures.ThreadPoolExecutor,
97         scope: "HTTPScope",
98     ):
99         self.app = app
100         self.executor = executor
101         self.scope = scope
102         self.status = None
103         self.response_headers = None
104         self.send_event = asyncio.Event()
105         self.send_queue: Deque[Optional["ASGISendEvent"]] = deque()
106         self.loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()
107         self.response_started = False
108         self.exc_info: Optional[ExcInfo] = None
109 
110     async def __call__(
111         self, receive: "ASGIReceiveCallable", send: "ASGISendCallable"
112     ) -> None:
113         message: HTTPRequestEvent = await receive()  # type: ignore[assignment]
114         body = io.BytesIO(message.get("body", b""))
115         more_body = message.get("more_body", False)
116         if more_body:
117             body.seek(0, io.SEEK_END)
118             while more_body:
119                 body_message: "HTTPRequestEvent" = (
120                     await receive()  # type: ignore[assignment]
121                 )
122                 body.write(body_message.get("body", b""))
123                 more_body = body_message.get("more_body", False)
124             body.seek(0)
125         environ = build_environ(self.scope, message, body)
126         self.loop = asyncio.get_event_loop()
127         wsgi = self.loop.run_in_executor(
128             self.executor, self.wsgi, environ, self.start_response
129         )
130         sender = self.loop.create_task(self.sender(send))
131         try:
132             await asyncio.wait_for(wsgi, None)
133         finally:
134             self.send_queue.append(None)
135             self.send_event.set()
136             await asyncio.wait_for(sender, None)
137         if self.exc_info is not None:
138             raise self.exc_info[0].with_traceback(self.exc_info[1], self.exc_info[2])
139 
140     async def sender(self, send: "ASGISendCallable") -> None:
141         while True:
142             if self.send_queue:
143                 message = self.send_queue.popleft()
144                 if message is None:
145                     return
146                 await send(message)
147             else:
148                 await self.send_event.wait()
149                 self.send_event.clear()
150 
151     def start_response(
152         self,
153         status: str,
154         response_headers: Iterable[Tuple[str, str]],
155         exc_info: Optional[ExcInfo] = None,
156     ) -> None:
157         self.exc_info = exc_info
158         if not self.response_started:
159             self.response_started = True
160             status_code_str, _ = status.split(" ", 1)
161             status_code = int(status_code_str)
162             headers = [
163                 (name.encode("ascii"), value.encode("ascii"))
164                 for name, value in response_headers
165             ]
166             http_response_start_event: HTTPResponseStartEvent = {
167                 "type": "http.response.start",
168                 "status": status_code,
169                 "headers": headers,
170             }
171             self.send_queue.append(http_response_start_event)
172             self.loop.call_soon_threadsafe(self.send_event.set)
173 
174     def wsgi(self, environ: Environ, start_response: StartResponse) -> None:
175         for chunk in self.app(environ, start_response):  # type: ignore
176             response_body: HTTPResponseBodyEvent = {
177                 "type": "http.response.body",
178                 "body": chunk,
179                 "more_body": True,
180             }
181             self.send_queue.append(response_body)
182             self.loop.call_soon_threadsafe(self.send_event.set)
183 
184         empty_body: HTTPResponseBodyEvent = {
185             "type": "http.response.body",
186             "body": b"",
187             "more_body": False,
188         }
189         self.send_queue.append(empty_body)
190         self.loop.call_soon_threadsafe(self.send_event.set)
191
```
--- 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/uvicorn/middleware/wsgi.py b/uvicorn/middleware/wsgi.py
--- a/uvicorn/middleware/wsgi.py
+++ b/uvicorn/middleware/wsgi.py
@@ -2,6 +2,7 @@
 import concurrent.futures
 import io
 import sys
+import warnings
 from collections import deque
 from typing import TYPE_CHECKING, Deque, Iterable, Optional, Tuple
 
@@ -73,8 +74,13 @@
     return environ
 
 
-class WSGIMiddleware:
+class _WSGIMiddleware:
     def __init__(self, app: WSGIApp, workers: int = 10):
+        warnings.warn(
+            "Uvicorn's native WSGI implementation is deprecated, you "
+            "should switch to a2wsgi (`pip install a2wsgi`).",
+            DeprecationWarning,
+        )
         self.app = app
         self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=workers)
 
@@ -188,3 +194,9 @@
         }
         self.send_queue.append(empty_body)
         self.loop.call_soon_threadsafe(self.send_event.set)
+
+
+try:
+    from a2wsgi import WSGIMiddleware
+except ModuleNotFoundError:
+    WSGIMiddleware = _WSGIMiddleware  # type: ignore[misc, assignment]

{"golden_diff": "diff --git a/uvicorn/middleware/wsgi.py b/uvicorn/middleware/wsgi.py\n--- a/uvicorn/middleware/wsgi.py\n+++ b/uvicorn/middleware/wsgi.py\n@@ -2,6 +2,7 @@\n import concurrent.futures\n import io\n import sys\n+import warnings\n from collections import deque\n from typing import TYPE_CHECKING, Deque, Iterable, Optional, Tuple\n \n@@ -73,8 +74,13 @@\n     return environ\n \n \n-class WSGIMiddleware:\n+class _WSGIMiddleware:\n     def __init__(self, app: WSGIApp, workers: int = 10):\n+        warnings.warn(\n+            \"Uvicorn's native WSGI implementation is deprecated, you \"\n+            \"should switch to a2wsgi (`pip install a2wsgi`).\",\n+            DeprecationWarning,\n+        )\n         self.app = app\n         self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=workers)\n \n@@ -188,3 +194,9 @@\n         }\n         self.send_queue.append(empty_body)\n         self.loop.call_soon_threadsafe(self.send_event.set)\n+\n+\n+try:\n+    from a2wsgi import WSGIMiddleware\n+except ModuleNotFoundError:\n+    WSGIMiddleware = _WSGIMiddleware  # type: ignore[misc, assignment]\n", "issue": "WSGI middleware should stream request body, rather than loading it all at once.\nHi! I'm deploying a Django app with uvicorn, running on k8s. Our containers were being killed, and I've found that when users upload large files, uvicorn increases memory usage, and slows to a crawl. Eventually causing an OOM.\r\n\r\nI'm not sure where this is happening yet, and I suspect could be related to https://github.com/django/channels/pull/1251, but I did a bit more digging and I'm not totally sure. I've tried running uvicorn in wsgi mode, and completely removed channels from the django install, and I'm getting the same behavior. File uploads are being loaded into memory (rather than streaming to disk, as they should be), and upload speeds slow to a crawl. The same app running on gunicorn works fine.\r\n\r\nThe file I'm testing with is about 470mb.\n", "before_files": [{"content": "import asyncio\nimport concurrent.futures\nimport io\nimport sys\nfrom collections import deque\nfrom typing import TYPE_CHECKING, Deque, Iterable, Optional, Tuple\n\nif TYPE_CHECKING:\n    from asgiref.typing import (\n        ASGIReceiveCallable,\n        ASGIReceiveEvent,\n        ASGISendCallable,\n        ASGISendEvent,\n        HTTPRequestEvent,\n        HTTPResponseBodyEvent,\n        HTTPResponseStartEvent,\n        HTTPScope,\n    )\n\nfrom uvicorn._types import Environ, ExcInfo, StartResponse, WSGIApp\n\n\ndef build_environ(\n    scope: \"HTTPScope\", message: \"ASGIReceiveEvent\", body: io.BytesIO\n) -> Environ:\n    \"\"\"\n    Builds a scope and request message into a WSGI environ object.\n    \"\"\"\n    environ = {\n        \"REQUEST_METHOD\": scope[\"method\"],\n        \"SCRIPT_NAME\": \"\",\n        \"PATH_INFO\": scope[\"path\"].encode(\"utf8\").decode(\"latin1\"),\n        \"QUERY_STRING\": scope[\"query_string\"].decode(\"ascii\"),\n        \"SERVER_PROTOCOL\": \"HTTP/%s\" % scope[\"http_version\"],\n        \"wsgi.version\": (1, 0),\n        \"wsgi.url_scheme\": scope.get(\"scheme\", \"http\"),\n        \"wsgi.input\": body,\n        \"wsgi.errors\": sys.stdout,\n        \"wsgi.multithread\": True,\n        \"wsgi.multiprocess\": True,\n        \"wsgi.run_once\": False,\n    }\n\n    # Get server name and port - required in WSGI, not in ASGI\n    server = scope.get(\"server\")\n    if server is None:\n        server = (\"localhost\", 80)\n    environ[\"SERVER_NAME\"] = server[0]\n    environ[\"SERVER_PORT\"] = server[1]\n\n    # Get client IP address\n    client = scope.get(\"client\")\n    if client is not None:\n        environ[\"REMOTE_ADDR\"] = client[0]\n\n    # Go through headers and make them into environ entries\n    for name, value in scope.get(\"headers\", []):\n        name_str: str = name.decode(\"latin1\")\n        if name_str == \"content-length\":\n            corrected_name = \"CONTENT_LENGTH\"\n        elif name_str == \"content-type\":\n            corrected_name = \"CONTENT_TYPE\"\n        else:\n            corrected_name = \"HTTP_%s\" % name_str.upper().replace(\"-\", \"_\")\n        # HTTPbis say only ASCII chars are allowed in headers, but we latin1\n        # just in case\n        value_str: str = value.decode(\"latin1\")\n        if corrected_name in environ:\n            corrected_name_environ = environ[corrected_name]\n            assert isinstance(corrected_name_environ, str)\n            value_str = corrected_name_environ + \",\" + value_str\n        environ[corrected_name] = value_str\n    return environ\n\n\nclass WSGIMiddleware:\n    def __init__(self, app: WSGIApp, workers: int = 10):\n        self.app = app\n        self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=workers)\n\n    async def __call__(\n        self,\n        scope: \"HTTPScope\",\n        receive: \"ASGIReceiveCallable\",\n        send: \"ASGISendCallable\",\n    ) -> None:\n        assert scope[\"type\"] == \"http\"\n        instance = WSGIResponder(self.app, self.executor, scope)\n        await instance(receive, send)\n\n\nclass WSGIResponder:\n    def __init__(\n        self,\n        app: WSGIApp,\n        executor: concurrent.futures.ThreadPoolExecutor,\n        scope: \"HTTPScope\",\n    ):\n        self.app = app\n        self.executor = executor\n        self.scope = scope\n        self.status = None\n        self.response_headers = None\n        self.send_event = asyncio.Event()\n        self.send_queue: Deque[Optional[\"ASGISendEvent\"]] = deque()\n        self.loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()\n        self.response_started = False\n        self.exc_info: Optional[ExcInfo] = None\n\n    async def __call__(\n        self, receive: \"ASGIReceiveCallable\", send: \"ASGISendCallable\"\n    ) -> None:\n        message: HTTPRequestEvent = await receive()  # type: ignore[assignment]\n        body = io.BytesIO(message.get(\"body\", b\"\"))\n        more_body = message.get(\"more_body\", False)\n        if more_body:\n            body.seek(0, io.SEEK_END)\n            while more_body:\n                body_message: \"HTTPRequestEvent\" = (\n                    await receive()  # type: ignore[assignment]\n                )\n                body.write(body_message.get(\"body\", b\"\"))\n                more_body = body_message.get(\"more_body\", False)\n            body.seek(0)\n        environ = build_environ(self.scope, message, body)\n        self.loop = asyncio.get_event_loop()\n        wsgi = self.loop.run_in_executor(\n            self.executor, self.wsgi, environ, self.start_response\n        )\n        sender = self.loop.create_task(self.sender(send))\n        try:\n            await asyncio.wait_for(wsgi, None)\n        finally:\n            self.send_queue.append(None)\n            self.send_event.set()\n            await asyncio.wait_for(sender, None)\n        if self.exc_info is not None:\n            raise self.exc_info[0].with_traceback(self.exc_info[1], self.exc_info[2])\n\n    async def sender(self, send: \"ASGISendCallable\") -> None:\n        while True:\n            if self.send_queue:\n                message = self.send_queue.popleft()\n                if message is None:\n                    return\n                await send(message)\n            else:\n                await self.send_event.wait()\n                self.send_event.clear()\n\n    def start_response(\n        self,\n        status: str,\n        response_headers: Iterable[Tuple[str, str]],\n        exc_info: Optional[ExcInfo] = None,\n    ) -> None:\n        self.exc_info = exc_info\n        if not self.response_started:\n            self.response_started = True\n            status_code_str, _ = status.split(\" \", 1)\n            status_code = int(status_code_str)\n            headers = [\n                (name.encode(\"ascii\"), value.encode(\"ascii\"))\n                for name, value in response_headers\n            ]\n            http_response_start_event: HTTPResponseStartEvent = {\n                \"type\": \"http.response.start\",\n                \"status\": status_code,\n                \"headers\": headers,\n            }\n            self.send_queue.append(http_response_start_event)\n            self.loop.call_soon_threadsafe(self.send_event.set)\n\n    def wsgi(self, environ: Environ, start_response: StartResponse) -> None:\n        for chunk in self.app(environ, start_response):  # type: ignore\n            response_body: HTTPResponseBodyEvent = {\n                \"type\": \"http.response.body\",\n                \"body\": chunk,\n                \"more_body\": True,\n            }\n            self.send_queue.append(response_body)\n            self.loop.call_soon_threadsafe(self.send_event.set)\n\n        empty_body: HTTPResponseBodyEvent = {\n            \"type\": \"http.response.body\",\n            \"body\": b\"\",\n            \"more_body\": False,\n        }\n        self.send_queue.append(empty_body)\n        self.loop.call_soon_threadsafe(self.send_event.set)\n", "path": "uvicorn/middleware/wsgi.py"}], "after_files": [{"content": "import asyncio\nimport concurrent.futures\nimport io\nimport sys\nimport warnings\nfrom collections import deque\nfrom typing import TYPE_CHECKING, Deque, Iterable, Optional, Tuple\n\nif TYPE_CHECKING:\n    from asgiref.typing import (\n        ASGIReceiveCallable,\n        ASGIReceiveEvent,\n        ASGISendCallable,\n        ASGISendEvent,\n        HTTPRequestEvent,\n        HTTPResponseBodyEvent,\n        HTTPResponseStartEvent,\n        HTTPScope,\n    )\n\nfrom uvicorn._types import Environ, ExcInfo, StartResponse, WSGIApp\n\n\ndef build_environ(\n    scope: \"HTTPScope\", message: \"ASGIReceiveEvent\", body: io.BytesIO\n) -> Environ:\n    \"\"\"\n    Builds a scope and request message into a WSGI environ object.\n    \"\"\"\n    environ = {\n        \"REQUEST_METHOD\": scope[\"method\"],\n        \"SCRIPT_NAME\": \"\",\n        \"PATH_INFO\": scope[\"path\"].encode(\"utf8\").decode(\"latin1\"),\n        \"QUERY_STRING\": scope[\"query_string\"].decode(\"ascii\"),\n        \"SERVER_PROTOCOL\": \"HTTP/%s\" % scope[\"http_version\"],\n        \"wsgi.version\": (1, 0),\n        \"wsgi.url_scheme\": scope.get(\"scheme\", \"http\"),\n        \"wsgi.input\": body,\n        \"wsgi.errors\": sys.stdout,\n        \"wsgi.multithread\": True,\n        \"wsgi.multiprocess\": True,\n        \"wsgi.run_once\": False,\n    }\n\n    # Get server name and port - required in WSGI, not in ASGI\n    server = scope.get(\"server\")\n    if server is None:\n        server = (\"localhost\", 80)\n    environ[\"SERVER_NAME\"] = server[0]\n    environ[\"SERVER_PORT\"] = server[1]\n\n    # Get client IP address\n    client = scope.get(\"client\")\n    if client is not None:\n        environ[\"REMOTE_ADDR\"] = client[0]\n\n    # Go through headers and make them into environ entries\n    for name, value in scope.get(\"headers\", []):\n        name_str: str = name.decode(\"latin1\")\n        if name_str == \"content-length\":\n            corrected_name = \"CONTENT_LENGTH\"\n        elif name_str == \"content-type\":\n            corrected_name = \"CONTENT_TYPE\"\n        else:\n            corrected_name = \"HTTP_%s\" % name_str.upper().replace(\"-\", \"_\")\n        # HTTPbis say only ASCII chars are allowed in headers, but we latin1\n        # just in case\n        value_str: str = value.decode(\"latin1\")\n        if corrected_name in environ:\n            corrected_name_environ = environ[corrected_name]\n            assert isinstance(corrected_name_environ, str)\n            value_str = corrected_name_environ + \",\" + value_str\n        environ[corrected_name] = value_str\n    return environ\n\n\nclass _WSGIMiddleware:\n    def __init__(self, app: WSGIApp, workers: int = 10):\n        warnings.warn(\n            \"Uvicorn's native WSGI implementation is deprecated, you \"\n            \"should switch to a2wsgi (`pip install a2wsgi`).\",\n            DeprecationWarning,\n        )\n        self.app = app\n        self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=workers)\n\n    async def __call__(\n        self,\n        scope: \"HTTPScope\",\n        receive: \"ASGIReceiveCallable\",\n        send: \"ASGISendCallable\",\n    ) -> None:\n        assert scope[\"type\"] == \"http\"\n        instance = WSGIResponder(self.app, self.executor, scope)\n        await instance(receive, send)\n\n\nclass WSGIResponder:\n    def __init__(\n        self,\n        app: WSGIApp,\n        executor: concurrent.futures.ThreadPoolExecutor,\n        scope: \"HTTPScope\",\n    ):\n        self.app = app\n        self.executor = executor\n        self.scope = scope\n        self.status = None\n        self.response_headers = None\n        self.send_event = asyncio.Event()\n        self.send_queue: Deque[Optional[\"ASGISendEvent\"]] = deque()\n        self.loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()\n        self.response_started = False\n        self.exc_info: Optional[ExcInfo] = None\n\n    async def __call__(\n        self, receive: \"ASGIReceiveCallable\", send: \"ASGISendCallable\"\n    ) -> None:\n        message: HTTPRequestEvent = await receive()  # type: ignore[assignment]\n        body = io.BytesIO(message.get(\"body\", b\"\"))\n        more_body = message.get(\"more_body\", False)\n        if more_body:\n            body.seek(0, io.SEEK_END)\n            while more_body:\n                body_message: \"HTTPRequestEvent\" = (\n                    await receive()  # type: ignore[assignment]\n                )\n                body.write(body_message.get(\"body\", b\"\"))\n                more_body = body_message.get(\"more_body\", False)\n            body.seek(0)\n        environ = build_environ(self.scope, message, body)\n        self.loop = asyncio.get_event_loop()\n        wsgi = self.loop.run_in_executor(\n            self.executor, self.wsgi, environ, self.start_response\n        )\n        sender = self.loop.create_task(self.sender(send))\n        try:\n            await asyncio.wait_for(wsgi, None)\n        finally:\n            self.send_queue.append(None)\n            self.send_event.set()\n            await asyncio.wait_for(sender, None)\n        if self.exc_info is not None:\n            raise self.exc_info[0].with_traceback(self.exc_info[1], self.exc_info[2])\n\n    async def sender(self, send: \"ASGISendCallable\") -> None:\n        while True:\n            if self.send_queue:\n                message = self.send_queue.popleft()\n                if message is None:\n                    return\n                await send(message)\n            else:\n                await self.send_event.wait()\n                self.send_event.clear()\n\n    def start_response(\n        self,\n        status: str,\n        response_headers: Iterable[Tuple[str, str]],\n        exc_info: Optional[ExcInfo] = None,\n    ) -> None:\n        self.exc_info = exc_info\n        if not self.response_started:\n            self.response_started = True\n            status_code_str, _ = status.split(\" \", 1)\n            status_code = int(status_code_str)\n            headers = [\n                (name.encode(\"ascii\"), value.encode(\"ascii\"))\n                for name, value in response_headers\n            ]\n            http_response_start_event: HTTPResponseStartEvent = {\n                \"type\": \"http.response.start\",\n                \"status\": status_code,\n                \"headers\": headers,\n            }\n            self.send_queue.append(http_response_start_event)\n            self.loop.call_soon_threadsafe(self.send_event.set)\n\n    def wsgi(self, environ: Environ, start_response: StartResponse) -> None:\n        for chunk in self.app(environ, start_response):  # type: ignore\n            response_body: HTTPResponseBodyEvent = {\n                \"type\": \"http.response.body\",\n                \"body\": chunk,\n                \"more_body\": True,\n            }\n            self.send_queue.append(response_body)\n            self.loop.call_soon_threadsafe(self.send_event.set)\n\n        empty_body: HTTPResponseBodyEvent = {\n            \"type\": \"http.response.body\",\n            \"body\": b\"\",\n            \"more_body\": False,\n        }\n        self.send_queue.append(empty_body)\n        self.loop.call_soon_threadsafe(self.send_event.set)\n\n\ntry:\n    from a2wsgi import WSGIMiddleware\nexcept ModuleNotFoundError:\n    WSGIMiddleware = _WSGIMiddleware  # type: ignore[misc, assignment]\n", "path": "uvicorn/middleware/wsgi.py"}]}