Skip to content

Enhance proxy handling for IPv6 scope in SocketsHttpHandler - #135581

Closed
makazeu wants to merge 1 commit into
dotnet:mainfrom
makazeu:fix-http-proxy-ipv6-scope
Closed

makazeu wants to merge 1 commit into
dotnet:mainfrom
makazeu:fix-http-proxy-ipv6-scope

Conversation

@makazeu

@makazeu makazeu commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

Closes #135103.

Summary

Fix IPv6 proxy connection-pool partitioning in SocketsHttpHandler by including the proxy's scope-preserving IdnHost in HttpConnectionKey equality and hashing.

This is a handler-local fix. It does not change Uri.Equals, Uri.GetHashCode, or any public API.

Problem

These proxy URIs identify different scoped endpoints:

http://[fe80::1%1]:8080/
http://[fe80::1%2]:8080/

However, URI equality and hashing ignore the IPv6 scope ID. Previously, HttpConnectionKey relied on those operations to compare ProxyUri, allowing both endpoints to select the same connection pool.

A pool retains the proxy URI with which it was created. Consequently, a later request selecting %2 can still attempt to connect through %1. This can happen even if the first connection attempt failed; it is not limited to reusing an established connection.

Implementation

For IPv6 proxy URIs:

  • Require matching IdnHost values in addition to the existing URI comparison.
  • Combine IdnHost with the existing connection-key hash.

IdnHost preserves the scope ID while canonicalizing the IPv6 address. This distinguishes scoped endpoints without treating equivalent compressed and expanded address spellings as different proxies.

The existing comparisons for connection kind, destination host, port, SSL host name, and identity remain unchanged. Existing URI equivalence rules are retained, with an additional scope-sensitive constraint for IPv6 proxies. The shared key applies this fix to HTTP proxy pools, CONNECT tunnels, and SOCKS tunnels.

Changing global URI equality or other URI-keyed caches is intentionally outside the scope of this PR.

Local verification of IdnHost

A small experiment using the existing PowerShell runtime, .NET 10.0.12, confirmed that Host excludes the scope ID while IdnHost preserves it:

$uri = [Uri]::new('http://[fe80::1%1]:8080/')
$uri.Host       # [fe80::1]
$uri.IdnHost    # fe80::1%1
Input host Host IdnHost
[fe80::1%1] [fe80::1] fe80::1%1
[fe80::1%2] [fe80::1] fe80::1%2
[fe80::1%eth0] [fe80::1] fe80::1%eth0
[fe80::1] [fe80::1] fe80::1
[FE80:0000:0000:0000:0000:0000:0000:0001%1] [fe80::1] fe80::1%1

All five cases matched their expected values. This experiment verifies existing URI behavior on the installed runtime; it is not a run against repository-built binaries and does not require network access.

Regression coverage

HttpConnectionKeyTest.Equals_ProxyUris_DistinguishesIPv6Scope covers 13 URI pairs, including different numeric and named scopes, absent versus present scope, case-sensitive scope names, canonical IPv6 spellings, and existing URI equivalence rules. Each pair exercises both SSL-host hashing branches and checks symmetric equality, collection behavior, and equal-key hash consistency.

Proxy_IPv6ScopeChange_ConnectsToSelectedProxy covers 12 combinations of HTTP/HTTPS/SOCKS5 proxies, HTTP/HTTPS destinations, and HTTP/1.1/HTTP/2 request policies. It changes WebProxy.Address from %1 to %2 and verifies the endpoint supplied to ConnectCallback. The callback deliberately throws before opening a socket, so these tests validate endpoint selection without requiring IPv6 interfaces or protocol negotiation.

Validation

Earlier local validation on Windows ARM64, Release, completed before the local toolchain was removed:

Check Result
Runtime/libraries baseline build Passed; zero warnings or errors
Selected key and regression tests against the unfixed implementation 32 total; 16 expected failures; zero skips
Changed System.Net.Http library build Passed; zero warnings or errors
Connection-key and proxy test classes after the fix 63 total; zero failures; one existing skip
Full functional inner-loop suite 5,508 total; zero failures; 48 skips

No repository builds or tests were rerun while preparing this description. Cross-platform validation remains for CI.

Performance considerations

The scope check adds work to connection-key operations, including ordinary proxy lookups. Previously collected Windows ARM64 BenchmarkDotNet measurements showed the following representative DNS-proxy results:

Operation Before After
Key hashing 32.66 ns 34.54 ns
Key equality with the same URI instance 4.46 ns 5.66 ns
Key equality with separate equivalent URI instances 5.39 ns 10.26 ns

These are isolated key-operation measurements, not end-to-end HTTP throughput results. No managed allocations were reported in the measured steady-state operations.

Resolves #135103

Note

This pull request description was generated with GitHub Copilot.

@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Oct 11, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @karelz, @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

@MihaZupan

Copy link
Copy Markdown
Member

Closing in favour of #135617

@MihaZupan MihaZupan closed this Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Net.Http community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inconsistent handling of IPv6 scope in SocketsHttpHandler's connection pool

2 participants