From 481cd473c25d7d84a19779ece4ff5b56b2ce6fc4 Mon Sep 17 00:00:00 2001 From: Scott Mitchell Date: Fri, 11 Sep 2015 09:56:00 -0700 Subject: [PATCH] HTTP/2 defines using String instead of CharSequence Motivation: Http2CodecUtils has some static variables which are defined as Strings instead of CharSequence. One of these defines is used as a header name and should be AsciiString. Modifications: - Change the String defines in Http2CodecUtils to CharSequence Result: Types are more consistently using CharSequence and adding the upgrade header will require less work. --- .../codec/http/HttpClientUpgradeHandler.java | 8 ++-- .../handler/codec/http/HttpHeaderValues.java | 4 ++ .../codec/http/HttpServerUpgradeHandler.java | 40 ++++++++++--------- .../codec/http2/Http2ClientUpgradeCodec.java | 10 ++--- .../handler/codec/http2/Http2CodecUtil.java | 7 ++-- .../codec/http2/Http2ServerUpgradeCodec.java | 4 +- .../main/java/io/netty/util/AsciiString.java | 37 +++++++++++++++++ .../server/Http2ServerInitializer.java | 5 ++- 8 files changed, 81 insertions(+), 34 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java index 4e29c5084e..093b35f76d 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java @@ -73,13 +73,13 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator { /** * Returns the name of the protocol supported by this codec, as indicated by the {@code 'UPGRADE'} header. */ - String protocol(); + CharSequence protocol(); /** * Sets any protocol-specific headers required to the upgrade request. Returns the names of * all headers that were added. These headers will be used to populate the CONNECTION header. */ - Collection setUpgradeHeaders(ChannelHandlerContext ctx, HttpRequest upgradeRequest); + Collection setUpgradeHeaders(ChannelHandlerContext ctx, HttpRequest upgradeRequest); /** * Performs an HTTP protocol upgrade from the source codec. This method is responsible for @@ -219,12 +219,12 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator { request.headers().set(HttpHeaderNames.UPGRADE, upgradeCodec.protocol()); // Add all protocol-specific headers to the request. - Set connectionParts = new LinkedHashSet(2); + Set connectionParts = new LinkedHashSet(2); connectionParts.addAll(upgradeCodec.setUpgradeHeaders(ctx, request)); // Set the CONNECTION header from the set of all protocol-specific headers that were added. StringBuilder builder = new StringBuilder(); - for (String part : connectionParts) { + for (CharSequence part : connectionParts) { builder.append(part); builder.append(','); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java index f2d1c91a9f..9c75473acc 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java @@ -156,6 +156,10 @@ public final class HttpHeaderValues { * {@code "none"} */ public static final AsciiString NONE = new AsciiString("none"); + /** + * {@code "0"} + */ + public static final AsciiString ZERO = new AsciiString("0"); /** * {@code "only-if-cached"} */ diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java index b6b50b29a2..3d47c443c1 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java @@ -14,6 +14,10 @@ */ package io.netty.handler.codec.http; +import static io.netty.util.AsciiString.containsContentEqualsIgnoreCase; +import static io.netty.util.AsciiString.containsAllContentEqualsIgnoreCase; + +import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; @@ -53,7 +57,7 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { * Gets all protocol-specific headers required by this protocol for a successful upgrade. * Any supplied header will be required to appear in the {@link HttpHeaderNames#CONNECTION} header as well. */ - Collection requiredUpgradeHeaders(); + Collection requiredUpgradeHeaders(); /** * Adds any headers to the 101 Switching protocols response that are appropriate for this protocol. @@ -87,7 +91,7 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { * * @return a new {@link UpgradeCodec}, or {@code null} if the specified protocol name is not supported */ - UpgradeCodec newUpgradeCodec(String protocol); + UpgradeCodec newUpgradeCodec(CharSequence protocol); } /** @@ -96,10 +100,10 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { * (if required) can be sent using the new protocol. */ public static final class UpgradeEvent implements ReferenceCounted { - private final String protocol; + private final CharSequence protocol; private final FullHttpRequest upgradeRequest; - private UpgradeEvent(String protocol, FullHttpRequest upgradeRequest) { + private UpgradeEvent(CharSequence protocol, FullHttpRequest upgradeRequest) { this.protocol = protocol; this.upgradeRequest = upgradeRequest; } @@ -107,7 +111,7 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { /** * The protocol that the channel has been upgraded to. */ - public String protocol() { + public CharSequence protocol() { return protocol; } @@ -163,8 +167,6 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { } } - private static final String UPGRADE_STRING = HttpHeaderNames.UPGRADE.toString(); - private final SourceCodec sourceCodec; private final UpgradeCodecFactory upgradeCodecFactory; private boolean handlingUpgrade; @@ -262,12 +264,12 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { */ private boolean upgrade(final ChannelHandlerContext ctx, final FullHttpRequest request) { // Select the best protocol based on those requested in the UPGRADE header. - final ArrayList requestedProtocols = splitHeader(request.headers().get(HttpHeaderNames.UPGRADE)); + final List requestedProtocols = splitHeader(request.headers().get(HttpHeaderNames.UPGRADE)); final int numRequestedProtocols = requestedProtocols.size(); UpgradeCodec upgradeCodec = null; - String upgradeProtocol = null; + CharSequence upgradeProtocol = null; for (int i = 0; i < numRequestedProtocols; i ++) { - final String p = requestedProtocols.get(i); + final CharSequence p = requestedProtocols.get(i); final UpgradeCodec c = upgradeCodecFactory.newUpgradeCodec(p); if (c != null) { upgradeProtocol = p; @@ -288,9 +290,10 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { } // Make sure the CONNECTION header contains UPGRADE as well as all protocol-specific headers. - Collection requiredHeaders = upgradeCodec.requiredUpgradeHeaders(); - List values = splitHeader(connectionHeader); - if (!values.contains(UPGRADE_STRING) || !values.containsAll(requiredHeaders)) { + Collection requiredHeaders = upgradeCodec.requiredUpgradeHeaders(); + List values = splitHeader(connectionHeader); + if (!containsContentEqualsIgnoreCase(values, HttpHeaderNames.UPGRADE) || + !containsAllContentEqualsIgnoreCase(values, requiredHeaders)) { return false; } @@ -340,11 +343,12 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { /** * Creates the 101 Switching Protocols response message. */ - private static FullHttpResponse createUpgradeResponse(String upgradeProtocol) { - DefaultFullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, SWITCHING_PROTOCOLS); + private static FullHttpResponse createUpgradeResponse(CharSequence upgradeProtocol) { + DefaultFullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, SWITCHING_PROTOCOLS, + Unpooled.EMPTY_BUFFER, false); res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE); res.headers().add(HttpHeaderNames.UPGRADE, upgradeProtocol); - res.headers().add(HttpHeaderNames.CONTENT_LENGTH, "0"); + res.headers().add(HttpHeaderNames.CONTENT_LENGTH, HttpHeaderValues.ZERO); return res; } @@ -352,9 +356,9 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { * Splits a comma-separated header value. The returned set is case-insensitive and contains each * part with whitespace removed. */ - private static ArrayList splitHeader(CharSequence header) { + private static List splitHeader(CharSequence header) { final StringBuilder builder = new StringBuilder(header.length()); - final ArrayList protocols = new ArrayList(4); + final List protocols = new ArrayList(4); for (int i = 0; i < header.length(); ++i) { char c = header.charAt(i); if (Character.isWhitespace(c)) { diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java index dc13efb5ec..7ac962fa9f 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java @@ -41,7 +41,7 @@ import java.util.List; */ public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.UpgradeCodec { - private static final List UPGRADE_HEADERS = Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER); + private static final List UPGRADE_HEADERS = Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER); private final String handlerName; private final Http2ConnectionHandler connectionHandler; @@ -69,14 +69,14 @@ public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.Upgrade } @Override - public String protocol() { + public CharSequence protocol() { return HTTP_UPGRADE_PROTOCOL_NAME; } @Override - public Collection setUpgradeHeaders(ChannelHandlerContext ctx, + public Collection setUpgradeHeaders(ChannelHandlerContext ctx, HttpRequest upgradeRequest) { - String settingsValue = getSettingsHeaderValue(ctx); + CharSequence settingsValue = getSettingsHeaderValue(ctx); upgradeRequest.headers().set(HTTP_UPGRADE_SETTINGS_HEADER, settingsValue); return UPGRADE_HEADERS; } @@ -95,7 +95,7 @@ public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.Upgrade * Converts the current settings for the handler to the Base64-encoded representation used in * the HTTP2-Settings upgrade header. */ - private String getSettingsHeaderValue(ChannelHandlerContext ctx) { + private CharSequence getSettingsHeaderValue(ChannelHandlerContext ctx) { ByteBuf buf = null; ByteBuf encodedBuf = null; try { diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2CodecUtil.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2CodecUtil.java index 107b803d0b..c8d9877ab8 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2CodecUtil.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2CodecUtil.java @@ -23,6 +23,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.channel.DefaultChannelPromise; import io.netty.handler.ssl.ApplicationProtocolNames; +import io.netty.util.AsciiString; import io.netty.util.concurrent.EventExecutor; import static io.netty.buffer.Unpooled.directBuffer; @@ -36,9 +37,9 @@ import static io.netty.util.CharsetUtil.UTF_8; public final class Http2CodecUtil { public static final int CONNECTION_STREAM_ID = 0; public static final int HTTP_UPGRADE_STREAM_ID = 1; - public static final String HTTP_UPGRADE_SETTINGS_HEADER = "HTTP2-Settings"; - public static final String HTTP_UPGRADE_PROTOCOL_NAME = "h2c"; - public static final String TLS_UPGRADE_PROTOCOL_NAME = ApplicationProtocolNames.HTTP_2; + public static final CharSequence HTTP_UPGRADE_SETTINGS_HEADER = new AsciiString("HTTP2-Settings"); + public static final CharSequence HTTP_UPGRADE_PROTOCOL_NAME = "h2c"; + public static final CharSequence TLS_UPGRADE_PROTOCOL_NAME = ApplicationProtocolNames.HTTP_2; public static final int PING_FRAME_PAYLOAD_LENGTH = 8; public static final short MAX_UNSIGNED_BYTE = 0xFF; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java index 9e76a404e3..eccf6f6f21 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java @@ -41,7 +41,7 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull; */ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.UpgradeCodec { - private static final List REQUIRED_UPGRADE_HEADERS = + private static final List REQUIRED_UPGRADE_HEADERS = Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER); private final String handlerName; @@ -72,7 +72,7 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade } @Override - public Collection requiredUpgradeHeaders() { + public Collection requiredUpgradeHeaders() { return REQUIRED_UPGRADE_HEADERS; } diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java index ea6aa6365b..df2a46a4ea 100644 --- a/common/src/main/java/io/netty/util/AsciiString.java +++ b/common/src/main/java/io/netty/util/AsciiString.java @@ -22,6 +22,7 @@ import io.netty.util.internal.PlatformDependent; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -887,6 +888,42 @@ public final class AsciiString extends ByteString implements CharSequence, Compa return true; } + /** + * Determine if {@code collection} contains {@code value} and using + * {@link #contentEqualsIgnoreCase(CharSequence, CharSequence)} to compare values. + * @param collection The collection to look for and equivalent element as {@code value}. + * @param value The value to look for in {@code collection}. + * @return {@code true} if {@code collection} contains {@code value} according to + * {@link #contentEqualsIgnoreCase(CharSequence, CharSequence)}. {@code false} otherwise. + * @see #contentEqualsIgnoreCase(CharSequence, CharSequence) + */ + public static boolean containsContentEqualsIgnoreCase(Collection collection, CharSequence value) { + for (CharSequence v : collection) { + if (contentEqualsIgnoreCase(value, v)) { + return true; + } + } + return false; + } + + /** + * Determine if {@code a} contains all of the values in {@code b} using + * {@link #contentEqualsIgnoreCase(CharSequence, CharSequence)} to compare values. + * @param a The collection under test. + * @param b The values to test for. + * @return {@code true} if {@code a} contains all of the values in {@code b} using + * {@link #contentEqualsIgnoreCase(CharSequence, CharSequence)} to compare values. {@code false} otherwise. + * @see #contentEqualsIgnoreCase(CharSequence, CharSequence) + */ + public static boolean containsAllContentEqualsIgnoreCase(Collection a, Collection b) { + for (CharSequence v : b) { + if (!containsContentEqualsIgnoreCase(a, v)) { + return false; + } + } + return true; + } + /** * Returns {@code true} if the content of both {@link CharSequence}'s are equals. This only supports 8-bit ASCII. */ diff --git a/example/src/main/java/io/netty/example/http2/helloworld/server/Http2ServerInitializer.java b/example/src/main/java/io/netty/example/http2/helloworld/server/Http2ServerInitializer.java index 55173de581..8dee1cd91c 100644 --- a/example/src/main/java/io/netty/example/http2/helloworld/server/Http2ServerInitializer.java +++ b/example/src/main/java/io/netty/example/http2/helloworld/server/Http2ServerInitializer.java @@ -31,6 +31,7 @@ import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; import io.netty.handler.codec.http2.Http2CodecUtil; import io.netty.handler.codec.http2.Http2ServerUpgradeCodec; import io.netty.handler.ssl.SslContext; +import io.netty.util.AsciiString; /** * Sets up the Netty pipeline for the example server. Depending on the endpoint config, sets up the @@ -40,8 +41,8 @@ public class Http2ServerInitializer extends ChannelInitializer { private static final UpgradeCodecFactory upgradeCodecFactory = new UpgradeCodecFactory() { @Override - public UpgradeCodec newUpgradeCodec(String protocol) { - if (Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME.equals(protocol)) { + public UpgradeCodec newUpgradeCodec(CharSequence protocol) { + if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) { return new Http2ServerUpgradeCodec(new HelloWorldHttp2Handler()); } else { return null;