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.
*/
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<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
@ -257,12 +257,12 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator implements Ch
request.headers().set(HttpHeaderNames.UPGRADE, upgradeCodec.protocol());
// 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));
// 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(',');
}

View File

@ -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"}
*/

View File

@ -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<String> requiredUpgradeHeaders();
Collection<CharSequence> 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<String> requestedProtocols = splitHeader(request.headers().get(HttpHeaderNames.UPGRADE));
final List<CharSequence> 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<String> requiredHeaders = upgradeCodec.requiredUpgradeHeaders();
List<String> values = splitHeader(connectionHeader);
if (!values.contains(UPGRADE_STRING) || !values.containsAll(requiredHeaders)) {
Collection<CharSequence> requiredHeaders = upgradeCodec.requiredUpgradeHeaders();
List<CharSequence> 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<String> splitHeader(CharSequence header) {
private static List<CharSequence> splitHeader(CharSequence header) {
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) {
char c = header.charAt(i);
if (Character.isWhitespace(c)) {

View File

@ -41,7 +41,7 @@ import java.util.List;
*/
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 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<String> setUpgradeHeaders(ChannelHandlerContext ctx,
public Collection<CharSequence> 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 {

View File

@ -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;

View File

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

View File

@ -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<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.
*/

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.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<SocketChannel> {
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;