openapi3: match media types case-insensitively - #1262
Draft
DmRomantsov wants to merge 2 commits into
Draft
DmRomantsov wants to merge 2 commits into
DmRomantsov wants to merge 2 commits into
Conversation
RFC 9110 defines media type and subtype tokens as case-insensitive, so apply case folding while preserving exact matches and parameter values. Co-authored-by: Cursor <cursoragent@cursor.com>
Matching content types case-insensitively let a body declared as text/plain be matched and decoded under a Content-Type of TEXT/PLAIN, but re-encoding it still required an exact registration. Setting a schema default on such a request therefore failed late, after validation had already succeeded: rewriting failed: unsupported content type "APPLICATION/JSON" decodeBody reports the media type using the casing from the header, and encodeBody looked it up in bodyEncoders with an exact map access. Resolve encoders through the same case-insensitive lookup already used for decoders, extracted into a shared helper so the two cannot drift apart again. The exported RegisteredBodyDecoder and RegisteredBodyEncoder keep their exact-match behaviour, so no public API changes. Also match media types without allocating: the case-insensitive fallback sorted every declared media type on each miss, which a Content-Type carrying parameters reaches on every request. Compare against the map directly, keeping the smallest match so several declared case variants still resolve deterministically, and skip re-searching the unparameterised mime that the previous stage already covered. Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Motivation
A document that declares
text/plaincurrently does not match a validContent-Type: TEXT/PLAIN.openapi3.Content.Getreturnsnil, and request and response validation reject the body with an unexpected Content-Type error.RFC 9110 section 8.3.1 defines media type and subtype tokens as case-insensitive.
Proposed changes
type/*matching stages.rewriting failed: unsupported content type "APPLICATION/JSON". Decoder and encoder resolution now share one helper so they cannot drift apart again.Getno longer re-searches the unparameterised mime that the previous stage already covered.Content.Get, body decoder resolution, and whole-body validation, which the repository previously had none of.Unrelated changes (optional)
None
Testing / Validation
masterforContent.Get, request validation, and response validation before applying the implementation.rewriting failed: unsupported content type "APPLICATION/JSON".make preparego test -count=1 ./...go test -count=10 -short ./...go test -count=2 -short -covermode=atomic ./...-race -count=10go vet ./...modernizeandnilnessanalyzersgoimports-reviseron changed Go filesshellcheck -S error maps.sh docs.shBenchmarks
benchstatover 10 runs, Apple M5 Pro, all deltas p=0.000. Both lookups are now allocation-free on every path.Content.Getopenapi3filtergetBodyDecoder, exactgetBodyDecoder, case variantgetBodyDecoder, unregistered (image/png)ValidateRequestBody,application/jsonValidateRequestBody,;charset=utf-8ValidateRequestBody,APPLICATION/JSONimage/pngis the path every binary part of amultipart/form-dataupload takes, since binary parts legitimately have no registered decoder.Notes (optional)
Exact matches continue to win. Parameter values remain case-sensitive,
type/*and*/*retain their existing precedence, and media types missing a subtype continue to be rejected rather than falling through to a wildcard. No public API or dependency changes are introduced: the exportedRegisteredBodyDecoderandRegisteredBodyEncoderkeep their exact-match behaviour, and only validation resolves content types case-insensitively.Parameter names are still compared case-sensitively, so a document declaring
application/json;charset=utf-8does not matchapplication/json;Charset=utf-8even though RFC 9110 section 5.6.6 makes parameter names case-insensitive. Handling that needsmime.ParseMediaType, which allocates a map per call, so it is left for a follow-up rather than added to this path.Related history: #91, #93, and #1201.
Dependencies (optional)
None