Skip to content

Commit c59955e

Browse files
committed
Validate Content-Length format in ClientRequest
1 parent 925ba85 commit c59955e

File tree

3 files changed

+46
-6
lines changed

3 files changed

+46
-6
lines changed

aiohttp/client_reqrep.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878

7979
_CONNECTION_CLOSED_EXCEPTION = ClientConnectionError("Connection closed")
8080
_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]")
81+
_DIGITS_RE = re.compile(r"\d+", re.ASCII)
8182

8283

8384
def _gen_default_accept_encoding() -> str:
@@ -753,12 +754,11 @@ def _get_content_length(self) -> int | None:
753754
return None
754755

755756
content_length_hdr = self.headers[hdrs.CONTENT_LENGTH]
756-
try:
757-
return int(content_length_hdr)
758-
except ValueError:
757+
if not _DIGITS_RE.fullmatch(content_length_hdr):
759758
raise ValueError(
760-
f"Invalid Content-Length header: {content_length_hdr}"
761-
) from None
759+
f"Invalid Content-Length header: {content_length_hdr!r}"
760+
)
761+
return int(content_length_hdr)
762762

763763
@property
764764
def _writer(self) -> asyncio.Task[None] | None:

tests/test_client_request.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1911,7 +1911,24 @@ async def test_get_content_length(make_client_request: _RequestMaker) -> None:
19111911

19121912
# Invalid Content-Length header
19131913
req.headers["Content-Length"] = "invalid"
1914-
with pytest.raises(ValueError, match="Invalid Content-Length header: invalid"):
1914+
with pytest.raises(ValueError, match="Invalid Content-Length header"):
1915+
req._get_content_length()
1916+
1917+
1918+
async def test_get_content_length_invalid_formats(
1919+
make_client_request: _RequestMaker,
1920+
) -> None:
1921+
req = make_client_request("get", URL("http://python.org/"))
1922+
1923+
req.headers["Content-Length"] = "100"
1924+
assert req._get_content_length() == 100
1925+
1926+
req.headers["Content-Length"] = "-100"
1927+
with pytest.raises(ValueError, match="Invalid Content-Length header"):
1928+
req._get_content_length()
1929+
1930+
req.headers["Content-Length"] = " 100"
1931+
with pytest.raises(ValueError, match="Invalid Content-Length header"):
19151932
req._get_content_length()
19161933

19171934

tests/test_web_functional.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,29 @@ async def handler(request: web.Request) -> web.Response:
196196
resp.release()
197197

198198

199+
async def test_content_length_invalid(
200+
aiohttp_client: AiohttpClient,
201+
) -> None:
202+
async def handler(request: web.Request) -> web.Response:
203+
body = await request.read()
204+
return web.Response(body=body)
205+
206+
app = web.Application()
207+
app.router.add_post("/", handler)
208+
client = await aiohttp_client(app)
209+
210+
resp = await client.post("/", data=b"hello world", headers={CONTENT_LENGTH: "11"})
211+
assert resp.status == 200
212+
assert await resp.read() == b"hello world"
213+
resp.release()
214+
215+
with pytest.raises(ValueError, match="Invalid Content-Length header"):
216+
await client.post("/", data=b"hello world", headers={CONTENT_LENGTH: "-100"})
217+
218+
with pytest.raises(ValueError, match="Invalid Content-Length header"):
219+
await client.post("/", data=b"hello world", headers={CONTENT_LENGTH: " 100"})
220+
221+
199222
@pytest.mark.skipif(sys.version_info < (3, 11), reason="Needs Task.cancelling()")
200223
async def test_cancel_shutdown(aiohttp_client: AiohttpClient) -> None:
201224
async def handler(request: web.Request) -> web.Response:

0 commit comments

Comments
 (0)