Class BinaryHeaderValueNormalizer

java.lang.Object
org.mockserver.matchers.BinaryHeaderValueNormalizer

public class BinaryHeaderValueNormalizer extends Object
Padding-insensitive comparison support for gRPC binary metadata header values.

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. See stripPaddingIfPaddedBase64(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 Details

    • BINARY_HEADER_SUFFIX

      public static final String BINARY_HEADER_SUFFIX
      The gRPC binary metadata key suffix (io.grpc.Metadata#BINARY_HEADER_SUFFIX).
      See Also:
  • Method Details

    • isBinaryHeaderName

      public static boolean isBinaryHeaderName(NottableString name)
      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 -bin counts, matching io.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 -bin key, 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 -bin key 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 -bin values with base64 padding removed from both, reporting any mismatch against the values as originally written.

      The normalised comparison runs with a null MatchDifference so it cannot record a difference naming the padding-stripped forms: a user who wrote AQIDBA== and mistyped the payload would otherwise be told the expected value was AQIDBA, 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

      public static NottableString normalize(NottableString value)
      Returns the value with base64 padding removed when it is entirely padded base64, otherwise the value unchanged. The not and optional markers are preserved, so !AQIDBA== normalises to !AQIDBA and keeps asserting inequality.