We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
WSGI middleware error in non-latin1 path
### 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

<!-- A clear and concise description of what the bug is. -->

The same bug has been addressed in https://github.com/encode/starlette/issues/997.

https://github.com/encode/uvicorn/blob/9d51e1cfa68ee6e4ee3fb8a23b8599bb44c3985a/uvicorn/middleware/wsgi.py#L14

In uvicorn's wsgi middleware, `scope["path"]` in asgi is utf8-encoded but the corresponding `PATH_INFO` required by wsgi should be encoded in latin1 according to the standards.

### To reproduce

<!-- Provide a *minimal* example with steps to reproduce the bug locally.

NOTE: try to keep any external dependencies *at an absolute minimum* .
In other words, remove anything that doesn't make the bug go away.

-->

```python
import flask
import uvicorn

app = flask.Flask(__name__)

@app.route("/<keyword>")
def home(keyword):
    return keyword

if __name__ == "__main__":
    uvicorn.run(app, interface="wsgi")
```

1. run the script.
2. send a request to "http://127.0.0.1:8000/文"

### Expected behavior

<!-- A clear and concise description of what you expected to happen. -->

Get a 200 response

### Actual behavior

<!-- A clear and concise description of what actually happens. -->

Get 500 Internal Server Error

### Debugging material

<!-- Any tracebacks, screenshots, etc. that can help understanding the problem.

