Repository: django/asgiref

Issue:
WSGI Error in non-ascii path
https://github.com/encode/starlette/issues/997

I noticed that the ASGI document clearly states that utf8 encoding is required, but WSGI seems to require latin-1 encoding.

https://www.python.org/dev/peps/pep-3333/#a-note-on-string-types
https://asgi.readthedocs.io/en/latest/specs/www.html#connection-scope

Discussion:
andrewgodwin: Looks good for the fix - any chance you could get a test in for this? It's just the sort of thing that we might break by mistake otherwise.
abersheeran: I will write a test for it. Wait a moment.

Patch:
diff --git a/asgiref/wsgi.py b/asgiref/wsgi.py
index 7155ab2..8811118 100644
--- a/asgiref/wsgi.py
+++ b/asgiref/wsgi.py
@@ -55,8 +55,8 @@ class WsgiToAsgiInstance:
         """
         environ = {
             "REQUEST_METHOD": scope["method"],
-            "SCRIPT_NAME": scope.get("root_path", ""),
-            "PATH_INFO": scope["path"],
+            "SCRIPT_NAME": scope.get("root_path", "").encode("utf8").decode("latin1"),
+            "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),


Test patch:
diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py
index 647f465..3490573 100644
--- a/tests/test_wsgi.py
+++ b/tests/test_wsgi.py
@@ -50,6 +50,48 @@ async def test_basic_wsgi():
     assert (await instance.receive_output(1)) == {"type": "http.response.body"}
 
 
+@pytest.mark.asyncio
+async def test_wsgi_path_encoding():
+    """
+    Makes sure the WSGI wrapper has basic functionality.
+    """
+    # Define WSGI app
+    def wsgi_application(environ, start_response):
+        assert environ["SCRIPT_NAME"] == "/中国".encode("utf8").decode("latin-1")
+        assert environ["PATH_INFO"] == "/中文".encode("utf8").decode("latin-1")
+        start_response("200 OK", [])
+        yield b""
+
+    # Wrap it
+    application = WsgiToAsgi(wsgi_application)
+    # Launch it as a test application
+    instance = ApplicationCommunicator(
+        application,
+        {
+            "type": "http",
+            "http_version": "1.0",
+            "method": "GET",
+            "path": "/中文",
+            "root_path": "/中国",
+            "query_string": b"bar=baz",
+            "headers": [],
+        },
+    )
+    await instance.send_input({"type": "http.request"})
+    # Check they send stuff
+    assert (await instance.receive_output(1)) == {
+        "type": "http.response.start",
+        "status": 200,
+        "headers": [],
+    }
+    assert (await instance.receive_output(1)) == {
+        "type": "http.response.body",
+        "body": b"",
+        "more_body": True,
+    }
+    assert (await instance.receive_output(1)) == {"type": "http.response.body"}
+
+
 @pytest.mark.asyncio
 async def test_wsgi_empty_body():
     """