Supply a builder for Http2Codec

Motivation:

DefaultHttp2FrameWriter has constructors that it would be a hassle to
expose as configuration parameters on Http2Codec. We should instead
make a builder for Http2Codec.

Modifications:

Get rid of the public constructors on Http2Codec and instead make sure
you can always use the builder where you would have used the constructor
before.

Result:

Http2Codec can be configured more flexibly, and the SensitivityDetector
can be configured.
This commit is contained in:
Moses Nakamura 2017-04-04 17:19:54 -07:00 committed by Norman Maurer
parent 963cd22a05
commit cf26227c6c
7 changed files with 142 additions and 60 deletions

View File

@ -19,7 +19,6 @@ package io.netty.handler.codec.http2;
import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector; import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS;
import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.util.internal.ObjectUtil.checkNotNull;
import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
@ -79,7 +78,7 @@ public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2Conne
private static final SensitivityDetector DEFAULT_HEADER_SENSITIVITY_DETECTOR = Http2HeadersEncoder.NEVER_SENSITIVE; private static final SensitivityDetector DEFAULT_HEADER_SENSITIVITY_DETECTOR = Http2HeadersEncoder.NEVER_SENSITIVE;
// The properties that can always be set. // The properties that can always be set.
private Http2Settings initialSettings = new Http2Settings().maxHeaderListSize(DEFAULT_HEADER_LIST_SIZE); private Http2Settings initialSettings = Http2Settings.defaultSettings();
private Http2FrameListener frameListener; private Http2FrameListener frameListener;
private long gracefulShutdownTimeoutMillis = DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS; private long gracefulShutdownTimeoutMillis = DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS;

View File

