1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package io.netty.example.http2.helloworld.client;
16
17 import io.netty.buffer.Unpooled;
18 import io.netty.channel.ChannelHandlerContext;
19 import io.netty.channel.ChannelInboundHandlerAdapter;
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.HttpHeaderNames;
27 import io.netty.handler.codec.http.HttpMethod;
28 import io.netty.handler.codec.http.HttpVersion;
29 import io.netty.handler.codec.http2.DefaultHttp2Connection;
30 import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener;
31 import io.netty.handler.codec.http2.Http2ClientUpgradeCodec;
32 import io.netty.handler.codec.http2.Http2Connection;
33 import io.netty.handler.codec.http2.Http2FrameLogger;
34 import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler;
35 import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
36 import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
37 import io.netty.handler.ssl.ApplicationProtocolNames;
38 import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
39 import io.netty.handler.ssl.SslContext;
40
41 import java.net.InetSocketAddress;
42
43 import static io.netty.handler.logging.LogLevel.INFO;
44
45
46
47
48 public class Http2ClientInitializer extends ChannelInitializer<SocketChannel> {
49 private static final Http2FrameLogger logger = new Http2FrameLogger(INFO, 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 connectionHandler = new HttpToHttp2ConnectionHandlerBuilder()
66 .frameListener(new DelegatingDecompressorFrameListener(
67 connection,
68 new InboundHttp2ToHttpAdapterBuilder(connection)
69 .maxContentLength(maxContentLength)
70 .propagateSettings(true)
71 .build()))
72 .frameLogger(logger)
73 .connection(connection)
74 .build();
75 responseHandler = new HttpResponseHandler();
76 settingsHandler = new Http2SettingsHandler(ch.newPromise());
77 if (sslCtx != null) {
78 configureSsl(ch);
79 } else {
80 configureClearText(ch);
81 }
82 }
83
84 public HttpResponseHandler responseHandler() {
85 return responseHandler;
86 }
87
88 public Http2SettingsHandler settingsHandler() {
89 return settingsHandler;
90 }
91
92 protected void configureEndOfPipeline(ChannelPipeline pipeline) {
93 pipeline.addLast(settingsHandler, responseHandler);
94 }
95
96
97
98
99 private void configureSsl(SocketChannel ch) {
100 ChannelPipeline pipeline = ch.pipeline();
101
102 pipeline.addLast(sslCtx.newHandler(ch.alloc(), Http2Client.HOST, Http2Client.PORT));
103
104
105 pipeline.addLast(new ApplicationProtocolNegotiationHandler("") {
106 @Override
107 protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
108 if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
109 ChannelPipeline p = ctx.pipeline();
110 p.addLast(connectionHandler);
111 configureEndOfPipeline(p);
112 return;
113 }
114 ctx.close();
115 throw new IllegalStateException("unknown protocol: " + protocol);
116 }
117 });
118 }
119
120
121
122
123 private void configureClearText(SocketChannel ch) {
124 HttpClientCodec sourceCodec = new HttpClientCodec();
125 Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(connectionHandler);
126 HttpClientUpgradeHandler upgradeHandler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, 65536);
127
128 ch.pipeline().addLast(sourceCodec,
129 upgradeHandler,
130 new UpgradeRequestHandler(),
131 new UserEventLogger());
132 }
133
134
135
136
137 private final class UpgradeRequestHandler extends ChannelInboundHandlerAdapter {
138
139 @Override
140 public void channelActive(ChannelHandlerContext ctx) throws Exception {
141 DefaultFullHttpRequest upgradeRequest =
142 new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/", Unpooled.EMPTY_BUFFER);
143
144
145 InetSocketAddress remote = (InetSocketAddress) ctx.channel().remoteAddress();
146 String hostString = remote.getHostString();
147 if (hostString == null) {
148 hostString = remote.getAddress().getHostAddress();
149 }
150 upgradeRequest.headers().set(HttpHeaderNames.HOST, hostString + ':' + remote.getPort());
151
152 ctx.writeAndFlush(upgradeRequest);
153
154 ctx.fireChannelActive();
155
156
157 ctx.pipeline().remove(this);
158
159 configureEndOfPipeline(ctx.pipeline());
160 }
161 }
162
163
164
165
166 private static class UserEventLogger extends ChannelInboundHandlerAdapter {
167 @Override
168 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
169 System.out.println("User Event Triggered: " + evt);
170 ctx.fireUserEventTriggered(evt);
171 }
172 }
173 }