Class GrpcResponseStatusResolver

java.lang.Object
org.mockserver.grpc.GrpcResponseStatusResolver

public class GrpcResponseStatusResolver extends Object
Resolves the gRPC status a client should observe for a given HttpResponse, independently of transport.

This lives in core, and is shared by every gRPC response path -- GrpcToHttpResponseHandler (HTTP/1.1 and HTTP/2), GrpcHttp3Adapter and Http3GrpcResponseWriter (HTTP/3) -- because the resolution rules are a property of the gRPC contract, not of the wire protocol. Duplicating them per transport is how the two defects this class was extracted to fix arose: an expectation authored with withTrailer("grpc-status", ...) returned NOT_FOUND over HTTP/2 but OK over HTTP/3, and an unmatched request over HTTP/3 fabricated a success.

Resolution order:

  1. grpc-status-name header (a MockServer convenience, mapped by name)
  2. numeric grpc-status, read from the headers or the trailers -- consumer documentation recommends authoring it as a trailer, so both must work
  3. for a non-2xx HTTP status, the gRPC-over-HTTP/2 specification's HTTP-status mapping (see GrpcStatusMapper.fromHttpTransportStatus(int)) -- this is a transport failure, and its body must be discarded rather than framed
  4. otherwise OK
A numeric status is carried through verbatim (parsed, so whitespace is normalised, then re-rendered) rather than round-tripped through GrpcStatusMapper.fromCode(int) -- that lookup is getOrDefault(code, UNKNOWN), so a non-standard or future code such as 42 would be silently rewritten to 2.
  • Field Details

    • GRPC_DEADLINE_RESPONSE_MARKER

      public static final String GRPC_DEADLINE_RESPONSE_MARKER
      MockServer's internal marker identifying a synthesized DEADLINE_EXCEEDED response.

      Defined here so core and netty cannot drift; GrpcToHttpResponseHandler.DEADLINE_RESPONSE_HEADER is an alias of this constant, and GrpcToHttpResponseHandlerTest pins them equal.

      See Also:
  • Constructor Details

    • GrpcResponseStatusResolver

      public GrpcResponseStatusResolver()
  • Method Details

    • resolve

      public static GrpcResponseStatusResolver.ResolvedStatus resolve(HttpResponse response)
      Resolves the status for a response, including the HTTP-status fallback.
    • explicitStatus

      public static String explicitStatus(HttpResponse response)
      Returns the explicitly-authored gRPC status as a decimal string, or null if the response carries none. A non-numeric grpc-status (a protocol violation) is treated as absent.
    • firstHeaderOrTrailer

      public static String firstHeaderOrTrailer(HttpResponse response, String name)
      Reads a value from the response headers, falling back to the trailers.
    • firstTrailer

      public static String firstTrailer(HttpResponse response, String name)
      Reads the first value of a response trailer, or null.
    • isGrpcProtocolMetadata

      public static boolean isGrpcProtocolMetadata(String name)
      Returns true if the header or trailer name is gRPC protocol metadata that a transport emits itself, or a connection-specific field that is illegal on HTTP/2 and HTTP/3, and which must therefore not be copied through from an expectation's response headers.
    • passThroughHeaders

      public static Headers passThroughHeaders(HttpResponse response)
      Returns the response's headers that should be passed through to a gRPC client, excluding gRPC protocol metadata the transport emits itself.
    • passThroughTrailers

      public static Headers passThroughTrailers(HttpResponse response)
      Returns the response's trailers that should be passed through to a gRPC client as trailing metadata, excluding gRPC protocol metadata the transport emits itself.

      This is the trailer twin of passThroughHeaders(HttpResponse) and exists for the same reason: the exclusion rule is a property of the gRPC contract rather than of a wire protocol, so it must not be re-derived per transport. Filtering out grpc-status/grpc-message/grpc-status-name is what stops a user-authored trailer overriding or spoofing the status the transport itself resolved and emits -- the same exclusion the HTTP/2 path applies through GrpcToHttpResponseHandler.remainingTrailers. Connection-specific fields, pseudo-header names and content-length/content-type are excluded too because RFC 9114 forbids all of them in a trailer section, and a conforming client "MUST treat" a message carrying one as malformed.