@ -16,70 +16,18 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import static io.netty.handler.logging.LogLevel.INFO;
/** /**
* An HTTP/2 channel handler that adds a {@link Http2FrameCodec} and {@link Http2MultiplexCodec} to the pipeline before * An HTTP/2 channel handler that adds a {@link Http2FrameCodec} and {@link Http2MultiplexCodec} to the pipeline before
* removing itself. * removing itself.
*/ */
@UnstableApi @UnstableApi
public final class Http2Codec extends ChannelDuplexHandler { public final class Http2Codec extends ChannelDuplexHandler {
private static final Http2FrameLogger HTTP2_FRAME_LOGGER = new Http2FrameLogger(INFO, Http2Codec.class);
private final Http2FrameCodec frameCodec; private final Http2FrameCodec frameCodec;
private final Http2MultiplexCodec multiplexCodec; private final Http2MultiplexCodec multiplexCodec;
/**
* Construct a new handler whose child channels run in the same event loop as this handler.
*
* @param server {@code true} this is a server
* @param streamHandler the handler added to channels for remotely-created streams. It must be
* {@link ChannelHandler.Sharable}. {@code null} if the event loop from the parent channel should be used.
*/
public Http2Codec(boolean server, ChannelHandler streamHandler) {
this(server, new Http2StreamChannelBootstrap().handler(streamHandler), HTTP2_FRAME_LOGGER);
}
/**
* Construct a new handler whose child channels run in the same event loop as this handler.
*
* @param server {@code true} this is a server
* @param streamHandler the handler added to channels for remotely-created streams. It must be
* {@link ChannelHandler.Sharable}. {@code null} if the event loop from the parent channel should be used.
* @param initialSettings non default initial settings to send to peer
*/
public Http2Codec(boolean server, ChannelHandler streamHandler, Http2Settings initialSettings) {
this(server, new Http2StreamChannelBootstrap().handler(streamHandler), HTTP2_FRAME_LOGGER,
initialSettings);
}
/**
* Construct a new handler whose child channels run in a different event loop.
*
* @param server {@code true} this is a server
* @param bootstrap bootstrap used to instantiate child channels for remotely-created streams.
*/
public Http2Codec(boolean server, Http2StreamChannelBootstrap bootstrap, Http2FrameLogger frameLogger) {
this(server, bootstrap, new DefaultHttp2FrameWriter(), frameLogger, new Http2Settings());
}
/**
* Construct a new handler whose child channels run in a different event loop.
*
* @param server {@code true} this is a server
* @param bootstrap bootstrap used to instantiate child channels for remotely-created streams.
* @param initialSettings non default initial settings to send to peer
*/
public Http2Codec(boolean server, Http2StreamChannelBootstrap bootstrap, Http2FrameLogger frameLogger,
Http2Settings initialSettings) {
this(server, bootstrap, new DefaultHttp2FrameWriter(), frameLogger, initialSettings);
}
// Visible for testing
Http2Codec(boolean server, Http2StreamChannelBootstrap bootstrap, Http2FrameWriter frameWriter, Http2Codec(boolean server, Http2StreamChannelBootstrap bootstrap, Http2FrameWriter frameWriter,
Http2FrameLogger frameLogger, Http2Settings initialSettings) { Http2FrameLogger frameLogger, Http2Settings initialSettings) {
frameCodec = new Http2FrameCodec(server, frameWriter, frameLogger, initialSettings); frameCodec = new Http2FrameCodec(server, frameWriter, frameLogger, initialSettings);

View File

@ -0,0 +1,130 @@
/*
* Copyright 2017 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.handler.codec.http2;
import io.netty.channel.ChannelHandler;
import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
import io.netty.util.internal.UnstableApi;
import static io.netty.handler.logging.LogLevel.INFO;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* A builder for {@link Http2Codec}.
*/
@UnstableApi
public final class Http2CodecBuilder {
private static final Http2FrameLogger HTTP2_FRAME_LOGGER = new Http2FrameLogger(INFO, Http2Codec.class);
private final Http2StreamChannelBootstrap bootstrap;
private final boolean server;
private Http2Settings initialSettings;
private Http2FrameLogger frameLogger;
private SensitivityDetector headersSensitivityDetector;
/**
* Creates a new {@link Http2Codec} builder.
*
* @param server {@code true} this is a server
* @param streamHandler the handler added to channels for remotely-created streams. It must be
* {@link ChannelHandler.Sharable}. {@code null} if the event loop from the parent channel should be used.
*/
public Http2CodecBuilder(boolean server, ChannelHandler streamHandler) {
this(server, new Http2StreamChannelBootstrap().handler(streamHandler));
}
/**
* Creates a new {@link Http2Codec} builder.
*
* @param server {@code true} this is a server
* @param bootstrap bootstrap used to instantiate child channels for remotely-created streams.
*/
public Http2CodecBuilder(boolean server, Http2StreamChannelBootstrap bootstrap) {
this.bootstrap = checkNotNull(bootstrap, "bootstrap");
this.server = server;
this.initialSettings = Http2Settings.defaultSettings();
this.frameLogger = HTTP2_FRAME_LOGGER;
this.headersSensitivityDetector = null;
}
/**
* Specifies the initial settings to send to peer.
*
* @param initialSettings non default initial settings to send to peer
* @return {@link Http2CodecBuilder} the builder for the {@link Http2Codec}
*/
public Http2CodecBuilder initialSettings(Http2Settings initialSettings) {
this.initialSettings = initialSettings;
return this;
}
/**
* Returns the initial settings to send to peer.
*/
public Http2Settings initialSettings() {
return initialSettings;
}
/**
* Specifies the frame logger to log messages with.
*
* @param frameLogger handler used to log all frames
* @return {@link Http2CodecBuilder} the builder for the {@link Http2Codec}
*/
public Http2CodecBuilder frameLogger(Http2FrameLogger frameLogger) {
this.frameLogger = frameLogger;
return this;
}
/**
* Returns the frame logger to log messages with.
*/
public Http2FrameLogger frameLogger() {
return frameLogger;
}
/**
* Specifies the headers sensitivity detector.
*
* @param headersSensitivityDetector decides whether headers should be considered sensitive or not
* @return {@link Http2CodecBuilder} the builder for the {@link Http2Codec}
*/
public Http2CodecBuilder headersSensitivityDetector(SensitivityDetector headersSensitivityDetector) {
this.headersSensitivityDetector = headersSensitivityDetector;
return this;
}
/**
* Returns the headers sensitivity detector.
*/
public SensitivityDetector headersSensitivityDetector() {
return headersSensitivityDetector;
}
private Http2FrameWriter frameWriter() {
return headersSensitivityDetector() == null ?
new DefaultHttp2FrameWriter() :
new DefaultHttp2FrameWriter(headersSensitivityDetector());
}
/**
* Builds/creates a new {@link Http2Codec} instance using this builder's current settings.
*/
public Http2Codec build() {
return new Http2Codec(server, bootstrap,
frameWriter(), frameLogger(), initialSettings());
}
}

View File

@ -18,6 +18,7 @@ package io.netty.handler.codec.http2;
import io.netty.util.collection.CharObjectHashMap; import io.netty.util.collection.CharObjectHashMap;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_CONCURRENT_STREAMS; import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_CONCURRENT_STREAMS;
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_LIST_SIZE; import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_LIST_SIZE;
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_TABLE_SIZE; import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_TABLE_SIZE;
@ -262,4 +263,8 @@ public final class Http2Settings extends CharObjectHashMap<Long> {
return super.keyToString(key); return super.keyToString(key);
} }
} }
public static Http2Settings defaultSettings() {
return new Http2Settings().maxHeaderListSize(DEFAULT_HEADER_LIST_SIZE);
}
} }

