Skip to content

feat: Add IPv6 zone ID detection and parsing support in HTTPAdapter#4

Closed
tboy1337 wants to merge 1 commit into
mainfrom
edit-14
Closed

feat: Add IPv6 zone ID detection and parsing support in HTTPAdapter#4
tboy1337 wants to merge 1 commit into
mainfrom
edit-14

Conversation

@tboy1337

@tboy1337 tboy1337 commented Apr 12, 2026

Copy link
Copy Markdown
Owner

Adds support for IPv6 zone identifiers (scope IDs) in URLs, enabling correct handling of link-local IPv6 addresses such as http://[fe80::1%25eth0]:8080/.

Changes:

  • adapters.py: Add _has_ipv6_zone_id() helper with precompiled regex (_IPV6_ZONE_ID_RE) to detect zone IDs in the URL authority section. Update _urllib3_request_context() to use urllib3's parse_url() for zone ID URLs and urlparse() for all others, preserving backward compatibility. The poolmanager parameter is kept as an optional default-None argument to avoid breaking subclass callers.
  • models.py: Add zone ID re-encoding logic in prepare_url() with three precompiled patterns (_AUTHORITY_BRACKET_RE, _RFC6874_ZONE_ID_RE, _RAW_ZONE_ID_RE). Reconstructs the bracketed host from the original URL to prevent parse_url's %25->% decoding from creating ambiguous percent sequences downstream (e.g. %2550 -> %50 on Python 3.14).
  • tests/test_adapters.py: Comprehensive test coverage including zone ID detection (38 parametrized cases), URL parsing (15 cases), and integration tests covering pool key generation, client certificates, full mocked request flow, encoding equivalence, URL preparation preservation, request_url() path extraction, and proxy code path.

Fixes psf#6927, psf#6735, psf#7088.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Enhanced IPv6 address handling to properly parse and manage URLs containing zone identifiers (scope IDs), improving compatibility with IPv6 link-local address configurations.
  • Tests

    • Added comprehensive test coverage for IPv6 zone identifier detection, parsing, and URL processing behavior.

@coderabbitai

coderabbitai Bot commented Apr 12, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 61d3af9b-9e4d-4186-9e34-7a553674a6b2

📥 Commits

Reviewing files that changed from the base of the PR and between 50246e5 and a16aad9.

📒 Files selected for processing (3)
  • src/requests/adapters.py
  • src/requests/models.py
  • tests/test_adapters.py

📝 Walkthrough

Walkthrough

Adds RFC 6874–aware IPv6 zone identifier handling: detects zone-ids in URLs, preserves/normalizes %25 encoding during URL preparation, and routes adapter pool-key parsing through urllib3 parsing for zone-id cases. Includes expanded tests covering detection, pool-key behavior, and adapter send flows.

Changes

Cohort / File(s) Summary
Adapter: zone-id detection & request context
src/requests/adapters.py
Added _IPV6_ZONE_ID_RE and _has_ipv6_zone_id(); made poolmanager optional in _urllib3_request_context(...); use urllib3.util.parse_url() for zone-id URLs (strip surrounding brackets for pool key host) and urlparse() otherwise; build_connection_pool_key_attributes() stops passing self.poolmanager.
PreparedRequest: RFC 6874 mitigation
src/requests/models.py
Added regexes (_AUTHORITY_BRACKET_RE, _RFC6874_ZONE_ID_RE, _RAW_ZONE_ID_RE) and logic in PreparedRequest.prepare_url to detect bracketed IPv6 authorities with zone-ids, reconstruct host to ensure %25-encoding for zone delimiters before IDNA/netloc processing and final quoting.
Tests: comprehensive zone-id coverage
tests/test_adapters.py
Added extensive tests validating _has_ipv6_zone_id across variants and false positives; pool-key extraction for bracketed IPv6 with/without zone-ids; connection pool key variability by zone-id; client-cert propagation; adapter.send() flows with mocked pools; %25 encoding preservation; proxy connection handling; and request_url() behavior.

Sequence Diagram

sequenceDiagram
    actor Client
    participant PreparedRequest
    participant HTTPAdapter
    participant _urllib3_request_context
    participant urllib3_PoolManager as urllib3 PoolManager

    Client->>PreparedRequest: prepare_url(url with IPv6 zone-id)
    Note over PreparedRequest: Detect bracketed authority with zone-id\nReconstruct host to ensure %25 encoding
    PreparedRequest-->>Client: Prepared URL (canonical encoding)

    Client->>HTTPAdapter: send(prepared_request)
    HTTPAdapter->>HTTPAdapter: build_connection_pool_key_attributes()
    HTTPAdapter->>_urllib3_request_context: call with URL (poolmanager optional)

    Note over _urllib3_request_context: Detect IPv6 zone-id via regex

    alt Zone-ID Present
        _urllib3_request_context->>_urllib3_request_context: Use urllib3.util.parse_url()
        _urllib3_request_context->>_urllib3_request_context: Derive host from parsed.host and strip brackets
    else No Zone-ID
        _urllib3_request_context->>_urllib3_request_context: Use urlparse()
        _urllib3_request_context->>_urllib3_request_context: Use hostname directly
    end

    _urllib3_request_context-->>HTTPAdapter: return pool key attrs (host, scheme, port)
    HTTPAdapter->>urllib3_PoolManager: connection_from_host(host, scheme, port)
    urllib3_PoolManager-->>HTTPAdapter: Connection pool
    HTTPAdapter-->>Client: Response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble percent signs with care,
Brackets snug around the air,
Zone-ids tamed to %25 light,
Pools now know each host's slight,
Hop on, connections run just right.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding IPv6 zone ID detection and parsing support in HTTPAdapter, which is the primary focus across all three modified files.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch edit-14

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/requests/adapters.py (1)

