Class BinaryHeaderValueNormalizer
The contract. The gRPC wire format requires the value of any metadata key whose
name ends -bin to be base64. MockServer treats a -bin value as already
base64-encoded by the user and passes it through unchanged in both directions — it never
encodes or decodes. Base64 is pure ASCII, so the value survives every transport site unaltered
(US-ASCII gRPC-Web trailers, CR/LF header sanitisation, HPACK/QPACK, JSON serialisation).
The one place pass-through is not enough: matching. grpc-java encodes outbound
binary metadata with BASE64_ENCODING_OMIT_PADDING
(io.grpc.internal.TransportFrameUtil#toHttp2Headers), so the value that actually arrives
on the wire has no = padding — AQIDBA, not AQIDBA==. A user who
writes the natural, padded form produced by Base64.getEncoder() in an expectation would
otherwise silently fail to match every real gRPC client. Both forms decode to identical bytes and
are therefore the same value, so the matcher compares them with the padding removed.
Normalisation is deliberately narrow:
- It applies only when the header name ends
-bin(case-insensitively), so ordinary HTTP header matching is untouched. - It applies only to header maps, not to query-string or path parameters.
- It strips trailing
=only from a structurally valid padded base64 value — base64 alphabet characters, one or two=, and a total length that is a multiple of 4. A JSON-schema matcher is never touched. Note that+and/are base64 alphabet characters as well as regex metacharacters, so a regex is left as written only when it fails that structural test; the length invariant is what makes the common metacharacter case (A+=) safe. SeestripPaddingIfPaddedBase64(java.lang.String). - Both the matcher side and the matched side are normalised through the same function, so the comparison stays symmetric and a padded expectation matches a padded actual value just as it did before.
- See Also:
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe gRPC binary metadata key suffix (io.grpc.Metadata#BINARY_HEADER_SUFFIX). -
Method Summary
Modifier and TypeMethodDescriptionstatic booleanReturns true when the given header name is a gRPC binary metadata key, i.e. it ends-bin.static booleanmatchesIgnoringPadding(RegexStringMatcher regexStringMatcher, MockServerLogger mockServerLogger, MatchDifference context, NottableString matcherValue, NottableString matchedValue) Compares two-binvalues with base64 padding removed from both, reporting any mismatch against the values as originally written.static NottableStringnormalize(NottableString value) Returns the value with base64 padding removed when it is entirely padded base64, otherwise the value unchanged.static booleanshouldNormalize(NottableString matcherKey, NottableString matchedKey, NottableString matcherValue, NottableString matchedValue) Returns true when a value comparison for the entry pair should be normalised: at least one of the two keys is a-binkey, and neither value is a JSON-schema matcher (a schema is evaluated against the raw string and must not be rewritten).
-
Field Details
-
BINARY_HEADER_SUFFIX
The gRPC binary metadata key suffix (io.grpc.Metadata#BINARY_HEADER_SUFFIX).- See Also:
-
-
Method Details
-
isBinaryHeaderName
Returns true when the given header name is a gRPC binary metadata key, i.e. it ends-bin. Matched case-insensitively because HTTP/2 lower-cases header names on the wire but an expectation may be written in any case.A name of exactly
-bincounts, matchingio.grpc.Metadata.Key, which tests the suffix without requiring a non-empty prefix. A name that merely contains the letters (e.g.x-cabin) does not — the suffix must be preceded by the hyphen it includes. -
shouldNormalize
public static boolean shouldNormalize(NottableString matcherKey, NottableString matchedKey, NottableString matcherValue, NottableString matchedValue) Returns true when a value comparison for the entry pair should be normalised: at least one of the two keys is a-binkey, and neither value is a JSON-schema matcher (a schema is evaluated against the raw string and must not be rewritten).Either key is enough because the matcher-side key may be a regular expression (e.g.
x-.*) that matches a literal-binkey on the request side. Triggering on the matcher key alone would not make the comparison asymmetric — this gate governs both normalisations, so both sides would simply stay raw — it would just silently miss the regex-key case and leave a padded expectation failing to match. (Asymmetry would arise from normalising at map-construction time instead, where each map only sees its own keys; that is the reason this is decided at the comparison site.) -
matchesIgnoringPadding
public static boolean matchesIgnoringPadding(RegexStringMatcher regexStringMatcher, MockServerLogger mockServerLogger, MatchDifference context, NottableString matcherValue, NottableString matchedValue) Compares two-binvalues with base64 padding removed from both, reporting any mismatch against the values as originally written.The normalised comparison runs with a
nullMatchDifferenceso it cannot record a difference naming the padding-stripped forms: a user who wroteAQIDBA==and mistyped the payload would otherwise be told the expected value wasAQIDBA, sending them to look for a padding bug that does not exist. On failure a single difference is added here from the original values, noting that padding was ignored so the reader knows why two visibly different spellings were still considered unequal. -
normalize
Returns the value with base64 padding removed when it is entirely padded base64, otherwise the value unchanged. Thenotandoptionalmarkers are preserved, so!AQIDBA==normalises to!AQIDBAand keeps asserting inequality.
-