View File

@ -72,7 +72,7 @@ public class Http2CodecTest {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {
serverConnectedChannel = ch; serverConnectedChannel = ch;
ch.pipeline().addLast(new Http2Codec(true, serverLastInboundHandler)); ch.pipeline().addLast(new Http2CodecBuilder(true, serverLastInboundHandler).build());
serverChannelLatch.countDown(); serverChannelLatch.countDown();
} }
}); });
@ -81,7 +81,7 @@ public class Http2CodecTest {
Bootstrap cb = new Bootstrap() Bootstrap cb = new Bootstrap()
.channel(LocalChannel.class) .channel(LocalChannel.class)
.group(group) .group(group)
.handler(new Http2Codec(false, new TestChannelInitializer())); .handler(new Http2CodecBuilder(false, new TestChannelInitializer()).build());
clientChannel = cb.connect(serverAddress).sync().channel(); clientChannel = cb.connect(serverAddress).sync().channel();
assertTrue(serverChannelLatch.await(5, SECONDS)); assertTrue(serverChannelLatch.await(5, SECONDS));
} }

View File

@ -18,7 +18,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.example.http2.helloworld.server.HelloWorldHttp1Handler; import io.netty.example.http2.helloworld.server.HelloWorldHttp1Handler;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http2.Http2Codec; import io.netty.handler.codec.http2.Http2CodecBuilder;
import io.netty.handler.ssl.ApplicationProtocolNames; import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler; import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
@ -37,7 +37,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 Http2Codec(true, new HelloWorldHttp2Handler())); ctx.pipeline().addLast(new Http2CodecBuilder(true, new HelloWorldHttp2Handler()).build());
return; return;
} }

View File

@ -29,7 +29,7 @@ import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpServerUpgradeHandler; import io.netty.handler.codec.http.HttpServerUpgradeHandler;
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec; import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec;
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
import io.netty.handler.codec.http2.Http2Codec; import io.netty.handler.codec.http2.Http2CodecBuilder;
import io.netty.handler.codec.http2.Http2CodecUtil; import io.netty.handler.codec.http2.Http2CodecUtil;
import io.netty.handler.codec.http2.Http2ServerUpgradeCodec; import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
@ -46,7 +46,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 Http2Codec(true, new HelloWorldHttp2Handler())); return new Http2ServerUpgradeCodec(new Http2CodecBuilder(true, new HelloWorldHttp2Handler()).build());
} else { } else {
return null; return null;
} }