Class Http2StreamIds

java.lang.Object
org.mockserver.mappers.Http2StreamIds

public final class Http2StreamIds extends Object
The single place that stamps the HTTP/2 stream id onto an outbound Netty response head.

MockServer's HTTP/2 server pipeline uses HttpToHttp2ConnectionHandler, which routes an outbound HttpMessage onto an HTTP/2 stream by reading the x-http2-stream-id extension header. When that header is absent Netty falls back to connection().local().incrementAndGetNextStreamId() — a new, server-initiated stream. A response written on a server-initiated stream is never delivered to the client that made the request, so the client simply hangs until it times out. Nothing is logged and nothing fails, which is why this defect class has repeatedly shipped undetected:

  • GitHub issue #2419 — server-streaming gRPC delivered zero messages over HTTP/2;
  • SSE responses, streaming bodies, the metrics endpoint and MCP — all of which built a Netty response head by hand and wrote it straight to the channel.

Every one of those sites bypassed MockServerHttpResponseToFullHttpResponse, which was the only code that knew about the header. Consolidating the knowledge here means a new direct-write site has one obvious thing to call, and Http2StreamIdAuditHandler makes a site that forgets it fail loudly instead of silently.

All methods first remove any existing x-http2-stream-id header before adding the derived one. That is deliberate: a stream id which leaked in as an ordinary model header (for example from a forwarded or proxied upstream HTTP/2 response) carries a foreign stream id, and writing on it triggers a PROTOCOL_ERROR/GOAWAY that hangs the client just as badly as the missing-header case. The stamped value is therefore always the single source of truth, and stamping is idempotent.

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final io.netty.util.AsciiString
    x-http2-stream-id — the Netty HTTP/1-to-HTTP/2 extension header used to route an outbound response head onto the stream that carried the request.
  • Method Summary

    Modifier and Type
    Method
    Description
    static void
    stamp(io.netty.handler.codec.http.HttpMessage nettyMessage, Integer streamId)
    Stamp streamId onto nettyMessage, replacing any header already present.
    static void
    stampFromNettyRequest(io.netty.handler.codec.http.HttpMessage nettyMessage, io.netty.handler.codec.http.HttpMessage inboundNettyRequest)
    Stamp the stream id carried by an inbound Netty request onto the outbound Netty response head.
    static void
    stampFromRequest(io.netty.handler.codec.http.HttpMessage nettyMessage, HttpRequest request)
    Stamp the stream id carried by a MockServer model HttpRequest onto the outbound Netty response head, so the response goes back down the stream the request arrived on.
    static Integer
    streamIdOf(io.netty.handler.codec.http.HttpMessage nettyMessage)
    The stream id carried by a Netty message, or null when there is none (HTTP/1.1) or the header value is not a valid integer.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • STREAM_ID_HEADER

      public static final io.netty.util.AsciiString STREAM_ID_HEADER
      x-http2-stream-id — the Netty HTTP/1-to-HTTP/2 extension header used to route an outbound response head onto the stream that carried the request.
  • Method Details

    • stamp

      public static void stamp(io.netty.handler.codec.http.HttpMessage nettyMessage, Integer streamId)
      Stamp streamId onto nettyMessage, replacing any header already present. A null streamId (the HTTP/1.1 case) strips the header and adds nothing, so this is safe to call unconditionally from transport-agnostic code.
    • stampFromRequest

      public static void stampFromRequest(io.netty.handler.codec.http.HttpMessage nettyMessage, HttpRequest request)
      Stamp the stream id carried by a MockServer model HttpRequest onto the outbound Netty response head, so the response goes back down the stream the request arrived on.
    • stampFromNettyRequest

      public static void stampFromNettyRequest(io.netty.handler.codec.http.HttpMessage nettyMessage, io.netty.handler.codec.http.HttpMessage inboundNettyRequest)
      Stamp the stream id carried by an inbound Netty request onto the outbound Netty response head. For handlers that work on raw Netty objects and never build a MockServer model request — on HTTP/2 the inbound request carries x-http2-stream-id, added by InboundHttp2ToHttpAdapter. A no-op on HTTP/1.1, where there is no id to copy.
    • streamIdOf

      public static Integer streamIdOf(io.netty.handler.codec.http.HttpMessage nettyMessage)
      The stream id carried by a Netty message, or null when there is none (HTTP/1.1) or the header value is not a valid integer.