View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * copy of the License at:
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software distributed under the License
11   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing permissions and limitations under
13   * the License.
14   */
15  package io.netty.example.http2.client;
16  
17  import static io.netty.util.internal.logging.InternalLogLevel.INFO;
18  import io.netty.channel.ChannelHandlerAdapter;
19  import io.netty.channel.ChannelHandlerContext;
20  import io.netty.channel.ChannelInitializer;
21  import io.netty.channel.ChannelPipeline;
22  import io.netty.channel.socket.SocketChannel;
23  import io.netty.handler.codec.http.DefaultFullHttpRequest;
24  import io.netty.handler.codec.http.HttpClientCodec;
25  import io.netty.handler.codec.http.HttpClientUpgradeHandler;
26  import io.netty.handler.codec.http.HttpMethod;
27  import io.netty.handler.codec.http.HttpVersion;
28  import io.netty.handler.codec.http2.DefaultHttp2Connection;
29  import io.netty.handler.codec.http2.DefaultHttp2FrameReader;
30  import io.netty.handler.codec.http2.DefaultHttp2FrameWriter;
31  import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener;
32  import io.netty.handler.codec.http2.Http2ClientUpgradeCodec;
33  import io.netty.handler.codec.http2.Http2Connection;
34  import io.netty.handler.codec.http2.Http2FrameLogger;
35  import io.netty.handler.codec.http2.Http2FrameReader;
36  import io.netty.handler.codec.http2.Http2FrameWriter;
37  import io.netty.handler.codec.http2.Http2InboundFrameLogger;
38  import io.netty.handler.codec.http2.Http2OutboundFrameLogger;
39  import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler;
40  import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter;
41  import io.netty.handler.ssl.SslContext;
42  import io.netty.util.internal.logging.InternalLoggerFactory;
43  
44  /**
45   * Configures the client pipeline to support HTTP/2 frames.
46   */
47  public class Http2ClientInitializer extends ChannelInitializer<SocketChannel> {
48      private static final Http2FrameLogger logger =
49                      new Http2FrameLogger(INFO, InternalLoggerFactory.getInstance(Http2ClientInitializer.class));
50  
51      private final SslContext sslCtx;
52      private final int maxContentLength;
53      private HttpToHttp2ConnectionHandler connectionHandler;
54      private HttpResponseHandler responseHandler;
55      private Http2SettingsHandler settingsHandler;
56  
57      public Http2ClientInitializer(SslContext sslCtx, int maxContentLength) {
58          this.sslCtx = sslCtx;
59          this.maxContentLength = maxContentLength;
60      }
61  
62      @Override
63      public void initChannel(SocketChannel ch) throws Exception {
64          final Http2Connection connection = new DefaultHttp2Connection(false);
65          final Http2FrameWriter frameWriter = frameWriter();
66          connectionHandler = new HttpToHttp2ConnectionHandler(connection,
67                  frameReader(),
68                  frameWriter,
69                  new DelegatingDecompressorFrameListener(connection,
70                          new InboundHttp2ToHttpAdapter.Builder(connection)
71                                  .maxContentLength(maxContentLength)
72                                  .propagateSettings(true)
73                                  .build()));
74          responseHandler = new HttpResponseHandler();
75          settingsHandler = new Http2SettingsHandler(ch.newPromise());
76          if (sslCtx != null) {
77              configureSsl(ch);
78          } else {
79              configureClearText(ch);
80          }
81      }
82  
83      public HttpResponseHandler responseHandler() {
84          return responseHandler;
85      }
86  
87      public Http2SettingsHandler settingsHandler() {
88          return settingsHandler;
89      }
90  
91      protected void configureEndOfPipeline(ChannelPipeline pipeline) {
92          pipeline.addLast("Http2SettingsHandler", settingsHandler);
93          pipeline.addLast("HttpResponseHandler", responseHandler);
94      }
95  
96      /**
97       * Configure the pipeline for TLS NPN negotiation to HTTP/2.
98       */
99      private void configureSsl(SocketChannel ch) {
100         ChannelPipeline pipeline = ch.pipeline();
101         pipeline.addLast("SslHandler", sslCtx.newHandler(ch.alloc()));
102         pipeline.addLast("Http2Handler", connectionHandler);
103         configureEndOfPipeline(pipeline);
104     }
105 
106     /**
107      * Configure the pipeline for a cleartext upgrade from HTTP to HTTP/2.
108      */
109     private void configureClearText(SocketChannel ch) {
110         HttpClientCodec sourceCodec = new HttpClientCodec();
111         Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(connectionHandler);
112         HttpClientUpgradeHandler upgradeHandler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, 65536);
113 
114         ch.pipeline().addLast("Http2SourceCodec", sourceCodec);
115         ch.pipeline().addLast("Http2UpgradeHandler", upgradeHandler);
116         ch.pipeline().addLast("Http2UpgradeRequestHandler", new UpgradeRequestHandler());
117         ch.pipeline().addLast("Logger", new UserEventLogger());
118     }
119 
120     /**
121      * A handler that triggers the cleartext upgrade to HTTP/2 by sending an initial HTTP request.
122      */
123     private final class UpgradeRequestHandler extends ChannelHandlerAdapter {
124         @Override
125         public void channelActive(ChannelHandlerContext ctx) throws Exception {
126             DefaultFullHttpRequest upgradeRequest =
127                     new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
128             ctx.writeAndFlush(upgradeRequest);
129 
130             super.channelActive(ctx);
131 
132             // Done with this handler, remove it from the pipeline.
133             ctx.pipeline().remove(this);
134 
135             Http2ClientInitializer.this.configureEndOfPipeline(ctx.pipeline());
136         }
137     }
138 
139     /**
140      * Class that logs any User Events triggered on this channel.
141      */
142     private static class UserEventLogger extends ChannelHandlerAdapter {
143         @Override
144         public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
145             System.out.println("User Event Triggered: " + evt);
146             super.userEventTriggered(ctx, evt);
147         }
148     }
149 
150     private static Http2FrameReader frameReader() {
151         return new Http2InboundFrameLogger(new DefaultHttp2FrameReader(), logger);
152     }
153 
154     private static Http2FrameWriter frameWriter() {
155         return new Http2OutboundFrameLogger(new DefaultHttp2FrameWriter(), logger);
156     }
157 }