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.
This commit is contained in:
Scott Mitchell 2015-09-11 09:56:00 -07:00
parent 1d4d5fe312
commit c7e3f6c6fd
8 changed files with 81 additions and 34 deletions

View File

@ -75,13 +75,13 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator implements Ch
/** /**
* Returns the name of the protocol supported by this codec, as indicated by the {@code 'UPGRADE'} header. * 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 * 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. * all headers that were added. These headers will be used to populate the CONNECTION header.
*/ */
Collection<String> setUpgradeHeaders(ChannelHandlerContext ctx, HttpRequest upgradeRequest); Collection<CharSequence> setUpgradeHeaders(ChannelHandlerContext ctx, HttpRequest upgradeRequest);
/** /**
* Performs an HTTP protocol upgrade from the source codec. This method is responsible for * Performs an HTTP protocol upgrade from the source codec. This method is responsible for
@ -257,12 +257,12 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator implements Ch
request.headers().set(HttpHeaderNames.UPGRADE, upgradeCodec.protocol()); request.headers().set(HttpHeaderNames.UPGRADE, upgradeCodec.protocol());
// Add all protocol-specific headers to the request. // Add all protocol-specific headers to the request.
Set<String> connectionParts = new LinkedHashSet<String>(2); Set<CharSequence> connectionParts = new LinkedHashSet<CharSequence>(2);
connectionParts.addAll(upgradeCodec.setUpgradeHeaders(ctx, request)); connectionParts.addAll(upgradeCodec.setUpgradeHeaders(ctx, request));
// Set the CONNECTION header from the set of all protocol-specific headers that were added. // Set the CONNECTION header from the set of all protocol-specific headers that were added.
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (String part : connectionParts) { for (CharSequence part : connectionParts) {
builder.append(part); builder.append(part);
builder.append(','); builder.append(',');
} }

View File

@ -156,6 +156,10 @@ public final class HttpHeaderValues {
* {@code "none"} * {@code "none"}
*/ */
public static final AsciiString NONE = new AsciiString("none"); public static final AsciiString NONE = new AsciiString("none");
/**
* {@code "0"}
*/
public static final AsciiString ZERO = new AsciiString("0");
/** /**
* {@code "only-if-cached"} * {@code "only-if-cached"}
*/ */

View File

