Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "bugfix",
"description": "Fix infinite hang when a bidirectional stream receives a non-2xx HTTP response from the server"
}
4 changes: 4 additions & 0 deletions packages/smithy-http/src/smithy_http/aio/crt.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ async def chunks(self) -> AsyncGenerator[bytes, None]:
chunk = await self._stream.get_next_response_chunk()
if chunk:
yield chunk
# CRT will hang infinitely and not close the stream if it is closed server side; to work around this,
# we need to break this loop ourselves after reading the first chunk.
if self._status >= 400:
break
else:
break

Expand Down
18 changes: 18 additions & 0 deletions packages/smithy-http/tests/unit/aio/test_crt.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,21 @@ def test_response_properties() -> None:
assert response.status == 404
assert response.fields == fields
assert response.reason is None


async def test_error_response_chunks_stops_after_first_chunk() -> None:
"""Test that error responses stop reading after the first chunk."""
mock_stream = AsyncMock()
mock_stream.get_next_response_chunk.side_effect = [
b"error body",
b"should not be read",
b"",
]

response = AWSCRTHTTPResponse(status=500, fields=Fields(), stream=mock_stream)

chunks: list[bytes] = []
async for chunk in response.chunks():
chunks.append(chunk)

assert chunks == [b"error body"]
Loading