Repository navigation
Avoid parsing Content-Type and allocating Items in HasFormContentType - #70163
Conversation
- Skip MediaTypeHeaderValue parsing when the Content-Type cannot be a form media type (both contain "form"). - Check IAntiforgeryValidationFeature before HttpContext.Items in the antiforgery guard so Items isn't lazily allocated on every form access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Missing frozen source identities and prior review feedback prevented verification of revisions and deduplication of feedback.
No open issues 🎉
What changed in this PR
Reduces the cost of HttpRequest.HasFormContentType in ASP.NET Core by avoiding unnecessary header parsing and lazy HttpContext.Items allocation, without changing public APIs.
Changes:
- Skips Content-Type parsing when the raw header cannot match either form media type.
- Checks for invalid antiforgery validation before accessing
Items. - Adds regression tests and benchmarks covering form detection and allocation costs.
| File | Description |
|---|---|
| src/Http/Http/test/Features/FormFeatureTests.cs | Tests content-type variants, antiforgery behavior, and avoidance of Items allocation. |
| src/Http/Http/src/Features/FormFeature.cs | Adds the parsing shortcut and reorders antiforgery checks to avoid unnecessary allocation. |
| src/Http/Http/perf/Microbenchmarks/FormFeatureBenchmark.cs | Benchmarks form detection against a fresh-context baseline for representative content types. |
Review effort: Balanced
|
The
I'm retriggering CI with an empty commit. 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
The 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
Command 'run
**🤖' is not supported by Azure Pipelines.
See additional documentation. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines hasn't started on the last two pushes to this PR (6f0b224 and f445af1), even though pipelines are running for other PRs pushed in the same window. The previous run's failure looks unrelated to this change:
Earlier runs also hit Could a maintainer comment 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
This change looks too big. Why the deletions |
Earlier "Retrigger CI" commits on this branch were created with an empty tree, deleting every file. This restores the tree of the last good commit so the PR diff is again only the intended change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
You're right, and thanks for catching it. The deletions weren't intentional. The three "Retrigger CI" commits (6f0b224, f445af1, 19f6f6b) were created with an empty tree instead of the parent's tree, so each one deleted every file in the repo. That's also why Azure Pipelines never started on them. Fixed in 1e58451, which restores the tree of the last good commit (5b80fd2). The PR diff is back to the intended change only: 3 files, +121/−4 ( 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Retriggered CI with an empty commit (17b1fde). The only failure was the Blazor E2E test 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Retriggered CI with an empty commit (667cd30). The previous run failed only unrelated tests:
This PR only touches form Content-Type detection in Http. 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
CI update: 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
/review |
|
✅ No documentation PR was created. Confidence that documentation is needed: 3% No docs needed: internal performance optimization with tests and benchmark only; no public API changes. Documentation surfaces considered
|
Avoid parsing Content-Type and allocating Items in HasFormContentType
Skip Content-Type parsing in HasFormContentType for non-form requests
Description
Makes
HttpRequest.HasFormContentTypeskip Content-Type parsing for non-form requests and stop allocatingHttpContext.Items.HttpRequest.HasFormContentTypeis called on many requests:It had two avoidable costs:
MediaTypeHeaderValue.TryParse, which allocates the header value, the media type string and the parameter list. This happened even for requests that obviously aren't forms, such asapplication/json; charset=utf-8. Both form media types (application/x-www-form-urlencodedandmultipart/form-data) containform, so if the raw header doesn't containform(OrdinalIgnoreCase, the same comparison used for the media type check), the parsed media type can't match. In that case we now returnfalsewithout parsing. Any value that does containformstill goes through the existing parse, so results don't change.HttpContext.Itemsallocation.HasFormContentTypereadsForm, which runs the antiforgery guard. The guard checkedItems.ContainsKey(...)twice before it checked whether an invalidIAntiforgeryValidationFeaturewas present, and that lazily allocatedItemsFeature+ItemsDictionaryon every request. It now checks the feature first and only touchesItemswhen the token is invalid. Both checks are side-effect-free reads, so swapping the order of the&&doesn't change behavior.No public API changes.
Benchmarks
Added
FormFeatureBenchmarktoMicrosoft.AspNetCore.Http.Microbenchmarks. It creates a freshDefaultHttpContextper op, so the lazy allocations show up. BenchmarkDotNet (InProcess, MemoryDiagnoser) on a 2-vCPU Linux VM, two interleaved A/B rounds. Both rounds reproduced. TheCreateContextbaseline, about 220–300 ns, is included in the times below.application/json; charset=utf-8application/x-www-form-urlencodedmultipart/form-data; boundary=…"Alloc delta" is the number of bytes
HasFormContentTypeallocates on top of creating the context, measured exactly withGC.GetAllocatedBytesForCurrentThread. The remaining 64 B is the lazily createdFormFeatureitself.Tests
New
FormFeatureTests:HasFormContentTyperesults for null, empty, JSON, near-miss values (text/plain; format=form,application/form,multipart/form-data-x) and case/whitespace variants. All of them pass onmainas well, which confirms the results are unchanged.HasFormContentTypedoesn't create anIItemsFeature. This fails onmain.Test runs:
Microsoft.AspNetCore.Http.Tests: 279 passed, 7 skipped.Microsoft.AspNetCore.Antiforgery.Test: 185 passed.🤖 Posted by ASP.NET Core Perf 2 (AI Agent)