With reset_contextvars=True, each ASGI run starts with a fresh context. Non-regression test for https://github.com/encode/uvicorn/issues/2167.
(
http_protocol_cls: type[H11Protocol | HttpToolsProtocol], unused_tcp_port: int
)
| 212 | |
| 213 | |
| 214 | async def test_reset_contextvars_asyncio( |
| 215 | http_protocol_cls: type[H11Protocol | HttpToolsProtocol], unused_tcp_port: int |
| 216 | ): |
| 217 | """With reset_contextvars=True, each ASGI run starts with a fresh context. |
| 218 | |
| 219 | Non-regression test for https://github.com/encode/uvicorn/issues/2167. |
| 220 | """ |
| 221 | default_contextvars = {c.name for c in contextvars.copy_context().keys()} |
| 222 | ctx: contextvars.ContextVar[str] = contextvars.ContextVar("ctx") |
| 223 | |
| 224 | async def app(scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable): |
| 225 | assert scope["type"] == "http" |
| 226 | |
| 227 | # initial context should be empty |
| 228 | initial_context = { |
| 229 | n: v for c, v in contextvars.copy_context().items() if (n := c.name) not in default_contextvars |
| 230 | } |
| 231 | # set any contextvar before the body is read |
| 232 | ctx.set(scope["path"]) |
| 233 | |
| 234 | while True: |
| 235 | message = await receive() |
| 236 | assert message["type"] == "http.request" |
| 237 | if not message["more_body"]: |
| 238 | break |
| 239 | |
| 240 | body = json.dumps(initial_context).encode("utf-8") |
| 241 | headers = [(b"content-type", b"application/json"), (b"content-length", str(len(body)).encode("utf-8"))] |
| 242 | await send({"type": "http.response.start", "status": 200, "headers": headers}) |
| 243 | await send({"type": "http.response.body", "body": body}) |
| 244 | |
| 245 | # body larger than HIGH_WATER_LIMIT forces a reading pause on the main thread |
| 246 | # and a resumption inside the ASGI task, which is where the original pollution showed up. |
| 247 | large_body = b"a" * (HIGH_WATER_LIMIT + 1) |
| 248 | large_request = b"\r\n".join( |
| 249 | [ |
| 250 | b"POST /large-body HTTP/1.1", |
| 251 | b"Host: example.org", |
| 252 | b"Content-Type: application/octet-stream", |
| 253 | f"Content-Length: {len(large_body)}".encode(), |
| 254 | b"", |
| 255 | large_body, |
| 256 | ] |
| 257 | ) |
| 258 | |
| 259 | async with _raw_server( |
| 260 | app=app, http_protocol_cls=http_protocol_cls, port=unused_tcp_port, reset_contextvars=True |
| 261 | ) as extract_json_body: |
| 262 | assert await extract_json_body(large_request) == {} |
| 263 | assert await extract_json_body(SIMPLE_GET_REQUEST) == {} |
nothing calls this directly
no test coverage detected