blob: 694c07dcece980b491053041ae4edd95ad9ec37f [file] [log] [blame]
package com.google.net.stubby.http2.netty;
import com.google.common.base.Throwables;
import com.google.net.stubby.RequestRegistry;
import com.google.net.stubby.Session;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.GenericFutureListener;
import javax.annotation.Nullable;
import javax.net.ssl.SSLEngine;
/**
* Simple client connection startup that creates a {@link Http2Session} for use
* with protocol bindings.
*/
public class Http2Client {
private final String host;
private final int port;
private final RequestRegistry requestRegistry;
private final SSLEngine sslEngine;
private Channel channel;
public Http2Client(String host, int port, RequestRegistry requestRegistry) {
this(host, port, requestRegistry, null);
}
public Http2Client(String host, int port, RequestRegistry requestRegistry,
@Nullable SSLEngine sslEngine) {
this.host = host;
this.port = port;
this.requestRegistry = requestRegistry;
this.sslEngine = sslEngine;
// TODO(user): NPN support
if (sslEngine != null) {
sslEngine.setUseClientMode(true);
}
}
public Session startAndWait() {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); // (1)
b.group(workerGroup); // (2)
b.channel(NioSocketChannel.class); // (3)
b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
// TODO(user): Evaluate use of pooled allocator
b.option(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT);
final Http2Codec http2Codec = new Http2Codec(requestRegistry);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
if (sslEngine != null) {
// Assume TLS when using SSL
ch.pipeline().addLast(new SslHandler(sslEngine, false));
}
ch.pipeline().addLast(http2Codec);
}
});
// Start the client.
ChannelFuture channelFuture = b.connect(host, port);
// Wait for the connection
channelFuture.sync(); // (5)
channel = channelFuture.channel();
ChannelFuture closeFuture = channel.closeFuture();
closeFuture.addListener(new WorkerCleanupListener(workerGroup));
return new Http2Session(http2Codec.getWriter(), requestRegistry);
} catch (Throwable t) {
workerGroup.shutdownGracefully();
throw Throwables.propagate(t);
}
}
public void stop() {
if (channel != null && channel.isOpen()) {
try {
channel.close().get();
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
channel = null;
}
private static class WorkerCleanupListener
implements GenericFutureListener<io.netty.util.concurrent.Future<Void>> {
private final EventLoopGroup workerGroup;
public WorkerCleanupListener(EventLoopGroup workerGroup) {
this.workerGroup = workerGroup;
}
@Override
public void operationComplete(io.netty.util.concurrent.Future<Void> future) throws Exception {
workerGroup.shutdownGracefully();
}
}
}