We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ASGITransport does not correctly simulate raw_path in the scope
I'm trying to switch Datasette's internal tests over to using `httpx` with `AsyncClient`.

This has almost worked perfectly, but I've run into one problem: it looks like the `ASGITransport` class used by the `AsyncClient(app=asgi_app)` mechanism does not correctly simulate the `raw_path` and `path` keys.

Here's the code in question: https://github.com/encode/httpx/blob/92ca4d0cc654859fc2257c492e55d8752370d427/httpx/_transports/asgi.py#L82-L97

As you can see, it's not populating `raw_path` even though that's part of the ASGI spec.

This matters for Datasette because it supports this URL: https://latest.datasette.io/fixtures/table%2Fwith%2Fslashes.csv - which refers to a SQLite database table called `table/with/slashes.csv` (a weird table name but that's test cases for you). The way it does this is through careful decoding of the `raw_path` ASGI scope variable.

Here are my notes when I first ran into this limitation of ASGITransport: https://github.com/simonw/datasette/pull/1000#issuecomment-705945591
ASGITransport does not correctly simulate raw_path in the scope
I'm trying to switch Datasette's internal tests over to using `httpx` with `AsyncClient`.

This has almost worked perfectly, but I've run into one problem: it looks like the `ASGITransport` class used by the `AsyncClient(app=asgi_app)` mechanism does not correctly simulate the `raw_path` and `path` keys.

Here's the code in question: https://github.com/encode/httpx/blob/92ca4d0cc654859fc2257c492e55d8752370d427/httpx/_transports/asgi.py#L82-L97

As you can see, it's not populating `raw_path` even though that's part of the ASGI spec.

This matters for Datasette because it supports this URL: https://latest.datasette.io/fixtures/table%2Fwith%2Fslashes.csv - which refers to a SQLite database table called `table/with/slashes.csv` (a weird table name but that's test cases for you). The way it does this is through careful decoding of the `raw_path` ASGI scope variable.