NOTE:
- Please list tracebacks in full (don't truncate them).
- If relevant, consider turning on DEBUG or TRACE logs for additional details (see the Logging section on https://www.uvicorn.org/settings/ specifically the `log-level` flag).
- Consider using `<details>` to make tracebacks/logs collapsible if they're very large (see https://gist.github.com/ericclemmons/b146fe5da72ca1f706b2ef72a20ac39d).
-->

<details>

```python
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 394, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\uvicorn\middleware\wsgi.py", line 64, in __call__
    await instance(receive, send)
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\uvicorn\middleware\wsgi.py", line 95, in __call__
    await asyncio.wait_for(wsgi, None)
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\asyncio\tasks.py", line 455, in wait_for
    return await fut
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\concurrent\futures\thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\uvicorn\middleware\wsgi.py", line 134, in wsgi
    for chunk in self.app(environ, start_response):
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\flask\app.py", line 2464, in __call__
    return self.wsgi_app(environ, start_response)
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\flask\app.py", line 2442, in wsgi_app
    ctx = self.request_context(environ)
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\flask\app.py", line 2359, in request_context
    return RequestContext(self, environ)
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\flask\ctx.py", line 292, in __init__
    self.url_adapter = app.create_url_adapter(self.request)
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\flask\app.py", line 2171, in create_url_adapter
    return self.url_map.bind_to_environ(
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\werkzeug\routing.py", line 1634, in bind_to_environ
    path_info = _get_wsgi_string("PATH_INFO")
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\werkzeug\routing.py", line 1631, in _get_wsgi_string
    return wsgi_decoding_dance(val, self.charset)
  File "C:\Users\cools\AppData\Local\Programs\Python\Python38\lib\site-packages\werkzeug\_compat.py", line 188, in wsgi_decoding_dance
    return s.encode("latin1").decode(charset, errors)
UnicodeEncodeError: 'latin-1' codec can't encode character '\u6587' in position 1: ordinal not in range(256)
INFO:     127.0.0.1:52574 - "GET /%E6%96%87 HTTP/1.1" 500 Internal Server Error
```

</details>

### Environment

- OS / Python / Uvicorn version: Running uvicorn 0.13.3 with CPython 3.8.5 on Windows
- The exact command you're running uvicorn with, all flags you passed included. If you run it with gunicorn please do the same. If there is a reverse-proxy involved and you cannot reproduce without it please give the minimal config of it to reproduce.

### Additional context

<!-- Any additional information that can help understanding the problem.

Eg. linked issues, or a description of what you were trying to achieve. -->

--- 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 
6 
7 def build_environ(scope, message, body):
8     """
9     Builds a scope and request message into a WSGI environ object.
10     """
11     environ = {
12         "REQUEST_METHOD": scope["method"],
13         "SCRIPT_NAME": "",
14         "PATH_INFO": scope["path"],
15         "QUERY_STRING": scope["query_string"].decode("ascii"),
16         "SERVER_PROTOCOL": "HTTP/%s" % scope["http_version"],
17         "wsgi.version": (1, 0),
18         "wsgi.url_scheme": scope.get("scheme", "http"),
19         "wsgi.input": io.BytesIO(body),
20         "wsgi.errors": sys.stdout,
21         "wsgi.multithread": True,
22         "wsgi.multiprocess": True,
23         "wsgi.run_once": False,
24     }
25 
26     # Get server name and port - required in WSGI, not in ASGI
27     server = scope.get("server")
28     if server is None:
29         server = ("localhost", 80)
30     environ["SERVER_NAME"] = server[0]
31     environ["SERVER_PORT"] = server[1]
32 
33     # Get client IP address
34     client = scope.get("client")
35     if client is not None:
36         environ["REMOTE_ADDR"] = client[0]
37 
38     # Go through headers and make them into environ entries
39     for name, value in scope.get("headers", []):
40         name = name.decode("latin1")
41         if name == "content-length":
42             corrected_name = "CONTENT_LENGTH"
43         elif name == "content-type":
44             corrected_name = "CONTENT_TYPE"
45         else:
46             corrected_name = "HTTP_%s" % name.upper().replace("-", "_")
47         # HTTPbis say only ASCII chars are allowed in headers, but we latin1
48         # just in case
49         value = value.decode("latin1")
50         if corrected_name in environ:
51             value = environ[corrected_name] + "," + value
52         environ[corrected_name] = value
53     return environ
54 
55 
56 class WSGIMiddleware:
57     def __init__(self, app, workers=10):
58         self.app = app
59         self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=workers)
60 
61     async def __call__(self, scope, receive, send):
62         assert scope["type"] == "http"
63         instance = WSGIResponder(self.app, self.executor, scope)
64         await instance(receive, send)
65 
66 
67 class WSGIResponder:
68     def __init__(self, app, executor, scope):
69         self.app = app
70         self.executor = executor
71         self.scope = scope
72         self.status = None
73         self.response_headers = None
74         self.send_event = asyncio.Event()
75         self.send_queue = []
76         self.loop = None
77         self.response_started = False
78         self.exc_info = None
79 
80     async def __call__(self, receive, send):
81         message = await receive()
82         body = message.get("body", b"")
83         more_body = message.get("more_body", False)
84         while more_body:
85             body_message = await receive()
86             body += body_message.get("body", b"")
87             more_body = body_message.get("more_body", False)
88         environ = build_environ(self.scope, message, body)
89         self.loop = asyncio.get_event_loop()
90         wsgi = self.loop.run_in_executor(
91             self.executor, self.wsgi, environ, self.start_response
92         )
93         sender = self.loop.create_task(self.sender(send))
94         try:
95             await asyncio.wait_for(wsgi, None)
96         finally:
97             self.send_queue.append(None)
98             self.send_event.set()
99             await asyncio.wait_for(sender, None)
100         if self.exc_info is not None:
101             raise self.exc_info[0].with_traceback(self.exc_info[1], self.exc_info[2])
102 
103     async def sender(self, send):
104         while True:
105             if self.send_queue:
106                 message = self.send_queue.pop(0)
107                 if message is None:
108                     return
109                 await send(message)
110             else:
111                 await self.send_event.wait()
112                 self.send_event.clear()
113 
114     def start_response(self, status, response_headers, exc_info=None):
115         self.exc_info = exc_info
116         if not self.response_started:
117             self.response_started = True
118             status_code, _ = status.split(" ", 1)
119             status_code = int(status_code)
120             headers = [
121                 (name.encode("ascii"), value.encode("ascii"))
122                 for name, value in response_headers
123             ]
124             self.send_queue.append(
125                 {
126                     "type": "http.response.start",
127                     "status": status_code,
128                     "headers": headers,
129                 }
130             )
131             self.loop.call_soon_threadsafe(self.send_event.set)
132 
133     def wsgi(self, environ, start_response):
134         for chunk in self.app(environ, start_response):
135             self.send_queue.append(
136                 {"type": "http.response.body", "body": chunk, "more_body": True}
137             )
138             self.loop.call_soon_threadsafe(self.send_event.set)
139 
140         self.send_queue.append({"type": "http.response.body", "body": b""})
141         self.loop.call_soon_threadsafe(self.send_event.set)
142
```
--- 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
@@ -11,7 +11,7 @@
     environ = {
         "REQUEST_METHOD": scope["method"],
         "SCRIPT_NAME": "",
-        "PATH_INFO": scope["path"],
+        "PATH_INFO": scope["path"].encode("utf8").decode("latin1"),
         "QUERY_STRING": scope["query_string"].decode("ascii"),
         "SERVER_PROTOCOL": "HTTP/%s" % scope["http_version"],
         "wsgi.version": (1, 0),

{"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@@ -11,7 +11,7 @@\n     environ = {\n         \"REQUEST_METHOD\": scope[\"method\"],\n         \"SCRIPT_NAME\": \"\",\n-        \"PATH_INFO\": scope[\"path\"],\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", "issue": "WSGI middleware error in non-latin1 path\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<!-- A clear and concise description of what the bug is. -->\r\n\r\nThe same bug has been addressed in https://github.com/encode/starlette/issues/997.\r\n\r\nhttps://github.com/encode/uvicorn/blob/9d51e1cfa68ee6e4ee3fb8a23b8599bb44c3985a/uvicorn/middleware/wsgi.py#L14\r\n\r\nIn uvicorn's wsgi middleware, `scope[\"path\"]` in asgi is utf8-encoded but the corresponding `PATH_INFO` required by wsgi should be encoded in latin1 according to the standards.\r\n\r\n### To reproduce\r\n\r\n<!-- Provide a *minimal* example with steps to reproduce the bug locally.\r\n\r\nNOTE: try to keep any external dependencies *at an absolute minimum* .\r\nIn other words, remove anything that doesn't make the bug go away.\r\n\r\n-->\r\n\r\n```python\r\nimport flask\r\nimport uvicorn\r\n\r\napp = flask.Flask(__name__)\r\n\r\n@app.route(\"/<keyword>\")\r\ndef home(keyword):\r\n    return keyword\r\n\r\nif __name__ == \"__main__\":\r\n    uvicorn.run(app, interface=\"wsgi\")\r\n```\r\n\r\n1. run the script.\r\n2. send a request to \"http://127.0.0.1:8000/\u6587\"\r\n\r\n### Expected behavior\r\n\r\n<!-- A clear and concise description of what you expected to happen. -->\r\n\r\nGet a 200 response\r\n\r\n### Actual behavior\r\n\r\n<!-- A clear and concise description of what actually happens. -->\r\n\r\nGet 500 Internal Server Error\r\n\r\n### Debugging material\r\n\r\n<!-- Any tracebacks, screenshots, etc. that can help understanding the problem.\r\n\r\nNOTE:\r\n- Please list tracebacks in full (don't truncate them).\r\n- If relevant, consider turning on DEBUG or TRACE logs for additional details (see the Logging section on https://www.uvicorn.org/settings/ specifically the `log-level` flag).\r\n- Consider using `<details>` to make tracebacks/logs collapsible if they're very large (see https://gist.github.com/ericclemmons/b146fe5da72ca1f706b2ef72a20ac39d).\r\n-->\r\n\r\n<details>\r\n\r\n```python\r\nERROR:    Exception in ASGI application\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\uvicorn\\protocols\\http\\h11_impl.py\", line 394, in run_asgi\r\n    result = await app(self.scope, self.receive, self.send)\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\uvicorn\\middleware\\proxy_headers.py\", line 45, in __call__\r\n    return await self.app(scope, receive, send)\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\uvicorn\\middleware\\wsgi.py\", line 64, in __call__\r\n    await instance(receive, send)\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\uvicorn\\middleware\\wsgi.py\", line 95, in __call__\r\n    await asyncio.wait_for(wsgi, None)\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\asyncio\\tasks.py\", line 455, in wait_for\r\n    return await fut\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\concurrent\\futures\\thread.py\", line 57, in run\r\n    result = self.fn(*self.args, **self.kwargs)\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\uvicorn\\middleware\\wsgi.py\", line 134, in wsgi\r\n    for chunk in self.app(environ, start_response):\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\flask\\app.py\", line 2464, in __call__\r\n    return self.wsgi_app(environ, start_response)\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\flask\\app.py\", line 2442, in wsgi_app\r\n    ctx = self.request_context(environ)\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\flask\\app.py\", line 2359, in request_context\r\n    return RequestContext(self, environ)\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\flask\\ctx.py\", line 292, in __init__\r\n    self.url_adapter = app.create_url_adapter(self.request)\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\flask\\app.py\", line 2171, in create_url_adapter\r\n    return self.url_map.bind_to_environ(\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\werkzeug\\routing.py\", line 1634, in bind_to_environ\r\n    path_info = _get_wsgi_string(\"PATH_INFO\")\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\werkzeug\\routing.py\", line 1631, in _get_wsgi_string\r\n    return wsgi_decoding_dance(val, self.charset)\r\n  File \"C:\\Users\\cools\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\werkzeug\\_compat.py\", line 188, in wsgi_decoding_dance\r\n    return s.encode(\"latin1\").decode(charset, errors)\r\nUnicodeEncodeError: 'latin-1' codec can't encode character '\\u6587' in position 1: ordinal not in range(256)\r\nINFO:     127.0.0.1:52574 - \"GET /%E6%96%87 HTTP/1.1\" 500 Internal Server Error\r\n```\r\n\r\n</details>\r\n\r\n### Environment\r\n\r\n- OS / Python / Uvicorn version: Running uvicorn 0.13.3 with CPython 3.8.5 on Windows\r\n- The exact command you're running uvicorn with, all flags you passed included. If you run it with gunicorn please do the same. If there is a reverse-proxy involved and you cannot reproduce without it please give the minimal config of it to reproduce.\r\n\r\n### Additional context\r\n\r\n<!-- Any additional information that can help understanding the problem.\r\n\r\nEg. linked issues, or a description of what you were trying to achieve. -->\n", "before_files": [{"content": "import asyncio\nimport concurrent.futures\nimport io\nimport sys\n\n\ndef build_environ(scope, message, body):\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\"],\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\": io.BytesIO(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 = name.decode(\"latin1\")\n        if name == \"content-length\":\n            corrected_name = \"CONTENT_LENGTH\"\n        elif name == \"content-type\":\n            corrected_name = \"CONTENT_TYPE\"\n        else:\n            corrected_name = \"HTTP_%s\" % name.upper().replace(\"-\", \"_\")\n        # HTTPbis say only ASCII chars are allowed in headers, but we latin1\n        # just in case\n        value = value.decode(\"latin1\")\n        if corrected_name in environ:\n            value = environ[corrected_name] + \",\" + value\n        environ[corrected_name] = value\n    return environ\n\n\nclass WSGIMiddleware:\n    def __init__(self, app, workers=10):\n        self.app = app\n        self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=workers)\n\n    async def __call__(self, scope, receive, send):\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__(self, app, executor, scope):\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 = []\n        self.loop = None\n        self.response_started = False\n        self.exc_info = None\n\n    async def __call__(self, receive, send):\n        message = await receive()\n        body = message.get(\"body\", b\"\")\n        more_body = message.get(\"more_body\", False)\n        while more_body:\n            body_message = await receive()\n            body += body_message.get(\"body\", b\"\")\n            more_body = body_message.get(\"more_body\", False)\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):\n        while True:\n            if self.send_queue:\n                message = self.send_queue.pop(0)\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(self, status, response_headers, exc_info=None):\n        self.exc_info = exc_info\n        if not self.response_started:\n            self.response_started = True\n            status_code, _ = status.split(\" \", 1)\n            status_code = int(status_code)\n            headers = [\n                (name.encode(\"ascii\"), value.encode(\"ascii\"))\n                for name, value in response_headers\n            ]\n            self.send_queue.append(\n                {\n                    \"type\": \"http.response.start\",\n                    \"status\": status_code,\n                    \"headers\": headers,\n                }\n            )\n            self.loop.call_soon_threadsafe(self.send_event.set)\n\n    def wsgi(self, environ, start_response):\n        for chunk in self.app(environ, start_response):\n            self.send_queue.append(\n                {\"type\": \"http.response.body\", \"body\": chunk, \"more_body\": True}\n            )\n            self.loop.call_soon_threadsafe(self.send_event.set)\n\n        self.send_queue.append({\"type\": \"http.response.body\", \"body\": b\"\"})\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\n\n\ndef build_environ(scope, message, body):\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\": io.BytesIO(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 = name.decode(\"latin1\")\n        if name == \"content-length\":\n            corrected_name = \"CONTENT_LENGTH\"\n        elif name == \"content-type\":\n            corrected_name = \"CONTENT_TYPE\"\n        else:\n            corrected_name = \"HTTP_%s\" % name.upper().replace(\"-\", \"_\")\n        # HTTPbis say only ASCII chars are allowed in headers, but we latin1\n        # just in case\n        value = value.decode(\"latin1\")\n        if corrected_name in environ:\n            value = environ[corrected_name] + \",\" + value\n        environ[corrected_name] = value\n    return environ\n\n\nclass WSGIMiddleware:\n    def __init__(self, app, workers=10):\n        self.app = app\n        self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=workers)\n\n    async def __call__(self, scope, receive, send):\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__(self, app, executor, scope):\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 = []\n        self.loop = None\n        self.response_started = False\n        self.exc_info = None\n\n    async def __call__(self, receive, send):\n        message = await receive()\n        body = message.get(\"body\", b\"\")\n        more_body = message.get(\"more_body\", False)\n        while more_body:\n            body_message = await receive()\n            body += body_message.get(\"body\", b\"\")\n            more_body = body_message.get(\"more_body\", False)\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):\n        while True:\n            if self.send_queue:\n                message = self.send_queue.pop(0)\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(self, status, response_headers, exc_info=None):\n        self.exc_info = exc_info\n        if not self.response_started:\n            self.response_started = True\n            status_code, _ = status.split(\" \", 1)\n            status_code = int(status_code)\n            headers = [\n                (name.encode(\"ascii\"), value.encode(\"ascii\"))\n                for name, value in response_headers\n            ]\n            self.send_queue.append(\n                {\n                    \"type\": \"http.response.start\",\n                    \"status\": status_code,\n                    \"headers\": headers,\n                }\n            )\n            self.loop.call_soon_threadsafe(self.send_event.set)\n\n    def wsgi(self, environ, start_response):\n        for chunk in self.app(environ, start_response):\n            self.send_queue.append(\n                {\"type\": \"http.response.body\", \"body\": chunk, \"more_body\": True}\n            )\n            self.loop.call_soon_threadsafe(self.send_event.set)\n\n        self.send_queue.append({\"type\": \"http.response.body\", \"body\": b\"\"})\n        self.loop.call_soon_threadsafe(self.send_event.set)\n", "path": "uvicorn/middleware/wsgi.py"}]}