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-message GrpcStreamMessage.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 to IncrementalGrpcFrameDecoder; 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 has endStream=true, calls finish(ChannelHandlerContext).
  • finish(): writes trailing HEADERS with configured grpc-status and endStream=true. Guarded to run at most once.

The handler supports two modes:

  • Phase 3b (GrpcBidiResponse-driven): Constructed with a GrpcBidiResponse config; eager messages, rules, status, and headers come from the config.
  • Phase 3a (legacy responder function): Constructed with a Function responder 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

    Constructors
    Constructor
    Description
    GrpcBidiStreamHandler(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 Type
    Method
    Description
    void
    channelInactive(io.netty.channel.ChannelHandlerContext ctx)
    Handles channel deactivation (client disconnect, connection closed, abandoned stream).
    void
    channelRead(io.netty.channel.ChannelHandlerContext ctx, Object msg)
     
    void
    exceptionCaught(io.netty.channel.ChannelHandlerContext ctx, Throwable cause)
     
    void
    handlerAdded(io.netty.channel.ChannelHandlerContext ctx)
     

    Methods inherited from class io.netty.channel.ChannelInboundHandlerAdapter

    channelActive, channelReadComplete, channelRegistered, channelUnregistered, channelWritabilityChanged, userEventTriggered

    Methods inherited from class io.netty.channel.ChannelHandlerAdapter

    ensureNotSharable, handlerRemoved, isSharable

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

    Methods 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 descriptor
      converter - JSON/protobuf converter for the method's message types
      responder - 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 descriptor
      converter - JSON/protobuf converter for the method's message types
      config - 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), clearing responseInProgress on the matched expectation.
      Parameters:
      methodDescriptor - the resolved gRPC method descriptor
      converter - JSON/protobuf converter for the method's message types
      config - the GrpcBidiResponse configuration from the matched expectation
      completionCallback - invoked once on stream finish to clear responseInProgress
  • Method Details

    • handlerAdded

      public void handlerAdded(io.netty.channel.ChannelHandlerContext ctx)
      Specified by:
      handlerAdded in interface io.netty.channel.ChannelHandler
      Overrides:
      handlerAdded in class io.netty.channel.ChannelHandlerAdapter
    • channelRead

      public void channelRead(io.netty.channel.ChannelHandlerContext ctx, Object msg)
      Specified by:
      channelRead in interface io.netty.channel.ChannelInboundHandler
      Overrides:
      channelRead in class io.netty.channel.ChannelInboundHandlerAdapter
    • exceptionCaught

      public void exceptionCaught(io.netty.channel.ChannelHandlerContext ctx, Throwable cause)
      Specified by:
      exceptionCaught in interface io.netty.channel.ChannelHandler
      Specified by:
      exceptionCaught in interface io.netty.channel.ChannelInboundHandler
      Overrides:
      exceptionCaught in class io.netty.channel.ChannelInboundHandlerAdapter
    • channelInactive

      public void channelInactive(io.netty.channel.ChannelHandlerContext ctx) throws Exception
      Handles channel deactivation (client disconnect, connection closed, abandoned stream). If the stream has not already finished via finish(io.netty.channel.ChannelHandlerContext) or writeTrailer(io.netty.channel.ChannelHandlerContext, org.mockserver.grpc.GrpcStatusMapper.GrpcStatusCode, java.lang.String), the completion callback is invoked to clear responseInProgress on 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:
      channelInactive in interface io.netty.channel.ChannelInboundHandler
      Overrides:
      channelInactive in class io.netty.channel.ChannelInboundHandlerAdapter
      Throws:
      Exception