Http2ConnectionHandler Builder instead of constructors

Motivation:
Using the builder pattern for Http2ConnectionHandler (and subclasses) would be advantageous for the following reasons:
1. Provides the consistent construction afforded by the builder pattern for 'optional' arguments. Users can specify these options 1 time in the builder and then re-use the builder after this.
2. Enforces that the Http2ConnectionHandler's internals (decoder Http2FrameListener) are initialized after construction.

Modifications:
- Add an extensible builder which can be used to build Http2ConnectionHandler objects
- Update classes which inherit from Http2ConnectionHandler

Result:
It is easier to specify options and construct Http2ConnectionHandler objects.
This commit is contained in:
Scott Mitchell 2015-09-26 17:44:11 -07:00
parent 179cd9a4a1
commit 284e3702d8
16 changed files with 272 additions and 165 deletions

View File

@ -51,6 +51,11 @@ public class DecoratingHttp2ConnectionDecoder implements Http2ConnectionDecoder
delegate.frameListener(listener); delegate.frameListener(listener);
} }
@Override
public Http2FrameListener frameListener() {
return delegate.frameListener();
}
@Override @Override
public void decodeFrame(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Http2Exception { public void decodeFrame(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Http2Exception {
delegate.decodeFrame(ctx, in, out); delegate.decodeFrame(ctx, in, out);

View File

@ -87,6 +87,11 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
this.listener = checkNotNull(listener, "listener"); this.listener = checkNotNull(listener, "listener");
} }
@Override
public Http2FrameListener frameListener() {
return listener;
}
@Override @Override
public boolean prefaceReceived() { public boolean prefaceReceived() {
return FrameReadListener.class == internalFrameListener.getClass(); return FrameReadListener.class == internalFrameListener.getClass();

View File

@ -50,6 +50,11 @@ public interface Http2ConnectionDecoder extends Closeable {
*/ */
void frameListener(Http2FrameListener listener); void frameListener(Http2FrameListener listener);
/**
* Get the {@link Http2FrameListener} which will be notified when frames are decoded.
*/
Http2FrameListener frameListener();
/** /**
* Called by the {@link Http2ConnectionHandler} to decode the next frame from the input buffer. * Called by the {@link Http2ConnectionHandler} to decode the next frame from the input buffer.
*/ */

View File

@ -35,6 +35,7 @@ import java.util.concurrent.TimeUnit;
import static io.netty.buffer.ByteBufUtil.hexDump; import static io.netty.buffer.ByteBufUtil.hexDump;
import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_STREAM_ID; import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_STREAM_ID;
import static io.netty.handler.codec.http2.Http2CodecUtil.SMALLEST_MAX_CONCURRENT_STREAMS;
import static io.netty.handler.codec.http2.Http2CodecUtil.connectionPrefaceBuf; import static io.netty.handler.codec.http2.Http2CodecUtil.connectionPrefaceBuf;
import static io.netty.handler.codec.http2.Http2CodecUtil.getEmbeddedHttp2Exception; import static io.netty.handler.codec.http2.Http2CodecUtil.getEmbeddedHttp2Exception;
import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
@ -69,61 +70,191 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
private final Http2Settings initialSettings; private final Http2Settings initialSettings;
private ChannelFutureListener closeListener; private ChannelFutureListener closeListener;
private BaseDecoder byteDecoder; private BaseDecoder byteDecoder;
private long gracefulShutdownTimeoutMillis = DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS; private long gracefulShutdownTimeoutMillis;
public Http2ConnectionHandler(boolean server) { /**
this(server, true); * Builder which builds {@link Http2ConnectionHandler} objects.
*/
public static final class Builder extends BuilderBase<Http2ConnectionHandler, Builder> {
@Override
public Http2ConnectionHandler build0(Http2ConnectionDecoder decoder,
Http2ConnectionEncoder encoder) {
return new Http2ConnectionHandler(decoder, encoder, initialSettings());
} }
public Http2ConnectionHandler(boolean server, boolean validateHeaders) {
this(new DefaultHttp2Connection(server), validateHeaders);
}
public Http2ConnectionHandler(Http2Connection connection) {
this(connection, true);
}
public Http2ConnectionHandler(Http2Connection connection, boolean validateHeaders) {
this(connection, new DefaultHttp2FrameReader(validateHeaders), new DefaultHttp2FrameWriter());
}
public Http2ConnectionHandler(Http2Connection connection, Http2FrameReader frameReader,
Http2FrameWriter frameWriter) {
initialSettings = null;
encoder = new DefaultHttp2ConnectionEncoder(connection, frameWriter);
decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, frameReader);
} }
/** /**
* Constructor for pre-configured encoder and decoder. Just sets the {@code this} as the * Base class for a {@code builder} of any subclass of {@link Http2ConnectionHandler}.
* {@link Http2LifecycleManager} and builds them. * @param <T> The type of handler created by this builder.
* @param <B> The concrete type for this builder.
*/ */
public Http2ConnectionHandler(Http2ConnectionDecoder decoder, public abstract static class BuilderBase<T extends Http2ConnectionHandler, B extends BuilderBase<T, B>> {
Http2ConnectionEncoder encoder) { private Http2Settings initialSettings = new Http2Settings();
this.initialSettings = null; private Http2FrameListener frameListener;
this.decoder = checkNotNull(decoder, "decoder"); private Http2FrameLogger frameLogger;
this.encoder = checkNotNull(encoder, "encoder"); private boolean validateHeaders = true;
if (encoder.connection() != decoder.connection()) { private boolean server = true;
throw new IllegalArgumentException("Encoder and Decoder do not share the same connection object"); private int encoderMaxConcurrentStreams = SMALLEST_MAX_CONCURRENT_STREAMS;
private long gracefulShutdownTimeoutMillis = DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS;
/**
* Sets the listener for inbound frames.
* This listener will only be set if the decoder's listener is {@code null}.
*/
public B frameListener(Http2FrameListener listener) {
frameListener = listener;
return thisB();
}
/**
* Determine if HTTP headers should be validated according to
* <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
*/
public B validateHeaders(boolean validate) {
validateHeaders = validate;
return thisB();
}
/**
* Get if HTTP headers should be validated according to
* <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.6">RFC 7540, 8.1.2.6</a>.
*/
public final boolean isValidateHeaders() {
return validateHeaders;
}
/**
* Settings to use for the initial connection settings exchange.
*/
public B initialSettings(Http2Settings settings) {
initialSettings = settings;
return thisB();
}
/**
* Get the settings to use for the initial connection settings exchange.
*/
public final Http2Settings initialSettings() {
return initialSettings;
}
/**
* Determines if {@link #build()} will to create a {@link Http2Connection} in server mode ({@code true})
* or client mode ({@code false}).
*/
public B server(boolean isServer) {
server = isServer;
return thisB();
}
/**
* Set the logger that is used for the encoder and decoder.
*/
public B frameLogger(Http2FrameLogger logger) {
frameLogger = logger;
return thisB();
}
public B gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
this.gracefulShutdownTimeoutMillis = gracefulShutdownTimeoutMillis;
return thisB();
}
/**
* Determine if the encoder should queue frames to honor the value set by
* {@link #encoderMaxConcurrentStreams(int)}.
*/
public B encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) {
encoderMaxConcurrentStreams = -1;
return thisB();
}
private boolean encoderEnforceMaxConcurrentStreams() {
return encoderMaxConcurrentStreams >= 0;
}
/**
* How many initial streams are allowed to exists concurrently. Frames will be queued if they would result in
* creating a stream which would cause the number of existing streams to exceed this number.
* @see #encoderEnforceMaxConcurrentStreams(boolean)
*/
public B encoderMaxConcurrentStreams(int encoderMaxConcurrentStreams) {
// This bounds are enforced here because the builder makes assumptions about its valid range to determine
// if it should be used.
if (encoderMaxConcurrentStreams < 0) {
throw new IllegalArgumentException("encoderMaxConcurrentStreams: " + encoderMaxConcurrentStreams +
" (expected >= 0)");
}
this.encoderMaxConcurrentStreams = encoderMaxConcurrentStreams;
return thisB();
}
/**
* Create a new {@link Http2Connection} and build a new instance.
*/
public final T build() {
return build(new DefaultHttp2Connection(server));
}
/**
* Build a new instance with an existing {@link Http2Connection}.
* <p>
* Methods that will be ignored due to objects already being created:
* <ul><li>{@link #server(boolean)}</li></ul>
*/
public final T build(Http2Connection connection) {
Http2FrameReader reader = new DefaultHttp2FrameReader(validateHeaders);
Http2FrameWriter writer = new DefaultHttp2FrameWriter();
if (frameLogger != null) {
reader = new Http2InboundFrameLogger(reader, frameLogger);
writer = new Http2OutboundFrameLogger(writer, frameLogger);
}
Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, writer);
if (encoderEnforceMaxConcurrentStreams()) {
encoder = new StreamBufferingEncoder(encoder, encoderMaxConcurrentStreams);
}
Http2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, reader);
return build(decoder, encoder);
}
/**
* Build a new instance with an existing {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder}.
* <p>
* Methods that will be ignored due to objects already being created:
* <ul><li>{@link #server(boolean)}</li><li>{@link #validateHttp2Headers(boolean)}</li><li>
* {@link #frameLogger(Http2FrameLogger)}</li><li>{@link #encoderEnforceMaxConcurrentStreams(boolean)}</li><li>
* {@link #encoderMaxConcurrentStreams(int)}</li></ul>
*/
public final T build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
// Call the abstract build method
T handler = build0(decoder, encoder);
// Setup post build options
handler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
if (handler.decoder().frameListener() == null) {
handler.decoder().frameListener(frameListener);
}
return handler;
}
/**
* Sub classes should override this to instantiate the concrete type.
* <p>
* The return of this method will be subject to the following:
* <ul><li>{@link #frameListener(Http2FrameListener)} will be set if not already set in the decoder</li><li>
* {@link #gracefulShutdownTimeoutMillis(long)} will be set</li></ul>
*/
protected abstract T build0(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder);
@SuppressWarnings("unchecked")
protected B thisB() {
return (B) this;
} }
} }
public Http2ConnectionHandler(Http2Connection connection, Http2Settings initialSettings) { protected Http2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
this(connection, new DefaultHttp2FrameReader(), new DefaultHttp2FrameWriter(),
initialSettings);
}
public Http2ConnectionHandler(Http2Connection connection, Http2FrameReader frameReader,
Http2FrameWriter frameWriter, Http2Settings initialSettings) {
this.initialSettings = initialSettings;
encoder = new DefaultHttp2ConnectionEncoder(connection, frameWriter);
decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, frameReader);
}
public Http2ConnectionHandler(Http2ConnectionDecoder decoder,
Http2ConnectionEncoder encoder,
Http2Settings initialSettings) { Http2Settings initialSettings) {
this.initialSettings = initialSettings; this.initialSettings = checkNotNull(initialSettings, "initialSettings");
this.decoder = checkNotNull(decoder, "decoder"); this.decoder = checkNotNull(decoder, "decoder");
this.encoder = checkNotNull(encoder, "encoder"); this.encoder = checkNotNull(encoder, "encoder");
if (encoder.connection() != decoder.connection()) { if (encoder.connection() != decoder.connection()) {
@ -378,7 +509,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
} }
// Both client and server must send their initial settings. // Both client and server must send their initial settings.
encoder.writeSettings(ctx, initialSettings(), ctx.newPromise()).addListener( encoder.writeSettings(ctx, initialSettings, ctx.newPromise()).addListener(
ChannelFutureListener.CLOSE_ON_FAILURE); ChannelFutureListener.CLOSE_ON_FAILURE);
} }
} }
@ -741,13 +872,6 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
} }
} }
/**
* Gets the initial settings to be sent to the remote endpoint.
*/
private Http2Settings initialSettings() {
return initialSettings != null ? initialSettings : decoder.localSettings();
}
/** /**
* Close the remote endpoint with with a {@code GO_AWAY} frame. Does <strong>not</strong> flush * Close the remote endpoint with with a {@code GO_AWAY} frame. Does <strong>not</strong> flush
* immediately, this is the responsibility of the caller. * immediately, this is the responsibility of the caller.

View File

@ -36,43 +36,20 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
private final boolean validateHeaders; private final boolean validateHeaders;
private int currentStreamId; private int currentStreamId;
public HttpToHttp2ConnectionHandler(boolean server) { /**
this(server, true); * Builder which builds {@link HttpToHttp2ConnectionHandler} objects.
} */
public static final class Builder extends BuilderBase<HttpToHttp2ConnectionHandler, Builder> {
public HttpToHttp2ConnectionHandler(boolean server, boolean validateHeaders) { @Override
super(server); public HttpToHttp2ConnectionHandler build0(Http2ConnectionDecoder decoder,
this.validateHeaders = validateHeaders;
}
public HttpToHttp2ConnectionHandler(Http2Connection connection) {
this(connection, true);
}
public HttpToHttp2ConnectionHandler(Http2Connection connection, boolean validateHeaders) {
super(connection);
this.validateHeaders = validateHeaders;
}
public HttpToHttp2ConnectionHandler(Http2Connection connection, Http2FrameReader frameReader,
Http2FrameWriter frameWriter) {
this(connection, frameReader, frameWriter, true);
}
public HttpToHttp2ConnectionHandler(Http2Connection connection, Http2FrameReader frameReader,
Http2FrameWriter frameWriter, boolean validateHeaders) {
super(connection, frameReader, frameWriter);
this.validateHeaders = validateHeaders;
}
public HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder,
Http2ConnectionEncoder encoder) { Http2ConnectionEncoder encoder) {
this(decoder, encoder, true); return new HttpToHttp2ConnectionHandler(decoder, encoder, initialSettings(), isValidateHeaders());
}
} }
public HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, protected HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
Http2ConnectionEncoder encoder, boolean validateHeaders) { Http2Settings initialSettings, boolean validateHeaders) {
super(decoder, encoder); super(decoder, encoder, initialSettings);
this.validateHeaders = validateHeaders; this.validateHeaders = validateHeaders;
} }