@ -14,6 +14,10 @@
*/ */
package io.netty.handler.codec.http; 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.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; 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. * 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. * Any supplied header will be required to appear in the {@link HttpHeaderNames#CONNECTION} header as well.
*/ */
Collection<String> requiredUpgradeHeaders(); Collection<CharSequence> requiredUpgradeHeaders();
/** /**
* Adds any headers to the 101 Switching protocols response that are appropriate for this protocol. * 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 * @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. * (if required) can be sent using the new protocol.
*/ */
public static final class UpgradeEvent implements ReferenceCounted { public static final class UpgradeEvent implements ReferenceCounted {
private final String protocol; private final CharSequence protocol;
private final FullHttpRequest upgradeRequest; private final FullHttpRequest upgradeRequest;
private UpgradeEvent(String protocol, FullHttpRequest upgradeRequest) { private UpgradeEvent(CharSequence protocol, FullHttpRequest upgradeRequest) {
this.protocol = protocol; this.protocol = protocol;
this.upgradeRequest = upgradeRequest; this.upgradeRequest = upgradeRequest;
} }
@ -107,7 +111,7 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator {
/** /**
* The protocol that the channel has been upgraded to. * The protocol that the channel has been upgraded to.
*/ */
public String protocol() { public CharSequence protocol() {
return 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 SourceCodec sourceCodec;
private final UpgradeCodecFactory upgradeCodecFactory; private final UpgradeCodecFactory upgradeCodecFactory;
private boolean handlingUpgrade; private boolean handlingUpgrade;
@ -262,12 +264,12 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator {
*/ */
private boolean upgrade(final ChannelHandlerContext ctx, final FullHttpRequest request) { private boolean upgrade(final ChannelHandlerContext ctx, final FullHttpRequest request) {
// Select the best protocol based on those requested in the UPGRADE header. // Select the best protocol based on those requested in the UPGRADE header.
final ArrayList<String> requestedProtocols = splitHeader(request.headers().get(HttpHeaderNames.UPGRADE)); final List<CharSequence> requestedProtocols = splitHeader(request.headers().get(HttpHeaderNames.UPGRADE));
final int numRequestedProtocols = requestedProtocols.size(); final int numRequestedProtocols = requestedProtocols.size();
UpgradeCodec upgradeCodec = null; UpgradeCodec upgradeCodec = null;
String upgradeProtocol = null; CharSequence upgradeProtocol = null;
for (int i = 0; i < numRequestedProtocols; i ++) { 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); final UpgradeCodec c = upgradeCodecFactory.newUpgradeCodec(p);
if (c != null) { if (c != null) {
upgradeProtocol = p; 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. // Make sure the CONNECTION header contains UPGRADE as well as all protocol-specific headers.
Collection<String> requiredHeaders = upgradeCodec.requiredUpgradeHeaders(); Collection<CharSequence> requiredHeaders = upgradeCodec.requiredUpgradeHeaders();
List<String> values = splitHeader(connectionHeader); List<CharSequence> values = splitHeader(connectionHeader);
if (!values.contains(UPGRADE_STRING) || !values.containsAll(requiredHeaders)) { if (!containsContentEqualsIgnoreCase(values, HttpHeaderNames.UPGRADE) ||
!containsAllContentEqualsIgnoreCase(values, requiredHeaders)) {
return false; return false;
} }
@ -340,11 +343,12 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator {
/** /**
* Creates the 101 Switching Protocols response message. * Creates the 101 Switching Protocols response message.
*/ */
private static FullHttpResponse createUpgradeResponse(String upgradeProtocol) { private static FullHttpResponse createUpgradeResponse(CharSequence upgradeProtocol) {
DefaultFullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, SWITCHING_PROTOCOLS); DefaultFullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, SWITCHING_PROTOCOLS,
Unpooled.EMPTY_BUFFER, false);
res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE); res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE);
res.headers().add(HttpHeaderNames.UPGRADE, upgradeProtocol); res.headers().add(HttpHeaderNames.UPGRADE, upgradeProtocol);
res.headers().add(HttpHeaderNames.CONTENT_LENGTH, "0"); res.headers().add(HttpHeaderNames.CONTENT_LENGTH, HttpHeaderValues.ZERO);
return res; 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 * Splits a comma-separated header value. The returned set is case-insensitive and contains each
* part with whitespace removed. * part with whitespace removed.
*/ */
private static ArrayList<String> splitHeader(CharSequence header) { private static List<CharSequence> splitHeader(CharSequence header) {
final StringBuilder builder = new StringBuilder(header.length()); final StringBuilder builder = new StringBuilder(header.length());
final ArrayList<String> protocols = new ArrayList<String>(4); final List<CharSequence> protocols = new ArrayList<CharSequence>(4);
for (int i = 0; i < header.length(); ++i) { for (int i = 0; i < header.length(); ++i) {
char c = header.charAt(i); char c = header.charAt(i);
if (Character.isWhitespace(c)) { if (Character.isWhitespace(c)) {

View File

@ -41,7 +41,7 @@ import java.util.List;
*/ */
public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.UpgradeCodec { public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.UpgradeCodec {
private static final List<String> UPGRADE_HEADERS = Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER); private static final List<CharSequence> UPGRADE_HEADERS = Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER);
private final String handlerName; private final String handlerName;
private final Http2ConnectionHandler connectionHandler; private final Http2ConnectionHandler connectionHandler;
@ -69,14 +69,14 @@ public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.Upgrade
} }
@Override @Override
public String protocol() { public CharSequence protocol() {
return HTTP_UPGRADE_PROTOCOL_NAME; return HTTP_UPGRADE_PROTOCOL_NAME;
} }
@Override @Override
public Collection<String> setUpgradeHeaders(ChannelHandlerContext ctx, public Collection<CharSequence> setUpgradeHeaders(ChannelHandlerContext ctx,
HttpRequest upgradeRequest) { HttpRequest upgradeRequest) {
String settingsValue = getSettingsHeaderValue(ctx); CharSequence settingsValue = getSettingsHeaderValue(ctx);
upgradeRequest.headers().set(HTTP_UPGRADE_SETTINGS_HEADER, settingsValue); upgradeRequest.headers().set(HTTP_UPGRADE_SETTINGS_HEADER, settingsValue);
return UPGRADE_HEADERS; 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 * Converts the current settings for the handler to the Base64-encoded representation used in
* the HTTP2-Settings upgrade header. * the HTTP2-Settings upgrade header.
*/ */
private String getSettingsHeaderValue(ChannelHandlerContext ctx) { private CharSequence getSettingsHeaderValue(ChannelHandlerContext ctx) {
ByteBuf buf = null; ByteBuf buf = null;
ByteBuf encodedBuf = null; ByteBuf encodedBuf = null;
try { try {

View File

@ -23,6 +23,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelPromise; import io.netty.channel.DefaultChannelPromise;
import io.netty.handler.ssl.ApplicationProtocolNames; import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.util.AsciiString;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import static io.netty.buffer.Unpooled.directBuffer; import static io.netty.buffer.Unpooled.directBuffer;
@ -36,9 +37,9 @@ import static io.netty.util.CharsetUtil.UTF_8;
public final class Http2CodecUtil { public final class Http2CodecUtil {
public static final int CONNECTION_STREAM_ID = 0; public static final int CONNECTION_STREAM_ID = 0;
public static final int HTTP_UPGRADE_STREAM_ID = 1; public static final int HTTP_UPGRADE_STREAM_ID = 1;
public static final String HTTP_UPGRADE_SETTINGS_HEADER = "HTTP2-Settings"; public static final CharSequence HTTP_UPGRADE_SETTINGS_HEADER = new AsciiString("HTTP2-Settings");
public static final String HTTP_UPGRADE_PROTOCOL_NAME = "h2c"; public static final CharSequence HTTP_UPGRADE_PROTOCOL_NAME = "h2c";
public static final String TLS_UPGRADE_PROTOCOL_NAME = ApplicationProtocolNames.HTTP_2; public static final CharSequence TLS_UPGRADE_PROTOCOL_NAME = ApplicationProtocolNames.HTTP_2;
public static final int PING_FRAME_PAYLOAD_LENGTH = 8; public static final int PING_FRAME_PAYLOAD_LENGTH = 8;
public static final short MAX_UNSIGNED_BYTE = 0xFF; public static final short MAX_UNSIGNED_BYTE = 0xFF;

View File

@ -41,7 +41,7 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull;
*/ */
public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.UpgradeCodec { public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.UpgradeCodec {
private static final List<String> REQUIRED_UPGRADE_HEADERS = private static final List<CharSequence> REQUIRED_UPGRADE_HEADERS =
Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER); Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER);
private final String handlerName; private final String handlerName;
@ -72,7 +72,7 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade
} }
@Override @Override
public Collection<String> requiredUpgradeHeaders() { public Collection<CharSequence> requiredUpgradeHeaders() {
return REQUIRED_UPGRADE_HEADERS; return REQUIRED_UPGRADE_HEADERS;
} }

View File

@ -22,6 +22,7 @@ import io.netty.util.internal.PlatformDependent;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; import java.util.regex.PatternSyntaxException;
@ -887,6 +888,42 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return true; 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<CharSequence> 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<CharSequence> a, Collection<CharSequence> 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. * Returns {@code true} if the content of both {@link CharSequence}'s are equals. This only supports 8-bit ASCII.
*/ */

View File

@ -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.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;
import io.netty.util.AsciiString;
/** /**
* Sets up the Netty pipeline for the example server. Depending on the endpoint config, sets up the * 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<SocketChannel> {
private static final UpgradeCodecFactory upgradeCodecFactory = new UpgradeCodecFactory() { private static final UpgradeCodecFactory upgradeCodecFactory = new UpgradeCodecFactory() {
@Override @Override
public UpgradeCodec newUpgradeCodec(String protocol) { public UpgradeCodec newUpgradeCodec(CharSequence protocol) {
if (Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME.equals(protocol)) { if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) {
return new Http2ServerUpgradeCodec(new HelloWorldHttp2Handler()); return new Http2ServerUpgradeCodec(new HelloWorldHttp2Handler());
} else { } else {
return null; return null;