diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractInboundHttp2ToHttpAdapterBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractInboundHttp2ToHttpAdapterBuilder.java new file mode 100644 index 00000000000..47580ed5dbe --- /dev/null +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractInboundHttp2ToHttpAdapterBuilder.java @@ -0,0 +1,132 @@ +/* + * Copyright 2015 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.handler.codec.TooLongFrameException; + +/** + * A skeletal builder implementation of {@link InboundHttp2ToHttpAdapter} and its subtypes. + */ +public abstract class AbstractInboundHttp2ToHttpAdapterBuilder< + T extends InboundHttp2ToHttpAdapter, B extends AbstractInboundHttp2ToHttpAdapterBuilder> { + + private final Http2Connection connection; + private int maxContentLength; + private boolean validateHttpHeaders; + private boolean propagateSettings; + + /** + * Creates a new {@link InboundHttp2ToHttpAdapter} builder for the specified {@link Http2Connection}. + * + * @param connection the object which will provide connection notification events + * for the current connection + */ + protected AbstractInboundHttp2ToHttpAdapterBuilder(Http2Connection connection) { + this.connection = connection; + } + + @SuppressWarnings("unchecked") + protected final B self() { + return (B) this; + } + + /** + * Returns the {@link Http2Connection}. + */ + protected Http2Connection connection() { + return connection; + } + + /** + * Returns the maximum length of the message content. + */ + protected int maxContentLength() { + return maxContentLength; + } + + /** + * Specifies the maximum length of the message content. + * + * @param maxContentLength the maximum length of the message content. If the length of the message content + * exceeds this value, a {@link TooLongFrameException} will be raised + * @return {@link AbstractInboundHttp2ToHttpAdapterBuilder} the builder for the {@link InboundHttp2ToHttpAdapter} + */ + protected B maxContentLength(int maxContentLength) { + this.maxContentLength = maxContentLength; + return self(); + } + + /** + * Return {@code true} if HTTP header validation should be performed. + */ + protected boolean isValidateHttpHeaders() { + return validateHttpHeaders; + } + + /** + * Specifies whether validation of HTTP headers should be performed. + * + * @param validate + * + * @return {@link AbstractInboundHttp2ToHttpAdapterBuilder} the builder for the {@link InboundHttp2ToHttpAdapter} + */ + protected B validateHttpHeaders(boolean validate) { + validateHttpHeaders = validate; + return self(); + } + + /** + * Returns {@code true} if a read settings frame should be propagated along the channel pipeline. + */ + protected boolean isPropagateSettings() { + return propagateSettings; + } + + /** + * Specifies whether a read settings frame should be propagated along the channel pipeline. + * + * @param propagate if {@code true} read settings will be passed along the pipeline. This can be useful + * to clients that need hold off sending data until they have received the settings. + * @return {@link AbstractInboundHttp2ToHttpAdapterBuilder} the builder for the {@link InboundHttp2ToHttpAdapter} + */ + protected B propagateSettings(boolean propagate) { + propagateSettings = propagate; + return self(); + } + + /** + * Builds/creates a new {@link InboundHttp2ToHttpAdapter} instance using this builder's current settings. + */ + protected T build() { + final T instance; + try { + instance = build(connection(), maxContentLength(), + isValidateHttpHeaders(), isPropagateSettings()); + } catch (Throwable t) { + throw new IllegalStateException("failed to create a new InboundHttp2ToHttpAdapter", t); + } + connection.addListener(instance); + return instance; + } + + /** + * Creates a new {@link InboundHttp2ToHttpAdapter} with the specified properties. + */ + protected abstract T build(Http2Connection connection, int maxContentLength, + boolean validateHttpHeaders, boolean propagateSettings) throws Exception; +} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java index 8c22cb70d10..d4ee6bdd684 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java @@ -14,22 +14,22 @@ */ package io.netty.handler.codec.http2; -import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; -import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; -import static io.netty.handler.codec.http2.Http2Exception.connectionError; -import static io.netty.util.internal.ObjectUtil.checkNotNull; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.http.FullHttpMessage; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpStatusClass; +import io.netty.handler.codec.http.HttpUtil; import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectMap; +import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; +import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; +import static io.netty.handler.codec.http2.Http2Exception.connectionError; +import static io.netty.util.internal.ObjectUtil.checkNotNull; + /** * This adapter provides just header/data events from the HTTP message flow defined * here HTTP/2 Spec Message Flow. @@ -67,81 +67,17 @@ public FullHttpMessage copyIfNeeded(FullHttpMessage msg) { protected final IntObjectMap messageMap; private final boolean propagateSettings; - public static class Builder { - - private final Http2Connection connection; - private int maxContentLength; - private boolean validateHttpHeaders; - private boolean propagateSettings; - - /** - * Creates a new {@link InboundHttp2ToHttpAdapter} builder for the specified {@link Http2Connection}. - * - * @param connection The object which will provide connection notification events for the current connection - */ - public Builder(Http2Connection connection) { - this.connection = connection; - } - - /** - * Specifies the maximum length of the message content. - * - * @param maxContentLength the maximum length of the message content. If the length of the message content - * exceeds this value, a {@link TooLongFrameException} will be raised - * @return {@link Builder} the builder for the {@link InboundHttp2ToHttpAdapter} - */ - public Builder maxContentLength(int maxContentLength) { - this.maxContentLength = maxContentLength; - return this; - } - - /** - * Specifies whether validation of HTTP headers should be performed. - * - * @param validate - * - * @return {@link Builder} the builder for the {@link InboundHttp2ToHttpAdapter} - */ - public Builder validateHttpHeaders(boolean validate) { - validateHttpHeaders = validate; - return this; - } - - /** - * Specifies whether a read settings frame should be propagated alone the channel pipeline. - * - * @param propagate if {@code true} read settings will be passed along the pipeline. This can be useful - * to clients that need hold off sending data until they have received the settings. - * @return {@link Builder} the builder for the {@link InboundHttp2ToHttpAdapter} - */ - public Builder propagateSettings(boolean propagate) { - propagateSettings = propagate; - return this; - } - - /** - * Builds/creates a new {@link InboundHttp2ToHttpAdapter} instance using this builders current settings. - */ - public InboundHttp2ToHttpAdapter build() { - InboundHttp2ToHttpAdapter instance = new InboundHttp2ToHttpAdapter(this); - connection.addListener(instance); - return instance; - } - } + protected InboundHttp2ToHttpAdapter(Http2Connection connection, int maxContentLength, + boolean validateHttpHeaders, boolean propagateSettings) { - protected InboundHttp2ToHttpAdapter(Builder builder) { - checkNotNull(builder.connection, "connection"); - if (builder.maxContentLength <= 0) { - throw new IllegalArgumentException("maxContentLength must be a positive integer: " - + builder.maxContentLength); + checkNotNull(connection, "connection"); + if (maxContentLength <= 0) { + throw new IllegalArgumentException("maxContentLength: " + maxContentLength + " (expected: > 0)"); } - connection = builder.connection; - maxContentLength = builder.maxContentLength; - validateHttpHeaders = builder.validateHttpHeaders; - propagateSettings = builder.propagateSettings; + this.connection = connection; + this.maxContentLength = maxContentLength; + this.validateHttpHeaders = validateHttpHeaders; + this.propagateSettings = propagateSettings; sendDetector = DEFAULT_SEND_DETECTOR; messageMap = new IntObjectHashMap(); } @@ -264,7 +200,7 @@ public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int throws Http2Exception { FullHttpMessage msg = messageMap.get(streamId); if (msg == null) { - throw connectionError(PROTOCOL_ERROR, "Data Frame recieved for unknown stream id %d", streamId); + throw connectionError(PROTOCOL_ERROR, "Data Frame received for unknown stream id %d", streamId); } ByteBuf content = msg.content(); @@ -316,7 +252,7 @@ public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promi // A push promise should not be allowed to add headers to an existing stream FullHttpMessage msg = processHeadersBegin(ctx, promisedStreamId, headers, false, false, false); if (msg == null) { - throw connectionError(PROTOCOL_ERROR, "Push Promise Frame recieved for pre-existing stream id %d", + throw connectionError(PROTOCOL_ERROR, "Push Promise Frame received for pre-existing stream id %d", promisedStreamId); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterBuilder.java new file mode 100644 index 00000000000..0cdd84bb14e --- /dev/null +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterBuilder.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015 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; + +/** + * Builds an {@link InboundHttp2ToHttpAdapter}. + */ +public final class InboundHttp2ToHttpAdapterBuilder + extends AbstractInboundHttp2ToHttpAdapterBuilder { + + /** + * Creates a new {@link InboundHttp2ToHttpAdapter} builder for the specified {@link Http2Connection}. + * + * @param connection the object which will provide connection notification events + * for the current connection + */ + public InboundHttp2ToHttpAdapterBuilder(Http2Connection connection) { + super(connection); + } + + @Override + public InboundHttp2ToHttpAdapterBuilder maxContentLength(int maxContentLength) { + return super.maxContentLength(maxContentLength); + } + + @Override + public InboundHttp2ToHttpAdapterBuilder validateHttpHeaders(boolean validate) { + return super.validateHttpHeaders(validate); + } + + @Override + public InboundHttp2ToHttpAdapterBuilder propagateSettings(boolean propagate) { + return super.propagateSettings(propagate); + } + + @Override + public InboundHttp2ToHttpAdapter build() { + return super.build(); + } + + @Override + protected InboundHttp2ToHttpAdapter build(Http2Connection connection, + int maxContentLength, + boolean validateHttpHeaders, + boolean propagateSettings) throws Exception { + + return new InboundHttp2ToHttpAdapter(connection, maxContentLength, + validateHttpHeaders, propagateSettings); + } +} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java index f81229d237c..5ae96dde980 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java @@ -43,27 +43,11 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA HttpConversionUtil.OUT_OF_MESSAGE_SEQUENCE_RETURN_CODE.toString()); private final IntObjectMap outOfMessageFlowHeaders; - public static final class Builder extends InboundHttp2ToHttpAdapter.Builder { - - /** - * Creates a new {@link InboundHttp2ToHttpPriorityAdapter} builder for the specified {@link Http2Connection}. - * - * @param connection The object which will provide connection notification events for the current connection - */ - public Builder(Http2Connection connection) { - super(connection); - } - - @Override - public InboundHttp2ToHttpPriorityAdapter build() { - final InboundHttp2ToHttpPriorityAdapter instance = new InboundHttp2ToHttpPriorityAdapter(this); - instance.connection.addListener(instance); - return instance; - } - } + InboundHttp2ToHttpPriorityAdapter(Http2Connection connection, int maxContentLength, + boolean validateHttpHeaders, + boolean propagateSettings) { - InboundHttp2ToHttpPriorityAdapter(Builder builder) { - super(builder); + super(connection, maxContentLength, validateHttpHeaders, propagateSettings); outOfMessageFlowHeaders = new IntObjectHashMap(); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapterBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapterBuilder.java new file mode 100644 index 00000000000..48575eae709 --- /dev/null +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapterBuilder.java @@ -0,0 +1,64 @@ +/* + * Copyright 2015 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; + +/** + * Builds an {@link InboundHttp2ToHttpPriorityAdapter}. + */ +public final class InboundHttp2ToHttpPriorityAdapterBuilder + extends AbstractInboundHttp2ToHttpAdapterBuilder { + + /** + * Creates a new {@link InboundHttp2ToHttpPriorityAdapter} builder for the specified + * {@link Http2Connection}. + * + * @param connection the object which will provide connection notification events + * for the current connection + */ + public InboundHttp2ToHttpPriorityAdapterBuilder(Http2Connection connection) { + super(connection); + } + + @Override + public InboundHttp2ToHttpPriorityAdapterBuilder maxContentLength(int maxContentLength) { + return super.maxContentLength(maxContentLength); + } + + @Override + public InboundHttp2ToHttpPriorityAdapterBuilder validateHttpHeaders(boolean validate) { + return super.validateHttpHeaders(validate); + } + + @Override + public InboundHttp2ToHttpPriorityAdapterBuilder propagateSettings(boolean propagate) { + return super.propagateSettings(propagate); + } + + @Override + public InboundHttp2ToHttpPriorityAdapter build() { + return super.build(); + } + + @Override + protected InboundHttp2ToHttpPriorityAdapter build(Http2Connection connection, + int maxContentLength, + boolean validateHttpHeaders, + boolean propagateSettings) throws Exception { + + return new InboundHttp2ToHttpPriorityAdapter(connection, maxContentLength, + validateHttpHeaders, propagateSettings); + } +} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java index 9d355a5b3d0..1a5acb86edb 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java @@ -728,7 +728,7 @@ protected void initChannel(Channel ch) throws Exception { p.addLast(new HttpAdapterFrameAdapter( connection, - new InboundHttp2ToHttpPriorityAdapter.Builder(connection) + new InboundHttp2ToHttpPriorityAdapterBuilder(connection) .maxContentLength(maxContentLength) .validateHttpHeaders(true) .propagateSettings(true) @@ -766,7 +766,7 @@ protected void initChannel(Channel ch) throws Exception { p.addLast(new HttpAdapterFrameAdapter( connection, - new InboundHttp2ToHttpPriorityAdapter.Builder(connection) + new InboundHttp2ToHttpPriorityAdapterBuilder(connection) .maxContentLength(maxContentLength) .build(), new CountDownLatch(10))); diff --git a/example/src/main/java/io/netty/example/http2/helloworld/client/Http2ClientInitializer.java b/example/src/main/java/io/netty/example/http2/helloworld/client/Http2ClientInitializer.java index 995a8509537..182c1565043 100644 --- a/example/src/main/java/io/netty/example/http2/helloworld/client/Http2ClientInitializer.java +++ b/example/src/main/java/io/netty/example/http2/helloworld/client/Http2ClientInitializer.java @@ -31,7 +31,7 @@ import io.netty.handler.codec.http2.Http2FrameLogger; import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler; import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder; -import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter; +import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder; import io.netty.handler.ssl.ApplicationProtocolNames; import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler; import io.netty.handler.ssl.SslContext; @@ -59,11 +59,12 @@ public Http2ClientInitializer(SslContext sslCtx, int maxContentLength) { public void initChannel(SocketChannel ch) throws Exception { final Http2Connection connection = new DefaultHttp2Connection(false); connectionHandler = new HttpToHttp2ConnectionHandlerBuilder() - .frameListener(new DelegatingDecompressorFrameListener(connection, - new InboundHttp2ToHttpAdapter.Builder(connection) - .maxContentLength(maxContentLength) - .propagateSettings(true) - .build())) + .frameListener(new DelegatingDecompressorFrameListener( + connection, + new InboundHttp2ToHttpAdapterBuilder(connection) + .maxContentLength(maxContentLength) + .propagateSettings(true) + .build())) .frameLogger(logger) .connection(connection) .build(); diff --git a/example/src/main/java/io/netty/example/http2/tiles/Http2OrHttpHandler.java b/example/src/main/java/io/netty/example/http2/tiles/Http2OrHttpHandler.java index 609ba2671a6..7f568c9462a 100644 --- a/example/src/main/java/io/netty/example/http2/tiles/Http2OrHttpHandler.java +++ b/example/src/main/java/io/netty/example/http2/tiles/Http2OrHttpHandler.java @@ -22,6 +22,7 @@ import io.netty.handler.codec.http2.DefaultHttp2Connection; import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder; import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter; +import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder; import io.netty.handler.ssl.ApplicationProtocolNames; import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler; @@ -54,14 +55,14 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) thr private static void configureHttp2(ChannelHandlerContext ctx) { DefaultHttp2Connection connection = new DefaultHttp2Connection(true); - InboundHttp2ToHttpAdapter listener = new InboundHttp2ToHttpAdapter.Builder(connection) - .propagateSettings(true).validateHttpHeaders(false).maxContentLength(MAX_CONTENT_LENGTH).build(); + InboundHttp2ToHttpAdapter listener = new InboundHttp2ToHttpAdapterBuilder(connection) + .propagateSettings(true).validateHttpHeaders(false) + .maxContentLength(MAX_CONTENT_LENGTH).build(); ctx.pipeline().addLast(new HttpToHttp2ConnectionHandlerBuilder() - .frameListener(listener) - // .frameLogger(TilesHttp2ToHttpHandler.logger) - .connection(connection) - .build()); + .frameListener(listener) + // .frameLogger(TilesHttp2ToHttpHandler.logger) + .connection(connection).build()); ctx.pipeline().addLast(new Http2RequestHandler()); }