117-178: ⚠️ Potential issue | 🟠 Major

Zone-ID URL parsing is incomplete — request preparation fails before reaching the pool key builder.

The new _urllib3_request_context() switches to urllib3.parse_url(), but request preparation itself still uses stdlib parsing at three sites: PreparedRequest.prepare_auth() calls get_auth_from_url(), RequestEncodingMixin.path_url uses urlsplit(), and proxied requests call urldefragauth(). All three use stdlib urlparse(), which validates and rejects IPv6 zone IDs on Python 3.14+. The test at tests/test_adapters.py:349-353 already expects failure (ValueErrororInvalidURL`) on Python 3.14+ rather than proving end-to-end support. Either route all URL reparses through urllib3-safe helpers or restructure to avoid reparsing the prepared URL.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/requests/adapters.py` around lines 117 - 178,
PreparedRequest.prepare_auth(), RequestEncodingMixin.path_url, and places
calling urldefragauth still use stdlib urlparse/urlsplit and reject IPv6 zone
IDs even though _urllib3_request_context now uses urllib3.parse_url; update the
code to avoid reparsing the prepared URL with stdlib parsers by routing reparses
through urllib3-safe helpers or by changing callers to accept the
already-prepared parsed result: replace usages in PreparedRequest.prepare_auth
(and its helper get_auth_from_url), RequestEncodingMixin.path_url, and any
proxied request code that calls urldefragauth/urlsplit so they call an
urllib3-safe parse helper (or accept/propagate the parsed_request_url built in
_urllib3_request_context) to preserve IPv6 zone-id support and prevent
ValueError/InvalidURL on Python 3.14+. Ensure you reference and update
get_auth_from_url, RequestEncodingMixin.path_url, and any urldefragauth callers
to use the new helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/requests/models.py`:
- Around line 90-96: Reformat the two regex definitions so they comply with
ruff-format: run ruff-format (or your project's formatting command) on the file
containing _RFC6874_ZONE_ID_RE and _RAW_ZONE_ID_RE to let the tool rewrite the
re.compile(...) blocks into the CI-expected layout, then re-stage and commit the
updated file; ensure you target the re.compile calls for _RFC6874_ZONE_ID_RE and
_RAW_ZONE_ID_RE so the lint job stops rewriting them.

In `@tests/test_adapters.py`:
- Around line 19-421: The file tests/test_adapters.py fails CI style checks; run
the project formatter (ruff format) on the file and commit the resulting changes
to fix signature/parametrize formatting in the test classes (notably
TestIPv6ZoneIDDetection, TestIPv6ZoneIDParsing, TestIPv6ZoneIDRequests and their
test_* methods like test_has_ipv6_zone_id and test_ipv6_zone_id_url_parsing);
simply run `ruff format tests/test_adapters.py` (or the repo's configured
ruff-format command), review the diff, and add/commit the formatted file.

---

Outside diff comments:
In `@src/requests/adapters.py`:
- Around line 117-178: PreparedRequest.prepare_auth(),
RequestEncodingMixin.path_url, and places calling urldefragauth still use stdlib
urlparse/urlsplit and reject IPv6 zone IDs even though _urllib3_request_context
now uses urllib3.parse_url; update the code to avoid reparsing the prepared URL
with stdlib parsers by routing reparses through urllib3-safe helpers or by
changing callers to accept the already-prepared parsed result: replace usages in
PreparedRequest.prepare_auth (and its helper get_auth_from_url),
RequestEncodingMixin.path_url, and any proxied request code that calls
urldefragauth/urlsplit so they call an urllib3-safe parse helper (or
accept/propagate the parsed_request_url built in _urllib3_request_context) to
preserve IPv6 zone-id support and prevent ValueError/InvalidURL on Python 3.14+.
Ensure you reference and update get_auth_from_url,
RequestEncodingMixin.path_url, and any urldefragauth callers to use the new
helper.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d7f02d69-94ba-45e2-ac12-9ed4557164a0

📥 Commits

Reviewing files that changed from the base of the PR and between 4b0b1a3 and 50246e5.

📒 Files selected for processing (3)
  • src/requests/adapters.py
  • src/requests/models.py
  • tests/test_adapters.py

Comment thread src/requests/models.py Outdated
Comment thread tests/test_adapters.py
Adds support for IPv6 zone identifiers (scope IDs) in URLs, enabling
correct handling of link-local IPv6 addresses such as
http://[fe80::1%25eth0]:8080/.

Changes:
- adapters.py: Add _has_ipv6_zone_id() helper with precompiled regex
  (_IPV6_ZONE_ID_RE) to detect zone IDs in the URL authority section.
  Update _urllib3_request_context() to use urllib3's parse_url() for
  zone ID URLs and urlparse() for all others, preserving backward
  compatibility. The poolmanager parameter is kept as an optional
  default-None argument to avoid breaking subclass callers.
- models.py: Add zone ID re-encoding logic in prepare_url() with three
  precompiled patterns (_AUTHORITY_BRACKET_RE, _RFC6874_ZONE_ID_RE,
  _RAW_ZONE_ID_RE). Reconstructs the bracketed host from the original
  URL to prevent parse_url's %25->% decoding from creating ambiguous
  percent sequences downstream (e.g. %2550 -> %50 on Python 3.14).
- tests/test_adapters.py: Comprehensive test coverage including zone ID
  detection (38 parametrized cases), URL parsing (15 cases), and
  integration tests covering pool key generation, client certificates,
  full mocked request flow, encoding equivalence, URL preparation
  preservation, request_url() path extraction, and proxy code path.

Fixes psf#6927, psf#6735, psf#7088.

Made-with: Cursor
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