Skip to content

[staging] Host header field contains scope of IPv6 link-local address - #1

Draft
loonister1 wants to merge 4 commits into
masterfrom
fix/13401-host-header-field-contains-scope-of-ipv6
Draft

[staging] Host header field contains scope of IPv6 link-local address#1
loonister1 wants to merge 4 commits into
masterfrom
fix/13401-host-header-field-contains-scope-of-ipv6

Conversation

@loonister1

Copy link
Copy Markdown
Owner

Staging review — this PR targets your own fork. Upstream sees nothing until you approve.

Upstream aio-libs/aiohttp
Issue #13401 — Host header field contains scope of IPv6 link-local address
Tests unknown scope
Diff 4 files, 24 lines
AI policy unstated

Commits

  1. d1af479e Strip IPv6 zone identifier from the Host header — 1 file(s), 5 lines
  2. cfdf3afb Add tests for IPv6 zone identifiers in the Host header — 1 file(s), 15 lines
  3. 5ce910bf Add changelog entry for the IPv6 zone identifier fix — 1 file(s), 3 lines
  4. 44d40027 Add Shahzaib Niaz to CONTRIBUTORS.txt — 1 file(s), 1 lines

What this change does

ClientRequestBase._update_headers now removes an IPv6 zone identifier from the value derived from URL.host_port_subcomponent before it becomes the Host header, per RFC 6874 §4, while leaving raw_host — and therefore connection keying and DNS/socket resolution — untouched so link-local addresses remain reachable. The strip is gated on the host being a bracketed IP-literal, so percent-encoded octets in ordinary reg-names are unaffected, and an explicitly supplied Host header still passes through verbatim as it does for the existing trailing-dot handling.

Reviewer notes

verdict=merge
reason=The patch strips the ZoneID at exactly the point where the Host header value is derived (host_port_subcomponent), leaves raw_host/connection keying alone so link-local targets stay reachable, and is covered by a behavioural test in the file's existing parametrize style alongside test_host_header_ipv6_with_port; changelog fragment and CONTRIBUTORS entry follow project convention (alphabetical placement is correct).
risk=1) Scope of RFC 6874 §4: the strip covers only the Host header. A request through a plain HTTP proxy uses absolute-form in the request line, and a CONNECT request builds its target from url.host_subcomponent — both still carry %zone. That is arguably the same MUST, and it is the maintainers' call whether to widen this PR, factor the strip into a helper both paths use, or push it upstream into yarl (which is where a host_port_subcomponent that already claims RFC-3986 conformance could reasonably do it). I would not block on it, but ask the contributor to say explicitly that they know it is deferred. 2) The slicing host[:index('%')] + host[index(']'):] is robust to both %eth0 and %25eth0 because ] always follows % in a bracketed literal — but whether yarl hands back the decoded or percent-encoded zone varies by how yarl normalises the netloc, so confirm the test passes against aiohttp's minimum pinned yarl, not just the 1.24.5 the reporter used. If yarl ever returns a host without a closing bracket, .index(']') raises ValueError rather than degrading; a partition-based form would avoid that and drop the double scan. 3) Hot path: _update_headers runs on every request; two extra string ops is small, but bdraco has previously reworked checks here for cost, so expect a nit toward host[0] == '[' or folding into one pass. 4) Untested claims from the summary: an explicitly supplied Host containing a zone id passing through verbatim, and a percent-containing reg-name being untouched — both look right from the code, neither has a test. 5) Cosmetic: the changelog credits :user:loonister1`` while CONTRIBUTORS.txt adds "Shahzaib Niaz"; normal (handle vs. real name) but worth a glance that both are the same person.
concerns=- Tests were not executed. Python execution is blocked in this session, so nothing was run — not the new test, not the surrounding test_client_request.py, not black/mypy. Please run `PYTHONPATH=. pytest tests/test_client_request.py` and `pre-commit run --all-files` before this goes anywhere. The formatting was hand-matched to black's slice style already used at `aiohttp/http_parser.py:1018`.

  • Deliberate scope limit. Two other outbound spellings of the URL still carry the zone and arguably also violate RFC 6874 §4: the CONNECT authority-form target (client_reqrep.py:945-947) and the absolute-form target sent to a non-SSL proxy (client_reqrep.py:949, str(self.url)). Both are proxy-only paths and neither is what the issue reports, so I left them; if maintainers want them covered it is a natural follow-up rather than a widening of this patch.
  • Changelog attribution uses :user:loonister1``, inferred from the git author's noreply address. Worth confirming that is the intended handle.
    attempts=1

Test output

[targeted; 4 commit(s) checked individually] $ /Users/m2/Documents/PW/github-bot/work/aio-libs__aiohttp/13401/.contrib-venv/bin/python -m pytest -q --no-header tests/test_client_request.py
============================= test session starts ==============================
collected 203 items

tests/test_client_request.py ........................................... [ 21%]
........................................................s....s.......... [ 56%]
s....................................................................... [ 92%]
................                                                         [100%]

============================= slowest 10 durations =============================
0.22s call     tests/test_client_request.py::test_host_header_host_unicode
0.15s setup    tests/test_client_request.py::test_terminate_with_closed_loop
0.06s call     tests/test_client_request.py::test_custom_req_rep
0.05s call     tests/test_client_request.py::test_terminate_with_closed_loop
0.05s call     tests/test_client_request.py::test_content_encoding[zlib]
0.05s call     tests/test_client_request.py::test_chunked_explicit
0.05s call     tests/test_client_request.py::test_content_encoding_header[zlib_ng.zlib_ng]
0.05s call     tests/test_client_request.py::test_content_encoding[zlib_ng.zlib_ng]
0.05s call     tests/test_client_request.py::test_content_encoding_header[zlib]
0.04s setup    tests/test_client_request.py::test_content_encoding[zlib_ng.zlib_ng]
=========================== short test summary info ============================
SKIPPED [1] tests/test_client_request.py:958: could not import 'isal.isal_zlib': No module named 'isal'
SKIPPED [1] tests/test_client_request.py:1007: could not import 'isal.isal_zlib': No module named 'isal'
SKIPPED [1] tests/test_client_request.py:1141: could not import 'isal.isal_zlib': No module named 'isal'
======================== 200 passed, 3 skipped in 4.70s ========================

Lint

(no linter)

To act on this, add a label:
contrib:submit to open it upstream · contrib:reject to discard · contrib:hold to leave it queued.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant