781a85520c
Motivation: Adding an example that showcases Netty’s HTTP/2 codec and that is slightly more complex than the existing hello-world example. It is based on the Gopher tiles example available here: https://http2.golang.org/gophertiles?latency=0 Modifications: Moved current http2 example to http2/helloworld. Added http2 tiles example under http2/tiles. Result: A Netty tiles example is available.
157 lines
6.4 KiB
Java
157 lines
6.4 KiB
Java
/*
|
|
* 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 io.netty.example.http2.helloworld.client;
|
|
|
|
import static io.netty.handler.logging.LogLevel.INFO;
|
|
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
|
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.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 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);
|
|
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());
|
|
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 ChannelInboundHandlerAdapter {
|
|
@Override
|
|
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
|
DefaultFullHttpRequest upgradeRequest =
|
|
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
|
|
ctx.writeAndFlush(upgradeRequest);
|
|
|
|
ctx.fireChannelActive();
|
|
|
|
// 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 ChannelInboundHandlerAdapter {
|
|
@Override
|
|
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
|
System.out.println("User Event Triggered: " + evt);
|
|
ctx.fireUserEventTriggered(evt);
|
|
}
|
|
}
|
|
|
|
private static Http2FrameReader frameReader() {
|
|
return new Http2InboundFrameLogger(new DefaultHttp2FrameReader(), logger);
|
|
}
|
|
|
|
private static Http2FrameWriter frameWriter() {
|
|
return new Http2OutboundFrameLogger(new DefaultHttp2FrameWriter(), logger);
|
|
}
|
|
}
|