fix(headers): stop rewriting raw header values - #2310
Merged
Conversation
- Check header presence through NonValidated instead of enumerating the collection. Enumerating forces the framework to parse every value already stored and write the parsed form back, so a value added verbatim was rewritten as soon as a second header was applied. - A method declaring "Accept: application/vnd.api.json;version=3.4.1" alongside any other header sent it with a space after the semicolon. Servers matching the media type as an exact string rejected it. - Fix both request paths at once by moving the check into HttpHeaderApplier, which the reflection builder and the generated runner already share. The reflection builder was affected on every target framework; the generated runner only on net4x. - On net4x, where NonValidated does not exist, probe the single named header instead. It leaves every other stored value untouched. Fixes #2306
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2310 +/- ##
==========================================
- Coverage 99.91% 99.91% -0.01%
==========================================
Files 192 192
Lines 10027 10022 -5
Branches 1924 1922 -2
==========================================
- Hits 10018 10013 -5
Partials 9 9 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
ChrisPulman
approved these changes
Aug 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What kind of change does this PR introduce?
Bug fix.
What is the new behavior?
A header value supplied verbatim reaches the wire byte for byte, however many headers the method declares.
HttpHeaders.NonValidated, which reports key presence without parsing or materializing any stored value, and never throws for a name the collection does not accept.HttpHeaderApplier, which the reflection request builder and the source-generated request runner already share, so the two copies cannot drift apart again.NonValidateddoes not exist, the check probes the single named header. That leaves every other stored value untouched.Containsthrows when the name belongs to the other collection or is not a legal token; neither name can be present, so both are reported as absent.What is the current behavior?
A raw header value is silently reformatted once a method declares more than one header.
Enumerating
HttpHeadersforces lazy parsing of every value already stored and writes the parsed form back, so applying header N rewrote headers 1..N-1.RefitSettings.ValidateHeadersdefaults tofalse, so values go throughTryAddWithoutValidationand are expected to reach the wire unchanged. Instead:Both forms are equivalent under RFC 9110, but servers matching the media type as an exact string reject the rewritten value.
Refit.Reflectionwas affected on every target framework. The generated path was affected on net462 through net481 only, since net6.0+ already usedNonValidatedthere.Closes #2306
What might this PR break?
None.
Checklist
mainbranchAdditional information
The new tests fail against the previous implementation and pass against this one.
ReflectionRawHeaderValueTestsreproduces the report at the interface level: the one-header method passes either way, the two-header method only passes with the fix.RawHeaderValueScenariosdrives the same assertions through both paths'SetHeaderseam, the way the existingHeaderValidationScenariosdoes.Assertions read
Headers.NonValidated, so they see what is actually stored rather than the parsed projectionGetValueswould hand back.