/* * This code was copied from HTTP/2 client examples of the Netty repository and modified only package name. */ /* * Copyright 2014 The Netty Project * * The Netty Project licenses this file to you under the Apache License, version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package jmeter.plugins.http2.sampler; import static io.netty.handler.logging.LogLevel.INFO; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; 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.Http2ConnectionHandler; 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.Http2Settings; import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler; import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter; import io.netty.handler.ssl.SslContext; /** * Configures the client pipeline to support HTTP/2 frames. */ public class Http2ClientInitializer extends ChannelInitializer<SocketChannel> { private static final Http2FrameLogger logger = new Http2FrameLogger(INFO, Http2ClientInitializer.class); private final SslContext sslCtx; private final int maxContentLength; /* private HttpToHttp2ConnectionHandler connectionHandler; */ private Http2ConnectionHandler connectionHandler; private HttpResponseHandler responseHandler; private Http2SettingsHandler settingsHandler; public Http2ClientInitializer(SslContext sslCtx, int maxContentLength) { this.sslCtx = sslCtx; this.maxContentLength = maxContentLength; } @Override public void initChannel(SocketChannel ch) throws Exception { final Http2Connection connection = new DefaultHttp2Connection(false); 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()); if (sslCtx != null) { configureSsl(ch); } else { 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 TLS NPN negotiation to HTTP/2. */ private void configureSsl(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("SslHandler", sslCtx.newHandler(ch.alloc())); pipeline.addLast("Http2Handler", connectionHandler); configureEndOfPipeline(pipeline); } /** * 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 Http2FrameReader frameReader() { return new Http2InboundFrameLogger(new DefaultHttp2FrameReader(), logger); } private Http2FrameWriter frameWriter() { // Set initial SETTINGS Http2Settings settings = new Http2Settings(); settings.pushEnabled(false); settings.maxConcurrentStreams(100); return new Http2OutboundFrameLogger(new CustomHttp2FrameWriter(settings), logger); } /** * Custom HTTP/2 frame writer. */ private class CustomHttp2FrameWriter extends DefaultHttp2FrameWriter { private final Http2Settings settings; public CustomHttp2FrameWriter(Http2Settings settings) { this.settings = settings; } /** * write customized SETTINGS */ @Override public ChannelFuture writeSettings(ChannelHandlerContext ctx, Http2Settings settings, ChannelPromise promise) { if(this.settings != null) { return super.writeSettings(ctx, this.settings, promise); } else { return super.writeSettings(ctx, settings, promise); } } } }