Package org.mockserver.netty.grpc
Class GrpcBidiStreamHandler
java.lang.Object
io.netty.channel.ChannelHandlerAdapter
io.netty.channel.ChannelInboundHandlerAdapter
org.mockserver.netty.grpc.GrpcBidiStreamHandler
- All Implemented Interfaces:
io.netty.channel.ChannelHandler,io.netty.channel.ChannelInboundHandler
public class GrpcBidiStreamHandler
extends io.netty.channel.ChannelInboundHandlerAdapter
Per-stream handler for true bidirectional gRPC streaming. NOT
@Sharable --
holds per-stream state (the incremental frame decoder, finished guard).
Phase 3b behaviour (rule-driven via GrpcBidiResponse):
- On
Http2HeadersFrame: applies the top-level action delay (if configured) before writing the initial response HEADERS (:status=200,content-type=application/grpc, plus any configured headers from the GrpcBidiResponse,endStream=false). Then writes any EAGER messages from the response, honouring per-messageGrpcStreamMessage.getDelay()via event-loop scheduling (messages are chained so ordering is preserved and the trailing grpc-status is written only after all scheduled messages complete). - On
Http2DataFrame: feeds content bytes toIncrementalGrpcFrameDecoder; for each complete inbound message, converts to JSON via the converter, evaluates rules in order (first match emits its responses as DATA frames). If no rule matches, no response is emitted. If the frame hasendStream=true, callsfinish(ChannelHandlerContext). finish(): writes trailing HEADERS with configured grpc-status andendStream=true. Guarded to run at most once.
The handler supports two modes:
- Phase 3b (GrpcBidiResponse-driven): Constructed with a
GrpcBidiResponseconfig; eager messages, rules, status, and headers come from the config. - Phase 3a (legacy responder function): Constructed with a
Functionresponder for backward compatibility with existing tests.
Flow control: the channel's autoRead is set to false when this handler is
added; after processing each inbound frame, ctx.read() is called to request
the next frame. If the decoder's buffer cap is exceeded, a RESOURCE_EXHAUSTED trailing
status is written and the stream is finished.
Error handling: exceptions during channelRead are caught and result in an INTERNAL grpc-status trailer, never an uncaught exception propagating up the pipeline.
-
Nested Class Summary
Nested classes/interfaces inherited from interface io.netty.channel.ChannelHandler
io.netty.channel.ChannelHandler.Sharable -
Constructor Summary
ConstructorsConstructorDescriptionGrpcBidiStreamHandler(com.google.protobuf.Descriptors.MethodDescriptor methodDescriptor, GrpcJsonMessageConverter converter, Function<String, List<String>> responder) Phase 3a constructor: function-based responder (backward compatible).GrpcBidiStreamHandler(com.google.protobuf.Descriptors.MethodDescriptor methodDescriptor, GrpcJsonMessageConverter converter, GrpcBidiResponse config) Phase 3b constructor: GrpcBidiResponse-driven (without completion callback).GrpcBidiStreamHandler(com.google.protobuf.Descriptors.MethodDescriptor methodDescriptor, GrpcJsonMessageConverter converter, GrpcBidiResponse config, Runnable completionCallback) Phase 3b constructor: GrpcBidiResponse-driven with completion callback. -
Method Summary
Modifier and TypeMethodDescriptionvoidchannelInactive(io.netty.channel.ChannelHandlerContext ctx) Handles channel deactivation (client disconnect, connection closed, abandoned stream).voidchannelRead(io.netty.channel.ChannelHandlerContext ctx, Object msg) voidexceptionCaught(io.netty.channel.ChannelHandlerContext ctx, Throwable cause) voidhandlerAdded(io.netty.channel.ChannelHandlerContext ctx) Methods inherited from class io.netty.channel.ChannelInboundHandlerAdapter
channelActive, channelReadComplete, channelRegistered, channelUnregistered, channelWritabilityChanged, userEventTriggeredMethods inherited from class io.netty.channel.ChannelHandlerAdapter
ensureNotSharable, 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
handlerRemoved
-
Constructor Details
-
GrpcBidiStreamHandler
public GrpcBidiStreamHandler(com.google.protobuf.Descriptors.MethodDescriptor methodDescriptor, GrpcJsonMessageConverter converter, Function<String, List<String>> responder) Phase 3a constructor: function-based responder (backward compatible).- Parameters:
methodDescriptor- the resolved gRPC method descriptorconverter- JSON/protobuf converter for the method's message typesresponder- maps an inbound message JSON string to a list of response JSON strings; for 3a the default is echo: returns[inboundJson]
-
GrpcBidiStreamHandler
public GrpcBidiStreamHandler(com.google.protobuf.Descriptors.MethodDescriptor methodDescriptor, GrpcJsonMessageConverter converter, GrpcBidiResponse config) Phase 3b constructor: GrpcBidiResponse-driven (without completion callback).- Parameters:
methodDescriptor- the resolved gRPC method descriptorconverter- JSON/protobuf converter for the method's message typesconfig- the GrpcBidiResponse configuration from the matched expectation
-
GrpcBidiStreamHandler
public GrpcBidiStreamHandler(com.google.protobuf.Descriptors.MethodDescriptor methodDescriptor, GrpcJsonMessageConverter converter, GrpcBidiResponse config, Runnable completionCallback) Phase 3b constructor: GrpcBidiResponse-driven with completion callback. The completion callback is invoked exactly once when the stream finishes (or errors), clearingresponseInProgresson the matched expectation.- Parameters:
methodDescriptor- the resolved gRPC method descriptorconverter- JSON/protobuf converter for the method's message typesconfig- the GrpcBidiResponse configuration from the matched expectationcompletionCallback- invoked once on stream finish to clear responseInProgress
-
-
Method Details
-
handlerAdded
public void handlerAdded(io.netty.channel.ChannelHandlerContext ctx) - Specified by:
handlerAddedin interfaceio.netty.channel.ChannelHandler- Overrides:
handlerAddedin classio.netty.channel.ChannelHandlerAdapter
-
channelRead
- Specified by:
channelReadin interfaceio.netty.channel.ChannelInboundHandler- Overrides:
channelReadin classio.netty.channel.ChannelInboundHandlerAdapter
-
exceptionCaught
- Specified by:
exceptionCaughtin interfaceio.netty.channel.ChannelHandler- Specified by:
exceptionCaughtin interfaceio.netty.channel.ChannelInboundHandler- Overrides:
exceptionCaughtin classio.netty.channel.ChannelInboundHandlerAdapter
-
channelInactive
Handles channel deactivation (client disconnect, connection closed, abandoned stream). If the stream has not already finished viafinish(io.netty.channel.ChannelHandlerContext)orwriteTrailer(io.netty.channel.ChannelHandlerContext, org.mockserver.grpc.GrpcStatusMapper.GrpcStatusCode, java.lang.String), the completion callback is invoked to clearresponseInProgresson the matched expectation. This prevents a times-limited expectation from being stuck forever when a bidi stream is abandoned without a clean END_STREAM.The callback is self-guarded by
callbackInvoked(AtomicBoolean CAS), so it runs exactly once across all terminal paths: normal finish, error trailer, channel inactive, and exception caught.- Specified by:
channelInactivein interfaceio.netty.channel.ChannelInboundHandler- Overrides:
channelInactivein classio.netty.channel.ChannelInboundHandlerAdapter- Throws:
Exception
-