View File

@ -300,8 +300,9 @@ public class DataCompressionHttp2Test {
new DefaultHttp2ConnectionEncoder(serverConnection, new DefaultHttp2FrameWriter())); new DefaultHttp2ConnectionEncoder(serverConnection, new DefaultHttp2FrameWriter()));
Http2ConnectionDecoder decoder = Http2ConnectionDecoder decoder =
new DefaultHttp2ConnectionDecoder(serverConnection, encoder, new DefaultHttp2FrameReader()); new DefaultHttp2ConnectionDecoder(serverConnection, encoder, new DefaultHttp2FrameReader());
decoder.frameListener(new DelegatingDecompressorFrameListener(serverConnection, serverListener)); Http2ConnectionHandler connectionHandler = new Http2ConnectionHandler.Builder()
Http2ConnectionHandler connectionHandler = new Http2ConnectionHandler(decoder, encoder); .frameListener(new DelegatingDecompressorFrameListener(serverConnection, serverListener))
.build(decoder, encoder);
p.addLast(connectionHandler); p.addLast(connectionHandler);
serverChannelLatch.countDown(); serverChannelLatch.countDown();
} }
@ -318,11 +319,11 @@ public class DataCompressionHttp2Test {
Http2ConnectionDecoder decoder = Http2ConnectionDecoder decoder =
new DefaultHttp2ConnectionDecoder(clientConnection, clientEncoder, new DefaultHttp2ConnectionDecoder(clientConnection, clientEncoder,
new DefaultHttp2FrameReader()); new DefaultHttp2FrameReader());
decoder.frameListener(new DelegatingDecompressorFrameListener(clientConnection, clientListener)); clientHandler = new Http2ConnectionHandler.Builder()
clientHandler = new Http2ConnectionHandler(decoder, clientEncoder); .frameListener(new DelegatingDecompressorFrameListener(clientConnection, clientListener))
// By default tests don't wait for server to gracefully shutdown streams // By default tests don't wait for server to gracefully shutdown streams
clientHandler.gracefulShutdownTimeoutMillis(0); .gracefulShutdownTimeoutMillis(0)
.build(decoder, clientEncoder);
p.addLast(clientHandler); p.addLast(clientHandler);
} }
}); });

