Class GrpcToHttpResponseHandler
- All Implemented Interfaces:
io.netty.channel.ChannelHandler,io.netty.channel.ChannelOutboundHandler
HttpResponse back into a gRPC wire response:
a length-prefixed protobuf frame plus a grpc-status trailer.
Service/method resolution. The gRPC service and method names are taken
from x-grpc-service/x-grpc-method response headers when present (the
forward-proxy path stamps them via
GrpcForwardTranslator.decodeResponseFromUpstream(org.mockserver.model.HttpResponse, java.lang.String, java.lang.String, org.mockserver.grpc.GrpcProtoDescriptorStore), and users may
set them explicitly). Otherwise they fall back to the per-connection
GrpcPendingRequests registry populated by GrpcToHttpRequestHandler when it
decoded the request, looked up by the response's HTTP/2 stream id. The matching pipeline does
not propagate the internal x-grpc-* request headers onto the matched response, so
without this fallback a normal mock expectation would return raw JSON on a stream the client
expects to be framed protobuf (issue #2419). The same trap is documented on the HTTP/3 path in
Http3GrpcResponseWriter.
Only successful responses are converted. A non-2xx response that carries no
explicit gRPC status did not come from a matched gRPC expectation -- most commonly the 404
notFoundResponse produced when nothing matched. Converting it would invent a
schema-valid example body and report OK, so a typo'd path would return a plausible
green response with no client-side indicator. Such responses are instead mapped to a gRPC
error status per the gRPC-over-HTTP/2 HTTP-status mapping (404 becomes
UNIMPLEMENTED, which is what a real gRPC server returns for an unknown method).
Trailers, not headers. Per gRPC-over-HTTP/2 (and HTTP/3), a unary response
must deliver grpc-status/grpc-message in a terminal trailing HEADERS frame,
never in the initial response headers. Only content-type: application/grpc is a real
header. gRPC-Web is the exception: it carries the status in an in-body trailer frame, so
convertToGrpcWebResponse(org.mockserver.model.HttpResponse, java.lang.String) consumes the trailers and strips them from the response.
-
Nested Class Summary
Nested classes/interfaces inherited from interface io.netty.channel.ChannelHandler
io.netty.channel.ChannelHandler.Sharable -
Constructor Summary
ConstructorsConstructorDescriptionGrpcToHttpResponseHandler(MockServerLogger mockServerLogger, GrpcProtoDescriptorStore descriptorStore) -
Method Summary
Modifier and TypeMethodDescriptionprotected voidencode(io.netty.channel.ChannelHandlerContext ctx, HttpResponse response, List<Object> out) voidwrite(io.netty.channel.ChannelHandlerContext ctx, Object msg, io.netty.channel.ChannelPromise promise) Releases the pending record when a response is written that this encoder will never see.Methods inherited from class io.netty.handler.codec.MessageToMessageEncoder
acceptOutboundMessageMethods inherited from class io.netty.channel.ChannelOutboundHandlerAdapter
bind, close, connect, deregister, disconnect, flush, readMethods inherited from class io.netty.channel.ChannelHandlerAdapter
ensureNotSharable, exceptionCaught, handlerAdded, handlerRemoved, isSharableMethods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface io.netty.channel.ChannelHandler
exceptionCaught, handlerAdded, handlerRemoved
-
Constructor Details
-
GrpcToHttpResponseHandler
public GrpcToHttpResponseHandler(MockServerLogger mockServerLogger, GrpcProtoDescriptorStore descriptorStore)
-
-
Method Details
-
write
public void write(io.netty.channel.ChannelHandlerContext ctx, Object msg, io.netty.channel.ChannelPromise promise) throws Exception Releases the pending record when a response is written that this encoder will never see.MessageToMessageEncoder<HttpResponse>only matches the MockServer model type. The gRPC streaming path (GrpcStreamResponseActionHandler) writes raw NettyDefaultHttpResponse/HttpContentobjects, soencode(io.netty.channel.ChannelHandlerContext, org.mockserver.model.HttpResponse, java.util.List<java.lang.Object>)-- and thereforeconsume()-- never runs for a streaming exchange, leaving its record orphaned. On HTTP/1.1 that record then poisons the single-shot slot: the next unary call on the same keep-alive connection is marked ambiguous,consumereturns null, and its response goes out as unframed JSON with nogrpc-status-- the exact issue #2419 shape. It also left the unary deadline timer armed alongside the streaming one, so both could terminate the stream.gRPC-Web runs over HTTP/1.1, so this is reachable in the primary browser scenario.
- Specified by:
writein interfaceio.netty.channel.ChannelOutboundHandler- Overrides:
writein classio.netty.handler.codec.MessageToMessageEncoder<HttpResponse>- Throws:
Exception
-
encode
protected void encode(io.netty.channel.ChannelHandlerContext ctx, HttpResponse response, List<Object> out) - Specified by:
encodein classio.netty.handler.codec.MessageToMessageEncoder<HttpResponse>
-