Class LenientInboundHttp2StreamFrameCodec
- All Implemented Interfaces:
io.netty.channel.ChannelHandler,io.netty.channel.ChannelInboundHandler,io.netty.channel.ChannelOutboundHandler
Http2StreamFrameToHttpObjectCodec that is lenient on the INBOUND (request) side but
strict on the OUTBOUND (response) side.
Netty's Http2StreamFrameToHttpObjectCodec carries a single validateHeaders flag
that governs both directions of the HTTP/1-object ↔ HTTP/2-frame conversion. On the
gRPC multiplex re-aggregating chain MockServer needs the two directions to differ:
- Inbound (lenient,
validateHeaders=false): a request header value with a leading space, an embedded0x7F(DEL), or a control character must be accepted and reach the matchers rather than being rejected withRST_STREAM(PROTOCOL_ERROR)before matching. This mirrors the shared-connection path, which setsInboundHttp2ToHttpAdapterBuilder.validateHttpHeaders(false). The base class applies this flag indecode/newMessage/newFullMessageviaHttpConversionUtil.toHttpRequest(int, io.netty.handler.codec.http2.Http2Headers, boolean)/toFullHttpRequest. - Outbound (strict): response header NAMES and trailer NAMES must still be
validated exactly as Netty validates them by default, matching the shared-connection path
whose
AbstractHttp2ConnectionHandlerBuilder.isValidateHeaders()defaults totrue. Passingfalseto the base class also silently relaxed this outbound name validation, which this subclass restores.
Only header/trailer NAMES are validated outbound — never values. The base class builds outbound
headers with HttpConversionUtil.toHttp2Headers(..., validateHeaders), which uses the 2-arg
DefaultHttp2Headers(validate, arraySizeHint) constructor: that installs a name validator
(HTTP2_NAME_VALIDATOR) when validate is true and no value validator either way. (The
separate 3-arg DefaultHttp2Headers(validate, validateValues, arraySizeHint) constructor
can install a value validator, but the codec does not use it.) Header names are also
lower-cased before validation, so an uppercase name never trips it — only an illegal token character
(space, control char, DEL, separator, non-ASCII) does.
How the strict outbound check is re-asserted: rather than re-implementing the RFC
token rule by hand (which would drift from Netty across upgrades), this override performs the exact
conversion the base class would have performed with validation on — toHttp2Headers(msg, true)
for a response head and toHttp2Headers(trailingHeaders, true) for non-empty trailers — and
discards the result, letting it throw exactly the Http2Exception the strict path would throw.
The subsequent super.encode then re-does the conversion with validateHeaders=false;
this is one extra header conversion per response head (and per trailer block), a deliberate and
acceptable cost to keep the check faithful to Netty. Per-chunk HttpContent
carries no headers and is not validated.
A response to HEAD is sent as its header block alone, which ends the stream: the base class
would send its body as well, which RFC 9110 section 9.3.2 does not allow. The header block keeps the
content-length a GET would be sent, as Netty's HttpServerCodec does on HTTP/1.1.
-
Nested Class Summary
Nested classes/interfaces inherited from interface io.netty.channel.ChannelHandler
io.netty.channel.ChannelHandler.Sharable -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprotected voiddecode(io.netty.channel.ChannelHandlerContext ctx, io.netty.handler.codec.http2.Http2StreamFrame frame, List<Object> out) protected voidencode(io.netty.channel.ChannelHandlerContext ctx, io.netty.handler.codec.http.HttpObject obj, List<Object> out) voidexceptionCaught(io.netty.channel.ChannelHandlerContext ctx, Throwable cause) A stream's own error, trailers overmaxHeaderSizeamong them, reaches the stream as an exception, already logged, and Netty then resets the stream with the error's code.voiduserEventTriggered(io.netty.channel.ChannelHandlerContext ctx, Object evt) voidwrite(io.netty.channel.ChannelHandlerContext ctx, Object msg, io.netty.channel.ChannelPromise promise) Methods inherited from class io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec
acceptInboundMessage, handlerAdded, isSslMethods inherited from class io.netty.handler.codec.MessageToMessageCodec
acceptOutboundMessage, channelRead, channelReadCompleteMethods inherited from class io.netty.channel.ChannelDuplexHandler
bind, close, connect, deregister, disconnect, flush, readMethods inherited from class io.netty.channel.ChannelInboundHandlerAdapter
channelActive, channelInactive, channelRegistered, channelUnregistered, channelWritabilityChangedMethods 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
-
LenientInboundHttp2StreamFrameCodec
public LenientInboundHttp2StreamFrameCodec()
-
-
Method Details
-
decode
protected void decode(io.netty.channel.ChannelHandlerContext ctx, io.netty.handler.codec.http2.Http2StreamFrame frame, List<Object> out) throws Exception - Overrides:
decodein classio.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec- Throws:
Exception
-
write
public void write(io.netty.channel.ChannelHandlerContext ctx, Object msg, io.netty.channel.ChannelPromise promise) throws Exception - Specified by:
writein interfaceio.netty.channel.ChannelOutboundHandler- Overrides:
writein classio.netty.handler.codec.MessageToMessageCodec<io.netty.handler.codec.http2.Http2StreamFrame,io.netty.handler.codec.http.HttpObject> - Throws:
Exception
-
encode
protected void encode(io.netty.channel.ChannelHandlerContext ctx, io.netty.handler.codec.http.HttpObject obj, List<Object> out) throws Exception - Overrides:
encodein classio.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec- Throws:
Exception
-
exceptionCaught
public void exceptionCaught(io.netty.channel.ChannelHandlerContext ctx, Throwable cause) throws Exception A stream's own error, trailers overmaxHeaderSizeamong them, reaches the stream as an exception, already logged, and Netty then resets the stream with the error's code. Do not pass it on: a handler further on logs it again and closes the stream itself, which resets it withCANCEL; for a header block sent after the request ended, on a stream whose whole response has been written, that close sends no reset at all.- Specified by:
exceptionCaughtin interfaceio.netty.channel.ChannelHandler- Specified by:
exceptionCaughtin interfaceio.netty.channel.ChannelInboundHandler- Overrides:
exceptionCaughtin classio.netty.channel.ChannelInboundHandlerAdapter- Throws:
Exception
-
userEventTriggered
public void userEventTriggered(io.netty.channel.ChannelHandlerContext ctx, Object evt) throws Exception - Specified by:
userEventTriggeredin interfaceio.netty.channel.ChannelInboundHandler- Overrides:
userEventTriggeredin classio.netty.channel.ChannelInboundHandlerAdapter- Throws:
Exception
-