View File

@ -180,7 +180,7 @@ public class Http2ConnectionHandlerTest {
} }
private Http2ConnectionHandler newHandler() throws Exception { private Http2ConnectionHandler newHandler() throws Exception {
Http2ConnectionHandler handler = new Http2ConnectionHandler(decoder, encoder); Http2ConnectionHandler handler = new Http2ConnectionHandler.Builder().build(decoder, encoder);
handler.handlerAdded(ctx); handler.handlerAdded(ctx);
return handler; return handler;
} }

View File

@ -482,9 +482,11 @@ public class Http2ConnectionRoundtripTest {
serverFrameCountDown = serverFrameCountDown =
new FrameCountDown(serverListener, serverSettingsAckLatch, new FrameCountDown(serverListener, serverSettingsAckLatch,
requestLatch, dataLatch, trailersLatch, goAwayLatch); requestLatch, dataLatch, trailersLatch, goAwayLatch);
Http2ConnectionHandler handler = new Http2ConnectionHandler(true, false); p.addLast(new Http2ConnectionHandler.Builder()
handler.decoder().frameListener(serverFrameCountDown); .server(true)
p.addLast(handler); .frameListener(serverFrameCountDown)
.validateHeaders(false)
.build());
} }
}); });
@ -494,9 +496,11 @@ public class Http2ConnectionRoundtripTest {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
ChannelPipeline p = ch.pipeline(); ChannelPipeline p = ch.pipeline();
Http2ConnectionHandler handler = new Http2ConnectionHandler(false, false); p.addLast(new Http2ConnectionHandler.Builder()
handler.decoder().frameListener(clientListener); .server(false)
p.addLast(handler); .frameListener(clientListener)
.validateHeaders(false)
.build());
} }
}); });

