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/10549.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Document and narrow the allowed ``compress`` request values to ``True``/``False``, ``"deflate"``, or ``"gzip"``, and reject unsupported string values.
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ Sergey Skripnick
Serhii Charykov
Serhii Kostel
Serhiy Storchaka
Shensheng Shen
Shubh Agarwal
Simon Kennedy
Sin-Woo Bang
Expand Down
4 changes: 2 additions & 2 deletions aiohttp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ class _RequestOptions(TypedDict, total=False):
auth: BasicAuth | None
allow_redirects: bool
max_redirects: int
compress: str | bool
compress: Literal["deflate", "gzip"] | bool
chunked: bool | None
expect100: bool
raise_for_status: None | bool | Callable[[ClientResponse], Awaitable[None]]
Expand Down Expand Up @@ -488,7 +488,7 @@ async def _request(
auth: BasicAuth | None = None,
allow_redirects: bool = True,
max_redirects: int = 10,
compress: str | bool = False,
compress: Literal["deflate", "gzip"] | bool = False,
chunked: bool | None = None,
expect100: bool = False,
raise_for_status: (
Expand Down
14 changes: 10 additions & 4 deletions aiohttp/client_reqrep.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from hashlib import md5, sha1, sha256
from http.cookies import BaseCookie, SimpleCookie
from types import MappingProxyType, TracebackType
from typing import TYPE_CHECKING, Any, NamedTuple, TypedDict
from typing import TYPE_CHECKING, Any, Literal, NamedTuple, TypedDict

from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy
from yarl import URL, Query
Expand Down Expand Up @@ -935,7 +935,7 @@ class ClientRequestArgs(TypedDict, total=False):
cookies: BaseCookie[str]
auth: BasicAuth | None
version: HttpVersion
compress: str | bool
compress: Literal["deflate", "gzip"] | bool
chunked: bool | None
expect100: bool
loop: asyncio.AbstractEventLoop
Expand Down Expand Up @@ -979,7 +979,7 @@ def __init__(
cookies: BaseCookie[str],
auth: BasicAuth | None,
version: HttpVersion,
compress: str | bool,
compress: Literal["deflate", "gzip"] | bool,
chunked: bool | None,
expect100: bool,
loop: asyncio.AbstractEventLoop,
Expand Down Expand Up @@ -1099,7 +1099,9 @@ def _update_cookies(self, cookies: BaseCookie[str]) -> None:

self.headers[hdrs.COOKIE] = c.output(header="", sep=";").strip()

def _update_content_encoding(self, data: Any, compress: bool | str) -> None:
def _update_content_encoding(
self, data: Any, compress: bool | Literal["deflate", "gzip"]
) -> None:
"""Set request content encoding."""
self.compress = None
if not data:
Expand All @@ -1111,6 +1113,10 @@ def _update_content_encoding(self, data: Any, compress: bool | str) -> None:
"compress can not be set if Content-Encoding header is set"
)
elif compress:
if isinstance(compress, str) and compress not in {"deflate", "gzip"}:
raise ValueError(
"compress must be one of True, False, 'deflate', or 'gzip'"
)
self.compress = compress if isinstance(compress, str) else "deflate"
self.headers[hdrs.CONTENT_ENCODING] = self.compress
self.chunked = True # enable chunked, no need to deal with length
Expand Down
9 changes: 8 additions & 1 deletion aiohttp/web_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,11 @@ def __call__(self) -> RequestHandler[_Request]:
for k, v in self._kwargs.items()
if k in ["debug", "access_log_class"]
}
return RequestHandler(self, loop=self._loop, **kwargs)
handler = RequestHandler(self, loop=self._loop, **kwargs)
handler.logger.warning(
"Failed to create request handler with custom kwargs %r, "
"falling back to filtered kwargs. This may indicate a "
"misconfiguration.",
self._kwargs,
)
return handler
11 changes: 7 additions & 4 deletions docs/client_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -978,10 +978,13 @@ certification chaining.
Ignored when ``allow_redirects=False``.
``10`` by default.

:param bool compress: Set to ``True`` if request has to be compressed
with deflate encoding. If `compress` can not be combined
with a *Content-Encoding* and *Content-Length* headers.
``None`` by default (optional).
:param compress: Set to ``True`` to compress the request body with
``deflate`` encoding, or pass ``"deflate"`` or ``"gzip"``
explicitly to choose the content encoding. ``False`` by
default.

This parameter cannot be combined with
*Content-Encoding* or *Content-Length* headers.

:param int chunked: Enables chunked transfer encoding.
It is up to the developer
Expand Down
16 changes: 16 additions & 0 deletions tests/test_client_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,22 @@ async def test_content_encoding_dont_set_headers_if_no_body(
resp.close()


async def test_content_encoding_rejects_unknown_string(
make_client_request: _RequestMaker,
) -> None:
with pytest.raises(
ValueError,
match="compress must be one of True, False, 'deflate', or 'gzip'",
):
make_client_request(
"post",
URL("http://python.org/"),
data="foo",
compress="br", # type: ignore[arg-type]
loop=asyncio.get_running_loop(),
)


@pytest.mark.usefixtures("parametrize_zlib_backend")
async def test_content_encoding_header( # type: ignore[misc]
loop: asyncio.AbstractEventLoop,
Expand Down
4 changes: 2 additions & 2 deletions tests/test_client_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from collections.abc import Awaitable, Callable, Iterator
from http.cookies import BaseCookie, SimpleCookie
from types import SimpleNamespace
from typing import Any, NoReturn, TypedDict, cast
from typing import Any, Literal, NoReturn, TypedDict, cast
from unittest import mock
from uuid import uuid4

Expand Down Expand Up @@ -44,7 +44,7 @@
class _Params(TypedDict):
headers: dict[str, str]
max_redirects: int
compress: str
compress: Literal["deflate", "gzip"]
chunked: bool
expect100: bool
read_until_eof: bool
Expand Down
Loading