prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Path prefix for PATH_INFO with WSGIMiddleware
  When mounting an app (e.g. Django) through `WSGIMiddleware` it seems to be required to also adjust `PATH_INFO` accordingly.
  
  With the following, Django would e.g. redirect to "/admin/" (without the outer "/dj" prefix).
  
  ```python
  djapp = django.core.wsgi.get_wsgi_application()
  app.mount("/dj", app=WSGIMiddleware(djapp))
  ```
  
  I've got this working through an additional "path_prefix" argument for WSGIMiddleware, but I think it makes sense to either prepend the mount path in `build_environ` always (using `"PATH_INFO": scope["root_path"] + scope["path"]`, and making it visible to the mounted app though), or handle e.g. "Location" response headers by prepending it there.
  The latter could likely result in redirect-loops, and does not help with Django POSTing to "/admin/login" then.
  
  I've not investigated too much into this yet, what do you think?
  
  --- 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/wsgi.py`
  Content:
  ```
  1 import asyncio
  2 import io
  3 import sys
  4 import typing
  5 
  6 from starlette.concurrency import run_in_threadpool
  7 from starlette.types import ASGIInstance, Message, Receive, Scope, Send
  8 
  9 
  10 def build_environ(scope: Scope, body: bytes) -> dict:
  11     """
  12     Builds a scope and request body into a WSGI environ object.
  13     """
  14     environ = {
  15         "REQUEST_METHOD": scope["method"],
  16         "SCRIPT_NAME": "",
  17         "PATH_INFO": scope["path"],
  18         "QUERY_STRING": scope["query_string"].decode("ascii"),
  19         "SERVER_PROTOCOL": f"HTTP/{scope['http_version']}",
  20         "wsgi.version": (1, 0),
  21         "wsgi.url_scheme": scope.get("scheme", "http"),
  22         "wsgi.input": io.BytesIO(body),
  23         "wsgi.errors": sys.stdout,
  24         "wsgi.multithread": True,
  25         "wsgi.multiprocess": True,
  26         "wsgi.run_once": False,
  27     }
  28 
  29     # Get server name and port - required in WSGI, not in ASGI
  30     server = scope.get("server") or ("localhost", 80)
  31     environ["SERVER_NAME"] = server[0]
  32     environ["SERVER_PORT"] = server[1]
  33 
  34     # Get client IP address
  35     if scope.get("client"):
  36         environ["REMOTE_ADDR"] = scope["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 = f"HTTP_{name}".upper().replace("-", "_")
  47         # HTTPbis say only ASCII chars are allowed in headers, but we latin1 just in case
  48         value = value.decode("latin1")
  49         if corrected_name in environ:
  50             value = environ[corrected_name] + "," + value
  51         environ[corrected_name] = value
  52     return environ
  53 
  54 
  55 class WSGIMiddleware:
  56     def __init__(self, app: typing.Callable, workers: int = 10) -> None:
  57         self.app = app
  58 
  59     def __call__(self, scope: Scope) -> ASGIInstance:
  60         assert scope["type"] == "http"
  61         return WSGIResponder(self.app, scope)
  62 
  63 
  64 class WSGIResponder:
  65     def __init__(self, app: typing.Callable, scope: Scope) -> None:
  66         self.app = app
  67         self.scope = scope
  68         self.status = None
  69         self.response_headers = None
  70         self.send_event = asyncio.Event()
  71         self.send_queue = []  # type: typing.List[typing.Optional[Message]]
  72         self.loop = asyncio.get_event_loop()
  73         self.response_started = False
  74         self.exc_info = None  # type: typing.Any
  75 
  76     async def __call__(self, receive: Receive, send: Send) -> None:
  77         body = b""
  78         more_body = True
  79         while more_body:
  80             message = await receive()
  81             body += message.get("body", b"")
  82             more_body = message.get("more_body", False)
  83         environ = build_environ(self.scope, body)
  84         try:
  85             sender = self.loop.create_task(self.sender(send))
  86             await run_in_threadpool(self.wsgi, environ, self.start_response)
  87             self.send_queue.append(None)
  88             self.send_event.set()
  89             await asyncio.wait_for(sender, None)
  90             if self.exc_info is not None:
  91                 raise self.exc_info[0].with_traceback(
  92                     self.exc_info[1], self.exc_info[2]
  93                 )
  94         finally:
  95             if not sender.done():
  96                 sender.cancel()  # pragma: no cover
  97 
  98     async def sender(self, send: Send) -> None:
  99         while True:
  100             if self.send_queue:
  101                 message = self.send_queue.pop(0)
  102                 if message is None:
  103                     return
  104                 await send(message)
  105             else:
  106                 await self.send_event.wait()
  107                 self.send_event.clear()
  108 
  109     def start_response(
  110         self,
  111         status: str,
  112         response_headers: typing.List[typing.Tuple[str, str]],
  113         exc_info: typing.Any = None,
  114     ) -> None:
  115         self.exc_info = exc_info
  116         if not self.response_started:
  117             self.response_started = True
  118             status_code_string, _ = status.split(" ", 1)
  119             status_code = int(status_code_string)
  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: dict, start_response: typing.Callable) -> None:
  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()
  
  ```
  
golden_diff: diff --git a/starlette/middleware/wsgi.py b/starlette/middleware/wsgi.py
  --- a/starlette/middleware/wsgi.py
  +++ b/starlette/middleware/wsgi.py
  @@ -13,7 +13,7 @@
       """
       environ = {
           "REQUEST_METHOD": scope["method"],
  -        "SCRIPT_NAME": "",
  +        "SCRIPT_NAME": scope.get("root_path", ""),
           "PATH_INFO": scope["path"],
           "QUERY_STRING": scope["query_string"].decode("ascii"),
           "SERVER_PROTOCOL": f"HTTP/{scope['http_version']}",
  
verification_info: {"golden_diff": "diff --git a/starlette/middleware/wsgi.py b/starlette/middleware/wsgi.py\n--- a/starlette/middleware/wsgi.py\n+++ b/starlette/middleware/wsgi.py\n@@ -13,7 +13,7 @@\n     \"\"\"\n     environ = {\n         \"REQUEST_METHOD\": scope[\"method\"],\n-        \"SCRIPT_NAME\": \"\",\n+        \"SCRIPT_NAME\": scope.get(\"root_path\", \"\"),\n         \"PATH_INFO\": scope[\"path\"],\n         \"QUERY_STRING\": scope[\"query_string\"].decode(\"ascii\"),\n         \"SERVER_PROTOCOL\": f\"HTTP/{scope['http_version']}\",\n", "issue": "Path prefix for PATH_INFO with WSGIMiddleware\nWhen mounting an app (e.g. Django) through `WSGIMiddleware` it seems to be required to also adjust `PATH_INFO` accordingly.\r\n\r\nWith the following, Django would e.g. redirect to \"/admin/\" (without the outer \"/dj\" prefix).\r\n\r\n```python\r\ndjapp = django.core.wsgi.get_wsgi_application()\r\napp.mount(\"/dj\", app=WSGIMiddleware(djapp))\r\n```\r\n\r\nI've got this working through an additional \"path_prefix\" argument for WSGIMiddleware, but I think it makes sense to either prepend the mount path in `build_environ` always (using `\"PATH_INFO\": scope[\"root_path\"] + scope[\"path\"]`, and making it visible to the mounted app though), or handle e.g. \"Location\" response headers by prepending it there.\r\nThe latter could likely result in redirect-loops, and does not help with Django POSTing to \"/admin/login\" then.\r\n\r\nI've not investigated too much into this yet, what do you think?\n", "before_files": [{"content": "import asyncio\nimport io\nimport sys\nimport typing\n\nfrom starlette.concurrency import run_in_threadpool\nfrom starlette.types import ASGIInstance, Message, Receive, Scope, Send\n\n\ndef build_environ(scope: Scope, body: bytes) -> dict:\n    \"\"\"\n    Builds a scope and request body 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\": f\"HTTP/{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\") or (\"localhost\", 80)\n    environ[\"SERVER_NAME\"] = server[0]\n    environ[\"SERVER_PORT\"] = server[1]\n\n    # Get client IP address\n    if scope.get(\"client\"):\n        environ[\"REMOTE_ADDR\"] = scope[\"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 = f\"HTTP_{name}\".upper().replace(\"-\", \"_\")\n        # HTTPbis say only ASCII chars are allowed in headers, but we latin1 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: typing.Callable, workers: int = 10) -> None:\n        self.app = app\n\n    def __call__(self, scope: Scope) -> ASGIInstance:\n        assert scope[\"type\"] == \"http\"\n        return WSGIResponder(self.app, scope)\n\n\nclass WSGIResponder:\n    def __init__(self, app: typing.Callable, scope: Scope) -> None:\n        self.app = app\n        self.scope = scope\n        self.status = None\n        self.response_headers = None\n        self.send_event = asyncio.Event()\n        self.send_queue = []  # type: typing.List[typing.Optional[Message]]\n        self.loop = asyncio.get_event_loop()\n        self.response_started = False\n        self.exc_info = None  # type: typing.Any\n\n    async def __call__(self, receive: Receive, send: Send) -> None:\n        body = b\"\"\n        more_body = True\n        while more_body:\n            message = await receive()\n            body += message.get(\"body\", b\"\")\n            more_body = message.get(\"more_body\", False)\n        environ = build_environ(self.scope, body)\n        try:\n            sender = self.loop.create_task(self.sender(send))\n            await run_in_threadpool(self.wsgi, environ, self.start_response)\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(\n                    self.exc_info[1], self.exc_info[2]\n                )\n        finally:\n            if not sender.done():\n                sender.cancel()  # pragma: no cover\n\n    async def sender(self, send: Send) -> None:\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(\n        self,\n        status: str,\n        response_headers: typing.List[typing.Tuple[str, str]],\n        exc_info: typing.Any = None,\n    ) -> None:\n        self.exc_info = exc_info\n        if not self.response_started:\n            self.response_started = True\n            status_code_string, _ = status.split(\" \", 1)\n            status_code = int(status_code_string)\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: dict, start_response: typing.Callable) -> None:\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": "starlette/middleware/wsgi.py"}], "after_files": [{"content": "import asyncio\nimport io\nimport sys\nimport typing\n\nfrom starlette.concurrency import run_in_threadpool\nfrom starlette.types import ASGIInstance, Message, Receive, Scope, Send\n\n\ndef build_environ(scope: Scope, body: bytes) -> dict:\n    \"\"\"\n    Builds a scope and request body into a WSGI environ object.\n    \"\"\"\n    environ = {\n        \"REQUEST_METHOD\": scope[\"method\"],\n        \"SCRIPT_NAME\": scope.get(\"root_path\", \"\"),\n        \"PATH_INFO\": scope[\"path\"],\n        \"QUERY_STRING\": scope[\"query_string\"].decode(\"ascii\"),\n        \"SERVER_PROTOCOL\": f\"HTTP/{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\") or (\"localhost\", 80)\n    environ[\"SERVER_NAME\"] = server[0]\n    environ[\"SERVER_PORT\"] = server[1]\n\n    # Get client IP address\n    if scope.get(\"client\"):\n        environ[\"REMOTE_ADDR\"] = scope[\"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 = f\"HTTP_{name}\".upper().replace(\"-\", \"_\")\n        # HTTPbis say only ASCII chars are allowed in headers, but we latin1 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: typing.Callable, workers: int = 10) -> None:\n        self.app = app\n\n    def __call__(self, scope: Scope) -> ASGIInstance:\n        assert scope[\"type\"] == \"http\"\n        return WSGIResponder(self.app, scope)\n\n\nclass WSGIResponder:\n    def __init__(self, app: typing.Callable, scope: Scope) -> None:\n        self.app = app\n        self.scope = scope\n        self.status = None\n        self.response_headers = None\n        self.send_event = asyncio.Event()\n        self.send_queue = []  # type: typing.List[typing.Optional[Message]]\n        self.loop = asyncio.get_event_loop()\n        self.response_started = False\n        self.exc_info = None  # type: typing.Any\n\n    async def __call__(self, receive: Receive, send: Send) -> None:\n        body = b\"\"\n        more_body = True\n        while more_body:\n            message = await receive()\n            body += message.get(\"body\", b\"\")\n            more_body = message.get(\"more_body\", False)\n        environ = build_environ(self.scope, body)\n        try:\n            sender = self.loop.create_task(self.sender(send))\n            await run_in_threadpool(self.wsgi, environ, self.start_response)\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(\n                    self.exc_info[1], self.exc_info[2]\n                )\n        finally:\n            if not sender.done():\n                sender.cancel()  # pragma: no cover\n\n    async def sender(self, send: Send) -> None:\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(\n        self,\n        status: str,\n        response_headers: typing.List[typing.Tuple[str, str]],\n        exc_info: typing.Any = None,\n    ) -> None:\n        self.exc_info = exc_info\n        if not self.response_started:\n            self.response_started = True\n            status_code_string, _ = status.split(\" \", 1)\n            status_code = int(status_code_string)\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: dict, start_response: typing.Callable) -> None:\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": "starlette/middleware/wsgi.py"}]}