Here are my notes when I first ran into this limitation of ASGITransport: https://github.com/simonw/datasette/pull/1000#issuecomment-705945591

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `httpx/_transports/asgi.py`
Content:
```
1 from typing import TYPE_CHECKING, Callable, List, Optional, Tuple, Union
2 from urllib.parse import unquote
3 
4 import httpcore
5 import sniffio
6 
7 if TYPE_CHECKING:  # pragma: no cover
8     import asyncio
9 
10     import trio
11 
12     Event = Union[asyncio.Event, trio.Event]
13 
14 
15 def create_event() -> "Event":
16     if sniffio.current_async_library() == "trio":
17         import trio
18 
19         return trio.Event()
20     else:
21         import asyncio
22 
23         return asyncio.Event()
24 
25 
26 class ASGITransport(httpcore.AsyncHTTPTransport):
27     """
28     A custom AsyncTransport that handles sending requests directly to an ASGI app.
29     The simplest way to use this functionality is to use the `app` argument.
30 
31     ```
32     client = httpx.AsyncClient(app=app)
33     ```
34 
35     Alternatively, you can setup the transport instance explicitly.
36     This allows you to include any additional configuration arguments specific
37     to the ASGITransport class:
38 
39     ```
40     transport = httpx.ASGITransport(
41         app=app,
42         root_path="/submount",
43         client=("1.2.3.4", 123)
44     )
45     client = httpx.AsyncClient(transport=transport)
46     ```
47 
48     Arguments:
49 
50     * `app` - The ASGI application.
51     * `raise_app_exceptions` - Boolean indicating if exceptions in the application
52        should be raised. Default to `True`. Can be set to `False` for use cases
53        such as testing the content of a client 500 response.
54     * `root_path` - The root path on which the ASGI application should be mounted.
55     * `client` - A two-tuple indicating the client IP and port of incoming requests.
56     ```
57     """
58 
59     def __init__(
60         self,
61         app: Callable,
62         raise_app_exceptions: bool = True,
63         root_path: str = "",
64         client: Tuple[str, int] = ("127.0.0.1", 123),
65     ) -> None:
66         self.app = app
67         self.raise_app_exceptions = raise_app_exceptions
68         self.root_path = root_path
69         self.client = client
70 
71     async def arequest(
72         self,
73         method: bytes,
74         url: Tuple[bytes, bytes, Optional[int], bytes],
75         headers: List[Tuple[bytes, bytes]] = None,
76         stream: httpcore.AsyncByteStream = None,
77         ext: dict = None,
78     ) -> Tuple[int, List[Tuple[bytes, bytes]], httpcore.AsyncByteStream, dict]:
79         headers = [] if headers is None else headers
80         stream = httpcore.PlainByteStream(content=b"") if stream is None else stream
81 
82         # ASGI scope.
83         scheme, host, port, full_path = url
84         path, _, query = full_path.partition(b"?")
85         scope = {
86             "type": "http",
87             "asgi": {"version": "3.0"},
88             "http_version": "1.1",
89             "method": method.decode(),
90             "headers": [(k.lower(), v) for (k, v) in headers],
91             "scheme": scheme.decode("ascii"),
92             "path": unquote(path.decode("ascii")),
93             "query_string": query,
94             "server": (host.decode("ascii"), port),
95             "client": self.client,
96             "root_path": self.root_path,
97         }
98 
99         # Request.
100         request_body_chunks = stream.__aiter__()
101         request_complete = False
102 
103         # Response.
104         status_code = None
105         response_headers = None
106         body_parts = []
107         response_started = False
108         response_complete = create_event()
109 
110         # ASGI callables.
111 
112         async def receive() -> dict:
113             nonlocal request_complete
114 
115             if request_complete:
116                 await response_complete.wait()
117                 return {"type": "http.disconnect"}
118 
119             try:
120                 body = await request_body_chunks.__anext__()
121             except StopAsyncIteration:
122                 request_complete = True
123                 return {"type": "http.request", "body": b"", "more_body": False}
124             return {"type": "http.request", "body": body, "more_body": True}
125 
126         async def send(message: dict) -> None:
127             nonlocal status_code, response_headers, response_started
128 
129             if message["type"] == "http.response.start":
130                 assert not response_started
131 
132                 status_code = message["status"]
133                 response_headers = message.get("headers", [])
134                 response_started = True
135 
136             elif message["type"] == "http.response.body":
137                 assert not response_complete.is_set()
138                 body = message.get("body", b"")
139                 more_body = message.get("more_body", False)
140 
141                 if body and method != b"HEAD":
142                     body_parts.append(body)
143 
144                 if not more_body:
145                     response_complete.set()
146 
147         try:
148             await self.app(scope, receive, send)
149         except Exception:
150             if self.raise_app_exceptions or not response_complete.is_set():
151                 raise
152 
153         assert response_complete.is_set()
154         assert status_code is not None
155         assert response_headers is not None
156 
157         stream = httpcore.PlainByteStream(content=b"".join(body_parts))
158         ext = {}
159 
160         return (status_code, response_headers, stream, ext)
161
```
--- 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/httpx/_transports/asgi.py b/httpx/_transports/asgi.py
--- a/httpx/_transports/asgi.py
+++ b/httpx/_transports/asgi.py
@@ -90,6 +90,7 @@
             "headers": [(k.lower(), v) for (k, v) in headers],
             "scheme": scheme.decode("ascii"),
             "path": unquote(path.decode("ascii")),
+            "raw_path": path,
             "query_string": query,
             "server": (host.decode("ascii"), port),
             "client": self.client,

{"golden_diff": "diff --git a/httpx/_transports/asgi.py b/httpx/_transports/asgi.py\n--- a/httpx/_transports/asgi.py\n+++ b/httpx/_transports/asgi.py\n@@ -90,6 +90,7 @@\n             \"headers\": [(k.lower(), v) for (k, v) in headers],\n             \"scheme\": scheme.decode(\"ascii\"),\n             \"path\": unquote(path.decode(\"ascii\")),\n+            \"raw_path\": path,\n             \"query_string\": query,\n             \"server\": (host.decode(\"ascii\"), port),\n             \"client\": self.client,\n", "issue": "ASGITransport does not correctly simulate raw_path in the scope\nI'm trying to switch Datasette's internal tests over to using `httpx` with `AsyncClient`.\r\n\r\nThis has almost worked perfectly, but I've run into one problem: it looks like the `ASGITransport` class used by the `AsyncClient(app=asgi_app)` mechanism does not correctly simulate the `raw_path` and `path` keys.\r\n\r\nHere's the code in question: https://github.com/encode/httpx/blob/92ca4d0cc654859fc2257c492e55d8752370d427/httpx/_transports/asgi.py#L82-L97\r\n\r\nAs you can see, it's not populating `raw_path` even though that's part of the ASGI spec.\r\n\r\nThis matters for Datasette because it supports this URL: https://latest.datasette.io/fixtures/table%2Fwith%2Fslashes.csv - which refers to a SQLite database table called `table/with/slashes.csv` (a weird table name but that's test cases for you). The way it does this is through careful decoding of the `raw_path` ASGI scope variable.\r\n\r\nHere are my notes when I first ran into this limitation of ASGITransport: https://github.com/simonw/datasette/pull/1000#issuecomment-705945591\nASGITransport does not correctly simulate raw_path in the scope\nI'm trying to switch Datasette's internal tests over to using `httpx` with `AsyncClient`.\r\n\r\nThis has almost worked perfectly, but I've run into one problem: it looks like the `ASGITransport` class used by the `AsyncClient(app=asgi_app)` mechanism does not correctly simulate the `raw_path` and `path` keys.\r\n\r\nHere's the code in question: https://github.com/encode/httpx/blob/92ca4d0cc654859fc2257c492e55d8752370d427/httpx/_transports/asgi.py#L82-L97\r\n\r\nAs you can see, it's not populating `raw_path` even though that's part of the ASGI spec.\r\n\r\nThis matters for Datasette because it supports this URL: https://latest.datasette.io/fixtures/table%2Fwith%2Fslashes.csv - which refers to a SQLite database table called `table/with/slashes.csv` (a weird table name but that's test cases for you). The way it does this is through careful decoding of the `raw_path` ASGI scope variable.\r\n\r\nHere are my notes when I first ran into this limitation of ASGITransport: https://github.com/simonw/datasette/pull/1000#issuecomment-705945591\n", "before_files": [{"content": "from typing import TYPE_CHECKING, Callable, List, Optional, Tuple, Union\nfrom urllib.parse import unquote\n\nimport httpcore\nimport sniffio\n\nif TYPE_CHECKING:  # pragma: no cover\n    import asyncio\n\n    import trio\n\n    Event = Union[asyncio.Event, trio.Event]\n\n\ndef create_event() -> \"Event\":\n    if sniffio.current_async_library() == \"trio\":\n        import trio\n\n        return trio.Event()\n    else:\n        import asyncio\n\n        return asyncio.Event()\n\n\nclass ASGITransport(httpcore.AsyncHTTPTransport):\n    \"\"\"\n    A custom AsyncTransport that handles sending requests directly to an ASGI app.\n    The simplest way to use this functionality is to use the `app` argument.\n\n    ```\n    client = httpx.AsyncClient(app=app)\n    ```\n\n    Alternatively, you can setup the transport instance explicitly.\n    This allows you to include any additional configuration arguments specific\n    to the ASGITransport class:\n\n    ```\n    transport = httpx.ASGITransport(\n        app=app,\n        root_path=\"/submount\",\n        client=(\"1.2.3.4\", 123)\n    )\n    client = httpx.AsyncClient(transport=transport)\n    ```\n\n    Arguments:\n\n    * `app` - The ASGI application.\n    * `raise_app_exceptions` - Boolean indicating if exceptions in the application\n       should be raised. Default to `True`. Can be set to `False` for use cases\n       such as testing the content of a client 500 response.\n    * `root_path` - The root path on which the ASGI application should be mounted.\n    * `client` - A two-tuple indicating the client IP and port of incoming requests.\n    ```\n    \"\"\"\n\n    def __init__(\n        self,\n        app: Callable,\n        raise_app_exceptions: bool = True,\n        root_path: str = \"\",\n        client: Tuple[str, int] = (\"127.0.0.1\", 123),\n    ) -> None:\n        self.app = app\n        self.raise_app_exceptions = raise_app_exceptions\n        self.root_path = root_path\n        self.client = client\n\n    async def arequest(\n        self,\n        method: bytes,\n        url: Tuple[bytes, bytes, Optional[int], bytes],\n        headers: List[Tuple[bytes, bytes]] = None,\n        stream: httpcore.AsyncByteStream = None,\n        ext: dict = None,\n    ) -> Tuple[int, List[Tuple[bytes, bytes]], httpcore.AsyncByteStream, dict]:\n        headers = [] if headers is None else headers\n        stream = httpcore.PlainByteStream(content=b\"\") if stream is None else stream\n\n        # ASGI scope.\n        scheme, host, port, full_path = url\n        path, _, query = full_path.partition(b\"?\")\n        scope = {\n            \"type\": \"http\",\n            \"asgi\": {\"version\": \"3.0\"},\n            \"http_version\": \"1.1\",\n            \"method\": method.decode(),\n            \"headers\": [(k.lower(), v) for (k, v) in headers],\n            \"scheme\": scheme.decode(\"ascii\"),\n            \"path\": unquote(path.decode(\"ascii\")),\n            \"query_string\": query,\n            \"server\": (host.decode(\"ascii\"), port),\n            \"client\": self.client,\n            \"root_path\": self.root_path,\n        }\n\n        # Request.\n        request_body_chunks = stream.__aiter__()\n        request_complete = False\n\n        # Response.\n        status_code = None\n        response_headers = None\n        body_parts = []\n        response_started = False\n        response_complete = create_event()\n\n        # ASGI callables.\n\n        async def receive() -> dict:\n            nonlocal request_complete\n\n            if request_complete:\n                await response_complete.wait()\n                return {\"type\": \"http.disconnect\"}\n\n            try:\n                body = await request_body_chunks.__anext__()\n            except StopAsyncIteration:\n                request_complete = True\n                return {\"type\": \"http.request\", \"body\": b\"\", \"more_body\": False}\n            return {\"type\": \"http.request\", \"body\": body, \"more_body\": True}\n\n        async def send(message: dict) -> None:\n            nonlocal status_code, response_headers, response_started\n\n            if message[\"type\"] == \"http.response.start\":\n                assert not response_started\n\n                status_code = message[\"status\"]\n                response_headers = message.get(\"headers\", [])\n                response_started = True\n\n            elif message[\"type\"] == \"http.response.body\":\n                assert not response_complete.is_set()\n                body = message.get(\"body\", b\"\")\n                more_body = message.get(\"more_body\", False)\n\n                if body and method != b\"HEAD\":\n                    body_parts.append(body)\n\n                if not more_body:\n                    response_complete.set()\n\n        try:\n            await self.app(scope, receive, send)\n        except Exception:\n            if self.raise_app_exceptions or not response_complete.is_set():\n                raise\n\n        assert response_complete.is_set()\n        assert status_code is not None\n        assert response_headers is not None\n\n        stream = httpcore.PlainByteStream(content=b\"\".join(body_parts))\n        ext = {}\n\n        return (status_code, response_headers, stream, ext)\n", "path": "httpx/_transports/asgi.py"}], "after_files": [{"content": "from typing import TYPE_CHECKING, Callable, List, Optional, Tuple, Union\nfrom urllib.parse import unquote\n\nimport httpcore\nimport sniffio\n\nif TYPE_CHECKING:  # pragma: no cover\n    import asyncio\n\n    import trio\n\n    Event = Union[asyncio.Event, trio.Event]\n\n\ndef create_event() -> \"Event\":\n    if sniffio.current_async_library() == \"trio\":\n        import trio\n\n        return trio.Event()\n    else:\n        import asyncio\n\n        return asyncio.Event()\n\n\nclass ASGITransport(httpcore.AsyncHTTPTransport):\n    \"\"\"\n    A custom AsyncTransport that handles sending requests directly to an ASGI app.\n    The simplest way to use this functionality is to use the `app` argument.\n\n    ```\n    client = httpx.AsyncClient(app=app)\n    ```\n\n    Alternatively, you can setup the transport instance explicitly.\n    This allows you to include any additional configuration arguments specific\n    to the ASGITransport class:\n\n    ```\n    transport = httpx.ASGITransport(\n        app=app,\n        root_path=\"/submount\",\n        client=(\"1.2.3.4\", 123)\n    )\n    client = httpx.AsyncClient(transport=transport)\n    ```\n\n    Arguments:\n\n    * `app` - The ASGI application.\n    * `raise_app_exceptions` - Boolean indicating if exceptions in the application\n       should be raised. Default to `True`. Can be set to `False` for use cases\n       such as testing the content of a client 500 response.\n    * `root_path` - The root path on which the ASGI application should be mounted.\n    * `client` - A two-tuple indicating the client IP and port of incoming requests.\n    ```\n    \"\"\"\n\n    def __init__(\n        self,\n        app: Callable,\n        raise_app_exceptions: bool = True,\n        root_path: str = \"\",\n        client: Tuple[str, int] = (\"127.0.0.1\", 123),\n    ) -> None:\n        self.app = app\n        self.raise_app_exceptions = raise_app_exceptions\n        self.root_path = root_path\n        self.client = client\n\n    async def arequest(\n        self,\n        method: bytes,\n        url: Tuple[bytes, bytes, Optional[int], bytes],\n        headers: List[Tuple[bytes, bytes]] = None,\n        stream: httpcore.AsyncByteStream = None,\n        ext: dict = None,\n    ) -> Tuple[int, List[Tuple[bytes, bytes]], httpcore.AsyncByteStream, dict]:\n        headers = [] if headers is None else headers\n        stream = httpcore.PlainByteStream(content=b\"\") if stream is None else stream\n\n        # ASGI scope.\n        scheme, host, port, full_path = url\n        path, _, query = full_path.partition(b\"?\")\n        scope = {\n            \"type\": \"http\",\n            \"asgi\": {\"version\": \"3.0\"},\n            \"http_version\": \"1.1\",\n            \"method\": method.decode(),\n            \"headers\": [(k.lower(), v) for (k, v) in headers],\n            \"scheme\": scheme.decode(\"ascii\"),\n            \"path\": unquote(path.decode(\"ascii\")),\n            \"raw_path\": path,\n            \"query_string\": query,\n            \"server\": (host.decode(\"ascii\"), port),\n            \"client\": self.client,\n            \"root_path\": self.root_path,\n        }\n\n        # Request.\n        request_body_chunks = stream.__aiter__()\n        request_complete = False\n\n        # Response.\n        status_code = None\n        response_headers = None\n        body_parts = []\n        response_started = False\n        response_complete = create_event()\n\n        # ASGI callables.\n\n        async def receive() -> dict:\n            nonlocal request_complete\n\n            if request_complete:\n                await response_complete.wait()\n                return {\"type\": \"http.disconnect\"}\n\n            try:\n                body = await request_body_chunks.__anext__()\n            except StopAsyncIteration:\n                request_complete = True\n                return {\"type\": \"http.request\", \"body\": b\"\", \"more_body\": False}\n            return {\"type\": \"http.request\", \"body\": body, \"more_body\": True}\n\n        async def send(message: dict) -> None:\n            nonlocal status_code, response_headers, response_started\n\n            if message[\"type\"] == \"http.response.start\":\n                assert not response_started\n\n                status_code = message[\"status\"]\n                response_headers = message.get(\"headers\", [])\n                response_started = True\n\n            elif message[\"type\"] == \"http.response.body\":\n                assert not response_complete.is_set()\n                body = message.get(\"body\", b\"\")\n                more_body = message.get(\"more_body\", False)\n\n                if body and method != b\"HEAD\":\n                    body_parts.append(body)\n\n                if not more_body:\n                    response_complete.set()\n\n        try:\n            await self.app(scope, receive, send)\n        except Exception:\n            if self.raise_app_exceptions or not response_complete.is_set():\n                raise\n\n        assert response_complete.is_set()\n        assert status_code is not None\n        assert response_headers is not None\n\n        stream = httpcore.PlainByteStream(content=b\"\".join(body_parts))\n        ext = {}\n\n        return (status_code, response_headers, stream, ext)\n", "path": "httpx/_transports/asgi.py"}]}