1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package io.netty5.example.http2.helloworld.client;
16
17 import io.netty5.channel.ChannelHandler;
18 import io.netty5.channel.ChannelHandlerContext;
19 import io.netty5.channel.ChannelInitializer;
20 import io.netty5.channel.ChannelPipeline;
21 import io.netty5.channel.socket.SocketChannel;
22 import io.netty5.handler.codec.http.DefaultFullHttpRequest;
23 import io.netty5.handler.codec.http.DefaultHttpContent;
24 import io.netty5.handler.codec.http.HttpClientCodec;
25 import io.netty5.handler.codec.http.HttpClientUpgradeHandler;
26 import io.netty5.handler.codec.http.HttpHeaderNames;
27 import io.netty5.handler.codec.http.HttpMethod;
28 import io.netty5.handler.codec.http.HttpVersion;
29 import io.netty5.handler.codec.http2.DefaultHttp2Connection;
30 import io.netty5.handler.codec.http2.DelegatingDecompressorFrameListener;
31 import io.netty5.handler.codec.http2.Http2ClientUpgradeCodec;
32 import io.netty5.handler.codec.http2.Http2Connection;
33 import io.netty5.handler.codec.http2.Http2FrameLogger;
34 import io.netty5.handler.codec.http2.HttpToHttp2ConnectionHandler;
35 import io.netty5.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
36 import io.netty5.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
37 import io.netty5.handler.ssl.ApplicationProtocolNames;
38 import io.netty5.handler.ssl.ApplicationProtocolNegotiationHandler;
39 import io.netty5.handler.ssl.SslContext;
40
41 import java.net.InetSocketAddress;
42
43 import static io.netty5.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.bufferAllocator(), 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 =
127 new HttpClientUpgradeHandler<DefaultHttpContent>(sourceCodec, upgradeCodec, 65536);
128
129 ch.pipeline().addLast(sourceCodec,
130 upgradeHandler,
131 new UpgradeRequestHandler(),
132 new UserEventLogger());
133 }
134
135
136
137
138 private final class UpgradeRequestHandler implements ChannelHandler {
139
140 @Override
141 public void channelActive(ChannelHandlerContext ctx) throws Exception {
142 DefaultFullHttpRequest upgradeRequest =
143 new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/",
144 ctx.bufferAllocator().allocate(0));
145
146
147 InetSocketAddress remote = (InetSocketAddress) ctx.channel().remoteAddress();
148 String hostString = remote.getHostString();
149 if (hostString == null) {
150 hostString = remote.getAddress().getHostAddress();
151 }
152 upgradeRequest.headers().set(HttpHeaderNames.HOST, hostString + ':' + remote.getPort());
153
154 ctx.writeAndFlush(upgradeRequest);
155
156 ctx.fireChannelActive();
157
158
159 ctx.pipeline().remove(this);
160
161 configureEndOfPipeline(ctx.pipeline());
162 }
163 }
164
165
166
167
168 private static class UserEventLogger implements ChannelHandler {
169 @Override
170 public void channelInboundEvent(ChannelHandlerContext ctx, Object evt) throws Exception {
171 System.out.println("User Event Triggered: " + evt);
172 ctx.fireChannelInboundEvent(evt);
173 }
174 }
175 }