Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGES/10587.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed the C parser failing to reject a response with a body when none was expected -- by :user:`Dreamsorcerer`.
27 changes: 14 additions & 13 deletions aiohttp/_http_parser.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -504,11 +504,14 @@ cdef class HttpParser:
upgrade, chunked)

if (
ULLONG_MAX > self._cparser.content_length > 0 or chunked or
self._cparser.method == cparser.HTTP_CONNECT or
(self._cparser.status_code >= 199 and
self._cparser.content_length == 0 and
self._read_until_eof)
self._response_with_body
and (
ULLONG_MAX > self._cparser.content_length > 0 or chunked or
self._cparser.method == cparser.HTTP_CONNECT or
(self._cparser.status_code >= 199 and
self._cparser.content_length == 0 and
self._read_until_eof)
)
):
payload = StreamReader(
self.protocol, timer=self._timer, loop=self._loop,
Expand All @@ -520,9 +523,6 @@ cdef class HttpParser:
if encoding is not None and self._auto_decompress:
self._payload = DeflateBuffer(payload, encoding, max_decompress_size=self._limit)

if not self._response_with_body:
payload = EMPTY_PAYLOAD

self._messages.append((msg, payload))

cdef _on_message_complete(self):
Expand Down Expand Up @@ -849,8 +849,9 @@ cdef int cb_on_headers_complete(cparser.llhttp_t* parser) except -1:
else:
if pyparser._upgraded or pyparser._cparser.method == cparser.HTTP_CONNECT:
return 2
else:
return 0
if not pyparser._response_with_body:
return 1
return 0


cdef int cb_on_body(cparser.llhttp_t* parser,
Expand Down Expand Up @@ -924,7 +925,6 @@ cdef parser_error_from_errno(cparser.llhttp_t* parser, data, pointer):
cparser.HPE_CB_MESSAGE_COMPLETE,
cparser.HPE_CB_CHUNK_HEADER,
cparser.HPE_CB_CHUNK_COMPLETE,
cparser.HPE_INVALID_CONSTANT,
cparser.HPE_INVALID_HEADER_TOKEN,
cparser.HPE_INVALID_CONTENT_LENGTH,
cparser.HPE_INVALID_CHUNK_SIZE,
Expand All @@ -934,8 +934,9 @@ cdef parser_error_from_errno(cparser.llhttp_t* parser, data, pointer):
elif errno == cparser.HPE_INVALID_METHOD:
return BadHttpMethod(error=err_msg)
elif errno in {cparser.HPE_INVALID_STATUS,
cparser.HPE_INVALID_VERSION}:
return BadStatusLine(error=err_msg)
cparser.HPE_INVALID_VERSION,
cparser.HPE_INVALID_CONSTANT}:
return BadStatusLine(error=f"Bad status line:\n {err_msg}")
elif errno == cparser.HPE_INVALID_URL:
return InvalidURLError(err_msg)

Expand Down
14 changes: 14 additions & 0 deletions tests/test_http_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2013,6 +2013,20 @@ def test_parse_payload_response_without_body(
assert payload.is_eof()


async def test_parse_payload_response_with_invalid_body(
protocol: BaseProtocol,
response_cls: type[HttpResponseParser],
) -> None:
loop = asyncio.get_running_loop()
parser = response_cls(protocol, loop, 2**16, response_with_body=False)
text = (
b"HTTP/1.1 200 Ok\r\nTransfer-Encoding: chunked\r\n\r\n"
b"7\r\nchunked\r\n0\r\n\r\n"
)
with pytest.raises(http_exceptions.BadHttpMessage, match="status line"):
parser.feed_data(text)[0][0]


def test_parse_length_payload(response: HttpResponseParser) -> None:
text = b"HTTP/1.1 200 Ok\r\ncontent-length: 4\r\n\r\n"
msg, payload = response.feed_data(text)[0][0]
Expand Down
Loading