Repository navigation
Conversation
…arders Return early when there are no forwarded entries, build each forwarder set inline instead of allocating a SetOfForwarders[] and track the current remote address without allocating an IPEndPoint for the connection's own address. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The missing frozen review bundle, including revision-bound base sources and existing feedback, prevented completion of compatibility and feedback-deduplication checks.
No open issues 🎉
What changed in this PR
This PR reduces per-request allocations in ForwardedHeadersMiddleware while preserving forwarded-header processing, endpoint logging, and invalid-port validation.
Changes:
- Returns early when there are no forwarded entries to process.
- Builds forwarder sets inline and avoids allocating an endpoint for the initial connection address.
- Adds regression tests for unchanged requests, invalid ports, proto-only forwarding, proxy-chain logging, and missing remote addresses.
| File | Description |
|---|---|
| src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs | Adds regression coverage for behavior preserved by the allocation changes. |
| src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs | Removes intermediate allocations and separates remote-address tracking from parsed endpoints. |
Review effort: Balanced
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Pushed empty commit 4ce46d2 to retrigger CI. The failure was in 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Pushed empty commit 2ec2037 to retrigger CI. The only failure is again 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
This PR only touches 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
The Blazor e2e and quarantined pipelines passed this time. 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
/azp run aspnetcore-components-e2e 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
No pipelines are associated with this pull request. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
On 9eca4c4 the Blazor e2e and quarantined pipelines passed. 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
/review |
There was a problem hiding this comment.
Source-only review of frozen head eb5706d73c63c82649b0565fd0007549ed51cbb6 found one new P2 compatibility regression: a negative ForwardLimit now silently disables forwarding and allows downstream request processing instead of failing before dispatch.
The routed docs/CrossCuttingGuidance.md review completed with gpt-5.6-sol (runtime effort not supplied/default), covering compatibility/API, correctness and validation, allocation behavior, options/DI, diagnostics, nullability, lifecycle, threading, portability, packaging, formatting, and test false-pass risk. No routed guide was incomplete; unresolved candidates: none. No prepared feedback duplicated this finding; the existing Copilot review reported no open issues because its bundle was unavailable.
Provenance: version-2 ready bundle for PR 70177, merge base/base tip 4f556f73a610b386e5ec29355a300e493d30c4c5, and reviewer guidance from immutable workflow commit 4f556f73a610b386e5ec29355a300e493d30c4c5. Proof is source-only. Running PR code, tests, CI, browser workflows, and security assessment were excluded by the trusted bundle; no execution claims are made. HttpOverrides had no specialist area guide beyond the completed cross-cutting review. Limitations are tracked Git bytes only; external symlink, submodule, and LFS target behavior is not established.
Generated by ASP.NET Core Pull Request Review for #70177 · copilot · gpt56 · 163.4 AIC · ⌖ 7.6 AIC · ⊞ 20.2K
Comment /review to run again
…ersMiddleware Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a Microsoft.AspNetCore.HttpOverrides.Microbenchmarks project that calls ApplyForwarders with no forwarded headers, a single X-Forwarded-For/Proto hop and a 3-hop For/Proto/Host chain. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
CI triage for head 77a200b: the three failing tests are unrelated to this change, which only touches
All other aspnetcore-ci jobs passed, as did the CoreCLR and WebAssembly E2E legs. 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Avoid per-request allocations in ForwardedHeadersMiddleware
Cut per-request allocations in
ForwardedHeadersMiddleware.ApplyForwarders.Description
ApplyForwardersnow allocates nothing for requests that carry no forwarded headers, and it allocates less for proxied requests.ForwardedHeadersMiddleware.ApplyForwardersruns on every request. Before this change, it:SetOfForwarders[entryCount]array, even whenentryCount == 0;new IPEndPoint(RemoteIpAddress, RemotePort)for the connection's own address. That endpoint is only ever used for itsAddress, for a debug log and to restore the same values.This PR changes it so that:
entryCount == 0. Previously the loop body never ran in this case, soapplyChangesstayedfalseand nothing was modified, which makes this exactly equivalent. Typical cases are health probes, direct traffic and internal calls.IPAddress, with the last parsedX-Forwarded-ForIPEndPointkept separately. TheUnknown proxy: {RemoteIpAndPort}debug log still receives anIPEndPointwith the same value; it is created only when that log is enabled. The finalcheckFor && currentRemote != nullbranch,X-Original-Forand the restoredRemoteIpAddress/RemotePortare unchanged. This includes the case where only the proto is applied:X-Original-Foris still written andX-Forwarded-Foris still consumed.RemotePort(with a non-null address) still throws the sameArgumentOutOfRangeExceptionfromIPEndPointas before.Benchmark
I used a local BenchmarkDotNet harness (not included). It calls
ApplyForwarderson aDefaultHttpContextwithFor | Proto | Hostenabled and twoKnownProxies.ContextOnlymeasures the context and request headers on their own. Timings are on 2 cores, 3 runs each, and allocations are per operation including the context.X-Forwarded-For+X-Forwarded-Proto(1 hop)Timings are within noise; the change is about allocations.
Tests
ApplyForwarders, and all of them also pass againstmain:NoForwardedHeadersLeavesRequestUnchangedInvalidRemotePortThrows(with and without forwarded headers)ProtoOnlyStillRecordsOriginalForAndConsumesForwardedForKnownProxiesStopAtUnknownPeerAndLogCurrentEndpoint, which checks the exactUnknown proxy: ip:portlog text for the original and forwarded endpoints, the remote IP and port, and the truncatedX-Forwarded-ForandX-Original-ForvaluesNoRemoteAddressWithKnownProxiesAppliesNothingBenchmark (3 interleaved rounds)
The benchmark is committed in
src/Middleware/HttpOverrides/perf/Microbenchmarks/ForwardedHeadersMiddlewareBenchmark.cs. It callsApplyForwarderson aDefaultHttpContextwithFor | Proto | Hostenabled, twoKnownProxiesandForwardLimit = null.ContextOnlymeasures creating the context on its own.Result: allocations drop in every case that runs the middleware: −56 B with no forwarded headers (the middleware itself now allocates 0 B), −96 B for one hop and −176 B for the 3-hop chain. The time differences (−2% to −8%) are of the same order as the
ContextOnlydrift between builds (−3.6%), so I don't claim a time win. This is an allocation-only improvement.BenchmarkDotNet v0.13.0, InProcess, 5 warmup + 15 measured iterations, Server GC. Rounds ran in the order main, PR, main, PR, main, PR. Both builds use the same benchmark source; "main" is this PR's merge base (4f556f7). Allocated is exact bytes per op from the GC counters. Machine: AMD EPYC 9V74, 2 cores, Ubuntu 24.04 (container), .NET SDK 12.0.100-alpha.1.26521.103, runtime 12.0.0 (12.0.26.52911).
🤖 Posted by ASP.NET Core Perf 2 (AI Agent)