package chapter7.recipe1.client; import static io.netty.util.internal.logging.InternalLogLevel.INFO; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpClientUpgradeHandler; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http2.DefaultHttp2Connection; import io.netty.handler.codec.http2.DefaultHttp2FrameReader; import io.netty.handler.codec.http2.DefaultHttp2FrameWriter; import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener; import io.netty.handler.codec.http2.Http2ClientUpgradeCodec; import io.netty.handler.codec.http2.Http2Connection; import io.netty.handler.codec.http2.Http2FrameLogger; import io.netty.handler.codec.http2.Http2FrameReader; import io.netty.handler.codec.http2.Http2FrameWriter; import io.netty.handler.codec.http2.Http2InboundFrameLogger; import io.netty.handler.codec.http2.Http2OutboundFrameLogger; import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler; import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter; import io.netty.util.internal.logging.InternalLoggerFactory; /** * Configures the client pipeline to support HTTP/2 frames. */ public class Http2ClientInitializer extends ChannelInitializer<SocketChannel> { private static final Http2FrameLogger logger = new Http2FrameLogger(INFO, InternalLoggerFactory.getInstance(Http2ClientInitializer.class)); private final int maxContentLength; private HttpToHttp2ConnectionHandler connectionHandler; private HttpResponseHandler responseHandler; private Http2SettingsHandler settingsHandler; public Http2ClientInitializer(int maxContentLength) { this.maxContentLength = maxContentLength; } @Override public void initChannel(SocketChannel ch) throws Exception { final Http2Connection connection = new DefaultHttp2Connection(false); final Http2FrameWriter frameWriter = frameWriter(); connectionHandler = new HttpToHttp2ConnectionHandler(connection, frameReader(), frameWriter, new DelegatingDecompressorFrameListener(connection, new InboundHttp2ToHttpAdapter.Builder(connection) .maxContentLength(maxContentLength) .propagateSettings(true) .build())); responseHandler = new HttpResponseHandler(); settingsHandler = new Http2SettingsHandler(ch.newPromise()); configureClearText(ch); } public HttpResponseHandler responseHandler() { return responseHandler; } public Http2SettingsHandler settingsHandler() { return settingsHandler; } protected void configureEndOfPipeline(ChannelPipeline pipeline) { pipeline.addLast("Http2SettingsHandler", settingsHandler); pipeline.addLast("HttpResponseHandler", responseHandler); } /** * Configure the pipeline for a cleartext upgrade from HTTP to HTTP/2. */ private void configureClearText(SocketChannel ch) { HttpClientCodec sourceCodec = new HttpClientCodec(); Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(connectionHandler); HttpClientUpgradeHandler upgradeHandler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, 65536); ch.pipeline().addLast("Http2SourceCodec", sourceCodec); ch.pipeline().addLast("Http2UpgradeHandler", upgradeHandler); ch.pipeline().addLast("Http2UpgradeRequestHandler", new UpgradeRequestHandler()); ch.pipeline().addLast("Logger", new UserEventLogger()); } /** * A handler that triggers the cleartext upgrade to HTTP/2 by sending an initial HTTP request. */ private final class UpgradeRequestHandler extends ChannelHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { DefaultFullHttpRequest upgradeRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); ctx.writeAndFlush(upgradeRequest); super.channelActive(ctx); // Done with this handler, remove it from the pipeline. ctx.pipeline().remove(this); Http2ClientInitializer.this.configureEndOfPipeline(ctx.pipeline()); } } /** * Class that logs any User Events triggered on this channel. */ private static class UserEventLogger extends ChannelHandlerAdapter { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { System.out.println("User Event Triggered: " + evt); super.userEventTriggered(ctx, evt); } } private static Http2FrameReader frameReader() { return new Http2InboundFrameLogger(new DefaultHttp2FrameReader(), logger); } private static Http2FrameWriter frameWriter() { return new Http2OutboundFrameLogger(new DefaultHttp2FrameWriter(), logger); } }