Skip to content

Commit

Permalink
h2c support for new Http2ServerHandler (#10663)
Browse files Browse the repository at this point in the history
* Implement de/compression in Http2ServerHandler
- Decompression implemented through DelegatingDecompressorFrameListener because backpressure is a bit awkward otherwise
- Compression implemented through custom code because we need it for the compression config options we provide
- Merged DecompressionSpec into CompressionSpec
- Moved CompressionSpec to micronaut HTTP client (for HTTP/3 support)
- Added subclasses of CompressionSpec for HTTP/2 and HTTP/3

* checkstyle

* h2c support

* fix checkstyleMain errors

---------

Co-authored-by: Sergio del Amo <sergio.delamo@softamo.com>
  • Loading branch information
yawkat and sdelamo committed Mar 29, 2024
1 parent 475515f commit 8aeb065
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.micronaut.http.ssl.ServerSslConfiguration;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOutboundHandler;
Expand Down Expand Up @@ -498,26 +499,48 @@ void configureForH2cSupport() {

// todo: move to Http2ServerHandler (upgrade request is a bit difficult)

final Http2FrameCodec connectionHandler = createHttp2FrameCodec();
final Http2FrameCodec frameCodec;
final Http2ConnectionHandler connectionHandler;
if (server.getServerConfiguration().isLegacyMultiplexHandlers()) {
frameCodec = createHttp2FrameCodec();
connectionHandler = frameCodec;
} else {
connectionHandler = createHttp2ServerHandler(false);
frameCodec = null;
}
final String fallbackHandlerName = "http1-fallback-handler";
HttpServerUpgradeHandler.UpgradeCodecFactory upgradeCodecFactory = protocol -> {
if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) {
class Http2ServerUpgradeCodecImpl extends Http2ServerUpgradeCodec {
public Http2ServerUpgradeCodecImpl(Http2ConnectionHandler connectionHandler) {
super(connectionHandler);
}

return new Http2ServerUpgradeCodec(connectionHandler, new Http2MultiplexHandler(new ChannelInitializer<Http2StreamChannel>() {
@Override
protected void initChannel(@NonNull Http2StreamChannel ch) {
StreamPipeline streamPipeline = new StreamPipeline(ch, sslHandler, connectionCustomizer.specializeForChannel(ch, NettyServerCustomizer.ChannelRole.REQUEST_STREAM));
streamPipeline.insertHttp2FrameHandlers();
streamPipeline.streamCustomizer.onStreamPipelineBuilt();
public Http2ServerUpgradeCodecImpl(Http2FrameCodec http2Codec, ChannelHandler... handlers) {
super(http2Codec, handlers);
}
})) {

@Override
public void upgradeTo(ChannelHandlerContext ctx, FullHttpRequest upgradeRequest) {
super.upgradeTo(ctx, upgradeRequest);
pipeline.remove(fallbackHandlerName);
new StreamPipeline(channel, sslHandler, connectionCustomizer).afterHttp2ServerHandlerSetUp();
onRequestPipelineBuilt();
}
};
}

if (frameCodec == null) {
return new Http2ServerUpgradeCodecImpl(connectionHandler);
} else {
return new Http2ServerUpgradeCodecImpl(frameCodec, new Http2MultiplexHandler(new ChannelInitializer<Http2StreamChannel>() {
@Override
protected void initChannel(@NonNull Http2StreamChannel ch) {
StreamPipeline streamPipeline = new StreamPipeline(ch, sslHandler, connectionCustomizer.specializeForChannel(ch, NettyServerCustomizer.ChannelRole.REQUEST_STREAM));
streamPipeline.insertHttp2FrameHandlers();
streamPipeline.streamCustomizer.onStreamPipelineBuilt();
}
}));
}
} else {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public class NettyHttpRequest<T> extends AbstractNettyHttpRequest<T> implements
* ONLY for NettyBodyAnnotationBinder use.
*/
@Internal
@SuppressWarnings("VisibilityModifier")
public ArgumentBinder.BindingResult<ConvertibleValues<?>> convertibleBody;

private final NettyHttpHeaders headers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
import io.netty.handler.codec.http2.AbstractHttp2ConnectionHandlerBuilder;
import io.netty.handler.codec.http2.DefaultHttp2Connection;
import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener;
Expand Down Expand Up @@ -228,6 +230,22 @@ protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
return true;
});
}

@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof HttpServerUpgradeHandler.UpgradeEvent upgrade) {
FullHttpRequest fhr = upgrade.upgradeRequest();
io.netty.handler.codec.http2.Http2Stream cs = connection().stream(1);
Http2ServerHandler.Http2Stream stream = handler.new Http2Stream(cs);
cs.setProperty(handler.streamKey, stream);
boolean empty = !fhr.content().isReadable();
stream.onHeadersRead(fhr, empty);
if (!empty) {
stream.onDataRead(fhr.content(), true);
}
}
super.userEventTriggered(ctx, evt);
}
}

/**
Expand Down Expand Up @@ -287,6 +305,10 @@ private final class Http2Stream extends MultiplexedStream {

@Override
void notifyDataConsumed(int n) {
if (stream.id() == 1) {
// ignore for upgrade stream
return;
}
try {
connectionHandler.connection().local().flowController().consumeBytes(stream, n);
} catch (Http2Exception e) {
Expand Down

0 comments on commit 8aeb065

Please sign in to comment.