View File

@ -487,9 +487,10 @@ public class HttpToHttp2ConnectionHandlerTest {
ChannelPipeline p = ch.pipeline(); ChannelPipeline p = ch.pipeline();
serverFrameCountDown = serverFrameCountDown =
new FrameCountDown(serverListener, serverSettingsAckLatch, requestLatch, null, trailersLatch); new FrameCountDown(serverListener, serverSettingsAckLatch, requestLatch, null, trailersLatch);
HttpToHttp2ConnectionHandler handler = new HttpToHttp2ConnectionHandler(true); p.addLast(new HttpToHttp2ConnectionHandler.Builder()
handler.decoder().frameListener(serverFrameCountDown); .server(true)
p.addLast(handler); .frameListener(serverFrameCountDown)
.build());
} }
}); });
@ -499,9 +500,11 @@ public class HttpToHttp2ConnectionHandlerTest {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
ChannelPipeline p = ch.pipeline(); ChannelPipeline p = ch.pipeline();
HttpToHttp2ConnectionHandler handler = new HttpToHttp2ConnectionHandler(false); HttpToHttp2ConnectionHandler handler = new HttpToHttp2ConnectionHandler.Builder()
handler.decoder().frameListener(clientListener); .server(false)
handler.gracefulShutdownTimeoutMillis(0); .frameListener(clientListener)
.gracefulShutdownTimeoutMillis(0)
.build();
p.addLast(handler); p.addLast(handler);
} }
}); });

