|
4 | 4 | import time
|
5 | 5 | from http.cookies import SimpleCookie
|
6 | 6 | from pathlib import Path
|
7 |
| -from typing import Any, AsyncIterator, Iterator |
| 7 | +from typing import Any, AsyncGenerator, AsyncIterator, Iterator |
8 | 8 |
|
9 | 9 | import anyio
|
10 | 10 | import pytest
|
11 | 11 |
|
12 | 12 | from starlette import status
|
13 | 13 | from starlette.background import BackgroundTask
|
14 | 14 | from starlette.datastructures import Headers
|
15 |
| -from starlette.requests import Request |
| 15 | +from starlette.requests import ClientDisconnect, Request |
16 | 16 | from starlette.responses import FileResponse, JSONResponse, RedirectResponse, Response, StreamingResponse
|
17 | 17 | from starlette.testclient import TestClient
|
18 | 18 | from starlette.types import Message, Receive, Scope, Send
|
@@ -542,6 +542,39 @@ async def stream_indefinitely() -> AsyncIterator[bytes]:
|
542 | 542 | assert not cancel_scope.cancel_called, "Content streaming should stop itself."
|
543 | 543 |
|
544 | 544 |
|
| 545 | +@pytest.mark.anyio |
| 546 | +async def test_streaming_response_on_client_disconnects() -> None: |
| 547 | + chunks = bytearray() |
| 548 | + streamed = False |
| 549 | + |
| 550 | + async def receive_disconnect() -> Message: |
| 551 | + raise NotImplementedError |
| 552 | + |
| 553 | + async def send(message: Message) -> None: |
| 554 | + nonlocal streamed |
| 555 | + if message["type"] == "http.response.body": |
| 556 | + if not streamed: |
| 557 | + chunks.extend(message.get("body", b"")) |
| 558 | + streamed = True |
| 559 | + else: |
| 560 | + raise OSError |
| 561 | + |
| 562 | + async def stream_indefinitely() -> AsyncGenerator[bytes, None]: |
| 563 | + while True: |
| 564 | + await anyio.sleep(0) |
| 565 | + yield b"chunk" |
| 566 | + |
| 567 | + stream = stream_indefinitely() |
| 568 | + response = StreamingResponse(content=stream) |
| 569 | + |
| 570 | + with anyio.move_on_after(1) as cancel_scope: |
| 571 | + with pytest.raises(ClientDisconnect): |
| 572 | + await response({"asgi": {"spec_version": "2.4"}}, receive_disconnect, send) |
| 573 | + assert not cancel_scope.cancel_called, "Content streaming should stop itself." |
| 574 | + assert chunks == b"chunk" |
| 575 | + await stream.aclose() |
| 576 | + |
| 577 | + |
545 | 578 | README = """\
|
546 | 579 | # BáiZé
|
547 | 580 |
|
|
0 commit comments