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
6 changes: 3 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ jobs:
uses: actions/checkout@v7

- name: Initialize CodeQL
uses: github/codeql-action/init@v4.37.3
uses: github/codeql-action/init@v4.37.4
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql.yml
queries: +security-and-quality

- name: Autobuild
uses: github/codeql-action/autobuild@v4.37.3
uses: github/codeql-action/autobuild@v4.37.4
if: ${{ matrix.language == 'python' || matrix.language == 'javascript' }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4.37.3
uses: github/codeql-action/analyze@v4.37.4
with:
category: "/language:${{ matrix.language }}"
1 change: 1 addition & 0 deletions CHANGES/13278.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Restricted cookie ``Expires`` date parsing to ASCII digits -- by :user:`dxbjavid`.
5 changes: 4 additions & 1 deletion aiohttp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,10 @@ class _WSConnectOptions(TypedDict, total=False):


# https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2
IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"})
# https://www.rfc-editor.org/info/rfc10008/#section-1-12
IDEMPOTENT_METHODS = frozenset(
{"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE", "QUERY"}
)

_RetType_co = TypeVar(
"_RetType_co",
Expand Down
12 changes: 7 additions & 5 deletions aiohttp/cookiejar.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,23 @@
class CookieJar(AbstractCookieJar):
"""Implements cookie storage adhering to RFC 6265."""

# https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.1
DATE_TOKENS_RE = re.compile(
r"[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]*"
r"(?P<token>[\x00-\x08\x0A-\x1F\d:a-zA-Z\x7F-\xFF]+)"
r"(?P<token>[\x00-\x08\x0A-\x1F\d:a-zA-Z\x7F-\xFF]+)",
re.ASCII,
)

DATE_HMS_TIME_RE = re.compile(r"(\d{1,2}):(\d{1,2}):(\d{1,2})")
DATE_HMS_TIME_RE = re.compile(r"(\d{1,2}):(\d{1,2}):(\d{1,2})", re.ASCII)

DATE_DAY_OF_MONTH_RE = re.compile(r"(\d{1,2})")
DATE_DAY_OF_MONTH_RE = re.compile(r"(\d{1,2})", re.ASCII)

DATE_MONTH_RE = re.compile(
"(jan)|(feb)|(mar)|(apr)|(may)|(jun)|(jul)|(aug)|(sep)|(oct)|(nov)|(dec)",
re.I,
re.I | re.ASCII,
)

DATE_YEAR_RE = re.compile(r"(\d{2,4})")
DATE_YEAR_RE = re.compile(r"(\d{2,4})", re.ASCII)

# calendar.timegm() fails for timestamps after datetime.datetime.max
# Minus one as a loss of precision occurs when timestamp() is called.
Expand Down
1 change: 1 addition & 0 deletions aiohttp/hdrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
METH_PATCH: Final[str] = "PATCH"
METH_POST: Final[str] = "POST"
METH_PUT: Final[str] = "PUT"
METH_QUERY: Final[str] = "QUERY"
METH_TRACE: Final[str] = "TRACE"

METH_ALL: Final[set[str]] = {
Expand Down
6 changes: 3 additions & 3 deletions requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ exceptiongroup==1.3.1
# pytest
execnet==2.1.2
# via pytest-xdist
filelock==3.32.0
filelock==3.32.2
# via
# python-discovery
# virtualenv
Expand Down Expand Up @@ -161,7 +161,7 @@ packaging==26.2
# wheel
pathspec==1.1.1
# via mypy
pip==26.1.2
pip==26.2
# via pip-tools
pip-tools==7.6.0
# via -r requirements/dev.in
Expand Down Expand Up @@ -333,7 +333,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
virtualenv==21.7.0
virtualenv==21.7.1
# via pre-commit
wheel==0.47.0
# via pip-tools
Expand Down
6 changes: 3 additions & 3 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ exceptiongroup==1.3.1
# pytest
execnet==2.1.2
# via pytest-xdist
filelock==3.32.0
filelock==3.32.2
# via
# python-discovery
# virtualenv
Expand Down Expand Up @@ -158,7 +158,7 @@ packaging==26.2
# wheel
pathspec==1.1.1
# via mypy
pip==26.1.2
pip==26.2
# via pip-tools
pip-tools==7.6.0
# via -r requirements/dev.in
Expand Down Expand Up @@ -323,7 +323,7 @@ uvloop==0.22.1 ; platform_system != "Windows" and implementation_name == "cpytho
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
virtualenv==21.7.0
virtualenv==21.7.1
# via pre-commit
wheel==0.47.0
# via pip-tools
Expand Down
4 changes: 2 additions & 2 deletions requirements/lint.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ exceptiongroup==1.3.1
# via
# aiofastnet
# pytest
filelock==3.32.0
filelock==3.32.2
# via
# python-discovery
# virtualenv
Expand Down Expand Up @@ -166,7 +166,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# via -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
virtualenv==21.7.0
virtualenv==21.7.1
# via pre-commit
yarl==1.24.5
# via aiohttp
Expand Down
6 changes: 6 additions & 0 deletions tests/test_cookiejar.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ def test_date_parsing() -> None:
# Invalid time
assert parse_func("Tue, 1 Jan 1970 77:88:99 GMT") is None

# Invalid digits
# https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.1
assert parse_func("Tue, ١ Jan ١٩٧٠ ٠٠:٠٠:٠٠ GMT") is None
assert parse_func("Tue, 1 Jan 1970 00:00:00 GMT") is None
assert parse_func("Tue, 1 Jan 1970 ٠٠:٠٠:٠٠ GMT") is None


def test_domain_matching() -> None:
test_func = CookieJar._is_domain_match
Expand Down
25 changes: 24 additions & 1 deletion tests/test_test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from yarl import URL

import aiohttp
from aiohttp import web
from aiohttp import hdrs, web
from aiohttp.helpers import HeadersDictProxy
from aiohttp.test_utils import (
REUSE_ADDRESS,
Expand Down Expand Up @@ -356,6 +356,29 @@ async def handler(request: web.Request) -> web.Response:
assert num_requests == 2


async def test_retry_persistent_connection_query_method(
aiohttp_client: AiohttpClient,
) -> None:
"""QUERY is safe and idempotent, so it must trigger retry."""
num_requests = 0

async def handler(request: web.Request) -> web.Response:
nonlocal num_requests
num_requests += 1
if num_requests == 1:
request.protocol.force_close()
return web.Response()

app = web.Application()
app.router.add_route(hdrs.METH_QUERY, "/", handler)
client = await aiohttp_client(app)
client.session._retry_connection = True
async with client.request(hdrs.METH_QUERY, "/") as resp:
assert resp.status == 200

assert num_requests == 2


async def test_server_context_manager(app: web.Application) -> None:
async with TestServer(app) as server:
async with aiohttp.ClientSession() as client:
Expand Down
Loading