View File

@ -111,9 +111,9 @@ public class StreamBufferingEncoderTest {
encoder = new StreamBufferingEncoder(defaultEncoder); encoder = new StreamBufferingEncoder(defaultEncoder);
DefaultHttp2ConnectionDecoder decoder = DefaultHttp2ConnectionDecoder decoder =
new DefaultHttp2ConnectionDecoder(connection, encoder, mock(Http2FrameReader.class)); new DefaultHttp2ConnectionDecoder(connection, encoder, mock(Http2FrameReader.class));
decoder.frameListener(mock(Http2FrameListener.class)); Http2ConnectionHandler handler = new Http2ConnectionHandler.Builder()
.frameListener(mock(Http2FrameListener.class)).build(decoder, encoder);
Http2ConnectionHandler handler = new Http2ConnectionHandler(decoder, encoder);
// Set LifeCycleManager on encoder and decoder // Set LifeCycleManager on encoder and decoder
when(ctx.channel()).thenReturn(channel); when(ctx.channel()).thenReturn(channel);
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);

View File

@ -25,16 +25,10 @@ import io.netty.handler.codec.http.HttpClientUpgradeHandler;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http2.DefaultHttp2Connection; 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.DelegatingDecompressorFrameListener;
import io.netty.handler.codec.http2.Http2ClientUpgradeCodec; import io.netty.handler.codec.http2.Http2ClientUpgradeCodec;
import io.netty.handler.codec.http2.Http2Connection; import io.netty.handler.codec.http2.Http2Connection;
import io.netty.handler.codec.http2.Http2FrameLogger; 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.HttpToHttp2ConnectionHandler;
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter; import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
@ -61,13 +55,14 @@ public class Http2ClientInitializer extends ChannelInitializer<SocketChannel> {
@Override @Override
public void initChannel(SocketChannel ch) throws Exception { public void initChannel(SocketChannel ch) throws Exception {
final Http2Connection connection = new DefaultHttp2Connection(false); final Http2Connection connection = new DefaultHttp2Connection(false);
final Http2FrameWriter frameWriter = frameWriter(); connectionHandler = new HttpToHttp2ConnectionHandler.Builder()
connectionHandler = new HttpToHttp2ConnectionHandler(connection, frameReader(), frameWriter); .frameListener(new DelegatingDecompressorFrameListener(connection,
connectionHandler.decoder().frameListener(new DelegatingDecompressorFrameListener(connection,
new InboundHttp2ToHttpAdapter.Builder(connection) new InboundHttp2ToHttpAdapter.Builder(connection)
.maxContentLength(maxContentLength) .maxContentLength(maxContentLength)
.propagateSettings(true) .propagateSettings(true)
.build())); .build()))
.frameLogger(logger)
.build(connection);
responseHandler = new HttpResponseHandler(); responseHandler = new HttpResponseHandler();
settingsHandler = new Http2SettingsHandler(ch.newPromise()); settingsHandler = new Http2SettingsHandler(ch.newPromise());
if (sslCtx != null) { if (sslCtx != null) {
@ -142,12 +137,4 @@ public class Http2ClientInitializer extends ChannelInitializer<SocketChannel> {
ctx.fireUserEventTriggered(evt); ctx.fireUserEventTriggered(evt);
} }
} }
private static Http2FrameReader frameReader() {
return new Http2InboundFrameLogger(new DefaultHttp2FrameReader(), logger);
}
private static Http2FrameWriter frameWriter() {
return new Http2OutboundFrameLogger(new DefaultHttp2FrameWriter(), logger);
}
} }

