Skip to content

Commit a0d234d

Browse files
Use lenient headers for response parser (#7490) (#7492)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> (cherry picked from commit 6396531)
1 parent f92b27b commit a0d234d

File tree

5 files changed

+39
-0
lines changed

5 files changed

+39
-0
lines changed

CHANGES/7490.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Enabled lenient headers for more flexible parsing in the client. -- by :user:`Dreamsorcerer`

aiohttp/_http_parser.pyx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ from multidict import CIMultiDict as _CIMultiDict, CIMultiDictProxy as _CIMultiD
2020
from yarl import URL as _URL
2121

2222
from aiohttp import hdrs
23+
from aiohttp.helpers import DEBUG
2324

2425
from .http_exceptions import (
2526
BadHttpMessage,
@@ -648,6 +649,9 @@ cdef class HttpResponseParser(HttpParser):
648649
max_line_size, max_headers, max_field_size,
649650
payload_exception, response_with_body, read_until_eof,
650651
auto_decompress)
652+
# Use strict parsing on dev mode, so users are warned about broken servers.
653+
if not DEBUG:
654+
cparser.llhttp_set_lenient_headers(self._cparser, 1)
651655

652656
cdef object _on_status_complete(self):
653657
if self._buf:

docs/index.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,18 @@ Server example:
118118
For more information please visit :ref:`aiohttp-client` and
119119
:ref:`aiohttp-web` pages.
120120

121+
Development mode
122+
================
123+
124+
When writing your code, we recommend enabling Python's
125+
`development mode <https://docs.python.org/3/library/devmode.html>`_
126+
(``python -X dev``). In addition to the extra features enabled for asyncio, aiohttp
127+
will:
128+
129+
- Use a strict parser in the client code (which can help detect malformed responses
130+
from a server).
131+
- Enable some additional checks (resulting in warnings in certain situations).
132+
121133
What's new in aiohttp 3?
122134
========================
123135

setup.cfg

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ addopts =
130130
# `pytest-cov`:
131131
--cov=aiohttp
132132
--cov=tests/
133+
134+
# run tests that are not marked with dev_mode
135+
-m "not dev_mode"
133136
filterwarnings =
134137
error
135138
ignore:module 'ssl' has no attribute 'OP_NO_COMPRESSION'. The Python interpreter is compiled against OpenSSL < 1.0.0. Ref. https.//docs.python.org/3/library/ssl.html#ssl.OP_NO_COMPRESSION:UserWarning
@@ -153,3 +156,5 @@ minversion = 3.8.2
153156
testpaths = tests/
154157
junit_family=xunit2
155158
xfail_strict = true
159+
markers =
160+
dev_mode: mark test to run in dev mode.

tests/test_http_parser.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,23 @@ def test_http_response_parser_no_reason(response) -> None:
721721
assert msg.reason == ""
722722

723723

724+
def test_http_response_parser_lenient_headers(response) -> None:
725+
messages, upgrade, tail = response.feed_data(
726+
b"HTTP/1.1 200 test\r\nFoo: abc\x01def\r\n\r\n"
727+
)
728+
msg = messages[0][0]
729+
730+
assert msg.headers["Foo"] == "abc\x01def"
731+
732+
733+
@pytest.mark.dev_mode
734+
def test_http_response_parser_strict_headers(response) -> None:
735+
if isinstance(response, HttpResponseParserPy):
736+
pytest.xfail("Py parser is lenient. May update py-parser later.")
737+
with pytest.raises(http_exceptions.BadHttpMessage):
738+
response.feed_data(b"HTTP/1.1 200 test\r\nFoo: abc\x01def\r\n\r\n")
739+
740+
724741
def test_http_response_parser_bad(response) -> None:
725742
with pytest.raises(http_exceptions.BadHttpMessage):
726743
response.feed_data(b"HTT/1\r\n\r\n")

0 commit comments

Comments
 (0)