View File

@ -19,21 +19,15 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpServerUpgradeHandler; import io.netty.handler.codec.http.HttpServerUpgradeHandler;
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.DefaultHttp2Headers; import io.netty.handler.codec.http2.DefaultHttp2Headers;
import io.netty.handler.codec.http2.Http2Connection; import io.netty.handler.codec.http2.Http2ConnectionDecoder;
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
import io.netty.handler.codec.http2.Http2ConnectionHandler; import io.netty.handler.codec.http2.Http2ConnectionHandler;
import io.netty.handler.codec.http2.Http2Exception; import io.netty.handler.codec.http2.Http2Exception;
import io.netty.handler.codec.http2.Http2Flags; import io.netty.handler.codec.http2.Http2Flags;
import io.netty.handler.codec.http2.Http2FrameListener; import io.netty.handler.codec.http2.Http2FrameListener;
import io.netty.handler.codec.http2.Http2FrameLogger; 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.Http2Headers; import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2InboundFrameLogger;
import io.netty.handler.codec.http2.Http2OutboundFrameLogger;
import io.netty.handler.codec.http2.Http2Settings; import io.netty.handler.codec.http2.Http2Settings;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
@ -47,21 +41,27 @@ import static io.netty.handler.logging.LogLevel.INFO;
/** /**
* A simple handler that responds with the message "Hello World!". * A simple handler that responds with the message "Hello World!".
*/ */
public class HelloWorldHttp2Handler extends Http2ConnectionHandler implements Http2FrameListener { public final class HelloWorldHttp2Handler extends Http2ConnectionHandler implements Http2FrameListener {
private static final Http2FrameLogger logger = new Http2FrameLogger(INFO, HelloWorldHttp2Handler.class); private static final Http2FrameLogger logger = new Http2FrameLogger(INFO, HelloWorldHttp2Handler.class);
static final ByteBuf RESPONSE_BYTES = unreleasableBuffer(copiedBuffer("Hello World", CharsetUtil.UTF_8)); static final ByteBuf RESPONSE_BYTES = unreleasableBuffer(copiedBuffer("Hello World", CharsetUtil.UTF_8));
public HelloWorldHttp2Handler() { public static final class Builder extends BuilderBase<HelloWorldHttp2Handler, Builder> {
this(new DefaultHttp2Connection(true), new Http2InboundFrameLogger( public Builder() {
new DefaultHttp2FrameReader(), logger), new Http2OutboundFrameLogger( frameLogger(logger);
new DefaultHttp2FrameWriter(), logger));
} }
private HelloWorldHttp2Handler(Http2Connection connection, Http2FrameReader frameReader, @Override
Http2FrameWriter frameWriter) { public HelloWorldHttp2Handler build0(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
super(connection, frameReader, frameWriter); HelloWorldHttp2Handler handler = new HelloWorldHttp2Handler(decoder, encoder, initialSettings());
decoder().frameListener(this); frameListener(handler);
return handler;
}
}
private HelloWorldHttp2Handler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
Http2Settings initialSettings) {
super(decoder, encoder, initialSettings);
} }
/** /**

View File

@ -35,7 +35,7 @@ public class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler {
@Override @Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception { protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) { if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
ctx.pipeline().addLast(new HelloWorldHttp2Handler()); ctx.pipeline().addLast(new HelloWorldHttp2Handler.Builder().build());
return; return;
} }

View File

@ -43,7 +43,7 @@ public class Http2ServerInitializer extends ChannelInitializer<SocketChannel> {
@Override @Override
public UpgradeCodec newUpgradeCodec(CharSequence protocol) { public UpgradeCodec newUpgradeCodec(CharSequence protocol) {
if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) { if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) {
return new Http2ServerUpgradeCodec(new HelloWorldHttp2Handler()); return new Http2ServerUpgradeCodec(new HelloWorldHttp2Handler.Builder().build());
} else { } else {
return null; return null;
} }

View File

@ -56,19 +56,13 @@ public class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler {
private static void configureHttp2(ChannelHandlerContext ctx) { private static void configureHttp2(ChannelHandlerContext ctx) {
DefaultHttp2Connection connection = new DefaultHttp2Connection(true); DefaultHttp2Connection connection = new DefaultHttp2Connection(true);
DefaultHttp2FrameWriter writer = new DefaultHttp2FrameWriter();
DefaultHttp2FrameReader reader = new DefaultHttp2FrameReader();
InboundHttp2ToHttpAdapter listener = new InboundHttp2ToHttpAdapter.Builder(connection) InboundHttp2ToHttpAdapter listener = new InboundHttp2ToHttpAdapter.Builder(connection)
.propagateSettings(true).validateHttpHeaders(false).maxContentLength(MAX_CONTENT_LENGTH).build(); .propagateSettings(true).validateHttpHeaders(false).maxContentLength(MAX_CONTENT_LENGTH).build();
HttpToHttp2ConnectionHandler handler = new HttpToHttp2ConnectionHandler( ctx.pipeline().addLast(new HttpToHttp2ConnectionHandler.Builder()
connection, .frameListener(listener)
// Loggers can be activated for debugging purposes // .frameLogger(TilesHttp2ToHttpHandler.logger)
// new Http2InboundFrameLogger(reader, TilesHttp2ToHttpHandler.logger), .build(connection));
// new Http2OutboundFrameLogger(writer, TilesHttp2ToHttpHandler.logger)
reader, writer);
handler.decoder().frameListener(listener);
ctx.pipeline().addLast(handler);
ctx.pipeline().addLast(new Http2RequestHandler()); ctx.pipeline().addLast(new Http2RequestHandler());
} }

View File

@ -263,8 +263,9 @@ public class Http2FrameWriterBenchmark extends AbstractSharedExecutorMicrobenchm
Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, environment.writer()); Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, environment.writer());
Http2ConnectionDecoder decoder = Http2ConnectionDecoder decoder =
new DefaultHttp2ConnectionDecoder(connection, encoder, new DefaultHttp2FrameReader()); new DefaultHttp2ConnectionDecoder(connection, encoder, new DefaultHttp2FrameReader());
decoder.frameListener(new Http2FrameAdapter()); Http2ConnectionHandler connectionHandler = new Http2ConnectionHandler.Builder()
Http2ConnectionHandler connectionHandler = new Http2ConnectionHandler(decoder, encoder); .encoderEnforceMaxConcurrentStreams(false)
.frameListener(new Http2FrameAdapter()).build(decoder, encoder);
p.addLast(connectionHandler); p.addLast(connectionHandler);
environment.context(p.lastContext()); environment.context(p.lastContext());
// Must wait for context to be set. // Must wait for context to be set.
@ -292,8 +293,9 @@ public class Http2FrameWriterBenchmark extends AbstractSharedExecutorMicrobenchm
Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, env.writer()); Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, env.writer());
Http2ConnectionDecoder decoder = Http2ConnectionDecoder decoder =
new DefaultHttp2ConnectionDecoder(connection, encoder, new DefaultHttp2FrameReader()); new DefaultHttp2ConnectionDecoder(connection, encoder, new DefaultHttp2FrameReader());
decoder.frameListener(new Http2FrameAdapter()); Http2ConnectionHandler connectionHandler = new Http2ConnectionHandler.Builder()
Http2ConnectionHandler connectionHandler = new Http2ConnectionHandler(decoder, encoder); .encoderEnforceMaxConcurrentStreams(false)
.frameListener(new Http2FrameAdapter()).build(decoder, encoder);
env.context(new EmbeddedChannelWriteReleaseHandlerContext(alloc, connectionHandler) { env.context(new EmbeddedChannelWriteReleaseHandlerContext(alloc, connectionHandler) {
@Override @Override
protected void handleException(Throwable t) { protected void handleException(Throwable t) {