Netty Headers Class Restructure and Algorithm Updates

Motivation:
Headers within netty do not cleanly share a common class hierarchy.  As a result some header types support some operations
and don't support others.  The consolidation of the class hierarchy will allow for maintenance and scalability for new codec.
The existing hierarchy also has a few short comings such as it is not clear when data conversions are happening.  This
could result unintentionally getting back a collection or iterator where a conversion on each entry must happen.

The current headers algorithm also prepends all elements which means to find the first element or return a collection
in insertion order often requires a complete traversal followed by a collections.reverse call.

Modifications:
-Provide a generic base class which provides all the implementation for headers in netty
-Provide an extension to this class which allows for name type conversions to happen (to accommodate legacy CharSequence to String conversions)
-Update the headers interface to clarify when conversions will happen.
-Update the headers data structure so that appends are done to avoid unnecessary iteration or collection reversal.

Result:
-More unified class hierarchy for headers in netty
-Improved headers data structure and algorithms
-headers API more clearly identify when conversions are required.
This commit is contained in:
Scott Mitchell 2014-09-18 19:04:35 -04:00 committed by Scott Mitchell
parent 6f35e608c3
commit 2374e17c6e
106 changed files with 7094 additions and 4166 deletions

View File

@ -17,7 +17,6 @@ package io.netty.handler.codec.http;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.DefaultTextHeaders;
import io.netty.handler.codec.TextHeaderProcessor;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.TextHeaders;
import java.util.Calendar; import java.util.Calendar;
@ -45,75 +44,15 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
LOOKUP_TABLE['='] = -1; LOOKUP_TABLE['='] = -1;
} }
protected final boolean validate; private static final class HttpHeadersValidationConverter extends DefaultTextValueTypeConverter {
private final boolean validate;
public DefaultHttpHeaders() { public HttpHeadersValidationConverter(boolean validate) {
this(true);
}
public DefaultHttpHeaders(boolean validate) {
this.validate = validate; this.validate = validate;
} }
@Override @Override
protected CharSequence convertName(CharSequence name) { public CharSequence convert(Object value) {
name = super.convertName(name);
if (validate) {
if (name instanceof AsciiString) {
validateName((AsciiString) name);
} else {
validateName(name);
}
}
return name;
}
private static void validateName(AsciiString name) {
// Go through each characters in the name
final int start = name.arrayOffset();
final int end = start + name.length();
final byte[] array = name.array();
for (int index = start; index < end; index ++) {
byte b = array[index];
// Check to see if the character is not an ASCII character
if (b < 0) {
throw new IllegalArgumentException(
"a header name cannot contain non-ASCII characters: " + name);
}
// Check for prohibited characters.
validateNameChar(name, b);
}
}
private static void validateName(CharSequence name) {
// Go through each characters in the name
for (int index = 0; index < name.length(); index ++) {
char character = name.charAt(index);
// Check to see if the character is not an ASCII character
if (character > 127) {
throw new IllegalArgumentException(
"a header name cannot contain non-ASCII characters: " + name);
}
// Check for prohibited characters.
validateNameChar(name, character);
}
}
private static void validateNameChar(CharSequence name, int character) {
if ((character & HIGHEST_INVALID_NAME_CHAR_MASK) == 0 && LOOKUP_TABLE[character] != 0) {
throw new IllegalArgumentException(
"a header name cannot contain the following prohibited characters: " +
"=,;: \\t\\r\\n\\v\\f: " + name);
}
}
@Override
protected CharSequence convertValue(Object value) {
if (value == null) { if (value == null) {
throw new NullPointerException("value"); throw new NullPointerException("value");
} }
@ -141,6 +80,87 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
return seq; return seq;
} }
}
static class HttpHeadersNameConverter implements NameConverter<CharSequence> {
protected final boolean validate;
public HttpHeadersNameConverter(boolean validate) {
this.validate = validate;
}
@Override
public CharSequence convertName(CharSequence name) {
if (validate) {
if (name instanceof AsciiString) {
validateName((AsciiString) name);
} else {
validateName(name);
}
}
return name;
}
}
private static final HttpHeadersValidationConverter
VALIDATE_OBJECT_CONVERTER = new HttpHeadersValidationConverter(true);
private static final HttpHeadersValidationConverter
NO_VALIDATE_OBJECT_CONVERTER = new HttpHeadersValidationConverter(false);
private static final HttpHeadersNameConverter VALIDATE_NAME_CONVERTER = new HttpHeadersNameConverter(true);
private static final HttpHeadersNameConverter NO_VALIDATE_NAME_CONVERTER = new HttpHeadersNameConverter(false);
public DefaultHttpHeaders() {
this(true);
}
public DefaultHttpHeaders(boolean validate) {
this(true, validate ? VALIDATE_NAME_CONVERTER : NO_VALIDATE_NAME_CONVERTER);
}
protected DefaultHttpHeaders(boolean validate, NameConverter<CharSequence> nameConverter) {
super(true, validate ? VALIDATE_OBJECT_CONVERTER : NO_VALIDATE_OBJECT_CONVERTER, nameConverter);
}
private static void validateName(AsciiString name) {
// Go through each characters in the name
final int start = name.arrayOffset();
final int end = start + name.length();
final byte[] array = name.array();
for (int index = start; index < end; index ++) {
byte b = array[index];
// Check to see if the character is not an ASCII character
if (b < 0) {
throw new IllegalArgumentException("a header name cannot contain non-ASCII characters: " + name);
}
// Check for prohibited characters.
validateNameChar(name, b);
}
}
private static void validateName(CharSequence name) {
// Go through each characters in the name
for (int index = 0; index < name.length(); index++) {
char character = name.charAt(index);
// Check to see if the character is not an ASCII character
if (character > 127) {
throw new IllegalArgumentException("a header name cannot contain non-ASCII characters: " + name);
}
// Check for prohibited characters.
validateNameChar(name, character);
}
}
private static void validateNameChar(CharSequence name, int character) {
if ((character & HIGHEST_INVALID_NAME_CHAR_MASK) == 0 && LOOKUP_TABLE[character] != 0) {
throw new IllegalArgumentException("a header name cannot contain the following prohibited characters: "
+ "=,;: \\t\\r\\n\\v\\f: " + name);
}
}
private static void validateValue(AsciiString seq) { private static void validateValue(AsciiString seq) {
int state = 0; int state = 0;
@ -148,46 +168,39 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
final int start = seq.arrayOffset(); final int start = seq.arrayOffset();
final int end = start + seq.length(); final int end = start + seq.length();
final byte[] array = seq.array(); final byte[] array = seq.array();
for (int index = start; index < end; index ++) { for (int index = start; index < end; index++) {
state = validateValueChar(seq, state, (char) (array[index] & 0xFF)); state = validateValueChar(seq, state, (char) (array[index] & 0xFF));
} }
if (state != 0) { if (state != 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq);
"a header value must not end with '\\r' or '\\n':" + seq);
} }
} }
private static void validateValue(CharSequence seq) { private static void validateValue(CharSequence seq) {
int state = 0; int state = 0;
// Start looping through each of the character // Start looping through each of the character
for (int index = 0; index < seq.length(); index ++) { for (int index = 0; index < seq.length(); index++) {
state = validateValueChar(seq, state, seq.charAt(index)); state = validateValueChar(seq, state, seq.charAt(index));
} }
if (state != 0) { if (state != 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq);
"a header value must not end with '\\r' or '\\n':" + seq);
} }
} }
private static int validateValueChar(CharSequence seq, int state, char character) { private static int validateValueChar(CharSequence seq, int state, char character) {
/* /*
* State: * State: 0: Previous character was neither CR nor LF 1: The previous character was CR 2: The previous character
* * was LF
* 0: Previous character was neither CR nor LF
* 1: The previous character was CR
* 2: The previous character was LF
*/ */
if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) { if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) {
// Check the absolutely prohibited characters. // Check the absolutely prohibited characters.
switch (character) { switch (character) {
case 0x0b: // Vertical tab case 0x0b: // Vertical tab
throw new IllegalArgumentException( throw new IllegalArgumentException("a header value contains a prohibited character '\\v': " + seq);
"a header value contains a prohibited character '\\v': " + seq);
case '\f': case '\f':
throw new IllegalArgumentException( throw new IllegalArgumentException("a header value contains a prohibited character '\\f': " + seq);
"a header value contains a prohibited character '\\f': " + seq);
} }
} }
@ -209,41 +222,106 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
state = 2; state = 2;
break; break;
default: default:
throw new IllegalArgumentException( throw new IllegalArgumentException("only '\\n' is allowed after '\\r': " + seq);
"only '\\n' is allowed after '\\r': " + seq);
} }
break; break;
case 2: case 2:
switch (character) { switch (character) {
case '\t': case ' ': case '\t':
case ' ':
state = 0; state = 0;
break; break;
default: default:
throw new IllegalArgumentException( throw new IllegalArgumentException("only ' ' and '\\t' are allowed after '\\n': " + seq);
"only ' ' and '\\t' are allowed after '\\n': " + seq);
} }
} }
return state; return state;
} }
@Override @Override
public HttpHeaders add(CharSequence name, Object value) { public HttpHeaders add(CharSequence name, CharSequence value) {
super.add(name, value); super.add(name, value);
return this; return this;
} }
@Override @Override
public HttpHeaders add(CharSequence name, Iterable<?> values) { public HttpHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@Override @Override
public HttpHeaders add(CharSequence name, Object... values) { public HttpHeaders add(CharSequence name, CharSequence... values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@Override
public HttpHeaders addObject(CharSequence name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public HttpHeaders addObject(CharSequence name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public HttpHeaders addObject(CharSequence name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public HttpHeaders addBoolean(CharSequence name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public HttpHeaders addChar(CharSequence name, char value) {
super.addChar(name, value);
return this;
}
@Override
public HttpHeaders addByte(CharSequence name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public HttpHeaders addShort(CharSequence name, short value) {
super.addShort(name, value);
return this;
}
@Override
public HttpHeaders addInt(CharSequence name, int value) {
super.addInt(name, value);
return this;
}
@Override
public HttpHeaders addLong(CharSequence name, long value) {
super.addLong(name, value);
return this;
}
@Override
public HttpHeaders addFloat(CharSequence name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public HttpHeaders addDouble(CharSequence name, double value) {
super.addDouble(name, value);
return this;
}
@Override @Override
public HttpHeaders add(TextHeaders headers) { public HttpHeaders add(TextHeaders headers) {
super.add(headers); super.add(headers);
@ -251,23 +329,89 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
} }
@Override @Override
public HttpHeaders set(CharSequence name, Object value) { public HttpHeaders set(CharSequence name, CharSequence value) {
super.set(name, value); super.set(name, value);
return this; return this;
} }
@Override @Override
public HttpHeaders set(CharSequence name, Object... values) { public HttpHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@Override @Override
public HttpHeaders set(CharSequence name, Iterable<?> values) { public HttpHeaders set(CharSequence name, CharSequence... values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@Override
public HttpHeaders setObject(CharSequence name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public HttpHeaders setObject(CharSequence name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public HttpHeaders setObject(CharSequence name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public HttpHeaders setBoolean(CharSequence name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public HttpHeaders setChar(CharSequence name, char value) {
super.setChar(name, value);
return this;
}
@Override
public HttpHeaders setByte(CharSequence name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public HttpHeaders setShort(CharSequence name, short value) {
super.setShort(name, value);
return this;
}
@Override
public HttpHeaders setInt(CharSequence name, int value) {
super.setInt(name, value);
return this;
}
@Override
public HttpHeaders setLong(CharSequence name, long value) {
super.setLong(name, value);
return this;
}
@Override
public HttpHeaders setFloat(CharSequence name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public HttpHeaders setDouble(CharSequence name, double value) {
super.setDouble(name, value);
return this;
}
@Override @Override
public HttpHeaders set(TextHeaders headers) { public HttpHeaders set(TextHeaders headers) {
super.set(headers); super.set(headers);
@ -275,14 +419,14 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
} }
@Override @Override
public HttpHeaders clear() { public HttpHeaders setAll(TextHeaders headers) {
super.clear(); super.setAll(headers);
return this; return this;
} }
@Override @Override
public HttpHeaders forEachEntry(TextHeaderProcessor processor) { public HttpHeaders clear() {
super.forEachEntry(processor); super.clear();
return this; return this;
} }
} }

View File

@ -108,7 +108,7 @@ public abstract class DefaultHttpMessage extends DefaultHttpObject implements Ht
} }
void appendHeaders(StringBuilder buf, HttpHeaders headers) { void appendHeaders(StringBuilder buf, HttpHeaders headers) {
for (Map.Entry<String, String> e: headers) { for (Map.Entry<CharSequence, CharSequence> e: headers) {
buf.append(e.getKey()); buf.append(e.getKey());
buf.append(": "); buf.append(": ");
buf.append(e.getValue()); buf.append(e.getValue());

View File

@ -15,6 +15,9 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaders.Names.TRAILER;
import static io.netty.handler.codec.http.HttpHeaders.Names.TRANSFER_ENCODING;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
@ -99,7 +102,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
} }
private void appendHeaders(StringBuilder buf) { private void appendHeaders(StringBuilder buf) {
for (Map.Entry<String, String> e: trailingHeaders()) { for (Map.Entry<CharSequence, CharSequence> e : trailingHeaders()) {
buf.append(e.getKey()); buf.append(e.getKey());
buf.append(": "); buf.append(": ");
buf.append(e.getValue()); buf.append(e.getValue());
@ -108,22 +111,32 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
} }
private static final class TrailingHttpHeaders extends DefaultHttpHeaders { private static final class TrailingHttpHeaders extends DefaultHttpHeaders {
TrailingHttpHeaders(boolean validate) { private static final class TrailingHttpHeadersNameConverter extends HttpHeadersNameConverter {
public TrailingHttpHeadersNameConverter(boolean validate) {
super(validate); super(validate);
} }
@Override @Override
protected CharSequence convertName(CharSequence name) { public CharSequence convertName(CharSequence name) {
name = super.convertName(name); name = super.convertName(name);
if (validate) { if (validate) {
if (AsciiString.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH, name) || if (AsciiString.equalsIgnoreCase(CONTENT_LENGTH, name)
AsciiString.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING, name) || || AsciiString.equalsIgnoreCase(TRANSFER_ENCODING, name)
AsciiString.equalsIgnoreCase(HttpHeaders.Names.TRAILER, name)) { || AsciiString.equalsIgnoreCase(TRAILER, name)) {
throw new IllegalArgumentException( throw new IllegalArgumentException("prohibited trailing header: " + name);
"prohibited trailing header: " + name);
} }
} }
return name; return name;
} }
} }
private static final TrailingHttpHeadersNameConverter
VALIDATE_NAME_CONVERTER = new TrailingHttpHeadersNameConverter(true);
private static final TrailingHttpHeadersNameConverter
NO_VALIDATE_NAME_CONVERTER = new TrailingHttpHeadersNameConverter(false);
TrailingHttpHeaders(boolean validate) {
super(validate, validate ? VALIDATE_NAME_CONVERTER : NO_VALIDATE_NAME_CONVERTER);
}
}
} }

View File

@ -17,33 +17,99 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.handler.codec.EmptyTextHeaders; import io.netty.handler.codec.EmptyTextHeaders;
import io.netty.handler.codec.TextHeaderProcessor;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.TextHeaders;
public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders { public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders {
public static final EmptyHttpHeaders INSTANCE = new EmptyHttpHeaders(); public static final EmptyHttpHeaders INSTANCE = new EmptyHttpHeaders();
protected EmptyHttpHeaders() { } protected EmptyHttpHeaders() {
}
@Override @Override
public HttpHeaders add(CharSequence name, Object value) { public HttpHeaders add(CharSequence name, CharSequence value) {
super.add(name, value); super.add(name, value);
return this; return this;
} }
@Override @Override
public HttpHeaders add(CharSequence name, Iterable<?> values) { public HttpHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@Override @Override
public HttpHeaders add(CharSequence name, Object... values) { public HttpHeaders add(CharSequence name, CharSequence... values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@Override
public HttpHeaders addObject(CharSequence name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public HttpHeaders addObject(CharSequence name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public HttpHeaders addObject(CharSequence name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public HttpHeaders addBoolean(CharSequence name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public HttpHeaders addChar(CharSequence name, char value) {
super.addChar(name, value);
return this;
}
@Override
public HttpHeaders addByte(CharSequence name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public HttpHeaders addShort(CharSequence name, short value) {
super.addShort(name, value);
return this;
}
@Override
public HttpHeaders addInt(CharSequence name, int value) {
super.addInt(name, value);
return this;
}
@Override
public HttpHeaders addLong(CharSequence name, long value) {
super.addLong(name, value);
return this;
}
@Override
public HttpHeaders addFloat(CharSequence name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public HttpHeaders addDouble(CharSequence name, double value) {
super.addDouble(name, value);
return this;
}
@Override @Override
public HttpHeaders add(TextHeaders headers) { public HttpHeaders add(TextHeaders headers) {
super.add(headers); super.add(headers);
@ -51,23 +117,89 @@ public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders {
} }
@Override @Override
public HttpHeaders set(CharSequence name, Object value) { public HttpHeaders set(CharSequence name, CharSequence value) {
super.set(name, value); super.set(name, value);
return this; return this;
} }
@Override @Override
public HttpHeaders set(CharSequence name, Object... values) { public HttpHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@Override @Override
public HttpHeaders set(CharSequence name, Iterable<?> values) { public HttpHeaders set(CharSequence name, CharSequence... values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@Override
public HttpHeaders setObject(CharSequence name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public HttpHeaders setObject(CharSequence name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public HttpHeaders setObject(CharSequence name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public HttpHeaders setBoolean(CharSequence name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public HttpHeaders setChar(CharSequence name, char value) {
super.setChar(name, value);
return this;
}
@Override
public HttpHeaders setByte(CharSequence name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public HttpHeaders setShort(CharSequence name, short value) {
super.setShort(name, value);
return this;
}
@Override
public HttpHeaders setInt(CharSequence name, int value) {
super.setInt(name, value);
return this;
}
@Override
public HttpHeaders setLong(CharSequence name, long value) {
super.setLong(name, value);
return this;
}
@Override
public HttpHeaders setFloat(CharSequence name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public HttpHeaders setDouble(CharSequence name, double value) {
super.setDouble(name, value);
return this;
}
@Override @Override
public HttpHeaders set(TextHeaders headers) { public HttpHeaders set(TextHeaders headers) {
super.set(headers); super.set(headers);
@ -75,14 +207,14 @@ public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders {
} }
@Override @Override
public HttpHeaders clear() { public HttpHeaders setAll(TextHeaders headers) {
super.clear(); super.setAll(headers);
return this; return this;
} }
@Override @Override
public HttpHeaders forEachEntry(TextHeaderProcessor processor) { public HttpHeaders clear() {
super.forEachEntry(processor); super.clear();
return this; return this;
} }
} }

View File

@ -16,6 +16,7 @@ package io.netty.handler.codec.http;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.AsciiString;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -177,12 +178,12 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator {
return; return;
} }
String upgradeHeader = response.headers().get(UPGRADE); CharSequence upgradeHeader = response.headers().get(UPGRADE);
if (upgradeHeader == null) { if (upgradeHeader == null) {
throw new IllegalStateException( throw new IllegalStateException(
"Switching Protocols response missing UPGRADE header"); "Switching Protocols response missing UPGRADE header");
} }
if (!upgradeCodec.protocol().equalsIgnoreCase(upgradeHeader)) { if (AsciiString.equalsIgnoreCase(upgradeCodec.protocol(), upgradeHeader)) {
throw new IllegalStateException( throw new IllegalStateException(
"Switching Protocols response with unexpected UPGRADE protocol: " "Switching Protocols response with unexpected UPGRADE protocol: "
+ upgradeHeader); + upgradeHeader);

View File

@ -95,7 +95,7 @@ public class HttpContentCompressor extends HttpContentEncoder {
@Override @Override
protected Result beginEncode(HttpResponse headers, CharSequence acceptEncoding) throws Exception { protected Result beginEncode(HttpResponse headers, CharSequence acceptEncoding) throws Exception {
String contentEncoding = headers.headers().get(HttpHeaders.Names.CONTENT_ENCODING); CharSequence contentEncoding = headers.headers().get(HttpHeaders.Names.CONTENT_ENCODING);
if (contentEncoding != null && if (contentEncoding != null &&
!AsciiString.equalsIgnoreCase(HttpHeaders.Values.IDENTITY, contentEncoding)) { !AsciiString.equalsIgnoreCase(HttpHeaders.Values.IDENTITY, contentEncoding)) {
return null; return null;

View File

@ -87,7 +87,7 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObj
this.message = null; this.message = null;
// Determine the content encoding. // Determine the content encoding.
String contentEncoding = headers.get(HttpHeaders.Names.CONTENT_ENCODING); String contentEncoding = headers.getAndConvert(HttpHeaders.Names.CONTENT_ENCODING);
if (contentEncoding != null) { if (contentEncoding != null) {
contentEncoding = contentEncoding.trim(); contentEncoding = contentEncoding.trim();
} else { } else {

View File

@ -33,7 +33,7 @@ public final class HttpHeaderUtil {
* {@link HttpVersion#isKeepAliveDefault()}. * {@link HttpVersion#isKeepAliveDefault()}.
*/ */
public static boolean isKeepAlive(HttpMessage message) { public static boolean isKeepAlive(HttpMessage message) {
String connection = message.headers().get(Names.CONNECTION); CharSequence connection = message.headers().get(Names.CONNECTION);
if (connection != null && AsciiString.equalsIgnoreCase(Values.CLOSE, connection)) { if (connection != null && AsciiString.equalsIgnoreCase(Values.CLOSE, connection)) {
return false; return false;
} }
@ -94,9 +94,9 @@ public final class HttpHeaderUtil {
* or its value is not a number * or its value is not a number
*/ */
public static long getContentLength(HttpMessage message) { public static long getContentLength(HttpMessage message) {
String value = message.headers().get(Names.CONTENT_LENGTH); Long value = message.headers().getLong(Names.CONTENT_LENGTH);
if (value != null) { if (value != null) {
return Long.parseLong(value); return value;
} }
// We know the content length if it's a Web Socket message even if // We know the content length if it's a Web Socket message even if
@ -121,13 +121,9 @@ public final class HttpHeaderUtil {
* a number * a number
*/ */
public static long getContentLength(HttpMessage message, long defaultValue) { public static long getContentLength(HttpMessage message, long defaultValue) {
String contentLength = message.headers().get(Names.CONTENT_LENGTH); Long value = message.headers().getLong(Names.CONTENT_LENGTH);
if (contentLength != null) { if (value != null) {
try { return value;
return Long.parseLong(contentLength);
} catch (NumberFormatException ignored) {
return defaultValue;
}
} }
// We know the content length if it's a Web Socket message even if // We know the content length if it's a Web Socket message even if
@ -172,7 +168,7 @@ public final class HttpHeaderUtil {
* Sets the {@code "Content-Length"} header. * Sets the {@code "Content-Length"} header.
*/ */
public static void setContentLength(HttpMessage message, long length) { public static void setContentLength(HttpMessage message, long length) {
message.headers().set(Names.CONTENT_LENGTH, length); message.headers().setLong(Names.CONTENT_LENGTH, length);
} }
public static boolean isContentLengthSet(HttpMessage m) { public static boolean isContentLengthSet(HttpMessage m) {
@ -195,7 +191,7 @@ public final class HttpHeaderUtil {
} }
// In most cases, there will be one or zero 'Expect' header. // In most cases, there will be one or zero 'Expect' header.
String value = message.headers().get(Names.EXPECT); CharSequence value = message.headers().get(Names.EXPECT);
if (value == null) { if (value == null) {
return false; return false;
} }
@ -237,7 +233,7 @@ public final class HttpHeaderUtil {
m.headers().add(Names.TRANSFER_ENCODING, Values.CHUNKED); m.headers().add(Names.TRANSFER_ENCODING, Values.CHUNKED);
m.headers().remove(Names.CONTENT_LENGTH); m.headers().remove(Names.CONTENT_LENGTH);
} else { } else {
List<CharSequence> values = m.headers().getAllUnconverted(Names.TRANSFER_ENCODING); List<CharSequence> values = m.headers().getAll(Names.TRANSFER_ENCODING);
if (values.isEmpty()) { if (values.isEmpty()) {
return; return;
} }

View File

@ -16,7 +16,6 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.TextHeaderProcessor;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.TextHeaders;
@ -25,326 +24,332 @@ import io.netty.handler.codec.TextHeaders;
* commonly used utility methods that accesses an {@link HttpMessage}. * commonly used utility methods that accesses an {@link HttpMessage}.
*/ */
public interface HttpHeaders extends TextHeaders { public interface HttpHeaders extends TextHeaders {
/** /**
* Standard HTTP header names. * Standard HTTP header names.
* <p>
* These are all defined as lowercase to support HTTP/2 requirements while also not
* violating HTTP/1.x requirements. New header names should always be lowercase.
*/ */
final class Names { final class Names {
/** /**
* {@code "Accept"} * {@code "accept"}
*/ */
public static final AsciiString ACCEPT = new AsciiString("Accept"); public static final AsciiString ACCEPT = new AsciiString("accept");
/** /**
* {@code "Accept-Charset"} * {@code "accept-charset"}
*/ */
public static final AsciiString ACCEPT_CHARSET = new AsciiString("Accept-Charset"); public static final AsciiString ACCEPT_CHARSET = new AsciiString("accept-charset");
/** /**
* {@code "Accept-Encoding"} * {@code "accept-encoding"}
*/ */
public static final AsciiString ACCEPT_ENCODING = new AsciiString("Accept-Encoding"); public static final AsciiString ACCEPT_ENCODING = new AsciiString("accept-encoding");
/** /**
* {@code "Accept-Language"} * {@code "accept-language"}
*/ */
public static final AsciiString ACCEPT_LANGUAGE = new AsciiString("Accept-Language"); public static final AsciiString ACCEPT_LANGUAGE = new AsciiString("accept-language");
/** /**
* {@code "Accept-Ranges"} * {@code "accept-ranges"}
*/ */
public static final AsciiString ACCEPT_RANGES = new AsciiString("Accept-Ranges"); public static final AsciiString ACCEPT_RANGES = new AsciiString("accept-ranges");
/** /**
* {@code "Accept-Patch"} * {@code "accept-patch"}
*/ */
public static final AsciiString ACCEPT_PATCH = new AsciiString("Accept-Patch"); public static final AsciiString ACCEPT_PATCH = new AsciiString("accept-patch");
/** /**
* {@code "Access-Control-Allow-Credentials"} * {@code "access-control-allow-credentials"}
*/ */
public static final AsciiString ACCESS_CONTROL_ALLOW_CREDENTIALS = public static final AsciiString ACCESS_CONTROL_ALLOW_CREDENTIALS =
new AsciiString("Access-Control-Allow-Credentials"); new AsciiString("access-control-allow-credentials");
/** /**
* {@code "Access-Control-Allow-Headers"} * {@code "access-control-allow-headers"}
*/ */
public static final AsciiString ACCESS_CONTROL_ALLOW_HEADERS = public static final AsciiString ACCESS_CONTROL_ALLOW_HEADERS =
new AsciiString("Access-Control-Allow-Headers"); new AsciiString("access-control-allow-headers");
/** /**
* {@code "Access-Control-Allow-Methods"} * {@code "access-control-allow-methods"}
*/ */
public static final AsciiString ACCESS_CONTROL_ALLOW_METHODS = public static final AsciiString ACCESS_CONTROL_ALLOW_METHODS =
new AsciiString("Access-Control-Allow-Methods"); new AsciiString("access-control-allow-methods");
/** /**
* {@code "Access-Control-Allow-Origin"} * {@code "access-control-allow-origin"}
*/ */
public static final AsciiString ACCESS_CONTROL_ALLOW_ORIGIN = public static final AsciiString ACCESS_CONTROL_ALLOW_ORIGIN =
new AsciiString("Access-Control-Allow-Origin"); new AsciiString("access-control-allow-origin");
/** /**
* {@code "Access-Control-Expose-Headers"} * {@code "access-control-expose-headers"}
*/ */
public static final AsciiString ACCESS_CONTROL_EXPOSE_HEADERS = public static final AsciiString ACCESS_CONTROL_EXPOSE_HEADERS =
new AsciiString("Access-Control-Expose-Headers"); new AsciiString("access-control-expose-headers");
/** /**
* {@code "Access-Control-Max-Age"} * {@code "access-control-max-age"}
*/ */
public static final AsciiString ACCESS_CONTROL_MAX_AGE = new AsciiString("Access-Control-Max-Age"); public static final AsciiString ACCESS_CONTROL_MAX_AGE = new AsciiString("access-control-max-age");
/** /**
* {@code "Access-Control-Request-Headers"} * {@code "access-control-request-headers"}
*/ */
public static final AsciiString ACCESS_CONTROL_REQUEST_HEADERS = public static final AsciiString ACCESS_CONTROL_REQUEST_HEADERS =
new AsciiString("Access-Control-Request-Headers"); new AsciiString("access-control-request-headers");
/** /**
* {@code "Access-Control-Request-Method"} * {@code "access-control-request-method"}
*/ */
public static final AsciiString ACCESS_CONTROL_REQUEST_METHOD = public static final AsciiString ACCESS_CONTROL_REQUEST_METHOD =
new AsciiString("Access-Control-Request-Method"); new AsciiString("access-control-request-method");
/** /**
* {@code "Age"} * {@code "age"}
*/ */
public static final AsciiString AGE = new AsciiString("Age"); public static final AsciiString AGE = new AsciiString("age");
/** /**
* {@code "Allow"} * {@code "allow"}
*/ */
public static final AsciiString ALLOW = new AsciiString("Allow"); public static final AsciiString ALLOW = new AsciiString("allow");
/** /**
* {@code "Authorization"} * {@code "authorization"}
*/ */
public static final AsciiString AUTHORIZATION = new AsciiString("Authorization"); public static final AsciiString AUTHORIZATION = new AsciiString("authorization");
/** /**
* {@code "Cache-Control"} * {@code "cache-control"}
*/ */
public static final AsciiString CACHE_CONTROL = new AsciiString("Cache-Control"); public static final AsciiString CACHE_CONTROL = new AsciiString("cache-control");
/** /**
* {@code "Connection"} * {@code "connection"}
*/ */
public static final AsciiString CONNECTION = new AsciiString("Connection"); public static final AsciiString CONNECTION = new AsciiString("connection");
/** /**
* {@code "Content-Base"} * {@code "content-base"}
*/ */
public static final AsciiString CONTENT_BASE = new AsciiString("Content-Base"); public static final AsciiString CONTENT_BASE = new AsciiString("content-base");
/** /**
* {@code "Content-Encoding"} * {@code "content-encoding"}
*/ */
public static final AsciiString CONTENT_ENCODING = new AsciiString("Content-Encoding"); public static final AsciiString CONTENT_ENCODING = new AsciiString("content-encoding");
/** /**
* {@code "Content-Language"} * {@code "content-language"}
*/ */
public static final AsciiString CONTENT_LANGUAGE = new AsciiString("Content-Language"); public static final AsciiString CONTENT_LANGUAGE = new AsciiString("content-language");
/** /**
* {@code "Content-Length"} * {@code "content-length"}
*/ */
public static final AsciiString CONTENT_LENGTH = new AsciiString("Content-Length"); public static final AsciiString CONTENT_LENGTH = new AsciiString("content-length");
/** /**
* {@code "Content-Location"} * {@code "content-location"}
*/ */
public static final AsciiString CONTENT_LOCATION = new AsciiString("Content-Location"); public static final AsciiString CONTENT_LOCATION = new AsciiString("content-location");
/** /**
* {@code "Content-Transfer-Encoding"} * {@code "content-transfer-encoding"}
*/ */
public static final AsciiString CONTENT_TRANSFER_ENCODING = new AsciiString("Content-Transfer-Encoding"); public static final AsciiString CONTENT_TRANSFER_ENCODING = new AsciiString("content-transfer-encoding");
/** /**
* {@code "Content-MD5"} * {@code "content-disposition"}
*/ */
public static final AsciiString CONTENT_MD5 = new AsciiString("Content-MD5"); public static final AsciiString CONTENT_DISPOSITION = new AsciiString("content-disposition");
/** /**
* {@code "Content-Range"} * {@code "content-md5"}
*/ */
public static final AsciiString CONTENT_RANGE = new AsciiString("Content-Range"); public static final AsciiString CONTENT_MD5 = new AsciiString("content-md5");
/** /**
* {@code "Content-Type"} * {@code "content-range"}
*/ */
public static final AsciiString CONTENT_TYPE = new AsciiString("Content-Type"); public static final AsciiString CONTENT_RANGE = new AsciiString("content-range");
/** /**
* {@code "Cookie"} * {@code "content-type"}
*/ */
public static final AsciiString COOKIE = new AsciiString("Cookie"); public static final AsciiString CONTENT_TYPE = new AsciiString("content-type");
/** /**
* {@code "Date"} * {@code "cookie"}
*/ */
public static final AsciiString DATE = new AsciiString("Date"); public static final AsciiString COOKIE = new AsciiString("cookie");
/** /**
* {@code "ETag"} * {@code "date"}
*/ */
public static final AsciiString ETAG = new AsciiString("ETag"); public static final AsciiString DATE = new AsciiString("date");
/** /**
* {@code "Expect"} * {@code "etag"}
*/ */
public static final AsciiString EXPECT = new AsciiString("Expect"); public static final AsciiString ETAG = new AsciiString("etag");
/** /**
* {@code "Expires"} * {@code "expect"}
*/ */
public static final AsciiString EXPIRES = new AsciiString("Expires"); public static final AsciiString EXPECT = new AsciiString("expect");
/** /**
* {@code "From"} * {@code "expires"}
*/ */
public static final AsciiString FROM = new AsciiString("From"); public static final AsciiString EXPIRES = new AsciiString("expires");
/** /**
* {@code "Host"} * {@code "from"}
*/ */
public static final AsciiString HOST = new AsciiString("Host"); public static final AsciiString FROM = new AsciiString("from");
/** /**
* {@code "If-Match"} * {@code "host"}
*/ */
public static final AsciiString IF_MATCH = new AsciiString("If-Match"); public static final AsciiString HOST = new AsciiString("host");
/** /**
* {@code "If-Modified-Since"} * {@code "if-match"}
*/ */
public static final AsciiString IF_MODIFIED_SINCE = new AsciiString("If-Modified-Since"); public static final AsciiString IF_MATCH = new AsciiString("if-match");
/** /**
* {@code "If-None-Match"} * {@code "if-modified-since"}
*/ */
public static final AsciiString IF_NONE_MATCH = new AsciiString("If-None-Match"); public static final AsciiString IF_MODIFIED_SINCE = new AsciiString("if-modified-since");
/** /**
* {@code "If-Range"} * {@code "if-none-match"}
*/ */
public static final AsciiString IF_RANGE = new AsciiString("If-Range"); public static final AsciiString IF_NONE_MATCH = new AsciiString("if-none-match");
/** /**
* {@code "If-Unmodified-Since"} * {@code "if-range"}
*/ */
public static final AsciiString IF_UNMODIFIED_SINCE = new AsciiString("If-Unmodified-Since"); public static final AsciiString IF_RANGE = new AsciiString("if-range");
/** /**
* {@code "Last-Modified"} * {@code "if-unmodified-since"}
*/ */
public static final AsciiString LAST_MODIFIED = new AsciiString("Last-Modified"); public static final AsciiString IF_UNMODIFIED_SINCE = new AsciiString("if-unmodified-since");
/** /**
* {@code "Location"} * {@code "last-modified"}
*/ */
public static final AsciiString LOCATION = new AsciiString("Location"); public static final AsciiString LAST_MODIFIED = new AsciiString("last-modified");
/** /**
* {@code "Max-Forwards"} * {@code "location"}
*/ */
public static final AsciiString MAX_FORWARDS = new AsciiString("Max-Forwards"); public static final AsciiString LOCATION = new AsciiString("location");
/** /**
* {@code "Origin"} * {@code "max-forwards"}
*/ */
public static final AsciiString ORIGIN = new AsciiString("Origin"); public static final AsciiString MAX_FORWARDS = new AsciiString("max-forwards");
/** /**
* {@code "Pragma"} * {@code "origin"}
*/ */
public static final AsciiString PRAGMA = new AsciiString("Pragma"); public static final AsciiString ORIGIN = new AsciiString("origin");
/** /**
* {@code "Proxy-Authenticate"} * {@code "pragma"}
*/ */
public static final AsciiString PROXY_AUTHENTICATE = new AsciiString("Proxy-Authenticate"); public static final AsciiString PRAGMA = new AsciiString("pragma");
/** /**
* {@code "Proxy-Authorization"} * {@code "proxy-authenticate"}
*/ */
public static final AsciiString PROXY_AUTHORIZATION = new AsciiString("Proxy-Authorization"); public static final AsciiString PROXY_AUTHENTICATE = new AsciiString("proxy-authenticate");
/** /**
* {@code "Range"} * {@code "proxy-authorization"}
*/ */
public static final AsciiString RANGE = new AsciiString("Range"); public static final AsciiString PROXY_AUTHORIZATION = new AsciiString("proxy-authorization");
/** /**
* {@code "Referer"} * {@code "range"}
*/ */
public static final AsciiString REFERER = new AsciiString("Referer"); public static final AsciiString RANGE = new AsciiString("range");
/** /**
* {@code "Retry-After"} * {@code "referer"}
*/ */
public static final AsciiString RETRY_AFTER = new AsciiString("Retry-After"); public static final AsciiString REFERER = new AsciiString("referer");
/** /**
* {@code "Sec-WebSocket-Key1"} * {@code "retry-after"}
*/ */
public static final AsciiString SEC_WEBSOCKET_KEY1 = new AsciiString("Sec-WebSocket-Key1"); public static final AsciiString RETRY_AFTER = new AsciiString("retry-after");
/** /**
* {@code "Sec-WebSocket-Key2"} * {@code "sec-websocket-key1"}
*/ */
public static final AsciiString SEC_WEBSOCKET_KEY2 = new AsciiString("Sec-WebSocket-Key2"); public static final AsciiString SEC_WEBSOCKET_KEY1 = new AsciiString("sec-websocket-key1");
/** /**
* {@code "Sec-WebSocket-Location"} * {@code "sec-websocket-key2"}
*/ */
public static final AsciiString SEC_WEBSOCKET_LOCATION = new AsciiString("Sec-WebSocket-Location"); public static final AsciiString SEC_WEBSOCKET_KEY2 = new AsciiString("sec-websocket-key2");
/** /**
* {@code "Sec-WebSocket-Origin"} * {@code "sec-websocket-location"}
*/ */
public static final AsciiString SEC_WEBSOCKET_ORIGIN = new AsciiString("Sec-WebSocket-Origin"); public static final AsciiString SEC_WEBSOCKET_LOCATION = new AsciiString("sec-websocket-location");
/** /**
* {@code "Sec-WebSocket-Protocol"} * {@code "sec-websocket-origin"}
*/ */
public static final AsciiString SEC_WEBSOCKET_PROTOCOL = new AsciiString("Sec-WebSocket-Protocol"); public static final AsciiString SEC_WEBSOCKET_ORIGIN = new AsciiString("sec-websocket-origin");
/** /**
* {@code "Sec-WebSocket-Version"} * {@code "sec-websocket-protocol"}
*/ */
public static final AsciiString SEC_WEBSOCKET_VERSION = new AsciiString("Sec-WebSocket-Version"); public static final AsciiString SEC_WEBSOCKET_PROTOCOL = new AsciiString("sec-websocket-protocol");
/** /**
* {@code "Sec-WebSocket-Key"} * {@code "sec-websocket-version"}
*/ */
public static final AsciiString SEC_WEBSOCKET_KEY = new AsciiString("Sec-WebSocket-Key"); public static final AsciiString SEC_WEBSOCKET_VERSION = new AsciiString("sec-websocket-version");
/** /**
* {@code "Sec-WebSocket-Accept"} * {@code "sec-websocket-key"}
*/ */
public static final AsciiString SEC_WEBSOCKET_ACCEPT = new AsciiString("Sec-WebSocket-Accept"); public static final AsciiString SEC_WEBSOCKET_KEY = new AsciiString("sec-websocket-key");
/** /**
* {@code "Sec-WebSocket-Protocol"} * {@code "sec-websocket-accept"}
*/ */
public static final AsciiString SEC_WEBSOCKET_EXTENSIONS = new AsciiString("Sec-WebSocket-Extensions"); public static final AsciiString SEC_WEBSOCKET_ACCEPT = new AsciiString("sec-websocket-accept");
/** /**
* {@code "Server"} * {@code "sec-websocket-protocol"}
*/ */
public static final AsciiString SERVER = new AsciiString("Server"); public static final AsciiString SEC_WEBSOCKET_EXTENSIONS = new AsciiString("sec-websocket-extensions");
/** /**
* {@code "Set-Cookie"} * {@code "server"}
*/ */
public static final AsciiString SET_COOKIE = new AsciiString("Set-Cookie"); public static final AsciiString SERVER = new AsciiString("server");
/** /**
* {@code "Set-Cookie2"} * {@code "set-cookie"}
*/ */
public static final AsciiString SET_COOKIE2 = new AsciiString("Set-Cookie2"); public static final AsciiString SET_COOKIE = new AsciiString("set-cookie");
/** /**
* {@code "TE"} * {@code "set-cookie2"}
*/ */
public static final AsciiString TE = new AsciiString("TE"); public static final AsciiString SET_COOKIE2 = new AsciiString("set-cookie2");
/** /**
* {@code "Trailer"} * {@code "te"}
*/ */
public static final AsciiString TRAILER = new AsciiString("Trailer"); public static final AsciiString TE = new AsciiString("te");
/** /**
* {@code "Transfer-Encoding"} * {@code "trailer"}
*/ */
public static final AsciiString TRANSFER_ENCODING = new AsciiString("Transfer-Encoding"); public static final AsciiString TRAILER = new AsciiString("trailer");
/** /**
* {@code "Upgrade"} * {@code "transfer-encoding"}
*/ */
public static final AsciiString UPGRADE = new AsciiString("Upgrade"); public static final AsciiString TRANSFER_ENCODING = new AsciiString("transfer-encoding");
/** /**
* {@code "User-Agent"} * {@code "upgrade"}
*/ */
public static final AsciiString USER_AGENT = new AsciiString("User-Agent"); public static final AsciiString UPGRADE = new AsciiString("upgrade");
/** /**
* {@code "Vary"} * {@code "user-agent"}
*/ */
public static final AsciiString VARY = new AsciiString("Vary"); public static final AsciiString USER_AGENT = new AsciiString("user-agent");
/** /**
* {@code "Via"} * {@code "vary"}
*/ */
public static final AsciiString VIA = new AsciiString("Via"); public static final AsciiString VARY = new AsciiString("vary");
/** /**
* {@code "Warning"} * {@code "via"}
*/ */
public static final AsciiString WARNING = new AsciiString("Warning"); public static final AsciiString VIA = new AsciiString("via");
/** /**
* {@code "WebSocket-Location"} * {@code "warning"}
*/ */
public static final AsciiString WEBSOCKET_LOCATION = new AsciiString("WebSocket-Location"); public static final AsciiString WARNING = new AsciiString("warning");
/** /**
* {@code "WebSocket-Origin"} * {@code "websocket-location"}
*/ */
public static final AsciiString WEBSOCKET_ORIGIN = new AsciiString("WebSocket-Origin"); public static final AsciiString WEBSOCKET_LOCATION = new AsciiString("websocket-location");
/** /**
* {@code "WebSocket-Protocol"} * {@code "websocket-origin"}
*/ */
public static final AsciiString WEBSOCKET_PROTOCOL = new AsciiString("WebSocket-Protocol"); public static final AsciiString WEBSOCKET_ORIGIN = new AsciiString("websocket-origin");
/** /**
* {@code "WWW-Authenticate"} * {@code "websocket-protocol"}
*/ */
public static final AsciiString WWW_AUTHENTICATE = new AsciiString("WWW-Authenticate"); public static final AsciiString WEBSOCKET_PROTOCOL = new AsciiString("websocket-protocol");
/** /**
* {@code "Keep-Alive"} * {@code "www-authenticate"}
*/
public static final AsciiString WWW_AUTHENTICATE = new AsciiString("www-authenticate");
/**
* {@code "keep-alive"}
* @deprecated use {@link #CONNECTION} * @deprecated use {@link #CONNECTION}
*/ */
@Deprecated @Deprecated
public static final AsciiString KEEP_ALIVE = new AsciiString("Keep-Alive"); public static final AsciiString KEEP_ALIVE = new AsciiString("keep-alive");
/** /**
* {@code "Proxy-Connection"} * {@code "proxy-connection"}
* @deprecated use {@link #CONNECTION} * @deprecated use {@link #CONNECTION}
*/ */
@Deprecated @Deprecated
public static final AsciiString PROXY_CONNECTION = new AsciiString("Proxy-Connection"); public static final AsciiString PROXY_CONNECTION = new AsciiString("proxy-connection");
private Names() { private Names() {
} }
@ -359,6 +364,14 @@ public interface HttpHeaders extends TextHeaders {
*/ */
public static final AsciiString APPLICATION_X_WWW_FORM_URLENCODED = public static final AsciiString APPLICATION_X_WWW_FORM_URLENCODED =
new AsciiString("application/x-www-form-urlencoded"); new AsciiString("application/x-www-form-urlencoded");
/**
* {@code "application/octet-stream"}
*/
public static final AsciiString APPLICATION_OCTET_STREAM = new AsciiString("application/octet-stream");
/**
* {@code "text/plain"}
*/
public static final AsciiString TEXT_PLAIN = new AsciiString("text/plain");
/** /**
* {@code "base64"} * {@code "base64"}
*/ */
@ -435,6 +448,10 @@ public interface HttpHeaders extends TextHeaders {
* {@code "multipart/form-data"} * {@code "multipart/form-data"}
*/ */
public static final AsciiString MULTIPART_FORM_DATA = new AsciiString("multipart/form-data"); public static final AsciiString MULTIPART_FORM_DATA = new AsciiString("multipart/form-data");
/**
* {@code "multipart/mixed"}
*/
public static final AsciiString MULTIPART_MIXED = new AsciiString("multipart/mixed");
/** /**
* {@code "must-revalidate"} * {@code "must-revalidate"}
*/ */
@ -491,38 +508,129 @@ public interface HttpHeaders extends TextHeaders {
* {@code "WebSocket"} * {@code "WebSocket"}
*/ */
public static final AsciiString WEBSOCKET = new AsciiString("WebSocket"); public static final AsciiString WEBSOCKET = new AsciiString("WebSocket");
/**
* {@code "name"}
* See {@link #HttpHeaders.Names.CONTENT_DISPOSITION}
*/
public static final AsciiString NAME = new AsciiString("name");
/**
* {@code "filename"}
* See {@link #HttpHeaders.Names.CONTENT_DISPOSITION}
*/
public static final AsciiString FILENAME = new AsciiString("filename");
/**
* {@code "form-data"}
* See {@link #HttpHeaders.Names.CONTENT_DISPOSITION}
*/
public static final AsciiString FORM_DATA = new AsciiString("form-data");
/**
* {@code "attachment"}
* See {@link #HttpHeaders.Names.CONTENT_DISPOSITION}
*/
public static final AsciiString ATTACHMENT = new AsciiString("attachment");
/**
* {@code "file"}
* See {@link #HttpHeaders.Names.CONTENT_DISPOSITION}
*/
public static final AsciiString FILE = new AsciiString("file");
private Values() { private Values() {
} }
} }
@Override @Override
HttpHeaders add(CharSequence name, Object value); HttpHeaders add(CharSequence name, CharSequence value);
@Override @Override
HttpHeaders add(CharSequence name, Iterable<?> values); HttpHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
@Override @Override
HttpHeaders add(CharSequence name, Object... values); HttpHeaders add(CharSequence name, CharSequence... values);
@Override
HttpHeaders addObject(CharSequence name, Object value);
@Override
HttpHeaders addObject(CharSequence name, Iterable<?> values);
@Override
HttpHeaders addObject(CharSequence name, Object... values);
@Override
HttpHeaders addBoolean(CharSequence name, boolean value);
@Override
HttpHeaders addByte(CharSequence name, byte value);
@Override
HttpHeaders addChar(CharSequence name, char value);
@Override
HttpHeaders addShort(CharSequence name, short value);
@Override
HttpHeaders addInt(CharSequence name, int value);
@Override
HttpHeaders addLong(CharSequence name, long value);
@Override
HttpHeaders addFloat(CharSequence name, float value);
@Override
HttpHeaders addDouble(CharSequence name, double value);
@Override @Override
HttpHeaders add(TextHeaders headers); HttpHeaders add(TextHeaders headers);
@Override @Override
HttpHeaders set(CharSequence name, Object value); HttpHeaders set(CharSequence name, CharSequence value);
@Override @Override
HttpHeaders set(CharSequence name, Iterable<?> values); HttpHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
@Override @Override
HttpHeaders set(CharSequence name, Object... values); HttpHeaders set(CharSequence name, CharSequence... values);
@Override
HttpHeaders setObject(CharSequence name, Object value);
@Override
HttpHeaders setObject(CharSequence name, Iterable<?> values);
@Override
HttpHeaders setObject(CharSequence name, Object... values);
@Override
HttpHeaders setBoolean(CharSequence name, boolean value);
@Override
HttpHeaders setByte(CharSequence name, byte value);
@Override
HttpHeaders setChar(CharSequence name, char value);
@Override
HttpHeaders setShort(CharSequence name, short value);
@Override
HttpHeaders setInt(CharSequence name, int value);
@Override
HttpHeaders setLong(CharSequence name, long value);
@Override
HttpHeaders setFloat(CharSequence name, float value);
@Override
HttpHeaders setDouble(CharSequence name, double value);
@Override @Override
HttpHeaders set(TextHeaders headers); HttpHeaders set(TextHeaders headers);
@Override @Override
HttpHeaders clear(); HttpHeaders setAll(TextHeaders headers);
@Override @Override
HttpHeaders forEachEntry(TextHeaderProcessor processor); HttpHeaders clear();
} }

View File

@ -18,9 +18,11 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.TextHeaderProcessor; import io.netty.handler.codec.TextHeaders.EntryVisitor;
final class HttpHeadersEncoder implements TextHeaderProcessor { import java.util.Map.Entry;
final class HttpHeadersEncoder implements EntryVisitor {
private final ByteBuf buf; private final ByteBuf buf;
@ -29,7 +31,9 @@ final class HttpHeadersEncoder implements TextHeaderProcessor {
} }
@Override @Override
public boolean process(CharSequence name, CharSequence value) throws Exception { public boolean visit(Entry<CharSequence, CharSequence> entry) throws Exception {
final CharSequence name = entry.getKey();
final CharSequence value = entry.getValue();
final ByteBuf buf = this.buf; final ByteBuf buf = this.buf;
final int nameLen = name.length(); final int nameLen = name.length();
final int valueLen = value.length(); final int valueLen = value.length();

View File

@ -58,7 +58,7 @@ public class HttpObjectAggregator
HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.EMPTY_BUFFER); HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.EMPTY_BUFFER);
static { static {
TOO_LARGE.headers().set(HttpHeaders.Names.CONTENT_LENGTH, 0); TOO_LARGE.headers().setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
} }
/** /**

View File

@ -550,11 +550,15 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<State> {
do { do {
char firstChar = line.charAt(0); char firstChar = line.charAt(0);
if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) { if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {
List<String> current = trailer.trailingHeaders().getAll(lastHeader); List<CharSequence> current = trailer.trailingHeaders().getAll(lastHeader);
if (!current.isEmpty()) { if (!current.isEmpty()) {
int lastPos = current.size() - 1; int lastPos = current.size() - 1;
String newString = current.get(lastPos) + line.toString().trim(); String lineTrimmed = line.toString().trim();
current.set(lastPos, newString); CharSequence currentLastPos = current.get(lastPos);
StringBuilder b = new StringBuilder(currentLastPos.length() + lineTrimmed.length());
b.append(currentLastPos);
b.append(lineTrimmed);
current.set(lastPos, b.toString());
} else { } else {
// Content-Length, Transfer-Encoding, or Trailer // Content-Length, Transfer-Encoding, or Trailer
} }

View File

@ -20,6 +20,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.FileRegion; import io.netty.channel.FileRegion;
import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import java.util.List; import java.util.List;
@ -137,7 +138,12 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
} else { } else {
ByteBuf buf = ctx.alloc().buffer(); ByteBuf buf = ctx.alloc().buffer();
buf.writeBytes(ZERO_CRLF); buf.writeBytes(ZERO_CRLF);
try {
headers.forEachEntry(new HttpHeadersEncoder(buf)); headers.forEachEntry(new HttpHeadersEncoder(buf));
} catch (Exception ex) {
buf.release();
PlatformDependent.throwException(ex);
}
buf.writeBytes(CRLF); buf.writeBytes(CRLF);
out.add(buf); out.add(buf);
} }

View File

@ -14,9 +14,15 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaders.Names.UPGRADE;
import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
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;
import io.netty.handler.codec.AsciiString;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted; import io.netty.util.ReferenceCounted;
@ -29,11 +35,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*;
import static java.lang.String.*;
/** /**
* A server-side handler that receives HTTP requests and optionally performs a protocol switch if * A server-side handler that receives HTTP requests and optionally performs a protocol switch if
* the requested protocol is supported. Once an upgrade is performed, this handler removes itself * the requested protocol is supported. Once an upgrade is performed, this handler removes itself
@ -249,7 +250,7 @@ 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.
String upgradeHeader = request.headers().get(UPGRADE); CharSequence upgradeHeader = request.headers().get(UPGRADE);
final UpgradeCodec upgradeCodec = selectUpgradeCodec(upgradeHeader); final UpgradeCodec upgradeCodec = selectUpgradeCodec(upgradeHeader);
if (upgradeCodec == null) { if (upgradeCodec == null) {
// None of the requested protocols are supported, don't upgrade. // None of the requested protocols are supported, don't upgrade.
@ -257,15 +258,15 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator {
} }
// Make sure the CONNECTION header is present. // Make sure the CONNECTION header is present.
String connectionHeader = request.headers().get(CONNECTION); CharSequence connectionHeader = request.headers().get(CONNECTION);
if (connectionHeader == null) { if (connectionHeader == null) {
return false; return false;
} }
// 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<String> requiredHeaders = upgradeCodec.requiredUpgradeHeaders();
Set<String> values = splitHeader(connectionHeader); Set<CharSequence> values = splitHeader(connectionHeader);
if (!values.contains(UPGRADE.toString()) || !values.containsAll(requiredHeaders)) { if (!values.contains(UPGRADE) || !values.containsAll(requiredHeaders)) {
return false; return false;
} }
@ -314,8 +315,8 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator {
* Looks up the most desirable supported upgrade codec from the list of choices in the UPGRADE * Looks up the most desirable supported upgrade codec from the list of choices in the UPGRADE
* header. If no suitable codec was found, returns {@code null}. * header. If no suitable codec was found, returns {@code null}.
*/ */
private UpgradeCodec selectUpgradeCodec(String upgradeHeader) { private UpgradeCodec selectUpgradeCodec(CharSequence upgradeHeader) {
Set<String> requestedProtocols = splitHeader(upgradeHeader); Set<CharSequence> requestedProtocols = splitHeader(upgradeHeader);
// Retain only the protocols that are in the protocol map. Maintain the original insertion // Retain only the protocols that are in the protocol map. Maintain the original insertion
// order into the protocolMap, so that the first one in the remaining set is the most // order into the protocolMap, so that the first one in the remaining set is the most
@ -345,9 +346,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 Set<String> splitHeader(String header) { private static Set<CharSequence> splitHeader(CharSequence header) {
StringBuilder builder = new StringBuilder(header.length()); StringBuilder builder = new StringBuilder(header.length());
Set<String> protocols = new TreeSet<String>(CASE_INSENSITIVE_ORDER); Set<CharSequence> protocols = new TreeSet<CharSequence>(AsciiString.CHARSEQUENCE_CASE_INSENSITIVE_ORDER);
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

@ -209,9 +209,9 @@ public final class CorsConfig {
for (Entry<CharSequence, Callable<?>> entry : this.preflightHeaders.entrySet()) { for (Entry<CharSequence, Callable<?>> entry : this.preflightHeaders.entrySet()) {
final Object value = getValue(entry.getValue()); final Object value = getValue(entry.getValue());
if (value instanceof Iterable) { if (value instanceof Iterable) {
preflightHeaders.add(entry.getKey(), (Iterable<?>) value); preflightHeaders.addObject(entry.getKey(), (Iterable<?>) value);
} else { } else {
preflightHeaders.add(entry.getKey(), value); preflightHeaders.addObject(entry.getKey(), value);
} }
} }
return preflightHeaders; return preflightHeaders;

View File

@ -86,7 +86,7 @@ public class CorsHandler extends ChannelHandlerAdapter {
} }
private boolean setOrigin(final HttpResponse response) { private boolean setOrigin(final HttpResponse response) {
final String origin = request.headers().get(ORIGIN); final CharSequence origin = request.headers().get(ORIGIN);
if (origin != null) { if (origin != null) {
if ("null".equals(origin) && config.isNullOriginAllowed()) { if ("null".equals(origin) && config.isNullOriginAllowed()) {
setAnyOrigin(response); setAnyOrigin(response);
@ -116,7 +116,7 @@ public class CorsHandler extends ChannelHandlerAdapter {
return true; return true;
} }
final String origin = request.headers().get(ORIGIN); final CharSequence origin = request.headers().get(ORIGIN);
if (origin == null) { if (origin == null) {
// Not a CORS request so we cannot validate it. It may be a non CORS request. // Not a CORS request so we cannot validate it. It may be a non CORS request.
return true; return true;
@ -141,7 +141,7 @@ public class CorsHandler extends ChannelHandlerAdapter {
setOrigin(response, "*"); setOrigin(response, "*");
} }
private static void setOrigin(final HttpResponse response, final String origin) { private static void setOrigin(final HttpResponse response, final CharSequence origin) {
response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, origin); response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
} }
@ -165,7 +165,7 @@ public class CorsHandler extends ChannelHandlerAdapter {
} }
private void setAllowMethods(final HttpResponse response) { private void setAllowMethods(final HttpResponse response) {
response.headers().set(ACCESS_CONTROL_ALLOW_METHODS, config.allowedRequestMethods()); response.headers().setObject(ACCESS_CONTROL_ALLOW_METHODS, config.allowedRequestMethods());
} }
private void setAllowHeaders(final HttpResponse response) { private void setAllowHeaders(final HttpResponse response) {
@ -173,7 +173,7 @@ public class CorsHandler extends ChannelHandlerAdapter {
} }
private void setMaxAge(final HttpResponse response) { private void setMaxAge(final HttpResponse response) {
response.headers().set(ACCESS_CONTROL_MAX_AGE, config.maxAge()); response.headers().setLong(ACCESS_CONTROL_MAX_AGE, config.maxAge());
} }
@Override @Override

View File

@ -16,6 +16,7 @@
package io.netty.handler.codec.http.multipart; package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@ -29,31 +30,31 @@ final class HttpPostBodyUtil {
/** /**
* HTTP content disposition header name. * HTTP content disposition header name.
*/ */
public static final String CONTENT_DISPOSITION = "Content-Disposition"; public static final String CONTENT_DISPOSITION = HttpHeaders.Names.CONTENT_DISPOSITION.toString();
public static final String NAME = "name"; public static final String NAME = HttpHeaders.Values.NAME.toString();
public static final String FILENAME = "filename"; public static final String FILENAME = HttpHeaders.Values.FILENAME.toString();
/** /**
* Content-disposition value for form data. * Content-disposition value for form data.
*/ */
public static final String FORM_DATA = "form-data"; public static final String FORM_DATA = HttpHeaders.Values.FORM_DATA.toString();
/** /**
* Content-disposition value for file attachment. * Content-disposition value for file attachment.
*/ */
public static final String ATTACHMENT = "attachment"; public static final String ATTACHMENT = HttpHeaders.Values.ATTACHMENT.toString();
/** /**
* Content-disposition value for file attachment. * Content-disposition value for file attachment.
*/ */
public static final String FILE = "file"; public static final String FILE = HttpHeaders.Values.FILE.toString();
/** /**
* HTTP content type body attribute for multiple uploads. * HTTP content type body attribute for multiple uploads.
*/ */
public static final String MULTIPART_MIXED = "multipart/mixed"; public static final String MULTIPART_MIXED = HttpHeaders.Values.MULTIPART_MIXED.toString();
/** /**
* Charset for 8BIT * Charset for 8BIT
@ -68,12 +69,12 @@ final class HttpPostBodyUtil {
/** /**
* Default Content-Type in binary form * Default Content-Type in binary form
*/ */
public static final String DEFAULT_BINARY_CONTENT_TYPE = "application/octet-stream"; public static final String DEFAULT_BINARY_CONTENT_TYPE = HttpHeaders.Values.APPLICATION_OCTET_STREAM.toString();
/** /**
* Default Content-Type in Text form * Default Content-Type in Text form
*/ */
public static final String DEFAULT_TEXT_CONTENT_TYPE = "text/plain"; public static final String DEFAULT_TEXT_CONTENT_TYPE = HttpHeaders.Values.TEXT_PLAIN.toString();
/** /**
* Allowed mechanism for multipart * Allowed mechanism for multipart

View File

@ -181,7 +181,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
this.factory = factory; this.factory = factory;
// Fill default values // Fill default values
setMultipart(this.request.headers().get(HttpHeaders.Names.CONTENT_TYPE)); setMultipart(this.request.headers().getAndConvert(HttpHeaders.Names.CONTENT_TYPE));
if (request instanceof HttpContent) { if (request instanceof HttpContent) {
// Offer automatically if the given request is als type of HttpContent // Offer automatically if the given request is als type of HttpContent
// See #1089 // See #1089

View File

@ -140,7 +140,7 @@ public class HttpPostRequestDecoder implements InterfaceHttpPostRequestDecoder {
*/ */
public static boolean isMultipart(HttpRequest request) { public static boolean isMultipart(HttpRequest request) {
if (request.headers().contains(HttpHeaders.Names.CONTENT_TYPE)) { if (request.headers().contains(HttpHeaders.Names.CONTENT_TYPE)) {
return getMultipartDataBoundary(request.headers().get(HttpHeaders.Names.CONTENT_TYPE)) != null; return getMultipartDataBoundary(request.headers().getAndConvert(HttpHeaders.Names.CONTENT_TYPE)) != null;
} else { } else {
return false; return false;
} }

View File

@ -711,8 +711,8 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
} }
HttpHeaders headers = request.headers(); HttpHeaders headers = request.headers();
List<String> contentTypes = headers.getAll(HttpHeaders.Names.CONTENT_TYPE); List<String> contentTypes = headers.getAllAndConvert(HttpHeaders.Names.CONTENT_TYPE);
List<String> transferEncoding = headers.getAll(HttpHeaders.Names.TRANSFER_ENCODING); List<CharSequence> transferEncoding = headers.getAll(HttpHeaders.Names.TRANSFER_ENCODING);
if (contentTypes != null) { if (contentTypes != null) {
headers.remove(HttpHeaders.Names.CONTENT_TYPE); headers.remove(HttpHeaders.Names.CONTENT_TYPE);
for (String contentType : contentTypes) { for (String contentType : contentTypes) {
@ -747,7 +747,7 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
isChunked = true; isChunked = true;
if (transferEncoding != null) { if (transferEncoding != null) {
headers.remove(HttpHeaders.Names.TRANSFER_ENCODING); headers.remove(HttpHeaders.Names.TRANSFER_ENCODING);
for (String v : transferEncoding) { for (CharSequence v : transferEncoding) {
if (AsciiString.equalsIgnoreCase(v, HttpHeaders.Values.CHUNKED)) { if (AsciiString.equalsIgnoreCase(v, HttpHeaders.Values.CHUNKED)) {
// ignore // ignore
} else { } else {

View File

@ -203,7 +203,7 @@ public abstract class WebSocketClientHandshaker {
// Verify the subprotocol that we received from the server. // Verify the subprotocol that we received from the server.
// This must be one of our expected subprotocols - or null/empty if we didn't want to speak a subprotocol // This must be one of our expected subprotocols - or null/empty if we didn't want to speak a subprotocol
String receivedProtocol = response.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_PROTOCOL); String receivedProtocol = response.headers().getAndConvert(HttpHeaders.Names.SEC_WEBSOCKET_PROTOCOL);
receivedProtocol = receivedProtocol != null ? receivedProtocol.trim() : null; receivedProtocol = receivedProtocol != null ? receivedProtocol.trim() : null;
String expectedProtocol = expectedSubprotocol != null ? expectedSubprotocol : ""; String expectedProtocol = expectedSubprotocol != null ? expectedSubprotocol : "";
boolean protocolValid = false; boolean protocolValid = false;

View File

@ -163,7 +163,7 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
// Set Content-Length to workaround some known defect. // Set Content-Length to workaround some known defect.
// See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html // See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html
headers.set(Names.CONTENT_LENGTH, key3.length); headers.setInt(Names.CONTENT_LENGTH, key3.length);
request.content().writeBytes(key3); request.content().writeBytes(key3);
return request; return request;
} }
@ -198,13 +198,13 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
HttpHeaders headers = response.headers(); HttpHeaders headers = response.headers();
String upgrade = headers.get(Names.UPGRADE); CharSequence upgrade = headers.get(Names.UPGRADE);
if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
+ upgrade); + upgrade);
} }
String connection = headers.get(Names.CONNECTION); CharSequence connection = headers.get(Names.CONNECTION);
if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " throw new WebSocketHandshakeException("Invalid handshake response connection: "
+ connection); + connection);

View File

@ -173,17 +173,17 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status()); throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status());
} }
String upgrade = headers.get(Names.UPGRADE); CharSequence upgrade = headers.get(Names.UPGRADE);
if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
} }
String connection = headers.get(Names.CONNECTION); CharSequence connection = headers.get(Names.CONNECTION);
if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
} }
String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); CharSequence accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) { if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format( throw new WebSocketHandshakeException(String.format(
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString)); "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));

View File

@ -174,17 +174,17 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status()); throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status());
} }
String upgrade = headers.get(Names.UPGRADE); CharSequence upgrade = headers.get(Names.UPGRADE);
if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
} }
String connection = headers.get(Names.CONNECTION); CharSequence connection = headers.get(Names.CONNECTION);
if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
} }
String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); CharSequence accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) { if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format( throw new WebSocketHandshakeException(String.format(
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString)); "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));

View File

@ -184,17 +184,17 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status()); throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status());
} }
String upgrade = headers.get(Names.UPGRADE); CharSequence upgrade = headers.get(Names.UPGRADE);
if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
} }
String connection = headers.get(Names.CONNECTION); CharSequence connection = headers.get(Names.CONNECTION);
if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
} }
String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); CharSequence accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) { if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format( throw new WebSocketHandshakeException(String.format(
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString)); "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));

View File

@ -133,7 +133,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
// New handshake getMethod with a challenge: // New handshake getMethod with a challenge:
res.headers().add(SEC_WEBSOCKET_ORIGIN, req.headers().get(ORIGIN)); res.headers().add(SEC_WEBSOCKET_ORIGIN, req.headers().get(ORIGIN));
res.headers().add(SEC_WEBSOCKET_LOCATION, uri()); res.headers().add(SEC_WEBSOCKET_LOCATION, uri());
String subprotocols = req.headers().get(SEC_WEBSOCKET_PROTOCOL); String subprotocols = req.headers().getAndConvert(SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) { if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols); String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) { if (selectedSubprotocol == null) {
@ -146,8 +146,8 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
} }
// Calculate the answer of the challenge. // Calculate the answer of the challenge.
String key1 = req.headers().get(SEC_WEBSOCKET_KEY1); String key1 = req.headers().getAndConvert(SEC_WEBSOCKET_KEY1);
String key2 = req.headers().get(SEC_WEBSOCKET_KEY2); String key2 = req.headers().getAndConvert(SEC_WEBSOCKET_KEY2);
int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) / int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) /
BEGINNING_SPACE.matcher(key1).replaceAll("").length()); BEGINNING_SPACE.matcher(key1).replaceAll("").length());
int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) / int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) /
@ -162,7 +162,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
// Old Hixie 75 handshake getMethod with no challenge: // Old Hixie 75 handshake getMethod with no challenge:
res.headers().add(WEBSOCKET_ORIGIN, req.headers().get(ORIGIN)); res.headers().add(WEBSOCKET_ORIGIN, req.headers().get(ORIGIN));
res.headers().add(WEBSOCKET_LOCATION, uri()); res.headers().add(WEBSOCKET_LOCATION, uri());
String protocol = req.headers().get(WEBSOCKET_PROTOCOL); String protocol = req.headers().getAndConvert(WEBSOCKET_PROTOCOL);
if (protocol != null) { if (protocol != null) {
res.headers().add(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol)); res.headers().add(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
} }

View File

@ -105,7 +105,7 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
res.headers().add(headers); res.headers().add(headers);
} }
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); CharSequence key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) { if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key"); throw new WebSocketHandshakeException("not a WebSocket request: missing key");
} }
@ -120,7 +120,7 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
res.headers().add(Names.UPGRADE, WEBSOCKET); res.headers().add(Names.UPGRADE, WEBSOCKET);
res.headers().add(Names.CONNECTION, Names.UPGRADE); res.headers().add(Names.CONNECTION, Names.UPGRADE);
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept); res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); String subprotocols = req.headers().getAndConvert(Names.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) { if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols); String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) { if (selectedSubprotocol == null) {

View File

@ -104,7 +104,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
res.headers().add(headers); res.headers().add(headers);
} }
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); CharSequence key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) { if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key"); throw new WebSocketHandshakeException("not a WebSocket request: missing key");
} }
@ -119,7 +119,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
res.headers().add(Names.UPGRADE, WEBSOCKET); res.headers().add(Names.UPGRADE, WEBSOCKET);
res.headers().add(Names.CONNECTION, Names.UPGRADE); res.headers().add(Names.CONNECTION, Names.UPGRADE);
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept); res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); String subprotocols = req.headers().getAndConvert(Names.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) { if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols); String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) { if (selectedSubprotocol == null) {

View File

@ -102,7 +102,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
res.headers().add(headers); res.headers().add(headers);
} }
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); CharSequence key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) { if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key"); throw new WebSocketHandshakeException("not a WebSocket request: missing key");
} }
@ -117,7 +117,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
res.headers().add(Names.UPGRADE, WEBSOCKET); res.headers().add(Names.UPGRADE, WEBSOCKET);
res.headers().add(Names.CONNECTION, Names.UPGRADE); res.headers().add(Names.CONNECTION, Names.UPGRADE);
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept); res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); String subprotocols = req.headers().getAndConvert(Names.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) { if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols); String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) { if (selectedSubprotocol == null) {

View File

@ -86,7 +86,7 @@ public class WebSocketServerHandshakerFactory {
*/ */
public WebSocketServerHandshaker newHandshaker(HttpRequest req) { public WebSocketServerHandshaker newHandshaker(HttpRequest req) {
String version = req.headers().get(Names.SEC_WEBSOCKET_VERSION); CharSequence version = req.headers().get(Names.SEC_WEBSOCKET_VERSION);
if (version != null) { if (version != null) {
if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) { if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification). // Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).

View File

@ -63,7 +63,7 @@ public class WebSocketClientExtensionHandler extends ChannelHandlerAdapter {
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade((HttpRequest) msg)) { if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade((HttpRequest) msg)) {
HttpRequest request = (HttpRequest) msg; HttpRequest request = (HttpRequest) msg;
String headerValue = request.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); String headerValue = request.headers().getAndConvert(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS);
for (WebSocketClientExtensionHandshaker extentionHandshaker : extensionHandshakers) { for (WebSocketClientExtensionHandshaker extentionHandshaker : extensionHandshakers) {
WebSocketExtensionData extensionData = extentionHandshaker.newRequestData(); WebSocketExtensionData extensionData = extentionHandshaker.newRequestData();
@ -84,7 +84,7 @@ public class WebSocketClientExtensionHandler extends ChannelHandlerAdapter {
HttpResponse response = (HttpResponse) msg; HttpResponse response = (HttpResponse) msg;
if (WebSocketExtensionUtil.isWebsocketUpgrade(response)) { if (WebSocketExtensionUtil.isWebsocketUpgrade(response)) {
String extensionsHeader = response.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); String extensionsHeader = response.headers().getAndConvert(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS);
if (extensionsHeader != null) { if (extensionsHeader != null) {
List<WebSocketExtensionData> extensions = List<WebSocketExtensionData> extensions =

View File

@ -69,7 +69,7 @@ public class WebSocketServerExtensionHandler extends ChannelHandlerAdapter {
HttpRequest request = (HttpRequest) msg; HttpRequest request = (HttpRequest) msg;
if (WebSocketExtensionUtil.isWebsocketUpgrade(request)) { if (WebSocketExtensionUtil.isWebsocketUpgrade(request)) {
String extensionsHeader = request.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); String extensionsHeader = request.headers().getAndConvert(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS);
if (extensionsHeader != null) { if (extensionsHeader != null) {
List<WebSocketExtensionData> extensions = List<WebSocketExtensionData> extensions =
@ -107,7 +107,7 @@ public class WebSocketServerExtensionHandler extends ChannelHandlerAdapter {
if (msg instanceof HttpResponse && if (msg instanceof HttpResponse &&
WebSocketExtensionUtil.isWebsocketUpgrade((HttpResponse) msg) && validExtensions != null) { WebSocketExtensionUtil.isWebsocketUpgrade((HttpResponse) msg) && validExtensions != null) {
HttpResponse response = (HttpResponse) msg; HttpResponse response = (HttpResponse) msg;
String headerValue = response.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); String headerValue = response.headers().getAndConvert(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS);
for (WebSocketServerExtension extension : validExtensions) { for (WebSocketServerExtension extension : validExtensions) {
WebSocketExtensionData extensionData = extension.newReponseData(); WebSocketExtensionData extensionData = extension.newReponseData();

View File

@ -17,31 +17,16 @@ package io.netty.handler.codec.spdy;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.DefaultTextHeaders;
import io.netty.handler.codec.TextHeaderProcessor; import io.netty.handler.codec.Headers;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.TextHeaders;
import java.util.Locale; import java.util.Locale;
public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeaders { public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeaders {
private static final Headers.ValueConverter<CharSequence> SPDY_VALUE_CONVERTER =
new DefaultTextValueTypeConverter() {
@Override @Override
protected CharSequence convertName(CharSequence name) { public CharSequence convert(Object value) {
name = super.convertName(name);
if (name instanceof AsciiString) {
name = ((AsciiString) name).toLowerCase();
} else {
name = name.toString().toLowerCase(Locale.US);
}
SpdyCodecUtil.validateHeaderName(name);
return name;
}
@Override
protected CharSequence convertValue(Object value) {
if (value == null) {
throw new NullPointerException("value");
}
CharSequence seq; CharSequence seq;
if (value instanceof CharSequence) { if (value instanceof CharSequence) {
seq = (CharSequence) value; seq = (CharSequence) value;
@ -52,25 +37,109 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
SpdyCodecUtil.validateHeaderValue(seq); SpdyCodecUtil.validateHeaderValue(seq);
return seq; return seq;
} }
};
private static final NameConverter<CharSequence> SPDY_NAME_CONVERTER = new NameConverter<CharSequence>() {
@Override
public CharSequence convertName(CharSequence name) {
if (name instanceof AsciiString) {
name = ((AsciiString) name).toLowerCase();
} else {
name = name.toString().toLowerCase(Locale.US);
}
SpdyCodecUtil.validateHeaderName(name);
return name;
}
};
public DefaultSpdyHeaders() {
super(true, SPDY_VALUE_CONVERTER, SPDY_NAME_CONVERTER);
}
@Override @Override
public SpdyHeaders add(CharSequence name, Object value) { public SpdyHeaders add(CharSequence name, CharSequence value) {
super.add(name, value); super.add(name, value);
return this; return this;
} }
@Override @Override
public SpdyHeaders add(CharSequence name, Iterable<?> values) { public SpdyHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@Override @Override
public SpdyHeaders add(CharSequence name, Object... values) { public SpdyHeaders add(CharSequence name, CharSequence... values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@Override
public SpdyHeaders addObject(CharSequence name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public SpdyHeaders addObject(CharSequence name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public SpdyHeaders addObject(CharSequence name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public SpdyHeaders addBoolean(CharSequence name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public SpdyHeaders addChar(CharSequence name, char value) {
super.addChar(name, value);
return this;
}
@Override
public SpdyHeaders addByte(CharSequence name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public SpdyHeaders addShort(CharSequence name, short value) {
super.addShort(name, value);
return this;
}
@Override
public SpdyHeaders addInt(CharSequence name, int value) {
super.addInt(name, value);
return this;
}
@Override
public SpdyHeaders addLong(CharSequence name, long value) {
super.addLong(name, value);
return this;
}
@Override
public SpdyHeaders addFloat(CharSequence name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public SpdyHeaders addDouble(CharSequence name, double value) {
super.addDouble(name, value);
return this;
}
@Override @Override
public SpdyHeaders add(TextHeaders headers) { public SpdyHeaders add(TextHeaders headers) {
super.add(headers); super.add(headers);
@ -78,23 +147,89 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
} }
@Override @Override
public SpdyHeaders set(CharSequence name, Object value) { public SpdyHeaders set(CharSequence name, CharSequence value) {
super.set(name, value); super.set(name, value);
return this; return this;
} }
@Override @Override
public SpdyHeaders set(CharSequence name, Object... values) { public SpdyHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@Override @Override
public SpdyHeaders set(CharSequence name, Iterable<?> values) { public SpdyHeaders set(CharSequence name, CharSequence... values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@Override
public SpdyHeaders setObject(CharSequence name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public SpdyHeaders setObject(CharSequence name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public SpdyHeaders setObject(CharSequence name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public SpdyHeaders setBoolean(CharSequence name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public SpdyHeaders setChar(CharSequence name, char value) {
super.setChar(name, value);
return this;
}
@Override
public SpdyHeaders setByte(CharSequence name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public SpdyHeaders setShort(CharSequence name, short value) {
super.setShort(name, value);
return this;
}
@Override
public SpdyHeaders setInt(CharSequence name, int value) {
super.setInt(name, value);
return this;
}
@Override
public SpdyHeaders setLong(CharSequence name, long value) {
super.setLong(name, value);
return this;
}
@Override
public SpdyHeaders setFloat(CharSequence name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public SpdyHeaders setDouble(CharSequence name, double value) {
super.setDouble(name, value);
return this;
}
@Override @Override
public SpdyHeaders set(TextHeaders headers) { public SpdyHeaders set(TextHeaders headers) {
super.set(headers); super.set(headers);
@ -102,14 +237,14 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
} }
@Override @Override
public SpdyHeaders clear() { public SpdyHeaders setAll(TextHeaders headers) {
super.clear(); super.setAll(headers);
return this; return this;
} }
@Override @Override
public SpdyHeaders forEachEntry(TextHeaderProcessor processor) { public SpdyHeaders clear() {
super.forEachEntry(processor); super.clear();
return this; return this;
} }
} }

View File

@ -98,7 +98,7 @@ public class DefaultSpdyHeadersFrame extends DefaultSpdyStreamFrame
} }
protected void appendHeaders(StringBuilder buf) { protected void appendHeaders(StringBuilder buf) {
for (Map.Entry<String, String> e: headers()) { for (Map.Entry<CharSequence, CharSequence> e: headers()) {
buf.append(" "); buf.append(" ");
buf.append(e.getKey()); buf.append(e.getKey());
buf.append(": "); buf.append(": ");

View File

@ -15,14 +15,15 @@
*/ */
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_MAX_NV_LENGTH;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.handler.codec.AsciiString;
import io.netty.util.CharsetUtil;
import java.util.Set; import java.util.Set;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder { public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
private final int version; private final int version;
@ -44,7 +45,7 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
@Override @Override
public ByteBuf encode(ByteBufAllocator alloc, SpdyHeadersFrame frame) throws Exception { public ByteBuf encode(ByteBufAllocator alloc, SpdyHeadersFrame frame) throws Exception {
Set<String> names = frame.headers().names(); Set<CharSequence> names = frame.headers().names();
int numHeaders = names.size(); int numHeaders = names.size();
if (numHeaders == 0) { if (numHeaders == 0) {
return Unpooled.EMPTY_BUFFER; return Unpooled.EMPTY_BUFFER;
@ -55,15 +56,15 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
} }
ByteBuf headerBlock = alloc.heapBuffer(); ByteBuf headerBlock = alloc.heapBuffer();
writeLengthField(headerBlock, numHeaders); writeLengthField(headerBlock, numHeaders);
for (String name: names) { for (CharSequence name: names) {
byte[] nameBytes = name.getBytes("UTF-8"); byte[] nameBytes = AsciiString.getBytes(name, CharsetUtil.UTF_8);
writeLengthField(headerBlock, nameBytes.length); writeLengthField(headerBlock, nameBytes.length);
headerBlock.writeBytes(nameBytes); headerBlock.writeBytes(nameBytes);
int savedIndex = headerBlock.writerIndex(); int savedIndex = headerBlock.writerIndex();
int valueLength = 0; int valueLength = 0;
writeLengthField(headerBlock, valueLength); writeLengthField(headerBlock, valueLength);
for (String value: frame.headers().getAll(name)) { for (CharSequence value: frame.headers().getAll(name)) {
byte[] valueBytes = value.getBytes("UTF-8"); byte[] valueBytes = AsciiString.getBytes(value, CharsetUtil.UTF_8);
if (valueBytes.length > 0) { if (valueBytes.length > 0) {
headerBlock.writeBytes(valueBytes); headerBlock.writeBytes(valueBytes);
headerBlock.writeByte(0); headerBlock.writeByte(0);

View File

@ -16,7 +16,6 @@
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.TextHeaderProcessor;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.TextHeaders;
/** /**
@ -58,32 +57,98 @@ public interface SpdyHeaders extends TextHeaders {
} }
@Override @Override
SpdyHeaders add(CharSequence name, Object value); SpdyHeaders add(CharSequence name, CharSequence value);
@Override @Override
SpdyHeaders add(CharSequence name, Iterable<?> values); SpdyHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
@Override @Override
SpdyHeaders add(CharSequence name, Object... values); SpdyHeaders add(CharSequence name, CharSequence... values);
@Override
SpdyHeaders addObject(CharSequence name, Object value);
@Override
SpdyHeaders addObject(CharSequence name, Iterable<?> values);
@Override
SpdyHeaders addObject(CharSequence name, Object... values);
@Override
SpdyHeaders addBoolean(CharSequence name, boolean value);
@Override
SpdyHeaders addByte(CharSequence name, byte value);
@Override
SpdyHeaders addChar(CharSequence name, char value);
@Override
SpdyHeaders addShort(CharSequence name, short value);
@Override
SpdyHeaders addInt(CharSequence name, int value);
@Override
SpdyHeaders addLong(CharSequence name, long value);
@Override
SpdyHeaders addFloat(CharSequence name, float value);
@Override
SpdyHeaders addDouble(CharSequence name, double value);
@Override @Override
SpdyHeaders add(TextHeaders headers); SpdyHeaders add(TextHeaders headers);
@Override @Override
SpdyHeaders set(CharSequence name, Object value); SpdyHeaders set(CharSequence name, CharSequence value);
@Override @Override
SpdyHeaders set(CharSequence name, Iterable<?> values); SpdyHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
@Override @Override
SpdyHeaders set(CharSequence name, Object... values); SpdyHeaders set(CharSequence name, CharSequence... values);
@Override
SpdyHeaders setBoolean(CharSequence name, boolean value);
@Override
SpdyHeaders setByte(CharSequence name, byte value);
@Override
SpdyHeaders setChar(CharSequence name, char value);
@Override
SpdyHeaders setShort(CharSequence name, short value);
@Override
SpdyHeaders setInt(CharSequence name, int value);
@Override
SpdyHeaders setLong(CharSequence name, long value);
@Override
SpdyHeaders setFloat(CharSequence name, float value);
@Override
SpdyHeaders setDouble(CharSequence name, double value);
@Override
SpdyHeaders setObject(CharSequence name, Object value);
@Override
SpdyHeaders setObject(CharSequence name, Iterable<?> values);
@Override
SpdyHeaders setObject(CharSequence name, Object... values);
@Override @Override
SpdyHeaders set(TextHeaders headers); SpdyHeaders set(TextHeaders headers);
@Override @Override
SpdyHeaders clear(); SpdyHeaders setAll(TextHeaders headers);
@Override @Override
SpdyHeaders forEachEntry(TextHeaderProcessor processor); SpdyHeaders clear();
} }

View File

@ -145,7 +145,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
return; return;
} }
String URL = spdySynStreamFrame.headers().get(PATH); CharSequence URL = spdySynStreamFrame.headers().get(PATH);
spdySynStreamFrame.headers().remove(PATH); spdySynStreamFrame.headers().remove(PATH);
// If a client receives a SYN_STREAM without a 'url' header // If a client receives a SYN_STREAM without a 'url' header
@ -171,9 +171,9 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
createHttpResponse(ctx, spdySynStreamFrame, validateHeaders); createHttpResponse(ctx, spdySynStreamFrame, validateHeaders);
// Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers // Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers
httpResponseWithEntity.headers().set(Names.STREAM_ID, streamId); httpResponseWithEntity.headers().setInt(Names.STREAM_ID, streamId);
httpResponseWithEntity.headers().set(Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId); httpResponseWithEntity.headers().setInt(Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId);
httpResponseWithEntity.headers().set(Names.PRIORITY, spdySynStreamFrame.priority()); httpResponseWithEntity.headers().setByte(Names.PRIORITY, spdySynStreamFrame.priority());
httpResponseWithEntity.headers().set(Names.URL, URL); httpResponseWithEntity.headers().set(Names.URL, URL);
if (spdySynStreamFrame.isLast()) { if (spdySynStreamFrame.isLast()) {
@ -197,8 +197,8 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
spdySynReplyFrame.setLast(true); spdySynReplyFrame.setLast(true);
SpdyHeaders frameHeaders = spdySynReplyFrame.headers(); SpdyHeaders frameHeaders = spdySynReplyFrame.headers();
frameHeaders.set(STATUS, HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE); frameHeaders.setInt(STATUS, HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.code());
frameHeaders.set(VERSION, HttpVersion.HTTP_1_0); frameHeaders.setObject(VERSION, HttpVersion.HTTP_1_0);
ctx.writeAndFlush(spdySynReplyFrame); ctx.writeAndFlush(spdySynReplyFrame);
return; return;
} }
@ -207,7 +207,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame); FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame);
// Set the Stream-ID as a header // Set the Stream-ID as a header
httpRequestWithEntity.headers().set(Names.STREAM_ID, streamId); httpRequestWithEntity.headers().setInt(Names.STREAM_ID, streamId);
if (spdySynStreamFrame.isLast()) { if (spdySynStreamFrame.isLast()) {
out.add(httpRequestWithEntity); out.add(httpRequestWithEntity);
@ -222,8 +222,8 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
spdySynReplyFrame.setLast(true); spdySynReplyFrame.setLast(true);
SpdyHeaders frameHeaders = spdySynReplyFrame.headers(); SpdyHeaders frameHeaders = spdySynReplyFrame.headers();
frameHeaders.set(STATUS, HttpResponseStatus.BAD_REQUEST); frameHeaders.setInt(STATUS, HttpResponseStatus.BAD_REQUEST.code());
frameHeaders.set(VERSION, HttpVersion.HTTP_1_0); frameHeaders.setObject(VERSION, HttpVersion.HTTP_1_0);
ctx.writeAndFlush(spdySynReplyFrame); ctx.writeAndFlush(spdySynReplyFrame);
} }
} }
@ -246,7 +246,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
FullHttpResponse httpResponseWithEntity = createHttpResponse(ctx, spdySynReplyFrame, validateHeaders); FullHttpResponse httpResponseWithEntity = createHttpResponse(ctx, spdySynReplyFrame, validateHeaders);
// Set the Stream-ID as a header // Set the Stream-ID as a header
httpResponseWithEntity.headers().set(Names.STREAM_ID, streamId); httpResponseWithEntity.headers().setInt(Names.STREAM_ID, streamId);
if (spdySynReplyFrame.isLast()) { if (spdySynReplyFrame.isLast()) {
HttpHeaderUtil.setContentLength(httpResponseWithEntity, 0); HttpHeaderUtil.setContentLength(httpResponseWithEntity, 0);
@ -276,7 +276,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
// Ignore trailers in a truncated HEADERS frame. // Ignore trailers in a truncated HEADERS frame.
if (!spdyHeadersFrame.isTruncated()) { if (!spdyHeadersFrame.isTruncated()) {
for (Map.Entry<String, String> e: spdyHeadersFrame.headers()) { for (Map.Entry<CharSequence, CharSequence> e: spdyHeadersFrame.headers()) {
fullHttpMessage.headers().add(e.getKey(), e.getValue()); fullHttpMessage.headers().add(e.getKey(), e.getValue());
} }
} }
@ -327,9 +327,9 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
throws Exception { throws Exception {
// Create the first line of the request from the name/value pairs // Create the first line of the request from the name/value pairs
SpdyHeaders headers = requestFrame.headers(); SpdyHeaders headers = requestFrame.headers();
HttpMethod method = HttpMethod.valueOf(headers.get(METHOD)); HttpMethod method = HttpMethod.valueOf(headers.getAndConvert(METHOD));
String url = headers.get(PATH); String url = headers.getAndConvert(PATH);
HttpVersion httpVersion = HttpVersion.valueOf(headers.get(VERSION)); HttpVersion httpVersion = HttpVersion.valueOf(headers.getAndConvert(VERSION));
headers.remove(METHOD); headers.remove(METHOD);
headers.remove(PATH); headers.remove(PATH);
headers.remove(VERSION); headers.remove(VERSION);
@ -340,11 +340,11 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
headers.remove(SCHEME); headers.remove(SCHEME);
// Replace the SPDY host header with the HTTP host header // Replace the SPDY host header with the HTTP host header
String host = headers.get(HOST); CharSequence host = headers.get(HOST);
headers.remove(HOST); headers.remove(HOST);
req.headers().set(HttpHeaders.Names.HOST, host); req.headers().set(HttpHeaders.Names.HOST, host);
for (Map.Entry<String, String> e: requestFrame.headers()) { for (Map.Entry<CharSequence, CharSequence> e: requestFrame.headers()) {
req.headers().add(e.getKey(), e.getValue()); req.headers().add(e.getKey(), e.getValue());
} }
@ -363,12 +363,12 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
// Create the first line of the response from the name/value pairs // Create the first line of the response from the name/value pairs
SpdyHeaders headers = responseFrame.headers(); SpdyHeaders headers = responseFrame.headers();
HttpResponseStatus status = HttpResponseStatus.parseLine(headers.get(STATUS)); HttpResponseStatus status = HttpResponseStatus.parseLine(headers.get(STATUS));
HttpVersion version = HttpVersion.valueOf(headers.get(VERSION)); HttpVersion version = HttpVersion.valueOf(headers.getAndConvert(VERSION));
headers.remove(STATUS); headers.remove(STATUS);
headers.remove(VERSION); headers.remove(VERSION);
FullHttpResponse res = new DefaultFullHttpResponse(version, status, ctx.alloc().buffer(), validateHeaders); FullHttpResponse res = new DefaultFullHttpResponse(version, status, ctx.alloc().buffer(), validateHeaders);
for (Map.Entry<String, String> e: responseFrame.headers()) { for (Map.Entry<CharSequence, CharSequence> e: responseFrame.headers()) {
res.headers().add(e.getKey(), e.getValue()); res.headers().add(e.getKey(), e.getValue());
} }

View File

@ -185,7 +185,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
} else { } else {
// Create SPDY HEADERS frame out of trailers // Create SPDY HEADERS frame out of trailers
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId); SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId);
for (Map.Entry<String, String> entry: trailers) { for (Map.Entry<CharSequence, CharSequence> entry: trailers) {
spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue()); spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue());
} }
@ -212,8 +212,8 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
int streamID = httpHeaders.getInt(Names.STREAM_ID); int streamID = httpHeaders.getInt(Names.STREAM_ID);
int associatedToStreamId = httpHeaders.getInt(Names.ASSOCIATED_TO_STREAM_ID, 0); int associatedToStreamId = httpHeaders.getInt(Names.ASSOCIATED_TO_STREAM_ID, 0);
byte priority = (byte) httpHeaders.getInt(Names.PRIORITY, 0); byte priority = (byte) httpHeaders.getInt(Names.PRIORITY, 0);
String URL = httpHeaders.get(Names.URL); CharSequence URL = httpHeaders.get(Names.URL);
String scheme = httpHeaders.get(Names.SCHEME); CharSequence scheme = httpHeaders.get(Names.SCHEME);
httpHeaders.remove(Names.STREAM_ID); httpHeaders.remove(Names.STREAM_ID);
httpHeaders.remove(Names.ASSOCIATED_TO_STREAM_ID); httpHeaders.remove(Names.ASSOCIATED_TO_STREAM_ID);
httpHeaders.remove(Names.PRIORITY); httpHeaders.remove(Names.PRIORITY);
@ -234,21 +234,21 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
SpdyHeaders frameHeaders = spdySynStreamFrame.headers(); SpdyHeaders frameHeaders = spdySynStreamFrame.headers();
if (httpMessage instanceof FullHttpRequest) { if (httpMessage instanceof FullHttpRequest) {
HttpRequest httpRequest = (HttpRequest) httpMessage; HttpRequest httpRequest = (HttpRequest) httpMessage;
frameHeaders.set(METHOD, httpRequest.method()); frameHeaders.setObject(METHOD, httpRequest.method());
frameHeaders.set(PATH, httpRequest.uri()); frameHeaders.set(PATH, httpRequest.uri());
frameHeaders.set(VERSION, httpMessage.protocolVersion()); frameHeaders.setObject(VERSION, httpMessage.protocolVersion());
} }
if (httpMessage instanceof HttpResponse) { if (httpMessage instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) httpMessage; HttpResponse httpResponse = (HttpResponse) httpMessage;
frameHeaders.set(STATUS, httpResponse.status()); frameHeaders.setInt(STATUS, httpResponse.status().code());
frameHeaders.set(PATH, URL); frameHeaders.set(PATH, URL);
frameHeaders.set(VERSION, httpMessage.protocolVersion()); frameHeaders.setObject(VERSION, httpMessage.protocolVersion());
spdySynStreamFrame.setUnidirectional(true); spdySynStreamFrame.setUnidirectional(true);
} }
// Replace the HTTP host header with the SPDY host header // Replace the HTTP host header with the SPDY host header
if (spdyVersion >= 3) { if (spdyVersion >= 3) {
CharSequence host = httpHeaders.getUnconverted(HttpHeaders.Names.HOST); CharSequence host = httpHeaders.get(HttpHeaders.Names.HOST);
httpHeaders.remove(HttpHeaders.Names.HOST); httpHeaders.remove(HttpHeaders.Names.HOST);
frameHeaders.set(HOST, host); frameHeaders.set(HOST, host);
} }
@ -260,7 +260,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
frameHeaders.set(SCHEME, scheme); frameHeaders.set(SCHEME, scheme);
// Transfer the remaining HTTP headers // Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpHeaders) { for (Map.Entry<CharSequence, CharSequence> entry: httpHeaders) {
frameHeaders.add(entry.getKey(), entry.getValue()); frameHeaders.add(entry.getKey(), entry.getValue());
} }
currentStreamId = spdySynStreamFrame.streamId(); currentStreamId = spdySynStreamFrame.streamId();
@ -286,11 +286,11 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
SpdyHeaders frameHeaders = spdySynReplyFrame.headers(); SpdyHeaders frameHeaders = spdySynReplyFrame.headers();
// Unfold the first line of the response into name/value pairs // Unfold the first line of the response into name/value pairs
frameHeaders.set(STATUS, httpResponse.status()); frameHeaders.setInt(STATUS, httpResponse.status().code());
frameHeaders.set(VERSION, httpResponse.protocolVersion()); frameHeaders.setObject(VERSION, httpResponse.protocolVersion());
// Transfer the remaining HTTP headers // Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpHeaders) { for (Map.Entry<CharSequence, CharSequence> entry: httpHeaders) {
spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue());
} }

View File

@ -44,7 +44,7 @@ public class SpdyHttpResponseStreamIdHandler extends
protected void encode(ChannelHandlerContext ctx, HttpMessage msg, List<Object> out) throws Exception { protected void encode(ChannelHandlerContext ctx, HttpMessage msg, List<Object> out) throws Exception {
Integer id = ids.poll(); Integer id = ids.poll();
if (id != null && id.intValue() != NO_ID && !msg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) { if (id != null && id.intValue() != NO_ID && !msg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) {
msg.headers().set(Names.STREAM_ID, id); msg.headers().setInt(Names.STREAM_ID, id);
} }
out.add(ReferenceCountUtil.retain(msg)); out.add(ReferenceCountUtil.retain(msg));

View File

@ -197,7 +197,7 @@ public class HttpContentCompressorTest {
FullHttpResponse res = new DefaultFullHttpResponse( FullHttpResponse res = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.copiedBuffer("Hello, World", CharsetUtil.US_ASCII)); Unpooled.copiedBuffer("Hello, World", CharsetUtil.US_ASCII));
res.headers().set(Names.CONTENT_LENGTH, res.content().readableBytes()); res.headers().setInt(Names.CONTENT_LENGTH, res.content().readableBytes());
ch.writeOutbound(res); ch.writeOutbound(res);
assertEncodedResponse(ch); assertEncodedResponse(ch);
@ -309,8 +309,8 @@ public class HttpContentCompressorTest {
HttpResponse res = (HttpResponse) o; HttpResponse res = (HttpResponse) o;
assertThat(res, is(not(instanceOf(HttpContent.class)))); assertThat(res, is(not(instanceOf(HttpContent.class))));
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().getAndConvert(Names.TRANSFER_ENCODING), is("chunked"));
assertThat(res.headers().get(Names.CONTENT_LENGTH), is(nullValue())); assertThat(res.headers().get(Names.CONTENT_LENGTH), is(nullValue()));
assertThat(res.headers().get(Names.CONTENT_ENCODING), is("gzip")); assertThat(res.headers().getAndConvert(Names.CONTENT_ENCODING), is("gzip"));
} }
} }

View File

@ -161,7 +161,7 @@ public class HttpContentEncoderTest {
FullHttpResponse res = new DefaultFullHttpResponse( FullHttpResponse res = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(new byte[42])); HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(new byte[42]));
res.headers().set(Names.CONTENT_LENGTH, 42); res.headers().setInt(Names.CONTENT_LENGTH, 42);
ch.writeOutbound(res); ch.writeOutbound(res);
assertEncodedResponse(ch); assertEncodedResponse(ch);
@ -260,8 +260,8 @@ public class HttpContentEncoderTest {
HttpResponse res = (HttpResponse) o; HttpResponse res = (HttpResponse) o;
assertThat(res, is(not(instanceOf(HttpContent.class)))); assertThat(res, is(not(instanceOf(HttpContent.class))));
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().getAndConvert(Names.TRANSFER_ENCODING), is("chunked"));
assertThat(res.headers().get(Names.CONTENT_LENGTH), is(nullValue())); assertThat(res.headers().get(Names.CONTENT_LENGTH), is(nullValue()));
assertThat(res.headers().get(Names.CONTENT_ENCODING), is("test")); assertThat(res.headers().getAndConvert(Names.CONTENT_ENCODING), is("test"));
} }
} }

View File

@ -43,7 +43,7 @@ public class HttpHeaderUtilTest {
assertEquals("1", headers.get("Foo")); assertEquals("1", headers.get("Foo"));
List<String> values = headers.getAll("Foo"); List<CharSequence> values = headers.getAll("Foo");
assertEquals(2, values.size()); assertEquals(2, values.size());
assertEquals("1", values.get(0)); assertEquals("1", values.get(0));
assertEquals("2", values.get(1)); assertEquals("2", values.get(1));

View File

@ -42,7 +42,7 @@ public class HttpObjectAggregatorTest {
EmbeddedChannel embedder = new EmbeddedChannel(aggr); EmbeddedChannel embedder = new EmbeddedChannel(aggr);
HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost");
message.headers().set("X-Test", true); message.headers().setBoolean("X-Test", true);
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
HttpContent chunk3 = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER); HttpContent chunk3 = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER);
@ -80,12 +80,12 @@ public class HttpObjectAggregatorTest {
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
EmbeddedChannel embedder = new EmbeddedChannel(aggr); EmbeddedChannel embedder = new EmbeddedChannel(aggr);
HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost");
message.headers().set("X-Test", true); message.headers().setBoolean("X-Test", true);
HttpHeaderUtil.setTransferEncodingChunked(message, true); HttpHeaderUtil.setTransferEncodingChunked(message, true);
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
LastHttpContent trailer = new DefaultLastHttpContent(); LastHttpContent trailer = new DefaultLastHttpContent();
trailer.trailingHeaders().set("X-Trailer", true); trailer.trailingHeaders().setObject("X-Trailer", true);
assertFalse(embedder.writeInbound(message)); assertFalse(embedder.writeInbound(message));
assertFalse(embedder.writeInbound(chunk1)); assertFalse(embedder.writeInbound(chunk1));
@ -281,7 +281,7 @@ public class HttpObjectAggregatorTest {
EmbeddedChannel embedder = new EmbeddedChannel(aggr); EmbeddedChannel embedder = new EmbeddedChannel(aggr);
HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "http://localhost"); HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "http://localhost");
message.headers().set("X-Test", true); message.headers().setBoolean("X-Test", true);
message.headers().set("Transfer-Encoding", "Chunked"); message.headers().set("Transfer-Encoding", "Chunked");
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));

View File

@ -98,7 +98,7 @@ public class HttpRequestDecoderTest {
} }
private static void checkHeader(HttpHeaders headers, String name, String value) { private static void checkHeader(HttpHeaders headers, String name, String value) {
List<String> header1 = headers.getAll(name); List<CharSequence> header1 = headers.getAll(name);
assertEquals(1, header1.size()); assertEquals(1, header1.size());
assertEquals(value, header1.get(0)); assertEquals(value, header1.get(0));
} }

View File

@ -184,7 +184,7 @@ public class HttpResponseDecoderTest {
HttpResponse res = ch.readInbound(); HttpResponse res = ch.readInbound();
assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
assertThat(res.status(), is(HttpResponseStatus.OK)); assertThat(res.status(), is(HttpResponseStatus.OK));
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().getAndConvert(Names.TRANSFER_ENCODING), is("chunked"));
assertThat(ch.readInbound(), is(nullValue())); assertThat(ch.readInbound(), is(nullValue()));
// Close the connection without sending anything. // Close the connection without sending anything.
@ -205,7 +205,7 @@ public class HttpResponseDecoderTest {
HttpResponse res = ch.readInbound(); HttpResponse res = ch.readInbound();
assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
assertThat(res.status(), is(HttpResponseStatus.OK)); assertThat(res.status(), is(HttpResponseStatus.OK));
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().getAndConvert(Names.TRANSFER_ENCODING), is("chunked"));
// Read the partial content. // Read the partial content.
HttpContent content = ch.readInbound(); HttpContent content = ch.readInbound();
@ -275,7 +275,7 @@ public class HttpResponseDecoderTest {
HttpResponse res = ch.readInbound(); HttpResponse res = ch.readInbound();
assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
assertThat(res.status(), is(HttpResponseStatus.OK)); assertThat(res.status(), is(HttpResponseStatus.OK));
assertThat(res.headers().get("X-Header"), is("h2=h2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); assertThat(res.headers().getAndConvert("X-Header"), is("h2=h2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT"));
assertThat(ch.readInbound(), is(nullValue())); assertThat(ch.readInbound(), is(nullValue()));
ch.writeInbound(Unpooled.wrappedBuffer(new byte[1024])); ch.writeInbound(Unpooled.wrappedBuffer(new byte[1024]));
@ -313,7 +313,7 @@ public class HttpResponseDecoderTest {
assertThat(lastContent.content().isReadable(), is(false)); assertThat(lastContent.content().isReadable(), is(false));
HttpHeaders headers = lastContent.trailingHeaders(); HttpHeaders headers = lastContent.trailingHeaders();
assertEquals(1, headers.names().size()); assertEquals(1, headers.names().size());
List<String> values = headers.getAll("Set-Cookie"); List<CharSequence> values = headers.getAll("Set-Cookie");
assertEquals(2, values.size()); assertEquals(2, values.size());
assertTrue(values.contains("t1=t1v1")); assertTrue(values.contains("t1=t1v1"));
assertTrue(values.contains("t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); assertTrue(values.contains("t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT"));
@ -363,7 +363,7 @@ public class HttpResponseDecoderTest {
assertThat(lastContent.content().isReadable(), is(false)); assertThat(lastContent.content().isReadable(), is(false));
HttpHeaders headers = lastContent.trailingHeaders(); HttpHeaders headers = lastContent.trailingHeaders();
assertEquals(1, headers.names().size()); assertEquals(1, headers.names().size());
List<String> values = headers.getAll("Set-Cookie"); List<CharSequence> values = headers.getAll("Set-Cookie");
assertEquals(2, values.size()); assertEquals(2, values.size());
assertTrue(values.contains("t1=t1v1")); assertTrue(values.contains("t1=t1v1"));
assertTrue(values.contains("t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); assertTrue(values.contains("t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT"));

View File

@ -39,7 +39,8 @@ public class HttpResponseEncoderTest {
ByteBuf buffer = channel.readOutbound(); ByteBuf buffer = channel.readOutbound();
assertEquals("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n", buffer.toString(CharsetUtil.US_ASCII)); assertEquals("HTTP/1.1 200 OK\r\n" + HttpHeaders.Names.TRANSFER_ENCODING + ": " +
HttpHeaders.Values.CHUNKED + "\r\n\r\n", buffer.toString(CharsetUtil.US_ASCII));
buffer.release(); buffer.release();
assertTrue(channel.writeOutbound(FILE_REGION)); assertTrue(channel.writeOutbound(FILE_REGION));
buffer = channel.readOutbound(); buffer = channel.readOutbound();

View File

@ -92,7 +92,7 @@ public class HttpServerCodecTest {
// Ensure the aggregator generates a full request. // Ensure the aggregator generates a full request.
FullHttpRequest req = ch.readInbound(); FullHttpRequest req = ch.readInbound();
assertThat(req.headers().get(CONTENT_LENGTH), is("1")); assertThat(req.headers().getAndConvert(CONTENT_LENGTH), is("1"));
assertThat(req.content().readableBytes(), is(1)); assertThat(req.content().readableBytes(), is(1));
assertThat(req.content().readByte(), is((byte) 42)); assertThat(req.content().readByte(), is((byte) 42));
req.release(); req.release();
@ -103,12 +103,13 @@ public class HttpServerCodecTest {
// Send the actual response. // Send the actual response.
FullHttpResponse res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CREATED); FullHttpResponse res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CREATED);
res.content().writeBytes("OK".getBytes(CharsetUtil.UTF_8)); res.content().writeBytes("OK".getBytes(CharsetUtil.UTF_8));
res.headers().set(CONTENT_LENGTH, 2); res.headers().setInt(CONTENT_LENGTH, 2);
ch.writeOutbound(res); ch.writeOutbound(res);
// Ensure the encoder handles the response after handling 100 Continue. // Ensure the encoder handles the response after handling 100 Continue.
ByteBuf encodedRes = ch.readOutbound(); ByteBuf encodedRes = ch.readOutbound();
assertThat(encodedRes.toString(CharsetUtil.UTF_8), is("HTTP/1.1 201 Created\r\nContent-Length: 2\r\n\r\nOK")); assertThat(encodedRes.toString(CharsetUtil.UTF_8), is("HTTP/1.1 201 Created\r\n" +
CONTENT_LENGTH + ": 2\r\n\r\nOK"));
encodedRes.release(); encodedRes.release();
ch.finish(); ch.finish();

View File

@ -95,20 +95,20 @@ public class CorsConfigTest {
@Test @Test
public void preflightResponseHeadersSingleValue() { public void preflightResponseHeadersSingleValue() {
final CorsConfig cors = withAnyOrigin().preflightResponseHeader("SingleValue", "value").build(); final CorsConfig cors = withAnyOrigin().preflightResponseHeader("SingleValue", "value").build();
assertThat(cors.preflightResponseHeaders().get("SingleValue"), equalTo("value")); assertThat(cors.preflightResponseHeaders().getAndConvert("SingleValue"), equalTo("value"));
} }
@Test @Test
public void preflightResponseHeadersMultipleValues() { public void preflightResponseHeadersMultipleValues() {
final CorsConfig cors = withAnyOrigin().preflightResponseHeader("MultipleValues", "value1", "value2").build(); final CorsConfig cors = withAnyOrigin().preflightResponseHeader("MultipleValues", "value1", "value2").build();
assertThat(cors.preflightResponseHeaders().getAll("MultipleValues"), hasItems("value1", "value2")); assertThat(cors.preflightResponseHeaders().getAllAndConvert("MultipleValues"), hasItems("value1", "value2"));
} }
@Test @Test
public void defaultPreflightResponseHeaders() { public void defaultPreflightResponseHeaders() {
final CorsConfig cors = withAnyOrigin().build(); final CorsConfig cors = withAnyOrigin().build();
assertThat(cors.preflightResponseHeaders().get(Names.DATE), is(notNullValue())); assertThat(cors.preflightResponseHeaders().get(Names.DATE), is(notNullValue()));
assertThat(cors.preflightResponseHeaders().get(Names.CONTENT_LENGTH), is("0")); assertThat(cors.preflightResponseHeaders().getAndConvert(Names.CONTENT_LENGTH), is("0"));
} }
@Test @Test

View File

@ -47,14 +47,14 @@ public class CorsHandlerTest {
@Test @Test
public void simpleRequestWithAnyOrigin() { public void simpleRequestWithAnyOrigin() {
final HttpResponse response = simpleRequest(CorsConfig.withAnyOrigin().build(), "http://localhost:7777"); final HttpResponse response = simpleRequest(CorsConfig.withAnyOrigin().build(), "http://localhost:7777");
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("*")); assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("*"));
} }
@Test @Test
public void simpleRequestWithOrigin() { public void simpleRequestWithOrigin() {
final String origin = "http://localhost:8888"; final String origin = "http://localhost:8888";
final HttpResponse response = simpleRequest(CorsConfig.withOrigin(origin).build(), origin); final HttpResponse response = simpleRequest(CorsConfig.withOrigin(origin).build(), origin);
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin)); assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin));
} }
@Test @Test
@ -63,9 +63,9 @@ public class CorsHandlerTest {
final String origin2 = "https://localhost:8888"; final String origin2 = "https://localhost:8888";
final String[] origins = {origin1, origin2}; final String[] origins = {origin1, origin2};
final HttpResponse response1 = simpleRequest(CorsConfig.withOrigins(origins).build(), origin1); final HttpResponse response1 = simpleRequest(CorsConfig.withOrigins(origins).build(), origin1);
assertThat(response1.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin1)); assertThat(response1.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin1));
final HttpResponse response2 = simpleRequest(CorsConfig.withOrigins(origins).build(), origin2); final HttpResponse response2 = simpleRequest(CorsConfig.withOrigins(origins).build(), origin2);
assertThat(response2.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin2)); assertThat(response2.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin2));
} }
@Test @Test
@ -81,9 +81,9 @@ public class CorsHandlerTest {
.allowedRequestMethods(GET, DELETE) .allowedRequestMethods(GET, DELETE)
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888"));
assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_METHODS), hasItems("GET", "DELETE")); assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_ALLOW_METHODS), hasItems("GET", "DELETE"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -93,19 +93,20 @@ public class CorsHandlerTest {
.allowedRequestHeaders("content-type", "xheader1") .allowedRequestHeaders("content-type", "xheader1")
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888"));
assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_METHODS), hasItems("OPTIONS", "GET")); assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_ALLOW_METHODS), hasItems("OPTIONS", "GET"));
assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_HEADERS), hasItems("content-type", "xheader1")); assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_ALLOW_HEADERS),
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); hasItems("content-type", "xheader1"));
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
public void preflightRequestWithDefaultHeaders() { public void preflightRequestWithDefaultHeaders() {
final CorsConfig config = CorsConfig.withOrigin("http://localhost:8888").build(); final CorsConfig config = CorsConfig.withOrigin("http://localhost:8888").build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().get(CONTENT_LENGTH), is("0")); assertThat(response.headers().getAndConvert(CONTENT_LENGTH), is("0"));
assertThat(response.headers().get(DATE), is(notNullValue())); assertThat(response.headers().get(DATE), is(notNullValue()));
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -114,8 +115,8 @@ public class CorsHandlerTest {
.preflightResponseHeader("CustomHeader", "somevalue") .preflightResponseHeader("CustomHeader", "somevalue")
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().get("CustomHeader"), equalTo("somevalue")); assertThat(response.headers().getAndConvert("CustomHeader"), equalTo("somevalue"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -124,8 +125,8 @@ public class CorsHandlerTest {
.preflightResponseHeader("CustomHeader", "value1", "value2") .preflightResponseHeader("CustomHeader", "value1", "value2")
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().getAll("CustomHeader"), hasItems("value1", "value2")); assertThat(response.headers().getAllAndConvert("CustomHeader"), hasItems("value1", "value2"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -134,8 +135,8 @@ public class CorsHandlerTest {
.preflightResponseHeader("CustomHeader", Arrays.asList("value1", "value2")) .preflightResponseHeader("CustomHeader", Arrays.asList("value1", "value2"))
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().getAll("CustomHeader"), hasItems("value1", "value2")); assertThat(response.headers().getAllAndConvert("CustomHeader"), hasItems("value1", "value2"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -148,8 +149,8 @@ public class CorsHandlerTest {
} }
}).build(); }).build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().get("GenHeader"), equalTo("generatedValue")); assertThat(response.headers().getAndConvert("GenHeader"), equalTo("generatedValue"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -157,7 +158,7 @@ public class CorsHandlerTest {
final String origin = "null"; final String origin = "null";
final CorsConfig config = CorsConfig.withOrigin(origin).allowNullOrigin().build(); final CorsConfig config = CorsConfig.withOrigin(origin).allowNullOrigin().build();
final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1"); final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1");
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(equalTo("*"))); assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(equalTo("*")));
} }
@Test @Test
@ -165,7 +166,7 @@ public class CorsHandlerTest {
final String origin = "null"; final String origin = "null";
final CorsConfig config = CorsConfig.withOrigin(origin).allowCredentials().build(); final CorsConfig config = CorsConfig.withOrigin(origin).allowCredentials().build();
final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1"); final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1");
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(equalTo("true"))); assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(equalTo("true")));
} }
@Test @Test
@ -180,15 +181,15 @@ public class CorsHandlerTest {
public void simpleRequestCustomHeaders() { public void simpleRequestCustomHeaders() {
final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("custom1", "custom2").build(); final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("custom1", "custom2").build();
final HttpResponse response = simpleRequest(config, "http://localhost:7777"); final HttpResponse response = simpleRequest(config, "http://localhost:7777");
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("*")); assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("*"));
assertThat(response.headers().getAll(ACCESS_CONTROL_EXPOSE_HEADERS), hasItems("custom1", "custom1")); assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_EXPOSE_HEADERS), hasItems("custom1", "custom1"));
} }
@Test @Test
public void simpleRequestAllowCredentials() { public void simpleRequestAllowCredentials() {
final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build(); final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build();
final HttpResponse response = simpleRequest(config, "http://localhost:7777"); final HttpResponse response = simpleRequest(config, "http://localhost:7777");
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true"));
} }
@Test @Test
@ -202,16 +203,16 @@ public class CorsHandlerTest {
public void anyOriginAndAllowCredentialsShouldEchoRequestOrigin() { public void anyOriginAndAllowCredentialsShouldEchoRequestOrigin() {
final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build(); final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build();
final HttpResponse response = simpleRequest(config, "http://localhost:7777"); final HttpResponse response = simpleRequest(config, "http://localhost:7777");
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true"));
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777")); assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
public void simpleRequestExposeHeaders() { public void simpleRequestExposeHeaders() {
final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("one", "two").build(); final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("one", "two").build();
final HttpResponse response = simpleRequest(config, "http://localhost:7777"); final HttpResponse response = simpleRequest(config, "http://localhost:7777");
assertThat(response.headers().getAll(ACCESS_CONTROL_EXPOSE_HEADERS), hasItems("one", "two")); assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_EXPOSE_HEADERS), hasItems("one", "two"));
} }
@Test @Test

View File

@ -18,11 +18,13 @@ package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder.EncoderMode; import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder.EncoderMode;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import org.junit.Test; import org.junit.Test;
import java.io.File; import java.io.File;
@ -47,15 +49,15 @@ public class HttpPostRequestEncoderTest {
String content = getRequestBody(encoder); String content = getRequestBody(encoder);
String expected = "--" + multipartDataBoundary + "\r\n" + String expected = "--" + multipartDataBoundary + "\r\n" +
"Content-Disposition: form-data; name=\"foo\"" + "\r\n" + CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" +
"Content-Type: text/plain; charset=UTF-8" + "\r\n" + CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" +
"\r\n" + "\r\n" +
"bar" + "bar" +
"\r\n" + "\r\n" +
"--" + multipartDataBoundary + "\r\n" + "--" + multipartDataBoundary + "\r\n" +
"Content-Disposition: form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" +
"Content-Type: text/plain" + "\r\n" + CONTENT_TYPE + ": text/plain" + "\r\n" +
"Content-Transfer-Encoding: binary" + "\r\n" + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
"\r\n" + "\r\n" +
"File 01" + StringUtil.NEWLINE + "File 01" + StringUtil.NEWLINE +
"\r\n" + "\r\n" +
@ -83,25 +85,25 @@ public class HttpPostRequestEncoderTest {
String content = getRequestBody(encoder); String content = getRequestBody(encoder);
String expected = "--" + multipartDataBoundary + "\r\n" + String expected = "--" + multipartDataBoundary + "\r\n" +
"Content-Disposition: form-data; name=\"foo\"" + "\r\n" + CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" +
"Content-Type: text/plain; charset=UTF-8" + "\r\n" + CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" +
"\r\n" + "\r\n" +
"bar" + "\r\n" + "bar" + "\r\n" +
"--" + multipartDataBoundary + "\r\n" + "--" + multipartDataBoundary + "\r\n" +
"Content-Disposition: form-data; name=\"quux\"" + "\r\n" + CONTENT_DISPOSITION + ": form-data; name=\"quux\"" + "\r\n" +
"Content-Type: multipart/mixed; boundary=" + multipartMixedBoundary + "\r\n" + CONTENT_TYPE + ": multipart/mixed; boundary=" + multipartMixedBoundary + "\r\n" +
"\r\n" + "\r\n" +
"--" + multipartMixedBoundary + "\r\n" + "--" + multipartMixedBoundary + "\r\n" +
"Content-Disposition: attachment; filename=\"file-02.txt\"" + "\r\n" + CONTENT_DISPOSITION + ": attachment; filename=\"file-02.txt\"" + "\r\n" +
"Content-Type: text/plain" + "\r\n" + CONTENT_TYPE + ": text/plain" + "\r\n" +
"Content-Transfer-Encoding: binary" + "\r\n" + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
"\r\n" + "\r\n" +
"File 01" + StringUtil.NEWLINE + "File 01" + StringUtil.NEWLINE +
"\r\n" + "\r\n" +
"--" + multipartMixedBoundary + "\r\n" + "--" + multipartMixedBoundary + "\r\n" +
"Content-Disposition: attachment; filename=\"file-02.txt\"" + "\r\n" + CONTENT_DISPOSITION + ": attachment; filename=\"file-02.txt\"" + "\r\n" +
"Content-Type: text/plain" + "\r\n" + CONTENT_TYPE + ": text/plain" + "\r\n" +
"Content-Transfer-Encoding: binary" + "\r\n" + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
"\r\n" + "\r\n" +
"File 02" + StringUtil.NEWLINE + "File 02" + StringUtil.NEWLINE +
"\r\n" + "\r\n" +
@ -130,20 +132,20 @@ public class HttpPostRequestEncoderTest {
String content = getRequestBody(encoder); String content = getRequestBody(encoder);
String expected = "--" + multipartDataBoundary + "\r\n" + String expected = "--" + multipartDataBoundary + "\r\n" +
"Content-Disposition: form-data; name=\"foo\"" + "\r\n" + CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" +
"Content-Type: text/plain; charset=UTF-8" + "\r\n" + CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" +
"\r\n" + "\r\n" +
"bar" + "\r\n" + "bar" + "\r\n" +
"--" + multipartDataBoundary + "\r\n" + "--" + multipartDataBoundary + "\r\n" +
"Content-Disposition: form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" +
"Content-Type: text/plain" + "\r\n" + CONTENT_TYPE + ": text/plain" + "\r\n" +
"Content-Transfer-Encoding: binary" + "\r\n" + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
"\r\n" + "\r\n" +
"File 01" + StringUtil.NEWLINE + "\r\n" + "File 01" + StringUtil.NEWLINE + "\r\n" +
"--" + multipartDataBoundary + "\r\n" + "--" + multipartDataBoundary + "\r\n" +
"Content-Disposition: form-data; name=\"quux\"; filename=\"file-02.txt\"" + "\r\n" + CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-02.txt\"" + "\r\n" +
"Content-Type: text/plain" + "\r\n" + CONTENT_TYPE + ": text/plain" + "\r\n" +
"Content-Transfer-Encoding: binary" + "\r\n" + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
"\r\n" + "\r\n" +
"File 02" + StringUtil.NEWLINE + "File 02" + StringUtil.NEWLINE +
"\r\n" + "\r\n" +
@ -169,15 +171,15 @@ public class HttpPostRequestEncoderTest {
String content = getRequestBody(encoder); String content = getRequestBody(encoder);
String expected = "--" + multipartDataBoundary + "\r\n" + String expected = "--" + multipartDataBoundary + "\r\n" +
"Content-Disposition: form-data; name=\"foo\"" + "\r\n" + CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" +
"Content-Type: text/plain; charset=UTF-8" + "\r\n" + CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" +
"\r\n" + "\r\n" +
"bar" + "bar" +
"\r\n" + "\r\n" +
"--" + multipartDataBoundary + "\r\n" + "--" + multipartDataBoundary + "\r\n" +
"Content-Disposition: form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" +
"Content-Type: text/plain" + "\r\n" + CONTENT_TYPE + ": text/plain" + "\r\n" +
"Content-Transfer-Encoding: binary" + "\r\n" + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
"\r\n" + "\r\n" +
"File 01" + StringUtil.NEWLINE + "File 01" + StringUtil.NEWLINE +
"\r\n" + "\r\n" +

View File

@ -69,14 +69,14 @@ public class WebSocketClientExtensionHandlerTest {
HttpRequest req2 = ch.readOutbound(); HttpRequest req2 = ch.readOutbound();
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
HttpResponse res = newUpgradeResponse("main"); HttpResponse res = newUpgradeResponse("main");
ch.writeInbound(res); ch.writeInbound(res);
HttpResponse res2 = ch.readInbound(); HttpResponse res2 = ch.readInbound();
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
// test // test
assertEquals(2, reqExts.size()); assertEquals(2, reqExts.size());
@ -119,14 +119,14 @@ public class WebSocketClientExtensionHandlerTest {
HttpRequest req2 = ch.readOutbound(); HttpRequest req2 = ch.readOutbound();
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
HttpResponse res = newUpgradeResponse("fallback"); HttpResponse res = newUpgradeResponse("fallback");
ch.writeInbound(res); ch.writeInbound(res);
HttpResponse res2 = ch.readInbound(); HttpResponse res2 = ch.readInbound();
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
// test // test
assertEquals(2, reqExts.size()); assertEquals(2, reqExts.size());
@ -182,14 +182,14 @@ public class WebSocketClientExtensionHandlerTest {
HttpRequest req2 = ch.readOutbound(); HttpRequest req2 = ch.readOutbound();
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
HttpResponse res = newUpgradeResponse("main, fallback"); HttpResponse res = newUpgradeResponse("main, fallback");
ch.writeInbound(res); ch.writeInbound(res);
HttpResponse res2 = ch.readInbound(); HttpResponse res2 = ch.readInbound();
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
// test // test
assertEquals(2, reqExts.size()); assertEquals(2, reqExts.size());
@ -239,7 +239,7 @@ public class WebSocketClientExtensionHandlerTest {
HttpRequest req2 = ch.readOutbound(); HttpRequest req2 = ch.readOutbound();
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
HttpResponse res = newUpgradeResponse("main, fallback"); HttpResponse res = newUpgradeResponse("main, fallback");
ch.writeInbound(res); ch.writeInbound(res);

View File

@ -79,7 +79,7 @@ public class WebSocketServerExtensionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
// test // test
assertEquals(1, resExts.size()); assertEquals(1, resExts.size());
@ -130,7 +130,7 @@ public class WebSocketServerExtensionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
// test // test
assertEquals(2, resExts.size()); assertEquals(2, resExts.size());

View File

@ -45,7 +45,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
Assert.assertTrue(exts.get(0).parameters().isEmpty()); Assert.assertTrue(exts.get(0).parameters().isEmpty());
@ -66,7 +66,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
Assert.assertEquals("10", exts.get(0).parameters().get(CLIENT_MAX_WINDOW)); Assert.assertEquals("10", exts.get(0).parameters().get(CLIENT_MAX_WINDOW));
@ -87,7 +87,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
Assert.assertTrue(exts.get(0).parameters().isEmpty()); Assert.assertTrue(exts.get(0).parameters().isEmpty());
@ -108,7 +108,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
Assert.assertEquals("10", exts.get(0).parameters().get(SERVER_MAX_WINDOW)); Assert.assertEquals("10", exts.get(0).parameters().get(SERVER_MAX_WINDOW));
@ -163,7 +163,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
Assert.assertTrue(exts.get(0).parameters().isEmpty()); Assert.assertTrue(exts.get(0).parameters().isEmpty());
@ -185,7 +185,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
Assert.assertTrue(exts.get(0).parameters().isEmpty()); Assert.assertTrue(exts.get(0).parameters().isEmpty());

View File

@ -81,9 +81,9 @@ public class SpdySessionHandlerTest {
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg; SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
assertEquals(streamId, spdyHeadersFrame.streamId()); assertEquals(streamId, spdyHeadersFrame.streamId());
assertEquals(last, spdyHeadersFrame.isLast()); assertEquals(last, spdyHeadersFrame.isLast());
for (String name: headers.names()) { for (CharSequence name: headers.names()) {
List<String> expectedValues = headers.getAll(name); List<CharSequence> expectedValues = headers.getAll(name);
List<String> receivedValues = spdyHeadersFrame.headers().getAll(name); List<CharSequence> receivedValues = spdyHeadersFrame.headers().getAll(name);
assertTrue(receivedValues.containsAll(expectedValues)); assertTrue(receivedValues.containsAll(expectedValues));
receivedValues.removeAll(expectedValues); receivedValues.removeAll(expectedValues);
assertTrue(receivedValues.isEmpty()); assertTrue(receivedValues.isEmpty());
@ -357,7 +357,7 @@ public class SpdySessionHandlerTest {
int streamId = spdySynStreamFrame.streamId(); int streamId = spdySynStreamFrame.streamId();
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
spdySynReplyFrame.setLast(spdySynStreamFrame.isLast()); spdySynReplyFrame.setLast(spdySynStreamFrame.isLast());
for (Map.Entry<String, String> entry: spdySynStreamFrame.headers()) { for (Map.Entry<CharSequence, CharSequence> entry: spdySynStreamFrame.headers()) {
spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue());
} }

View File

@ -19,10 +19,6 @@ import io.netty.handler.codec.BinaryHeaders;
import io.netty.handler.codec.DefaultBinaryHeaders; import io.netty.handler.codec.DefaultBinaryHeaders;
public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2Headers { public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2Headers {
public DefaultHttp2Headers() {
}
@Override @Override
public Http2Headers add(AsciiString name, AsciiString value) { public Http2Headers add(AsciiString name, AsciiString value) {
super.add(name, value); super.add(name, value);
@ -30,7 +26,7 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
} }
@Override @Override
public Http2Headers add(AsciiString name, Iterable<AsciiString> values) { public Http2Headers add(AsciiString name, Iterable<? extends AsciiString> values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@ -41,6 +37,72 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
return this; return this;
} }
@Override
public Http2Headers addObject(AsciiString name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public Http2Headers addObject(AsciiString name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public Http2Headers addObject(AsciiString name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public Http2Headers addBoolean(AsciiString name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public Http2Headers addChar(AsciiString name, char value) {
super.addChar(name, value);
return this;
}
@Override
public Http2Headers addByte(AsciiString name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public Http2Headers addShort(AsciiString name, short value) {
super.addShort(name, value);
return this;
}
@Override
public Http2Headers addInt(AsciiString name, int value) {
super.addInt(name, value);
return this;
}
@Override
public Http2Headers addLong(AsciiString name, long value) {
super.addLong(name, value);
return this;
}
@Override
public Http2Headers addFloat(AsciiString name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public Http2Headers addDouble(AsciiString name, double value) {
super.addDouble(name, value);
return this;
}
@Override @Override
public Http2Headers add(BinaryHeaders headers) { public Http2Headers add(BinaryHeaders headers) {
super.add(headers); super.add(headers);
@ -54,7 +116,7 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
} }
@Override @Override
public Http2Headers set(AsciiString name, Iterable<AsciiString> values) { public Http2Headers set(AsciiString name, Iterable<? extends AsciiString> values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@ -65,6 +127,72 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
return this; return this;
} }
@Override
public Http2Headers setObject(AsciiString name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public Http2Headers setObject(AsciiString name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public Http2Headers setObject(AsciiString name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public Http2Headers setBoolean(AsciiString name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public Http2Headers setChar(AsciiString name, char value) {
super.setChar(name, value);
return this;
}
@Override
public Http2Headers setByte(AsciiString name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public Http2Headers setShort(AsciiString name, short value) {
super.setShort(name, value);
return this;
}
@Override
public Http2Headers setInt(AsciiString name, int value) {
super.setInt(name, value);
return this;
}
@Override
public Http2Headers setLong(AsciiString name, long value) {
super.setLong(name, value);
return this;
}
@Override
public Http2Headers setFloat(AsciiString name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public Http2Headers setDouble(AsciiString name, double value) {
super.setDouble(name, value);
return this;
}
@Override @Override
public Http2Headers set(BinaryHeaders headers) { public Http2Headers set(BinaryHeaders headers) {
super.set(headers); super.set(headers);
@ -83,26 +211,6 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
return this; return this;
} }
@Override
public Http2Headers forEachEntry(final BinaryHeaders.BinaryHeaderVisitor visitor) {
super.forEachEntry(visitor);
return this;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Http2Headers)) {
return false;
}
return super.equals((BinaryHeaders) o);
}
@Override @Override
public Http2Headers method(AsciiString value) { public Http2Headers method(AsciiString value) {
set(PseudoHeaderName.METHOD.value(), value); set(PseudoHeaderName.METHOD.value(), value);

View File

@ -20,12 +20,13 @@ import static io.netty.handler.codec.http2.Http2Exception.protocolError;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.ByteBufOutputStream;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.BinaryHeaders.EntryVisitor;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Collections; import java.util.Collections;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
@ -71,18 +72,21 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea
encodeHeader(name, value, stream); encodeHeader(name, value, stream);
} }
} }
headers.forEachEntry(new BinaryHeaders.BinaryHeaderVisitor() {
headers.forEachEntry(new EntryVisitor() {
@Override @Override
public boolean visit(AsciiString name, AsciiString value) throws Exception { public boolean visit(Entry<AsciiString, AsciiString> entry) throws Exception {
final AsciiString name = entry.getKey();
final AsciiString value = entry.getValue();
if (!Http2Headers.PseudoHeaderName.isPseudoHeader(name)) { if (!Http2Headers.PseudoHeaderName.isPseudoHeader(name)) {
encodeHeader(name, value, stream); encodeHeader(name, value, stream);
} }
return true; return true;
} }
}); });
} catch (IOException e) { } catch (Exception e) {
throw Http2Exception.format(Http2Error.COMPRESSION_ERROR, throw Http2Exception.format(Http2Error.COMPRESSION_ERROR, "Failed encoding headers block: %s",
"Failed encoding headers block: %s", e.getMessage()); e.getMessage());
} finally { } finally {
try { try {
stream.close(); stream.close();

View File

@ -35,8 +35,6 @@ import io.netty.handler.codec.compression.ZlibWrapper;
* stream. * stream.
*/ */
public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecorator { public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecorator {
private static final AsciiString CONTENT_ENCODING_LOWER_CASE = CONTENT_ENCODING.toLowerCase();
private static final AsciiString CONTENT_LENGTH_LOWER_CASE = CONTENT_LENGTH.toLowerCase();
private static final Http2ConnectionAdapter CLEAN_UP_LISTENER = new Http2ConnectionAdapter() { private static final Http2ConnectionAdapter CLEAN_UP_LISTENER = new Http2ConnectionAdapter() {
@Override @Override
public void streamRemoved(Http2Stream stream) { public void streamRemoved(Http2Stream stream) {
@ -171,7 +169,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
if (decompressor == null) { if (decompressor == null) {
if (!endOfStream) { if (!endOfStream) {
// Determine the content encoding. // Determine the content encoding.
AsciiString contentEncoding = headers.get(CONTENT_ENCODING_LOWER_CASE); AsciiString contentEncoding = headers.get(CONTENT_ENCODING);
if (contentEncoding == null) { if (contentEncoding == null) {
contentEncoding = IDENTITY; contentEncoding = IDENTITY;
} }
@ -182,9 +180,9 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
// so that the message looks like a decoded message. // so that the message looks like a decoded message.
AsciiString targetContentEncoding = getTargetContentEncoding(contentEncoding); AsciiString targetContentEncoding = getTargetContentEncoding(contentEncoding);
if (IDENTITY.equalsIgnoreCase(targetContentEncoding)) { if (IDENTITY.equalsIgnoreCase(targetContentEncoding)) {
headers.remove(CONTENT_ENCODING_LOWER_CASE); headers.remove(CONTENT_ENCODING);
} else { } else {
headers.set(CONTENT_ENCODING_LOWER_CASE, targetContentEncoding); headers.set(CONTENT_ENCODING, targetContentEncoding);
} }
} }
} }
@ -195,7 +193,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
// The content length will be for the compressed data. Since we will decompress the data // The content length will be for the compressed data. Since we will decompress the data
// this content-length will not be correct. Instead of queuing messages or delaying sending // this content-length will not be correct. Instead of queuing messages or delaying sending
// header frames...just remove the content-length header // header frames...just remove the content-length header
headers.remove(CONTENT_LENGTH_LOWER_CASE); headers.remove(CONTENT_LENGTH);
} }
} }

View File

@ -26,67 +26,194 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2
} }
@Override @Override
public EmptyHttp2Headers add(AsciiString name, AsciiString value) { public Http2Headers add(AsciiString name, AsciiString value) {
super.add(name, value); super.add(name, value);
return this; return this;
} }
@Override @Override
public EmptyHttp2Headers add(AsciiString name, Iterable<AsciiString> values) { public Http2Headers add(AsciiString name, Iterable<? extends AsciiString> values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@Override @Override
public EmptyHttp2Headers add(AsciiString name, AsciiString... values) { public Http2Headers add(AsciiString name, AsciiString... values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@Override @Override
public EmptyHttp2Headers add(BinaryHeaders headers) { public Http2Headers addObject(AsciiString name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public Http2Headers addObject(AsciiString name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public Http2Headers addObject(AsciiString name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public Http2Headers addBoolean(AsciiString name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public Http2Headers addChar(AsciiString name, char value) {
super.addChar(name, value);
return this;
}
@Override
public Http2Headers addByte(AsciiString name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public Http2Headers addShort(AsciiString name, short value) {
super.addShort(name, value);
return this;
}
@Override
public Http2Headers addInt(AsciiString name, int value) {
super.addInt(name, value);
return this;
}
@Override
public Http2Headers addLong(AsciiString name, long value) {
super.addLong(name, value);
return this;
}
@Override
public Http2Headers addFloat(AsciiString name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public Http2Headers addDouble(AsciiString name, double value) {
super.addDouble(name, value);
return this;
}
@Override
public Http2Headers add(BinaryHeaders headers) {
super.add(headers); super.add(headers);
return this; return this;
} }
@Override @Override
public EmptyHttp2Headers set(AsciiString name, AsciiString value) { public Http2Headers set(AsciiString name, AsciiString value) {
super.set(name, value); super.set(name, value);
return this; return this;
} }
@Override @Override
public EmptyHttp2Headers set(AsciiString name, Iterable<AsciiString> values) { public Http2Headers set(AsciiString name, Iterable<? extends AsciiString> values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@Override @Override
public EmptyHttp2Headers set(AsciiString name, AsciiString... values) { public Http2Headers set(AsciiString name, AsciiString... values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@Override @Override
public EmptyHttp2Headers set(BinaryHeaders headers) { public Http2Headers setObject(AsciiString name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public Http2Headers setObject(AsciiString name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public Http2Headers setObject(AsciiString name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public Http2Headers setBoolean(AsciiString name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public Http2Headers setChar(AsciiString name, char value) {
super.setChar(name, value);
return this;
}
@Override
public Http2Headers setByte(AsciiString name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public Http2Headers setShort(AsciiString name, short value) {
super.setShort(name, value);
return this;
}
@Override
public Http2Headers setInt(AsciiString name, int value) {
super.setInt(name, value);
return this;
}
@Override
public Http2Headers setLong(AsciiString name, long value) {
super.setLong(name, value);
return this;
}
@Override
public Http2Headers setFloat(AsciiString name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public Http2Headers setDouble(AsciiString name, double value) {
super.setDouble(name, value);
return this;
}
@Override
public Http2Headers set(BinaryHeaders headers) {
super.set(headers); super.set(headers);
return this; return this;
} }
@Override @Override
public EmptyHttp2Headers setAll(BinaryHeaders headers) { public Http2Headers setAll(BinaryHeaders headers) {
super.setAll(headers); super.setAll(headers);
return this; return this;
} }
@Override @Override
public EmptyHttp2Headers clear() { public Http2Headers clear() {
return this; super.clear();
}
@Override
public EmptyHttp2Headers forEachEntry(BinaryHeaderVisitor visitor) {
super.forEachEntry(visitor);
return this; return this;
} }

View File

@ -84,11 +84,44 @@ public interface Http2Headers extends BinaryHeaders {
Http2Headers add(AsciiString name, AsciiString value); Http2Headers add(AsciiString name, AsciiString value);
@Override @Override
Http2Headers add(AsciiString name, Iterable<AsciiString> values); Http2Headers add(AsciiString name, Iterable<? extends AsciiString> values);
@Override @Override
Http2Headers add(AsciiString name, AsciiString... values); Http2Headers add(AsciiString name, AsciiString... values);
@Override
Http2Headers addObject(AsciiString name, Object value);
@Override
Http2Headers addObject(AsciiString name, Iterable<?> values);
@Override
Http2Headers addObject(AsciiString name, Object... values);
@Override
Http2Headers addBoolean(AsciiString name, boolean value);
@Override
Http2Headers addByte(AsciiString name, byte value);
@Override
Http2Headers addChar(AsciiString name, char value);
@Override
Http2Headers addShort(AsciiString name, short value);
@Override
Http2Headers addInt(AsciiString name, int value);
@Override
Http2Headers addLong(AsciiString name, long value);
@Override
Http2Headers addFloat(AsciiString name, float value);
@Override
Http2Headers addDouble(AsciiString name, double value);
@Override @Override
Http2Headers add(BinaryHeaders headers); Http2Headers add(BinaryHeaders headers);
@ -96,11 +129,44 @@ public interface Http2Headers extends BinaryHeaders {
Http2Headers set(AsciiString name, AsciiString value); Http2Headers set(AsciiString name, AsciiString value);
@Override @Override
Http2Headers set(AsciiString name, Iterable<AsciiString> values); Http2Headers set(AsciiString name, Iterable<? extends AsciiString> values);
@Override @Override
Http2Headers set(AsciiString name, AsciiString... values); Http2Headers set(AsciiString name, AsciiString... values);
@Override
Http2Headers setObject(AsciiString name, Object value);
@Override
Http2Headers setObject(AsciiString name, Iterable<?> values);
@Override
Http2Headers setObject(AsciiString name, Object... values);
@Override
Http2Headers setBoolean(AsciiString name, boolean value);
@Override
Http2Headers setByte(AsciiString name, byte value);
@Override
Http2Headers setChar(AsciiString name, char value);
@Override
Http2Headers setShort(AsciiString name, short value);
@Override
Http2Headers setInt(AsciiString name, int value);
@Override
Http2Headers setLong(AsciiString name, long value);
@Override
Http2Headers setFloat(AsciiString name, float value);
@Override
Http2Headers setDouble(AsciiString name, double value);
@Override @Override
Http2Headers set(BinaryHeaders headers); Http2Headers set(BinaryHeaders headers);
@ -110,9 +176,6 @@ public interface Http2Headers extends BinaryHeaders {
@Override @Override
Http2Headers clear(); Http2Headers clear();
@Override
Http2Headers forEachEntry(BinaryHeaderVisitor visitor);
/** /**
* Sets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header * Sets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header
*/ */

View File

@ -25,6 +25,7 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.base64.Base64;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -86,8 +87,7 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade
try { try {
// Decode the HTTP2-Settings header and set the settings on the handler to make // Decode the HTTP2-Settings header and set the settings on the handler to make
// sure everything is fine with the request. // sure everything is fine with the request.
String settingsHeader = upgradeRequest.headers().get(HTTP_UPGRADE_SETTINGS_HEADER); settings = decodeSettingsHeader(ctx, upgradeRequest.headers().get(HTTP_UPGRADE_SETTINGS_HEADER));
settings = decodeSettingsHeader(ctx, settingsHeader);
connectionHandler.onHttpServerUpgrade(settings); connectionHandler.onHttpServerUpgrade(settings);
// Everything looks good, no need to modify the response. // Everything looks good, no need to modify the response.
} catch (Throwable e) { } catch (Throwable e) {
@ -108,9 +108,9 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade
/** /**
* Decodes the settings header and returns a {@link Http2Settings} object. * Decodes the settings header and returns a {@link Http2Settings} object.
*/ */
private Http2Settings decodeSettingsHeader(ChannelHandlerContext ctx, String settingsHeader) private Http2Settings decodeSettingsHeader(ChannelHandlerContext ctx, CharSequence settingsHeader)
throws Http2Exception { throws Http2Exception {
ByteBuf header = Unpooled.wrappedBuffer(settingsHeader.getBytes(CharsetUtil.UTF_8)); ByteBuf header = Unpooled.wrappedBuffer(AsciiString.getBytes(settingsHeader, CharsetUtil.UTF_8));
try { try {
// Decode the SETTINGS payload. // Decode the SETTINGS payload.
ByteBuf payload = Base64.decode(header, URL_SAFE); ByteBuf payload = Base64.decode(header, URL_SAFE);

View File

@ -51,23 +51,10 @@ public class Http2ToHttpConnectionHandler extends Http2ConnectionHandler {
* *
* @param httpHeaders The HTTP/1.x headers object to look for the stream id * @param httpHeaders The HTTP/1.x headers object to look for the stream id
* @return The stream id to use with this {@link HttpHeaders} object * @return The stream id to use with this {@link HttpHeaders} object
* @throws Http2Exception If the {@code httpHeaders} object specifies an invalid stream id * @throws Exception If the {@code httpHeaders} object specifies an invalid stream id
*/ */
private int getStreamId(HttpHeaders httpHeaders) throws Http2Exception { private int getStreamId(HttpHeaders httpHeaders) throws Exception {
int streamId = 0; return httpHeaders.getInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), connection().local().nextStreamId());
String value = httpHeaders.get(HttpUtil.ExtensionHeaderNames.STREAM_ID.text());
if (value == null) {
streamId = connection().local().nextStreamId();
} else {
try {
streamId = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw Http2Exception.format(Http2Error.INTERNAL_ERROR, "Invalid user-specified stream id value '%s'",
value);
}
}
return streamId;
} }
/** /**
@ -83,7 +70,7 @@ public class Http2ToHttpConnectionHandler extends Http2ConnectionHandler {
int streamId = 0; int streamId = 0;
try { try {
streamId = getStreamId(httpMsg.headers()); streamId = getStreamId(httpMsg.headers());
} catch (Http2Exception e) { } catch (Exception e) {
httpMsg.release(); httpMsg.release();
promise.setFailure(e); promise.setFailure(e);
return; return;
@ -91,16 +78,17 @@ public class Http2ToHttpConnectionHandler extends Http2ConnectionHandler {
// Convert and write the headers. // Convert and write the headers.
Http2Headers http2Headers = HttpUtil.toHttp2Headers(httpMsg); Http2Headers http2Headers = HttpUtil.toHttp2Headers(httpMsg);
Http2ConnectionEncoder encoder = encoder();
if (hasData) { if (hasData) {
ChannelPromiseAggregator promiseAggregator = new ChannelPromiseAggregator(promise); ChannelPromiseAggregator promiseAggregator = new ChannelPromiseAggregator(promise);
ChannelPromise headerPromise = ctx.newPromise(); ChannelPromise headerPromise = ctx.newPromise();
ChannelPromise dataPromise = ctx.newPromise(); ChannelPromise dataPromise = ctx.newPromise();
promiseAggregator.add(headerPromise, dataPromise); promiseAggregator.add(headerPromise, dataPromise);
encoder().writeHeaders(ctx, streamId, http2Headers, 0, false, headerPromise); encoder.writeHeaders(ctx, streamId, http2Headers, 0, false, headerPromise);
encoder().writeData(ctx, streamId, httpMsg.content(), 0, true, dataPromise); encoder.writeData(ctx, streamId, httpMsg.content(), 0, true, dataPromise);
} else { } else {
encoder().writeHeaders(ctx, streamId, http2Headers, 0, true, promise); encoder.writeHeaders(ctx, streamId, http2Headers, 0, true, promise);
} }
} else { } else {
ctx.write(msg, promise); ctx.write(msg, promise);

View File

@ -16,7 +16,7 @@ package io.netty.handler.codec.http2;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.BinaryHeaders;
import io.netty.handler.codec.TextHeaderProcessor; import io.netty.handler.codec.TextHeaders.EntryVisitor;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpMessage; import io.netty.handler.codec.http.FullHttpMessage;
@ -29,109 +29,109 @@ import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.internal.PlatformDependent;
import java.net.URI; import java.net.URI;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
/** /**
* Provides utility methods and constants for the HTTP/2 to HTTP conversion * Provides utility methods and constants for the HTTP/2 to HTTP conversion
*/ */
@SuppressWarnings("deprecation")
public final class HttpUtil { public final class HttpUtil {
/** /**
* The set of headers that should not be directly copied when converting headers from HTTP to HTTP/2. * The set of headers that should not be directly copied when converting headers from HTTP to HTTP/2.
*/ */
private static final Set<CharSequence> HTTP_TO_HTTP2_HEADER_BLACKLIST = new HashSet<CharSequence>(); @SuppressWarnings("deprecation")
static { private static final Set<CharSequence> HTTP_TO_HTTP2_HEADER_BLACKLIST = new HashSet<CharSequence>() {
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.CONNECTION.toLowerCase()); private static final long serialVersionUID = -5678614530214167043L;
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.KEEP_ALIVE.toLowerCase()); {
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.PROXY_CONNECTION.toLowerCase()); add(HttpHeaders.Names.CONNECTION);
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.TRANSFER_ENCODING.toLowerCase()); add(HttpHeaders.Names.KEEP_ALIVE);
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.HOST.toLowerCase()); add(HttpHeaders.Names.PROXY_CONNECTION);
// These are already defined as lower-case. add(HttpHeaders.Names.TRANSFER_ENCODING);
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.STREAM_ID.text()); add(HttpHeaders.Names.HOST);
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.AUTHORITY.text()); add(ExtensionHeaderNames.STREAM_ID.text());
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.SCHEME.text()); add(ExtensionHeaderNames.AUTHORITY.text());
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.PATH.text()); add(ExtensionHeaderNames.SCHEME.text());
add(ExtensionHeaderNames.PATH.text());
} }
};
/** /**
* This will be the method used for {@link HttpRequest} objects generated * This will be the method used for {@link HttpRequest} objects generated out of the HTTP message flow defined in <a
* out of the HTTP message flow defined in * href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.">HTTP/2 Spec Message Flow</a>
* <a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.">HTTP/2 Spec Message Flow</a>
*/ */
public static final HttpMethod OUT_OF_MESSAGE_SEQUENCE_METHOD = HttpMethod.OPTIONS; public static final HttpMethod OUT_OF_MESSAGE_SEQUENCE_METHOD = HttpMethod.OPTIONS;
/** /**
* This will be the path used for {@link HttpRequest} objects generated * This will be the path used for {@link HttpRequest} objects generated out of the HTTP message flow defined in <a
* out of the HTTP message flow defined in * href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.">HTTP/2 Spec Message Flow</a>
* <a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.">HTTP/2 Spec Message Flow</a>
*/ */
public static final String OUT_OF_MESSAGE_SEQUENCE_PATH = ""; public static final String OUT_OF_MESSAGE_SEQUENCE_PATH = "";
/** /**
* This will be the status code used for {@link HttpResponse} objects generated * This will be the status code used for {@link HttpResponse} objects generated out of the HTTP message flow defined
* out of the HTTP message flow defined in * in <a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.">HTTP/2 Spec Message Flow</a>
* <a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.">HTTP/2 Spec Message Flow</a>
*/ */
public static final HttpResponseStatus OUT_OF_MESSAGE_SEQUENCE_RETURN_CODE = HttpResponseStatus.OK; public static final HttpResponseStatus OUT_OF_MESSAGE_SEQUENCE_RETURN_CODE = HttpResponseStatus.OK;
private HttpUtil() { } private HttpUtil() {
}
/** /**
* Provides the HTTP header extensions used to carry HTTP/2 information in HTTP objects * Provides the HTTP header extensions used to carry HTTP/2 information in HTTP objects
*/ */
public enum ExtensionHeaderNames { public enum ExtensionHeaderNames {
/** /**
* HTTP extension header which will identify the stream id from the HTTP/2 event(s) * HTTP extension header which will identify the stream id from the HTTP/2 event(s) responsible for generating a
* responsible for generating a {@code HttpObject} * {@code HttpObject}
* <p> * <p>
* {@code "x-http2-stream-id"} * {@code "x-http2-stream-id"}
*/ */
STREAM_ID("x-http2-stream-id"), STREAM_ID("x-http2-stream-id"),
/** /**
* HTTP extension header which will identify the authority pseudo header from the HTTP/2 * HTTP extension header which will identify the authority pseudo header from the HTTP/2 event(s) responsible
* event(s) responsible for generating a {@code HttpObject} * for generating a {@code HttpObject}
* <p> * <p>
* {@code "x-http2-authority"} * {@code "x-http2-authority"}
*/ */
AUTHORITY("x-http2-authority"), AUTHORITY("x-http2-authority"),
/** /**
* HTTP extension header which will identify the scheme pseudo header from the HTTP/2 * HTTP extension header which will identify the scheme pseudo header from the HTTP/2 event(s) responsible for
* event(s) responsible for generating a {@code HttpObject} * generating a {@code HttpObject}
* <p> * <p>
* {@code "x-http2-scheme"} * {@code "x-http2-scheme"}
*/ */
SCHEME("x-http2-scheme"), SCHEME("x-http2-scheme"),
/** /**
* HTTP extension header which will identify the path pseudo header from the HTTP/2 event(s) * HTTP extension header which will identify the path pseudo header from the HTTP/2 event(s) responsible for
* responsible for generating a {@code HttpObject} * generating a {@code HttpObject}
* <p> * <p>
* {@code "x-http2-path"} * {@code "x-http2-path"}
*/ */
PATH("x-http2-path"), PATH("x-http2-path"),
/** /**
* HTTP extension header which will identify the stream id used to create this stream in a * HTTP extension header which will identify the stream id used to create this stream in a HTTP/2 push promise
* HTTP/2 push promise frame * frame
* <p> * <p>
* {@code "x-http2-stream-promise-id"} * {@code "x-http2-stream-promise-id"}
*/ */
STREAM_PROMISE_ID("x-http2-stream-promise-id"), STREAM_PROMISE_ID("x-http2-stream-promise-id"),
/** /**
* HTTP extension header which will identify the stream id which this stream is dependent * HTTP extension header which will identify the stream id which this stream is dependent on. This stream will
* on. This stream will be a child node of the stream id associated with this header value. * be a child node of the stream id associated with this header value.
* <p> * <p>
* {@code "x-http2-stream-dependency-id"} * {@code "x-http2-stream-dependency-id"}
*/ */
STREAM_DEPENDENCY_ID("x-http2-stream-dependency-id"), STREAM_DEPENDENCY_ID("x-http2-stream-dependency-id"),
/** /**
* HTTP extension header which will identify the weight (if non-default and the priority is * HTTP extension header which will identify the weight (if non-default and the priority is not on the default
* not on the default stream) of the associated HTTP/2 stream responsible responsible for * stream) of the associated HTTP/2 stream responsible responsible for generating a {@code HttpObject}
* generating a {@code HttpObject}
* <p> * <p>
* {@code "x-http2-stream-weight"} * {@code "x-http2-stream-weight"}
*/ */
@ -176,21 +176,19 @@ public final class HttpUtil {
* *
* @param streamId The stream associated with the response * @param streamId The stream associated with the response
* @param http2Headers The initial set of HTTP/2 headers to create the response with * @param http2Headers The initial set of HTTP/2 headers to create the response with
* @param validateHttpHeaders * @param validateHttpHeaders <ul>
* <ul>
* <li>{@code true} to validate HTTP headers in the http-codec</li> * <li>{@code true} to validate HTTP headers in the http-codec</li>
* <li>{@code false} not to validate HTTP headers in the http-codec</li> * <li>{@code false} not to validate HTTP headers in the http-codec</li>
* </ul> * </ul>
* @return A new response object which represents headers/data * @return A new response object which represents headers/data
* @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, Map)} * @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, Map)}
*/ */
public static FullHttpResponse toHttpResponse(int streamId, Http2Headers http2Headers, public static FullHttpResponse toHttpResponse(int streamId, Http2Headers http2Headers, boolean validateHttpHeaders)
boolean validateHttpHeaders) throws Http2Exception { throws Http2Exception {
HttpResponseStatus status = parseStatus(http2Headers.status()); HttpResponseStatus status = parseStatus(http2Headers.status());
// HTTP/2 does not define a way to carry the version or reason phrase that is included in an // HTTP/2 does not define a way to carry the version or reason phrase that is included in an
// HTTP/1.1 status line. // HTTP/1.1 status line.
FullHttpResponse msg = FullHttpResponse msg = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, validateHttpHeaders);
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, validateHttpHeaders);
addHttp2ToHttpHeaders(streamId, http2Headers, msg, false); addHttp2ToHttpHeaders(streamId, http2Headers, msg, false);
return msg; return msg;
} }
@ -200,8 +198,7 @@ public final class HttpUtil {
* *
* @param streamId The stream associated with the request * @param streamId The stream associated with the request
* @param http2Headers The initial set of HTTP/2 headers to create the request with * @param http2Headers The initial set of HTTP/2 headers to create the request with
* @param validateHttpHeaders * @param validateHttpHeaders <ul>
* <ul>
* <li>{@code true} to validate HTTP headers in the http-codec</li> * <li>{@code true} to validate HTTP headers in the http-codec</li>
* <li>{@code false} not to validate HTTP headers in the http-codec</li> * <li>{@code false} not to validate HTTP headers in the http-codec</li>
* </ul> * </ul>
@ -212,9 +209,8 @@ public final class HttpUtil {
throws Http2Exception { throws Http2Exception {
// HTTP/2 does not define a way to carry the version identifier that is // HTTP/2 does not define a way to carry the version identifier that is
// included in the HTTP/1.1 request line. // included in the HTTP/1.1 request line.
FullHttpRequest msg = FullHttpRequest msg = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(http2Headers.method()
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(http2Headers .toString()), http2Headers.path().toString(), validateHttpHeaders);
.method().toString()), http2Headers.path().toString(), validateHttpHeaders);
addHttp2ToHttpHeaders(streamId, http2Headers, msg, false); addHttp2ToHttpHeaders(streamId, http2Headers, msg, false);
return msg; return msg;
} }
@ -225,25 +221,26 @@ public final class HttpUtil {
* @param streamId The stream associated with {@code sourceHeaders} * @param streamId The stream associated with {@code sourceHeaders}
* @param sourceHeaders The HTTP/2 headers to convert * @param sourceHeaders The HTTP/2 headers to convert
* @param destinationMessage The object which will contain the resulting HTTP/1.x headers * @param destinationMessage The object which will contain the resulting HTTP/1.x headers
* @param addToTrailer {@code true} to add to trailing headers. {@code false} to add to initial * @param addToTrailer {@code true} to add to trailing headers. {@code false} to add to initial headers.
* headers.
* @throws Http2Exception If not all HTTP/2 headers can be translated to HTTP/1.x * @throws Http2Exception If not all HTTP/2 headers can be translated to HTTP/1.x
*/ */
public static void addHttp2ToHttpHeaders(int streamId, Http2Headers sourceHeaders, public static void addHttp2ToHttpHeaders(int streamId, Http2Headers sourceHeaders,
FullHttpMessage destinationMessage, boolean addToTrailer) FullHttpMessage destinationMessage, boolean addToTrailer) throws Http2Exception {
throws Http2Exception {
HttpHeaders headers = addToTrailer ? destinationMessage.trailingHeaders() : destinationMessage.headers(); HttpHeaders headers = addToTrailer ? destinationMessage.trailingHeaders() : destinationMessage.headers();
boolean request = destinationMessage instanceof HttpRequest; boolean request = destinationMessage instanceof HttpRequest;
Http2ToHttpHeaderTranslator visitor = new Http2ToHttpHeaderTranslator(headers, request); Http2ToHttpHeaderTranslator visitor = new Http2ToHttpHeaderTranslator(headers, request);
try {
sourceHeaders.forEachEntry(visitor); sourceHeaders.forEachEntry(visitor);
if (visitor.cause() != null) { } catch (Http2Exception ex) {
throw visitor.cause(); throw ex;
} catch (Exception ex) {
PlatformDependent.throwException(ex);
} }
headers.remove(HttpHeaders.Names.TRANSFER_ENCODING); headers.remove(HttpHeaders.Names.TRANSFER_ENCODING);
headers.remove(HttpHeaders.Names.TRAILER); headers.remove(HttpHeaders.Names.TRAILER);
if (!addToTrailer) { if (!addToTrailer) {
headers.set(ExtensionHeaderNames.STREAM_ID.text(), streamId); headers.setInt(ExtensionHeaderNames.STREAM_ID.text(), streamId);
HttpHeaderUtil.setKeepAlive(destinationMessage, true); HttpHeaderUtil.setKeepAlive(destinationMessage, true);
} }
} }
@ -259,7 +256,7 @@ public final class HttpUtil {
out.path(new AsciiString(request.uri())); out.path(new AsciiString(request.uri()));
out.method(new AsciiString(request.method().toString())); out.method(new AsciiString(request.method().toString()));
String value = inHeaders.get(HttpHeaders.Names.HOST); String value = inHeaders.getAndConvert(HttpHeaders.Names.HOST);
if (value != null) { if (value != null) {
URI hostUri = URI.create(value); URI hostUri = URI.create(value);
// The authority MUST NOT include the deprecated "userinfo" subcomponent // The authority MUST NOT include the deprecated "userinfo" subcomponent
@ -274,15 +271,15 @@ public final class HttpUtil {
} }
// Consume the Authority extension header if present // Consume the Authority extension header if present
value = inHeaders.get(ExtensionHeaderNames.AUTHORITY.text()); CharSequence cValue = inHeaders.get(ExtensionHeaderNames.AUTHORITY.text());
if (value != null) { if (cValue != null) {
out.authority(new AsciiString(value)); out.authority(AsciiString.of(cValue));
} }
// Consume the Scheme extension header if present // Consume the Scheme extension header if present
value = inHeaders.get(ExtensionHeaderNames.SCHEME.text()); cValue = inHeaders.get(ExtensionHeaderNames.SCHEME.text());
if (value != null) { if (cValue != null) {
out.scheme(new AsciiString(value)); out.scheme(AsciiString.of(cValue));
} }
} else if (in instanceof HttpResponse) { } else if (in instanceof HttpResponse) {
HttpResponse response = (HttpResponse) in; HttpResponse response = (HttpResponse) in;
@ -290,90 +287,81 @@ public final class HttpUtil {
} }
// Add the HTTP headers which have not been consumed above // Add the HTTP headers which have not been consumed above
inHeaders.forEachEntry(new TextHeaderProcessor() { try {
inHeaders.forEachEntry(new EntryVisitor() {
@Override @Override
public boolean process(CharSequence name, CharSequence value) throws Exception { public boolean visit(Entry<CharSequence, CharSequence> entry) throws Exception {
AsciiString aName = AsciiString.of(name); final AsciiString aName = AsciiString.of(entry.getKey()).toLowerCase();
if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName.toLowerCase())) { if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName)) {
AsciiString aValue = AsciiString.of(value); AsciiString aValue = AsciiString.of(entry.getValue());
out.add(aName, aValue); out.add(aName, aValue);
} }
return true; return true;
} }
}); });
} catch (Exception ex) {
PlatformDependent.throwException(ex);
}
return out; return out;
} }
/** /**
* A visitor which translates HTTP/2 headers to HTTP/1 headers * A visitor which translates HTTP/2 headers to HTTP/1 headers
*/ */
private static final class Http2ToHttpHeaderTranslator implements BinaryHeaders.BinaryHeaderVisitor { private static final class Http2ToHttpHeaderTranslator implements BinaryHeaders.EntryVisitor {
/** /**
* Translations from HTTP/2 header name to the HTTP/1.x equivalent. * Translations from HTTP/2 header name to the HTTP/1.x equivalent.
*/ */
private static final Map<AsciiString, String> REQUEST_HEADER_TRANSLATIONS = private static final Map<AsciiString, AsciiString>
new HashMap<AsciiString, String>(); REQUEST_HEADER_TRANSLATIONS = new HashMap<AsciiString, AsciiString>();
private static final Map<AsciiString, String> RESPONSE_HEADER_TRANSLATIONS = private static final Map<AsciiString, AsciiString>
new HashMap<AsciiString, String>(); RESPONSE_HEADER_TRANSLATIONS = new HashMap<AsciiString, AsciiString>();
static { static {
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.AUTHORITY.value(), RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.AUTHORITY.value(),
ExtensionHeaderNames.AUTHORITY.text().toString()); ExtensionHeaderNames.AUTHORITY.text());
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.SCHEME.value(), RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.SCHEME.value(),
ExtensionHeaderNames.SCHEME.text().toString()); ExtensionHeaderNames.SCHEME.text());
REQUEST_HEADER_TRANSLATIONS.putAll(RESPONSE_HEADER_TRANSLATIONS); REQUEST_HEADER_TRANSLATIONS.putAll(RESPONSE_HEADER_TRANSLATIONS);
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.PATH.value(), RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.PATH.value(),
ExtensionHeaderNames.PATH.text().toString()); ExtensionHeaderNames.PATH.text());
} }
private final HttpHeaders output; private final HttpHeaders output;
private final Map<AsciiString, String> translations; private final Map<AsciiString, AsciiString> translations;
private Http2Exception e;
/** /**
* Create a new instance * Create a new instance
* *
* @param output The HTTP/1.x headers object to store the results of the translation * @param output The HTTP/1.x headers object to store the results of the translation
* @param request if {@code true}, translates headers using the request translation map. * @param request if {@code true}, translates headers using the request translation map. Otherwise uses the
* Otherwise uses the response translation map. * response translation map.
*/ */
public Http2ToHttpHeaderTranslator(HttpHeaders output, boolean request) { public Http2ToHttpHeaderTranslator(HttpHeaders output, boolean request) {
this.output = output; this.output = output;
translations = request? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS; translations = request ? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS;
} }
@Override @Override
public boolean visit(AsciiString name, AsciiString value) { public boolean visit(Entry<AsciiString, AsciiString> entry) throws Http2Exception {
String translatedName = translations.get(name); final AsciiString name = entry.getKey();
final AsciiString value = entry.getValue();
AsciiString translatedName = translations.get(name);
if (translatedName != null || !Http2Headers.PseudoHeaderName.isPseudoHeader(name)) { if (translatedName != null || !Http2Headers.PseudoHeaderName.isPseudoHeader(name)) {
if (translatedName == null) { if (translatedName == null) {
translatedName = name.toString(); translatedName = name;
} }
// http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.2.3 // http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.2.3
// All headers that start with ':' are only valid in HTTP/2 context // All headers that start with ':' are only valid in HTTP/2 context
if (translatedName.isEmpty() || translatedName.charAt(0) == ':') { if (translatedName.isEmpty() || translatedName.charAt(0) == ':') {
e = Http2Exception throw Http2Exception
.protocolError("Unknown HTTP/2 header '%s' encountered in translation to HTTP/1.x", .protocolError("Unknown HTTP/2 header '%s' encountered in translation to HTTP/1.x",
translatedName); translatedName);
return false;
} else { } else {
output.add(translatedName, value.toString()); output.add(translatedName, value);
} }
} }
return true; return true;
} }
/**
* Get any exceptions encountered while translating HTTP/2 headers to HTTP/1.x headers
*
* @return
* <ul>
* <li>{@code null} if no exceptions where encountered</li>
* <li>Otherwise an exception describing what went wrong</li>
* </ul>
*/
public Http2Exception cause() {
return e;
}
} }
} }

View File

@ -308,7 +308,7 @@ public class InboundHttp2ToHttpAdapter extends Http2EventAdapter {
promisedStreamId); promisedStreamId);
} }
msg.headers().set(HttpUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), streamId); msg.headers().setInt(HttpUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), streamId);
processHeadersEnd(ctx, promisedStreamId, msg, false); processHeadersEnd(ctx, promisedStreamId, msg, false);
} }

View File

@ -14,15 +14,18 @@
*/ */
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import java.util.Map.Entry;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.TextHeaderProcessor; import io.netty.handler.codec.TextHeaders.EntryVisitor;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.FullHttpMessage; import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap; import io.netty.util.collection.IntObjectMap;
import io.netty.util.internal.PlatformDependent;
/** /**
* Translate header/data/priority HTTP/2 frame events into HTTP events. Just as {@link InboundHttp2ToHttpAdapter} * Translate header/data/priority HTTP/2 frame events into HTTP events. Just as {@link InboundHttp2ToHttpAdapter}
@ -179,13 +182,17 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA
* @param http2Headers The target HTTP/2 headers * @param http2Headers The target HTTP/2 headers
*/ */
private void addHttpHeadersToHttp2Headers(HttpHeaders httpHeaders, final Http2Headers http2Headers) { private void addHttpHeadersToHttp2Headers(HttpHeaders httpHeaders, final Http2Headers http2Headers) {
httpHeaders.forEachEntry(new TextHeaderProcessor() { try {
httpHeaders.forEachEntry(new EntryVisitor() {
@Override @Override
public boolean process(CharSequence name, CharSequence value) throws Exception { public boolean visit(Entry<CharSequence, CharSequence> entry) throws Exception {
http2Headers.add(new AsciiString(name), new AsciiString(value)); http2Headers.add(AsciiString.of(entry.getKey()), AsciiString.of(entry.getValue()));
return true; return true;
} }
}); });
} catch (Exception ex) {
PlatformDependent.throwException(ex);
}
} }
@Override @Override
@ -215,7 +222,7 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA
// and the HTTP message flow exists in OPEN. // and the HTTP message flow exists in OPEN.
if (parent != null && !parent.equals(connection.connectionStream())) { if (parent != null && !parent.equals(connection.connectionStream())) {
HttpHeaders headers = new DefaultHttpHeaders(); HttpHeaders headers = new DefaultHttpHeaders();
headers.set(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), parent.id()); headers.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), parent.id());
importOutOfMessageFlowHeaders(stream.id(), headers); importOutOfMessageFlowHeaders(stream.id(), headers);
} }
} else { } else {
@ -224,7 +231,7 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA
removePriorityRelatedHeaders(msg.trailingHeaders()); removePriorityRelatedHeaders(msg.trailingHeaders());
} else if (!parent.equals(connection.connectionStream())) { } else if (!parent.equals(connection.connectionStream())) {
HttpHeaders headers = getActiveHeaders(msg); HttpHeaders headers = getActiveHeaders(msg);
headers.set(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), parent.id()); headers.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), parent.id());
} }
} }
} }
@ -242,7 +249,7 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA
} else { } else {
headers = getActiveHeaders(msg); headers = getActiveHeaders(msg);
} }
headers.set(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), stream.weight()); headers.setShort(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), stream.weight());
} }
@Override @Override

View File

@ -106,7 +106,7 @@ public class DefaultHttp2ToHttpConnectionHandlerTest {
final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/example"); final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/example");
try { try {
final HttpHeaders httpHeaders = request.headers(); final HttpHeaders httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders.set(HttpHeaders.Names.HOST, httpHeaders.set(HttpHeaders.Names.HOST,
"http://my-user_name@www.example.org:5555/example"); "http://my-user_name@www.example.org:5555/example");
httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "www.example.org:5555"); httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "www.example.org:5555");

View File

@ -169,8 +169,8 @@ public class InboundHttp2ToHttpAdapterTest {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.SCHEME.text(), "https"); httpHeaders.set(HttpUtil.ExtensionHeaderNames.SCHEME.text(), "https");
httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org"); httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org");
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
final Http2Headers http2Headers = final Http2Headers http2Headers =
new DefaultHttp2Headers().method(as("GET")).scheme(as("https")) new DefaultHttp2Headers().method(as("GET")).scheme(as("https"))
.authority(as("example.org")) .authority(as("example.org"))
@ -200,8 +200,8 @@ public class InboundHttp2ToHttpAdapterTest {
"/some/path/resource2", content, true); "/some/path/resource2", content, true);
try { try {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")) final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET"))
.path(as("/some/path/resource2")); .path(as("/some/path/resource2"));
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@ -230,8 +230,8 @@ public class InboundHttp2ToHttpAdapterTest {
"/some/path/resource2", content, true); "/some/path/resource2", content, true);
try { try {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")) final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET"))
.path(as("/some/path/resource2")); .path(as("/some/path/resource2"));
final int midPoint = text.length() / 2; final int midPoint = text.length() / 2;
@ -264,8 +264,8 @@ public class InboundHttp2ToHttpAdapterTest {
"/some/path/resource2", content, true); "/some/path/resource2", content, true);
try { try {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")) final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET"))
.path(as("/some/path/resource2")); .path(as("/some/path/resource2"));
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@ -298,8 +298,8 @@ public class InboundHttp2ToHttpAdapterTest {
"/some/path/resource2", content, true); "/some/path/resource2", content, true);
try { try {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
HttpHeaders trailingHeaders = request.trailingHeaders(); HttpHeaders trailingHeaders = request.trailingHeaders();
trailingHeaders.set("FoO", "goo"); trailingHeaders.set("FoO", "goo");
trailingHeaders.set("foO2", "goo2"); trailingHeaders.set("foO2", "goo2");
@ -338,8 +338,8 @@ public class InboundHttp2ToHttpAdapterTest {
"/some/path/resource2", content, true); "/some/path/resource2", content, true);
try { try {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
HttpHeaders trailingHeaders = request.trailingHeaders(); HttpHeaders trailingHeaders = request.trailingHeaders();
trailingHeaders.set("Foo", "goo"); trailingHeaders.set("Foo", "goo");
trailingHeaders.set("fOo2", "goo2"); trailingHeaders.set("fOo2", "goo2");
@ -383,13 +383,13 @@ public class InboundHttp2ToHttpAdapterTest {
"/some/path/resource2", content2, true); "/some/path/resource2", content2, true);
try { try {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
HttpHeaders httpHeaders2 = request2.headers(); HttpHeaders httpHeaders2 = request2.headers();
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3); httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3);
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 123); httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 123);
httpHeaders2.set(HttpHeaders.Names.CONTENT_LENGTH, text2.length()); httpHeaders2.setInt(HttpHeaders.Names.CONTENT_LENGTH, text2.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT")) final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT"))
.path(as("/some/path/resource")); .path(as("/some/path/resource"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT")) final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT"))
@ -432,20 +432,20 @@ public class InboundHttp2ToHttpAdapterTest {
HttpUtil.OUT_OF_MESSAGE_SEQUENCE_METHOD, HttpUtil.OUT_OF_MESSAGE_SEQUENCE_PATH, true); HttpUtil.OUT_OF_MESSAGE_SEQUENCE_METHOD, HttpUtil.OUT_OF_MESSAGE_SEQUENCE_PATH, true);
try { try {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
HttpHeaders httpHeaders2 = request2.headers(); HttpHeaders httpHeaders2 = request2.headers();
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders2.set(HttpHeaders.Names.CONTENT_LENGTH, text2.length()); httpHeaders2.setInt(HttpHeaders.Names.CONTENT_LENGTH, text2.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT")) final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT"))
.path(as("/some/path/resource")); .path(as("/some/path/resource"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT")) final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT"))
.path(as("/some/path/resource2")); .path(as("/some/path/resource2"));
HttpHeaders httpHeaders3 = request3.headers(); HttpHeaders httpHeaders3 = request3.headers();
httpHeaders3.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders3.set(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3); httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3);
httpHeaders3.set(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 222); httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 222);
httpHeaders3.set(HttpHeaders.Names.CONTENT_LENGTH, 0); httpHeaders3.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {
@ -486,18 +486,18 @@ public class InboundHttp2ToHttpAdapterTest {
HttpMethod.GET, "/push/test", true); HttpMethod.GET, "/push/test", true);
try { try {
HttpHeaders httpHeaders = response.headers(); HttpHeaders httpHeaders = response.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
HttpHeaders httpHeaders2 = response2.headers(); HttpHeaders httpHeaders2 = response2.headers();
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.SCHEME.text(), "https"); httpHeaders2.set(HttpUtil.ExtensionHeaderNames.SCHEME.text(), "https");
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org"); httpHeaders2.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org");
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), 3); httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), 3);
httpHeaders2.set(HttpHeaders.Names.CONTENT_LENGTH, text2.length()); httpHeaders2.setInt(HttpHeaders.Names.CONTENT_LENGTH, text2.length());
httpHeaders = request.headers(); httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(as("GET")) final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(as("GET"))
.path(as("/push/test")); .path(as("/push/test"));
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@ -545,9 +545,9 @@ public class InboundHttp2ToHttpAdapterTest {
final FullHttpMessage request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "/info/test", final FullHttpMessage request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "/info/test",
true); true);
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.EXPECT, HttpHeaders.Values.CONTINUE); httpHeaders.set(HttpHeaders.Names.EXPECT, HttpHeaders.Values.CONTINUE);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
final Http2Headers http2Headers = final Http2Headers http2Headers =
new DefaultHttp2Headers() new DefaultHttp2Headers()
.method(as("PUT")) .method(as("PUT"))
@ -577,8 +577,8 @@ public class InboundHttp2ToHttpAdapterTest {
reset(serverListener); reset(serverListener);
httpHeaders = response.headers(); httpHeaders = response.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(as("100")); final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(as("100"));
runInChannel(serverConnectedChannel, new Http2Runnable() { runInChannel(serverConnectedChannel, new Http2Runnable() {
@Override @Override
@ -597,7 +597,7 @@ public class InboundHttp2ToHttpAdapterTest {
setServerLatch(1); setServerLatch(1);
httpHeaders = request2.headers(); httpHeaders = request2.headers();
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
httpHeaders.remove(HttpHeaders.Names.EXPECT); httpHeaders.remove(HttpHeaders.Names.EXPECT);
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@Override @Override
@ -614,8 +614,8 @@ public class InboundHttp2ToHttpAdapterTest {
setClientLatch(1); setClientLatch(1);
httpHeaders = response2.headers(); httpHeaders = response2.headers();
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0); httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(as("200")); final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(as("200"));
runInChannel(serverConnectedChannel, new Http2Runnable() { runInChannel(serverConnectedChannel, new Http2Runnable() {
@Override @Override

View File

@ -17,29 +17,94 @@
package io.netty.handler.codec.stomp; package io.netty.handler.codec.stomp;
import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.DefaultTextHeaders;
import io.netty.handler.codec.TextHeaderProcessor;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.TextHeaders;
public class DefaultStompHeaders extends DefaultTextHeaders implements StompHeaders { public class DefaultStompHeaders extends DefaultTextHeaders implements StompHeaders {
@Override @Override
public StompHeaders add(CharSequence name, Object value) { public StompHeaders add(CharSequence name, CharSequence value) {
super.add(name, value); super.add(name, value);
return this; return this;
} }
@Override @Override
public StompHeaders add(CharSequence name, Iterable<?> values) { public StompHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@Override @Override
public StompHeaders add(CharSequence name, Object... values) { public StompHeaders add(CharSequence name, CharSequence... values) {
super.add(name, values); super.add(name, values);
return this; return this;
} }
@Override
public StompHeaders addObject(CharSequence name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public StompHeaders addObject(CharSequence name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public StompHeaders addObject(CharSequence name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public StompHeaders addBoolean(CharSequence name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public StompHeaders addChar(CharSequence name, char value) {
super.addChar(name, value);
return this;
}
@Override
public StompHeaders addByte(CharSequence name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public StompHeaders addShort(CharSequence name, short value) {
super.addShort(name, value);
return this;
}
@Override
public StompHeaders addInt(CharSequence name, int value) {
super.addInt(name, value);
return this;
}
@Override
public StompHeaders addLong(CharSequence name, long value) {
super.addLong(name, value);
return this;
}
@Override
public StompHeaders addFloat(CharSequence name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public StompHeaders addDouble(CharSequence name, double value) {
super.addDouble(name, value);
return this;
}
@Override @Override
public StompHeaders add(TextHeaders headers) { public StompHeaders add(TextHeaders headers) {
super.add(headers); super.add(headers);
@ -47,23 +112,89 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead
} }
@Override @Override
public StompHeaders set(CharSequence name, Object value) { public StompHeaders set(CharSequence name, CharSequence value) {
super.set(name, value); super.set(name, value);
return this; return this;
} }
@Override @Override
public StompHeaders set(CharSequence name, Object... values) { public StompHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@Override @Override
public StompHeaders set(CharSequence name, Iterable<?> values) { public StompHeaders set(CharSequence name, CharSequence... values) {
super.set(name, values); super.set(name, values);
return this; return this;
} }
@Override
public StompHeaders setObject(CharSequence name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public StompHeaders setObject(CharSequence name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public StompHeaders setObject(CharSequence name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public StompHeaders setBoolean(CharSequence name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public StompHeaders setChar(CharSequence name, char value) {
super.setChar(name, value);
return this;
}
@Override
public StompHeaders setByte(CharSequence name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public StompHeaders setShort(CharSequence name, short value) {
super.setShort(name, value);
return this;
}
@Override
public StompHeaders setInt(CharSequence name, int value) {
super.setInt(name, value);
return this;
}
@Override
public StompHeaders setLong(CharSequence name, long value) {
super.setLong(name, value);
return this;
}
@Override
public StompHeaders setFloat(CharSequence name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public StompHeaders setDouble(CharSequence name, double value) {
super.setDouble(name, value);
return this;
}
@Override @Override
public StompHeaders set(TextHeaders headers) { public StompHeaders set(TextHeaders headers) {
super.set(headers); super.set(headers);
@ -71,14 +202,14 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead
} }
@Override @Override
public StompHeaders clear() { public StompHeaders setAll(TextHeaders headers) {
super.clear(); super.setAll(headers);
return this; return this;
} }
@Override @Override
public StompHeaders forEachEntry(TextHeaderProcessor processor) { public StompHeaders clear() {
super.forEachEntry(processor); super.clear();
return this; return this;
} }
} }

View File

@ -16,7 +16,6 @@
package io.netty.handler.codec.stomp; package io.netty.handler.codec.stomp;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.TextHeaderProcessor;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.TextHeaders;
/** /**
@ -46,32 +45,98 @@ public interface StompHeaders extends TextHeaders {
AsciiString CONTENT_TYPE = new AsciiString("content-type"); AsciiString CONTENT_TYPE = new AsciiString("content-type");
@Override @Override
StompHeaders add(CharSequence name, Object value); StompHeaders add(CharSequence name, CharSequence value);
@Override @Override
StompHeaders add(CharSequence name, Iterable<?> values); StompHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
@Override @Override
StompHeaders add(CharSequence name, Object... values); StompHeaders add(CharSequence name, CharSequence... values);
@Override
StompHeaders addObject(CharSequence name, Object value);
@Override
StompHeaders addObject(CharSequence name, Iterable<?> values);
@Override
StompHeaders addObject(CharSequence name, Object... values);
@Override
StompHeaders addBoolean(CharSequence name, boolean value);
@Override
StompHeaders addByte(CharSequence name, byte value);
@Override
StompHeaders addChar(CharSequence name, char value);
@Override
StompHeaders addShort(CharSequence name, short value);
@Override
StompHeaders addInt(CharSequence name, int value);
@Override
StompHeaders addLong(CharSequence name, long value);
@Override
StompHeaders addFloat(CharSequence name, float value);
@Override
StompHeaders addDouble(CharSequence name, double value);
@Override @Override
StompHeaders add(TextHeaders headers); StompHeaders add(TextHeaders headers);
@Override @Override
StompHeaders set(CharSequence name, Object value); StompHeaders set(CharSequence name, CharSequence value);
@Override @Override
StompHeaders set(CharSequence name, Iterable<?> values); StompHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
@Override @Override
StompHeaders set(CharSequence name, Object... values); StompHeaders set(CharSequence name, CharSequence... values);
@Override
StompHeaders setObject(CharSequence name, Object value);
@Override
StompHeaders setObject(CharSequence name, Iterable<?> values);
@Override
StompHeaders setObject(CharSequence name, Object... values);
@Override
StompHeaders setBoolean(CharSequence name, boolean value);
@Override
StompHeaders setByte(CharSequence name, byte value);
@Override
StompHeaders setChar(CharSequence name, char value);
@Override
StompHeaders setShort(CharSequence name, short value);
@Override
StompHeaders setInt(CharSequence name, int value);
@Override
StompHeaders setLong(CharSequence name, long value);
@Override
StompHeaders setFloat(CharSequence name, float value);
@Override
StompHeaders setDouble(CharSequence name, double value);
@Override @Override
StompHeaders set(TextHeaders headers); StompHeaders set(TextHeaders headers);
@Override @Override
StompHeaders clear(); StompHeaders setAll(TextHeaders headers);
@Override @Override
StompHeaders forEachEntry(TextHeaderProcessor processor); StompHeaders clear();
} }

View File

@ -69,8 +69,7 @@ public class StompSubframeAggregator
@Override @Override
protected long contentLength(StompHeadersSubframe start) throws Exception { protected long contentLength(StompHeadersSubframe start) throws Exception {
String value = start.headers().get(StompHeaders.CONTENT_LENGTH); return start.headers().getLong(StompHeaders.CONTENT_LENGTH, 0);
return Long.parseLong(value);
} }
@Override @Override

View File

@ -219,15 +219,7 @@ public class StompSubframeDecoder extends ReplayingDecoder<State> {
} }
private static long getContentLength(StompHeaders headers, long defaultValue) { private static long getContentLength(StompHeaders headers, long defaultValue) {
String contentLength = headers.get(StompHeaders.CONTENT_LENGTH); return headers.getLong(StompHeaders.CONTENT_LENGTH, defaultValue);
if (contentLength != null) {
try {
return Long.parseLong(contentLength);
} catch (NumberFormatException ignored) {
return defaultValue;
}
}
return defaultValue;
} }
private static void skipNullCharacter(ByteBuf buffer) { private static void skipNullCharacter(ByteBuf buffer) {

View File

@ -22,6 +22,7 @@ import io.netty.handler.codec.AsciiHeadersEncoder.NewlineType;
import io.netty.handler.codec.AsciiHeadersEncoder.SeparatorType; import io.netty.handler.codec.AsciiHeadersEncoder.SeparatorType;
import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.internal.PlatformDependent;
import java.util.List; import java.util.List;
@ -65,7 +66,12 @@ public class StompSubframeEncoder extends MessageToMessageEncoder<StompSubframe>
buf.writeBytes(frame.command().toString().getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(frame.command().toString().getBytes(CharsetUtil.US_ASCII));
buf.writeByte(StompConstants.LF); buf.writeByte(StompConstants.LF);
try {
frame.headers().forEachEntry(new AsciiHeadersEncoder(buf, SeparatorType.COLON, NewlineType.LF)); frame.headers().forEachEntry(new AsciiHeadersEncoder(buf, SeparatorType.COLON, NewlineType.LF));
} catch (Exception ex) {
buf.release();
PlatformDependent.throwException(ex);
}
buf.writeByte(StompConstants.LF); buf.writeByte(StompConstants.LF);
return buf; return buf;
} }

View File

@ -17,9 +17,12 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.buffer.ByteBuf; import java.util.Map.Entry;
public final class AsciiHeadersEncoder implements TextHeaderProcessor { import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.TextHeaders.EntryVisitor;
public final class AsciiHeadersEncoder implements EntryVisitor {
/** /**
* The separator characters to insert between a header name and a header value. * The separator characters to insert between a header name and a header value.
@ -74,7 +77,9 @@ public final class AsciiHeadersEncoder implements TextHeaderProcessor {
} }
@Override @Override
public boolean process(CharSequence name, CharSequence value) throws Exception { public boolean visit(Entry<CharSequence, CharSequence> entry) throws Exception {
final CharSequence name = entry.getKey();
final CharSequence value = entry.getValue();
final ByteBuf buf = this.buf; final ByteBuf buf = this.buf;
final int nameLen = name.length(); final int nameLen = name.length();
final int valueLen = value.length(); final int valueLen = value.length();

File diff suppressed because it is too large Load Diff

View File

@ -16,354 +16,123 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
/** /**
* A typical {@code AsciiString} multimap used by protocols that use binary headers (such as HTTP/2) * A typical {@code AsciiString} multimap used by protocols that use binary headers (such as HTTP/2) for the
* for the representation of arbitrary key-value data. {@link AsciiString} is just a wrapper around * representation of arbitrary key-value data. {@link AsciiString} is just a wrapper around a byte array but provides
* a byte array but provides some additional utility when handling text data. * some additional utility when handling text data.
*/ */
public interface BinaryHeaders extends Iterable<Map.Entry<AsciiString, AsciiString>> { public interface BinaryHeaders extends Headers<AsciiString> {
/** /**
* A visitor that helps reduce GC pressure while iterating over a collection of {@link BinaryHeaders}. * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}.
*/ */
public interface BinaryHeaderVisitor { public interface EntryVisitor extends Headers.EntryVisitor<AsciiString> {
/**
* @return
* <ul>
* <li>{@code true} if the processor wants to continue the loop and handle the entry.</li>
* <li>{@code false} if the processor wants to stop handling headers and abort the loop.</li>
* </ul>
*/
boolean visit(AsciiString name, AsciiString value) throws Exception;
} }
/** /**
* Returns the value of a header with the specified name. If there are * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}.
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @return the first header value if the header is found.
* {@code null} if there's no such header.
*/ */
AsciiString get(AsciiString name); public interface NameVisitor extends Headers.NameVisitor<AsciiString> {
}
/** @Override
* Returns the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @param defaultValue the default value
* @return the first header value if the header is found.
* {@code defaultValue} if there's no such header.
*/
AsciiString get(AsciiString name, AsciiString defaultValue);
/**
* Returns and removes the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @return the first header value or {@code null} if there is no such header
*/
AsciiString getAndRemove(AsciiString name);
/**
* Returns and removes the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @param defaultValue the default value
* @return the first header value or {@code defaultValue} if there is no such header
*/
AsciiString getAndRemove(AsciiString name, AsciiString defaultValue);
/**
* Returns the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values are found
*/
List<AsciiString> getAll(AsciiString name);
/**
* Returns and Removes the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values are found
*/
List<AsciiString> getAllAndRemove(AsciiString name);
/**
* Returns a new {@link List} that contains all headers in this object. Note that modifying the
* returned {@link List} will not affect the state of this object. If you intend to enumerate over the header
* entries only, use {@link #iterator()} instead, which has much less overhead.
*/
List<Entry<AsciiString, AsciiString>> entries();
/**
* Returns {@code true} if and only if this collection contains the header with the specified name.
*
* @param name The name of the header to search for
* @return {@code true} if at least one header is found
*/
boolean contains(AsciiString name);
/**
* Returns the number of header entries in this collection.
*/
int size();
/**
* Returns {@code true} if and only if this collection contains no header entries.
*/
boolean isEmpty();
/**
* Returns a new {@link Set} that contains the names of all headers in this object. Note that modifying the
* returned {@link Set} will not affect the state of this object. If you intend to enumerate over the header
* entries only, use {@link #iterator()} instead, which has much less overhead.
*/
Set<AsciiString> names();
/**
* Adds a new header with the specified name and value.
*
* If the specified value is not a {@link String}, it is converted
* into a {@link String} by {@link Object#toString()}, except in the cases
* of {@link java.util.Date} and {@link java.util.Calendar}, which are formatted to the date
* format defined in <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
*
* @param name the name of the header being added
* @param value the value of the header being added
*
* @return {@code this}
*/
BinaryHeaders add(AsciiString name, AsciiString value); BinaryHeaders add(AsciiString name, AsciiString value);
/** @Override
* Adds a new header with the specified name and values. BinaryHeaders add(AsciiString name, Iterable<? extends AsciiString> values);
*
* This getMethod can be represented approximately as the following code:
* <pre>
* for (Object v: values) {
* if (v == null) {
* break;
* }
* headers.add(name, v);
* }
* </pre>
*
* @param name the name of the headers being set
* @param values the values of the headers being set
* @return {@code this}
*/
BinaryHeaders add(AsciiString name, Iterable<AsciiString> values);
/** @Override
* Adds a new header with the specified name and values.
*
* This getMethod can be represented approximately as the following code:
* <pre>
* for (Object v: values) {
* if (v == null) {
* break;
* }
* headers.add(name, v);
* }
* </pre>
*
* @param name the name of the headers being set
* @param values the values of the headers being set
* @return {@code this}
*/
BinaryHeaders add(AsciiString name, AsciiString... values); BinaryHeaders add(AsciiString name, AsciiString... values);
@Override
BinaryHeaders addObject(AsciiString name, Object value);
@Override
BinaryHeaders addObject(AsciiString name, Iterable<?> values);
@Override
BinaryHeaders addObject(AsciiString name, Object... values);
@Override
BinaryHeaders addBoolean(AsciiString name, boolean value);
@Override
BinaryHeaders addByte(AsciiString name, byte value);
@Override
BinaryHeaders addChar(AsciiString name, char value);
@Override
BinaryHeaders addShort(AsciiString name, short value);
@Override
BinaryHeaders addInt(AsciiString name, int value);
@Override
BinaryHeaders addLong(AsciiString name, long value);
@Override
BinaryHeaders addFloat(AsciiString name, float value);
@Override
BinaryHeaders addDouble(AsciiString name, double value);
/** /**
* Adds all header entries of the specified {@code headers}. * See {@link Headers#add(Headers)}
*
* @return {@code this}
*/ */
BinaryHeaders add(BinaryHeaders headers); BinaryHeaders add(BinaryHeaders headers);
/** @Override
* Sets a header with the specified name and value.
*
* If there is an existing header with the same name, it is removed.
* If the specified value is not a {@link String}, it is converted into a
* {@link String} by {@link Object#toString()}, except for {@link java.util.Date}
* and {@link java.util.Calendar}, which are formatted to the date format defined in
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
*
* @param name The name of the header being set
* @param value The value of the header being set
* @return {@code this}
*/
BinaryHeaders set(AsciiString name, AsciiString value); BinaryHeaders set(AsciiString name, AsciiString value);
/** @Override
* Sets a header with the specified name and values. BinaryHeaders set(AsciiString name, Iterable<? extends AsciiString> values);
*
* If there is an existing header with the same name, it is removed.
* This getMethod can be represented approximately as the following code:
* <pre>
* headers.remove(name);
* for (Object v: values) {
* if (v == null) {
* break;
* }
* headers.add(name, v);
* }
* </pre>
*
* @param name the name of the headers being set
* @param values the values of the headers being set
* @return {@code this}
*/
BinaryHeaders set(AsciiString name, Iterable<AsciiString> values);
/** @Override
* Sets a header with the specified name and values.
*
* If there is an existing header with the same name, it is removed.
* This getMethod can be represented approximately as the following code:
* <pre>
* headers.remove(name);
* for (Object v: values) {
* if (v == null) {
* break;
* }
* headers.add(name, v);
* }
* </pre>
*
* @param name the name of the headers being set
* @param values the values of the headers being set
* @return {@code this}
*/
BinaryHeaders set(AsciiString name, AsciiString... values); BinaryHeaders set(AsciiString name, AsciiString... values);
@Override
BinaryHeaders setObject(AsciiString name, Object value);
@Override
BinaryHeaders setObject(AsciiString name, Iterable<?> values);
@Override
BinaryHeaders setObject(AsciiString name, Object... values);
@Override
BinaryHeaders setBoolean(AsciiString name, boolean value);
@Override
BinaryHeaders setByte(AsciiString name, byte value);
@Override
BinaryHeaders setChar(AsciiString name, char value);
@Override
BinaryHeaders setShort(AsciiString name, short value);
@Override
BinaryHeaders setInt(AsciiString name, int value);
@Override
BinaryHeaders setLong(AsciiString name, long value);
@Override
BinaryHeaders setFloat(AsciiString name, float value);
@Override
BinaryHeaders setDouble(AsciiString name, double value);
/** /**
* Cleans the current header entries and copies all header entries of the specified {@code headers}. * See {@link Headers#set(Headers)}
*
* @return {@code this}
*/ */
BinaryHeaders set(BinaryHeaders headers); BinaryHeaders set(BinaryHeaders headers);
/** /**
* Retains all current headers but calls {@link #set(AsciiString, Object)} for each entry in {@code headers} * See {@link Headers#setAll(Headers)}
* @param headers The headers used to {@link #set(AsciiString, Object)} values in this instance
* @return {@code this}
*/ */
BinaryHeaders setAll(BinaryHeaders headers); BinaryHeaders setAll(BinaryHeaders headers);
/**
* Removes the header with the specified name.
*
* @param name The name of the header to remove
* @return {@code true} if and only if at least one entry has been removed
*/
boolean remove(AsciiString name);
/**
* Removes all headers.
*
* @return {@code this}
*/
BinaryHeaders clear();
/**
* Returns {@code true} if a header with the name and value exists.
*
* @param name the header name
* @param value the header value
* @return {@code true} if it contains it {@code false} otherwise
*/
boolean contains(AsciiString name, AsciiString value);
@Override @Override
Iterator<Entry<AsciiString, AsciiString>> iterator(); BinaryHeaders clear();
BinaryHeaders forEachEntry(BinaryHeaderVisitor visitor);
/**
* Common utilities for {@link BinaryHeaders}.
*/
public static final class Utils {
private static final int HASH_CODE_PRIME = 31;
/**
* Generates a hash code for a {@link BinaryHeaders} object.
*/
public static int hashCode(BinaryHeaders headers) {
int result = 1;
for (AsciiString name : headers.names()) {
result = HASH_CODE_PRIME * result + name.hashCode();
Set<AsciiString> values = new TreeSet<AsciiString>(headers.getAll(name));
for (AsciiString value : values) {
result = HASH_CODE_PRIME * result + value.hashCode();
}
}
return result;
}
/**
* Compares the contents of two {@link BinaryHeaders} objects.
*/
public static boolean equals(BinaryHeaders h1, BinaryHeaders h2) {
// First, check that the set of names match.
Set<AsciiString> names = h1.names();
if (!names.equals(h2.names())) {
return false;
}
// Compare the values for each name.
for (AsciiString name : names) {
List<AsciiString> values = h1.getAll(name);
List<AsciiString> otherValues = h2.getAll(name);
if (values.size() != otherValues.size()) {
return false;
}
// Convert the values to a set and remove values from the other object to see if
// they match.
Set<AsciiString> valueSet = new HashSet<AsciiString>(values);
valueSet.removeAll(otherValues);
if (!valueSet.isEmpty()) {
return false;
}
}
return true;
}
/**
* Generates a {@link String} representation of the {@link BinaryHeaders}, assuming all of
* the names and values are {@code UTF-8} strings.
*/
public static String toStringUtf8(BinaryHeaders headers) {
StringBuilder builder =
new StringBuilder(headers.getClass().getSimpleName()).append('[');
boolean first = true;
Set<AsciiString> names = new TreeSet<AsciiString>(headers.names());
for (AsciiString name : names) {
Set<AsciiString> valueSet = new TreeSet<AsciiString>(headers.getAll(name));
for (AsciiString value : valueSet) {
if (!first) {
builder.append(", ");
}
first = false;
builder.append(name).append(": ").append(value);
}
}
return builder.append("]").toString();
}
}
} }

View File

@ -0,0 +1,112 @@
/*
* Copyright 2014 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;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Extension to the {@link Headers} interface to provide methods which convert the
* native {@code UnconvertedType} to the not-native {@code ConvertedType}
*/
public interface ConvertibleHeaders<UnconvertedType, ConvertedType> extends Headers<UnconvertedType> {
/**
* Interface to do conversions to and from the two generic type parameters
*/
public interface TypeConverter<UnconvertedType, ConvertedType> {
/**
* Convert a native value
* @param value The value to be converted
* @return The conversion results
*/
ConvertedType toConvertedType(UnconvertedType value);
/**
* Undo a conversion and restore the original native type
* @param value The converted value
* @return The original native type
*/
UnconvertedType toUnconvertedType(ConvertedType value);
}
/**
* Invokes {@link Headers#get(Object)} and does a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The value corresponding to {@code name} and then converted
*/
ConvertedType getAndConvert(UnconvertedType name);
/**
* Invokes {@link Headers#get(Object, Object)} and does a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The value corresponding to {@code name} and then converted
*/
ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue);
/**
* Invokes {@link Headers#getAndRemove(Object)} and does a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The value corresponding to {@code name} and then converted
*/
ConvertedType getAndRemoveAndConvert(UnconvertedType name);
/**
* Invokes {@link Headers#getAndRemove(Object, Object)} and does
* a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The value corresponding to {@code name} and then converted
*/
ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue);
/**
* Invokes {@link Headers#getAll(Object)} and does a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The values corresponding to {@code name} and then converted
*/
List<ConvertedType> getAllAndConvert(UnconvertedType name);
/**
* Invokes {@link Headers#getAllAndRemove(Object)} and does a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The values corresponding to {@code name} and then converted
*/
List<ConvertedType> getAllAndRemoveAndConvert(UnconvertedType name);
/**
* Invokes {@link Headers#entries()} and lazily does a conversion on the results as they are accessed
* @param name The name of entry to get
* @return The values corresponding to {@code name} and then lazily converted
*/
List<Map.Entry<ConvertedType, ConvertedType>> entriesConverted();
/**
* Invokes {@link Headers#iterator()} and lazily does a conversion on the results as they are accessed
* @param name The name of entry to get
* @return Iterator which will provide converted values corresponding to {@code name}
*/
Iterator<Entry<ConvertedType, ConvertedType>> iteratorConverted();
/**
* Invokes {@link Headers#names()} and does a conversion on the results
* @param name The name of entry to get
* @return The values corresponding to {@code name} and then converted
*/
Set<ConvertedType> namesAndConvert(Comparator<ConvertedType> comparator);
}

View File

@ -14,329 +14,334 @@
*/ */
package io.netty.handler.codec; package io.netty.handler.codec;
import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.handler.codec.AsciiString.CASE_INSENSITIVE_ORDER;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import java.util.Arrays; import java.text.ParseException;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
public class DefaultBinaryHeaders implements BinaryHeaders { public class DefaultBinaryHeaders extends DefaultHeaders<AsciiString> implements BinaryHeaders {
private final HeaderMap.ValueUnmarshaller<AsciiString> VALUE_UNMARSHALLER = private static final HashCodeGenerator<AsciiString> ASCII_HASH_CODE_GENERATOR =
new HeaderMap.ValueUnmarshaller<AsciiString>() { new HashCodeGenerator<AsciiString>() {
@Override @Override
public AsciiString unmarshal(CharSequence value) { public int generateHashCode(AsciiString name) {
return AsciiString.caseInsensitiveHashCode(name);
}
};
private static final ValueConverter<AsciiString> OBJECT_TO_ASCII = new ValueConverter<AsciiString>() {
@Override
public AsciiString convert(Object value) {
if (value instanceof AsciiString) {
return (AsciiString) value; return (AsciiString) value;
} else if (value instanceof CharSequence) {
return new AsciiString((CharSequence) value);
}
return new AsciiString(value.toString());
}
@Override
public AsciiString convert(int value) {
return new AsciiString(String.valueOf(value));
}
@Override
public AsciiString convert(long value) {
return new AsciiString(String.valueOf(value));
}
@Override
public AsciiString convert(double value) {
return new AsciiString(String.valueOf(value));
}
@Override
public AsciiString convert(char value) {
return new AsciiString(String.valueOf(value));
}
@Override
public AsciiString convert(boolean value) {
return new AsciiString(String.valueOf(value));
}
@Override
public AsciiString convert(float value) {
return new AsciiString(String.valueOf(value));
}
@Override
public int convertToInt(AsciiString value) {
return value.parseInt();
}
@Override
public long convertToLong(AsciiString value) {
return value.parseLong();
}
@Override
public long convertToTimeMillis(AsciiString value) {
try {
return HeaderDateFormat.get().parse(value.toString());
} catch (ParseException e) {
PlatformDependent.throwException(e);
}
return 0;
}
@Override
public double convertToDouble(AsciiString value) {
return value.parseDouble();
}
@Override
public char convertToChar(AsciiString value) {
return value.charAt(0);
}
@Override
public boolean convertToBoolean(AsciiString value) {
return value.byteAt(0) != 0;
}
@Override
public float convertToFloat(AsciiString value) {
return value.parseFloat();
}
@Override
public AsciiString convert(short value) {
return new AsciiString(String.valueOf(value));
}
@Override
public short convertToShort(AsciiString value) {
return value.parseShort();
}
@Override
public AsciiString convert(byte value) {
return new AsciiString(String.valueOf(value));
}
@Override
public byte convertToByte(AsciiString value) {
return value.byteAt(0);
} }
}; };
private final BinaryHeaderVisitor addAll = new BinaryHeaderVisitor() { private static final NameConverter<AsciiString> ASCII_TO_LOWER_CONVERTER = new NameConverter<AsciiString>() {
@Override @Override
public boolean visit(AsciiString name, AsciiString value) throws Exception { public AsciiString convertName(AsciiString name) {
add(name, value); return name.toLowerCase();
return true;
}
};
private final BinaryHeaderVisitor setAll = new BinaryHeaderVisitor() {
@Override
public boolean visit(AsciiString name, AsciiString value) throws Exception {
set(name, value);
return true;
} }
}; };
private final HeaderMap headers; private static final NameConverter<AsciiString> ASCII_IDENTITY_CONVERTER = new NameConverter<AsciiString>() {
@Override
public AsciiString convertName(AsciiString name) {
return name;
}
};
public DefaultBinaryHeaders() { public DefaultBinaryHeaders() {
// Binary headers are case-sensitive. It's up the HTTP/1 translation layer to convert headers to this(false);
// lowercase. }
headers = new HeaderMap(false);
public DefaultBinaryHeaders(boolean forceKeyToLower) {
super(CASE_INSENSITIVE_ORDER, CASE_INSENSITIVE_ORDER, ASCII_HASH_CODE_GENERATOR, OBJECT_TO_ASCII,
forceKeyToLower ? ASCII_TO_LOWER_CONVERTER : ASCII_IDENTITY_CONVERTER);
} }
@Override @Override
public BinaryHeaders add(AsciiString name, AsciiString value) { public BinaryHeaders add(AsciiString name, AsciiString value) {
headers.add(name, value); super.add(name, value);
return this; return this;
} }
@Override @Override
public BinaryHeaders add(AsciiString name, Iterable<AsciiString> values) { public BinaryHeaders add(AsciiString name, Iterable<? extends AsciiString> values) {
headers.add(name, values); super.add(name, values);
return this; return this;
} }
@Override @Override
public BinaryHeaders add(AsciiString name, AsciiString... values) { public BinaryHeaders add(AsciiString name, AsciiString... values) {
headers.add(name, values); super.add(name, values);
return this;
}
@Override
public BinaryHeaders addObject(AsciiString name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public BinaryHeaders addObject(AsciiString name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public BinaryHeaders addObject(AsciiString name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public BinaryHeaders addBoolean(AsciiString name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public BinaryHeaders addChar(AsciiString name, char value) {
super.addChar(name, value);
return this;
}
@Override
public BinaryHeaders addByte(AsciiString name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public BinaryHeaders addShort(AsciiString name, short value) {
super.addShort(name, value);
return this;
}
@Override
public BinaryHeaders addInt(AsciiString name, int value) {
super.addInt(name, value);
return this;
}
@Override
public BinaryHeaders addLong(AsciiString name, long value) {
super.addLong(name, value);
return this;
}
@Override
public BinaryHeaders addFloat(AsciiString name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public BinaryHeaders addDouble(AsciiString name, double value) {
super.addDouble(name, value);
return this; return this;
} }
@Override @Override
public BinaryHeaders add(BinaryHeaders headers) { public BinaryHeaders add(BinaryHeaders headers) {
checkNotNull(headers, "headers"); super.add(headers);
add0(headers);
return this; return this;
} }
private void add0(BinaryHeaders headers) {
if (headers.isEmpty()) {
return;
}
if (headers instanceof DefaultBinaryHeaders) {
this.headers.add(((DefaultBinaryHeaders) headers).headers);
} else {
forEachEntry(addAll);
}
}
@Override
public boolean remove(AsciiString name) {
return headers.remove(name);
}
@Override @Override
public BinaryHeaders set(AsciiString name, AsciiString value) { public BinaryHeaders set(AsciiString name, AsciiString value) {
headers.set(name, value); super.set(name, value);
return this; return this;
} }
@Override @Override
public BinaryHeaders set(AsciiString name, Iterable<AsciiString> values) { public BinaryHeaders set(AsciiString name, Iterable<? extends AsciiString> values) {
headers.set(name, values); super.set(name, values);
return this; return this;
} }
@Override @Override
public BinaryHeaders set(AsciiString name, AsciiString... values) { public BinaryHeaders set(AsciiString name, AsciiString... values) {
headers.set(name, values); super.set(name, values);
return this;
}
@Override
public BinaryHeaders setObject(AsciiString name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public BinaryHeaders setObject(AsciiString name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public BinaryHeaders setObject(AsciiString name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public BinaryHeaders setBoolean(AsciiString name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public BinaryHeaders setChar(AsciiString name, char value) {
super.setChar(name, value);
return this;
}
@Override
public BinaryHeaders setByte(AsciiString name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public BinaryHeaders setShort(AsciiString name, short value) {
super.setShort(name, value);
return this;
}
@Override
public BinaryHeaders setInt(AsciiString name, int value) {
super.setInt(name, value);
return this;
}
@Override
public BinaryHeaders setLong(AsciiString name, long value) {
super.setLong(name, value);
return this;
}
@Override
public BinaryHeaders setFloat(AsciiString name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public BinaryHeaders setDouble(AsciiString name, double value) {
super.setDouble(name, value);
return this; return this;
} }
@Override @Override
public BinaryHeaders set(BinaryHeaders headers) { public BinaryHeaders set(BinaryHeaders headers) {
checkNotNull(headers, "headers"); super.set(headers);
clear();
add0(headers);
return this; return this;
} }
@Override @Override
public BinaryHeaders setAll(BinaryHeaders headers) { public BinaryHeaders setAll(BinaryHeaders headers) {
checkNotNull(headers, "headers"); super.setAll(headers);
if (headers instanceof DefaultBinaryHeaders) {
this.headers.setAll(((DefaultBinaryHeaders) headers).headers);
} else {
forEachEntry(setAll);
}
return this; return this;
} }
@Override @Override
public BinaryHeaders clear() { public BinaryHeaders clear() {
headers.clear(); super.clear();
return this; return this;
} }
@Override
public AsciiString get(AsciiString name) {
return (AsciiString) headers.get(name);
}
@Override
public AsciiString get(AsciiString name, AsciiString defaultValue) {
AsciiString v = get(name);
if (v == null) {
return defaultValue;
}
return v;
}
@Override
public AsciiString getAndRemove(AsciiString name) {
return (AsciiString) headers.getAndRemove(name);
}
@Override
public AsciiString getAndRemove(AsciiString name, AsciiString defaultValue) {
AsciiString v = getAndRemove(name);
if (v == null) {
return defaultValue;
}
return v;
}
@Override
public List<AsciiString> getAll(AsciiString name) {
return headers.getAll(name, VALUE_UNMARSHALLER);
}
@Override
public List<AsciiString> getAllAndRemove(AsciiString name) {
return headers.getAllAndRemove(name, VALUE_UNMARSHALLER);
}
@Override
public List<Map.Entry<AsciiString, AsciiString>> entries() {
int size = size();
@SuppressWarnings("unchecked")
final Map.Entry<AsciiString, AsciiString>[] all = new Map.Entry[size];
headers.forEachEntry(new HeaderMap.EntryVisitor() {
int cnt;
@Override
public boolean visit(Entry<CharSequence, CharSequence> entry) {
all[cnt++] = new AsciiStringHeaderEntry(entry);
return true;
}
});
return Arrays.asList(all);
}
@Override
public Iterator<Entry<AsciiString, AsciiString>> iterator() {
return new AsciiStringHeaderIterator();
}
@Override
public boolean contains(AsciiString name) {
return get(name) != null;
}
@Override
public int size() {
return headers.size();
}
@Override
public boolean isEmpty() {
return headers.isEmpty();
}
@Override
public boolean contains(AsciiString name, AsciiString value) {
return contains(name, value, false);
}
public boolean contains(AsciiString name, AsciiString value, boolean ignoreCase) {
return headers.contains(name, value);
}
@Override
public Set<AsciiString> names() {
return names(headers.isIgnoreCase());
}
/**
* Get the set of names for all text headers
* @param caseInsensitive {@code true} if names should be added in a case insensitive
* @return The set of names for all text headers
*/
public Set<AsciiString> names(boolean caseInsensitive) {
final Set<AsciiString> names = caseInsensitive ? new TreeSet<AsciiString>(AsciiString.CASE_INSENSITIVE_ORDER)
: new LinkedHashSet<AsciiString>(size());
headers.forEachName(new HeaderMap.NameVisitor() {
@Override
public boolean visit(CharSequence name) {
names.add((AsciiString) name);
return true;
}
});
return names;
}
@Override
public BinaryHeaders forEachEntry(final BinaryHeaders.BinaryHeaderVisitor visitor) {
headers.forEachEntry(new HeaderMap.EntryVisitor() {
@Override
public boolean visit(Entry<CharSequence, CharSequence> entry) {
try {
return visitor.visit((AsciiString) entry.getKey(),
(AsciiString) entry.getValue());
} catch (Exception e) {
PlatformDependent.throwException(e);
return false;
}
}
});
return this;
}
@Override
public int hashCode() {
return Utils.hashCode(this);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof BinaryHeaders)) {
return false;
}
return Utils.equals(this, (BinaryHeaders) o);
}
@Override
public String toString() {
return Utils.toStringUtf8(this);
}
private static final class AsciiStringHeaderEntry implements Map.Entry<AsciiString, AsciiString> {
private final Entry<CharSequence, CharSequence> entry;
AsciiStringHeaderEntry(Entry<CharSequence, CharSequence> entry) {
this.entry = entry;
}
@Override
public AsciiString getKey() {
return (AsciiString) entry.getKey();
}
@Override
public AsciiString getValue() {
return (AsciiString) entry.getValue();
}
@Override
public AsciiString setValue(AsciiString value) {
checkNotNull(value, "value");
return (AsciiString) entry.setValue(value);
}
@Override
public String toString() {
return entry.toString();
}
}
private final class AsciiStringHeaderIterator implements Iterator<Map.Entry<AsciiString, AsciiString>> {
private Iterator<Entry<CharSequence, CharSequence>> iter = headers.iterator();
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public Entry<AsciiString, AsciiString> next() {
Entry<CharSequence, CharSequence> entry = iter.next();
return new AsciiStringHeaderEntry(entry);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
} }

View File

@ -0,0 +1,181 @@
/*
* Copyright 2014 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;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
public class DefaultConvertibleHeaders<UnconvertedType, ConvertedType> extends DefaultHeaders<UnconvertedType>
implements ConvertibleHeaders<UnconvertedType, ConvertedType> {
private final TypeConverter<UnconvertedType, ConvertedType> typeConverter;
public DefaultConvertibleHeaders(Comparator<? super UnconvertedType> keyComparator,
Comparator<? super UnconvertedType> valueComparator,
HashCodeGenerator<UnconvertedType> hashCodeGenerator,
ValueConverter<UnconvertedType> valueConverter,
TypeConverter<UnconvertedType, ConvertedType> typeConverter) {
super(keyComparator, valueComparator, hashCodeGenerator, valueConverter);
this.typeConverter = typeConverter;
}
public DefaultConvertibleHeaders(Comparator<? super UnconvertedType> keyComparator,
Comparator<? super UnconvertedType> valueComparator,
HashCodeGenerator<UnconvertedType> hashCodeGenerator,
ValueConverter<UnconvertedType> valueConverter,
TypeConverter<UnconvertedType, ConvertedType> typeConverter,
NameConverter<UnconvertedType> nameConverter) {
super(keyComparator, valueComparator, hashCodeGenerator, valueConverter, nameConverter);
this.typeConverter = typeConverter;
}
@Override
public ConvertedType getAndConvert(UnconvertedType name) {
return getAndConvert(name, null);
}
@Override
public ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue) {
UnconvertedType v = get(name);
if (v == null) {
return defaultValue;
}
return typeConverter.toConvertedType(v);
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name) {
return getAndRemoveAndConvert(name, null);
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue) {
UnconvertedType v = getAndRemove(name);
if (v == null) {
return defaultValue;
}
return typeConverter.toConvertedType(v);
}
@Override
public List<ConvertedType> getAllAndConvert(UnconvertedType name) {
List<UnconvertedType> all = getAll(name);
List<ConvertedType> allConverted = new ArrayList<ConvertedType>(all.size());
for (int i = 0; i < all.size(); ++i) {
allConverted.add(typeConverter.toConvertedType(all.get(i)));
}
return allConverted;
}
@Override
public List<ConvertedType> getAllAndRemoveAndConvert(UnconvertedType name) {
List<UnconvertedType> all = getAllAndRemove(name);
List<ConvertedType> allConverted = new ArrayList<ConvertedType>(all.size());
for (int i = 0; i < all.size(); ++i) {
allConverted.add(typeConverter.toConvertedType(all.get(i)));
}
return allConverted;
}
@Override
public List<Entry<ConvertedType, ConvertedType>> entriesConverted() {
List<Entry<UnconvertedType, UnconvertedType>> entries = entries();
List<Entry<ConvertedType, ConvertedType>> entriesConverted = new ArrayList<Entry<ConvertedType, ConvertedType>>(
entries.size());
for (int i = 0; i < entries.size(); ++i) {
entriesConverted.add(new ConvertedEntry(entries.get(i)));
}
return entriesConverted;
}
@Override
public Iterator<Entry<ConvertedType, ConvertedType>> iteratorConverted() {
return new ConvertedIterator();
}
@Override
public Set<ConvertedType> namesAndConvert(Comparator<ConvertedType> comparator) {
Set<UnconvertedType> names = names();
Set<ConvertedType> namesConverted = new TreeSet<ConvertedType>(comparator);
for (UnconvertedType unconverted : names) {
namesConverted.add(typeConverter.toConvertedType(unconverted));
}
return namesConverted;
}
private final class ConvertedIterator implements Iterator<Entry<ConvertedType, ConvertedType>> {
private Iterator<Entry<UnconvertedType, UnconvertedType>> iter = iterator();
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public Entry<ConvertedType, ConvertedType> next() {
Entry<UnconvertedType, UnconvertedType> next = iter.next();
return new ConvertedEntry(next);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private final class ConvertedEntry implements Entry<ConvertedType, ConvertedType> {
private final Entry<UnconvertedType, UnconvertedType> entry;
private ConvertedType name;
private ConvertedType value;
public ConvertedEntry(Entry<UnconvertedType, UnconvertedType> entry) {
this.entry = entry;
}
@Override
public ConvertedType getKey() {
if (name == null) {
name = typeConverter.toConvertedType(entry.getKey());
}
return name;
}
@Override
public ConvertedType getValue() {
if (value == null) {
value = typeConverter.toConvertedType(entry.getValue());
}
return value;
}
@Override
public ConvertedType setValue(ConvertedType value) {
ConvertedType old = getValue();
entry.setValue(typeConverter.toUnconvertedType(value));
return old;
}
@Override
public String toString() {
return entry.toString();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -16,154 +16,199 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import java.util.Collections; public class EmptyBinaryHeaders extends EmptyHeaders<AsciiString> implements BinaryHeaders {
import java.util.Iterator; protected EmptyBinaryHeaders() {
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
public class EmptyBinaryHeaders implements BinaryHeaders {
@Override
public AsciiString get(AsciiString name) {
return null;
}
@Override
public AsciiString get(AsciiString name, AsciiString defaultValue) {
return defaultValue;
}
@Override
public AsciiString getAndRemove(AsciiString name) {
return null;
}
@Override
public AsciiString getAndRemove(AsciiString name, AsciiString defaultValue) {
return defaultValue;
}
@Override
public List<AsciiString> getAll(AsciiString name) {
return Collections.emptyList();
}
@Override
public List<AsciiString> getAllAndRemove(AsciiString name) {
return Collections.emptyList();
}
@Override
public List<Entry<AsciiString, AsciiString>> entries() {
return Collections.emptyList();
}
@Override
public boolean contains(AsciiString name) {
return false;
}
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Set<AsciiString> names() {
return Collections.emptySet();
} }
@Override @Override
public BinaryHeaders add(AsciiString name, AsciiString value) { public BinaryHeaders add(AsciiString name, AsciiString value) {
throw new UnsupportedOperationException("read only"); super.add(name, value);
return this;
} }
@Override @Override
public BinaryHeaders add(AsciiString name, Iterable<AsciiString> values) { public BinaryHeaders add(AsciiString name, Iterable<? extends AsciiString> values) {
throw new UnsupportedOperationException("read only"); super.add(name, values);
return this;
} }
@Override @Override
public BinaryHeaders add(AsciiString name, AsciiString... values) { public BinaryHeaders add(AsciiString name, AsciiString... values) {
throw new UnsupportedOperationException("read only"); super.add(name, values);
return this;
}
@Override
public BinaryHeaders addObject(AsciiString name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public BinaryHeaders addObject(AsciiString name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public BinaryHeaders addObject(AsciiString name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public BinaryHeaders addBoolean(AsciiString name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public BinaryHeaders addChar(AsciiString name, char value) {
super.addChar(name, value);
return this;
}
@Override
public BinaryHeaders addByte(AsciiString name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public BinaryHeaders addShort(AsciiString name, short value) {
super.addShort(name, value);
return this;
}
@Override
public BinaryHeaders addInt(AsciiString name, int value) {
super.addInt(name, value);
return this;
}
@Override
public BinaryHeaders addLong(AsciiString name, long value) {
super.addLong(name, value);
return this;
}
@Override
public BinaryHeaders addFloat(AsciiString name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public BinaryHeaders addDouble(AsciiString name, double value) {
super.addDouble(name, value);
return this;
} }
@Override @Override
public BinaryHeaders add(BinaryHeaders headers) { public BinaryHeaders add(BinaryHeaders headers) {
throw new UnsupportedOperationException("read only"); super.add(headers);
return this;
} }
@Override @Override
public BinaryHeaders set(AsciiString name, AsciiString value) { public BinaryHeaders set(AsciiString name, AsciiString value) {
throw new UnsupportedOperationException("read only"); super.set(name, value);
return this;
} }
@Override @Override
public BinaryHeaders set(AsciiString name, Iterable<AsciiString> values) { public BinaryHeaders set(AsciiString name, Iterable<? extends AsciiString> values) {
throw new UnsupportedOperationException("read only"); super.set(name, values);
return this;
} }
@Override @Override
public BinaryHeaders set(AsciiString name, AsciiString... values) { public BinaryHeaders set(AsciiString name, AsciiString... values) {
throw new UnsupportedOperationException("read only"); super.set(name, values);
return this;
}
@Override
public BinaryHeaders setObject(AsciiString name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public BinaryHeaders setObject(AsciiString name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public BinaryHeaders setObject(AsciiString name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public BinaryHeaders setBoolean(AsciiString name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public BinaryHeaders setChar(AsciiString name, char value) {
super.setChar(name, value);
return this;
}
@Override
public BinaryHeaders setByte(AsciiString name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public BinaryHeaders setShort(AsciiString name, short value) {
super.setShort(name, value);
return this;
}
@Override
public BinaryHeaders setInt(AsciiString name, int value) {
super.setInt(name, value);
return this;
}
@Override
public BinaryHeaders setLong(AsciiString name, long value) {
super.setLong(name, value);
return this;
}
@Override
public BinaryHeaders setFloat(AsciiString name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public BinaryHeaders setDouble(AsciiString name, double value) {
super.setDouble(name, value);
return this;
} }
@Override @Override
public BinaryHeaders set(BinaryHeaders headers) { public BinaryHeaders set(BinaryHeaders headers) {
throw new UnsupportedOperationException("read only"); super.set(headers);
return this;
} }
@Override @Override
public BinaryHeaders setAll(BinaryHeaders headers) { public BinaryHeaders setAll(BinaryHeaders headers) {
throw new UnsupportedOperationException("read only"); super.setAll(headers);
} return this;
@Override
public boolean remove(AsciiString name) {
return false;
} }
@Override @Override
public BinaryHeaders clear() { public BinaryHeaders clear() {
super.clear();
return this; return this;
} }
@Override
public boolean contains(AsciiString name, AsciiString value) {
return false;
}
@Override
public Iterator<Entry<AsciiString, AsciiString>> iterator() {
return entries().iterator();
}
@Override
public BinaryHeaders forEachEntry(BinaryHeaderVisitor processor) {
return this;
}
@Override
public int hashCode() {
return BinaryHeaders.Utils.hashCode(this);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof BinaryHeaders)) {
return false;
}
return ((BinaryHeaders) obj).isEmpty();
}
@Override
public String toString() {
return BinaryHeaders.Utils.toStringUtf8(this);
}
} }

View File

@ -0,0 +1,71 @@
/*
* Copyright 2014 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;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
public class EmptyConvertibleHeaders<UnconvertedType, ConvertedType> extends
EmptyHeaders<UnconvertedType> implements ConvertibleHeaders<UnconvertedType, ConvertedType> {
@Override
public ConvertedType getAndConvert(UnconvertedType name) {
return null;
}
@Override
public ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue) {
return defaultValue;
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name) {
return null;
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue) {
return defaultValue;
}
@Override
public List<ConvertedType> getAllAndConvert(UnconvertedType name) {
return Collections.emptyList();
}
@Override
public List<ConvertedType> getAllAndRemoveAndConvert(UnconvertedType name) {
return Collections.emptyList();
}
@Override
public List<Entry<ConvertedType, ConvertedType>> entriesConverted() {
return Collections.emptyList();
}
@Override
public Iterator<Entry<ConvertedType, ConvertedType>> iteratorConverted() {
return entriesConverted().iterator();
}
@Override
public Set<ConvertedType> namesAndConvert(Comparator<ConvertedType> comparator) {
return Collections.emptySet();
}
}

View File

@ -0,0 +1,536 @@
/*
* Copyright 2014 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;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
public class EmptyHeaders<T> implements Headers<T> {
@Override
public T get(T name) {
return null;
}
@Override
public T get(T name, T defaultValue) {
return null;
}
@Override
public T getAndRemove(T name) {
return null;
}
@Override
public T getAndRemove(T name, T defaultValue) {
return null;
}
@Override
public List<T> getAll(T name) {
return Collections.emptyList();
}
@Override
public List<T> getAllAndRemove(T name) {
return Collections.emptyList();
}
@Override
public Boolean getBoolean(T name) {
return null;
}
@Override
public boolean getBoolean(T name, boolean defaultValue) {
return defaultValue;
}
@Override
public Byte getByte(T name) {
return null;
}
@Override
public byte getByte(T name, byte defaultValue) {
return defaultValue;
}
@Override
public Character getChar(T name) {
return null;
}
@Override
public char getChar(T name, char defaultValue) {
return defaultValue;
}
@Override
public Short getShort(T name) {
return null;
}
@Override
public short getInt(T name, short defaultValue) {
return defaultValue;
}
@Override
public Integer getInt(T name) {
return null;
}
@Override
public int getInt(T name, int defaultValue) {
return defaultValue;
}
@Override
public Long getLong(T name) {
return null;
}
@Override
public long getLong(T name, long defaultValue) {
return defaultValue;
}
@Override
public Float getFloat(T name) {
return null;
}
@Override
public float getFloat(T name, float defaultValue) {
return defaultValue;
}
@Override
public Double getDouble(T name) {
return null;
}
@Override
public double getDouble(T name, double defaultValue) {
return defaultValue;
}
@Override
public Long getTimeMillis(T name) {
return null;
}
@Override
public long getTimeMillis(T name, long defaultValue) {
return defaultValue;
}
@Override
public Boolean getBooleanAndRemove(T name) {
return null;
}
@Override
public boolean getBooleanAndRemove(T name, boolean defaultValue) {
return defaultValue;
}
@Override
public Byte getByteAndRemove(T name) {
return null;
}
@Override
public byte getByteAndRemove(T name, byte defaultValue) {
return defaultValue;
}
@Override
public Character getCharAndRemove(T name) {
return null;
}
@Override
public char getCharAndRemove(T name, char defaultValue) {
return defaultValue;
}
@Override
public Short getShortAndRemove(T name) {
return null;
}
@Override
public short getShortAndRemove(T name, short defaultValue) {
return defaultValue;
}
@Override
public Integer getIntAndRemove(T name) {
return null;
}
@Override
public int getIntAndRemove(T name, int defaultValue) {
return defaultValue;
}
@Override
public Long getLongAndRemove(T name) {
return null;
}
@Override
public long getLongAndRemove(T name, long defaultValue) {
return defaultValue;
}
@Override
public Float getFloatAndRemove(T name) {
return null;
}
@Override
public float getFloatAndRemove(T name, float defaultValue) {
return defaultValue;
}
@Override
public Double getDoubleAndRemove(T name) {
return null;
}
@Override
public double getDoubleAndRemove(T name, double defaultValue) {
return defaultValue;
}
@Override
public Long getTimeMillisAndRemove(T name) {
return null;
}
@Override
public long getTimeMillisAndRemove(T name, long defaultValue) {
return defaultValue;
}
@Override
public List<Entry<T, T>> entries() {
return Collections.emptyList();
}
@Override
public boolean contains(T name) {
return false;
}
@Override
public boolean contains(T name, T value) {
return false;
}
@Override
public boolean containsObject(T name, Object value) {
return false;
}
@Override
public boolean containsBoolean(T name, int value) {
return false;
}
@Override
public boolean containsByte(T name, byte value) {
return false;
}
@Override
public boolean containsChar(T name, char value) {
return false;
}
@Override
public boolean containsShort(T name, byte value) {
return false;
}
@Override
public boolean containsInt(T name, int value) {
return false;
}
@Override
public boolean containsLong(T name, long value) {
return false;
}
@Override
public boolean containsFloat(T name, float value) {
return false;
}
@Override
public boolean containsDouble(T name, double value) {
return false;
}
@Override
public boolean contains(T name, T value, Comparator<? super T> comparator) {
return false;
}
@Override
public boolean contains(T name, T value,
Comparator<? super T> keyComparator, Comparator<? super T> valueComparator) {
return false;
}
@Override
public boolean containsObject(T name, Object value, Comparator<? super T> comparator) {
return false;
}
@Override
public boolean containsObject(T name, Object value, Comparator<? super T> keyComparator,
Comparator<? super T> valueComparator) {
return false;
}
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Set<T> names() {
return Collections.emptySet();
}
@Override
public List<T> namesList() {
return Collections.emptyList();
}
@Override
public Headers<T> add(T name, T value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> add(T name, Iterable<? extends T> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> add(T name, T... values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addObject(T name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addObject(T name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addObject(T name, Object... values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addBoolean(T name, boolean value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addByte(T name, byte value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addChar(T name, char value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addShort(T name, short value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addInt(T name, int value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addLong(T name, long value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addFloat(T name, float value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addDouble(T name, double value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> add(Headers<T> headers) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> set(T name, T value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> set(T name, Iterable<? extends T> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> set(T name, T... values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setObject(T name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setObject(T name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setObject(T name, Object... values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setBoolean(T name, boolean value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setByte(T name, byte value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setChar(T name, char value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setShort(T name, short value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setInt(T name, int value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setLong(T name, long value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setFloat(T name, float value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setDouble(T name, double value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> set(Headers<T> headers) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setAll(Headers<T> headers) {
throw new UnsupportedOperationException("read only");
}
@Override
public boolean remove(T name) {
return false;
}
@Override
public Headers<T> clear() {
return this;
}
@Override
public Iterator<Entry<T, T>> iterator() {
return entries().iterator();
}
@Override
public Entry<T, T> forEachEntry(io.netty.handler.codec.Headers.EntryVisitor<T> visitor) throws Exception {
return null;
}
@Override
public T forEachName(io.netty.handler.codec.Headers.NameVisitor<T> visitor) throws Exception {
return null;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Headers)) {
return false;
}
Headers<?> rhs = (Headers<?>) o;
return isEmpty() && rhs.isEmpty();
}
@Override
public int hashCode() {
return 1;
}
@Override
public String toString() {
return new StringBuilder(getClass().getSimpleName()).append('[').append(']').toString();
}
}

View File

@ -16,238 +16,209 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import java.util.Collections; public class EmptyTextHeaders extends EmptyConvertibleHeaders<CharSequence, String> implements TextHeaders {
import java.util.Iterator; protected EmptyTextHeaders() {
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
public class EmptyTextHeaders implements TextHeaders {
protected EmptyTextHeaders() { }
@Override
public String get(CharSequence name) {
return null;
} }
@Override @Override
public String get(CharSequence name, String defaultValue) { public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
return defaultValue;
}
@Override
public Integer getInt(CharSequence name) {
return null;
}
@Override
public int getInt(CharSequence name, int defaultValue) {
return defaultValue;
}
@Override
public Long getLong(CharSequence name) {
return null;
}
@Override
public long getLong(CharSequence name, long defaultValue) {
return defaultValue;
}
@Override
public Long getTimeMillis(CharSequence name) {
return null;
}
@Override
public long getTimeMillis(CharSequence name, long defaultValue) {
return defaultValue;
}
@Override
public String getAndRemove(CharSequence name) {
return null;
}
@Override
public String getAndRemove(CharSequence name, String defaultValue) {
return defaultValue;
}
@Override
public Integer getIntAndRemove(CharSequence name) {
return null;
}
@Override
public int getIntAndRemove(CharSequence name, int defaultValue) {
return defaultValue;
}
@Override
public Long getLongAndRemove(CharSequence name) {
return null;
}
@Override
public long getLongAndRemove(CharSequence name, long defaultValue) {
return defaultValue;
}
@Override
public Long getTimeMillisAndRemove(CharSequence name) {
return null;
}
@Override
public long getTimeMillisAndRemove(CharSequence name, long defaultValue) {
return defaultValue;
}
@Override
public CharSequence getUnconverted(CharSequence name) {
return null;
}
@Override
public CharSequence getUnconvertedAndRemove(CharSequence name) {
return null;
}
@Override
public List<String> getAll(CharSequence name) {
return Collections.emptyList();
}
@Override
public List<CharSequence> getAllUnconverted(CharSequence name) {
return Collections.emptyList();
}
@Override
public List<String> getAllAndRemove(CharSequence name) {
return Collections.emptyList();
}
@Override
public List<CharSequence> getAllUnconvertedAndRemove(CharSequence name) {
return Collections.emptyList();
}
@Override
public List<Entry<String, String>> entries() {
return Collections.emptyList();
}
@Override
public List<Entry<CharSequence, CharSequence>> unconvertedEntries() {
return Collections.emptyList();
}
@Override
public boolean contains(CharSequence name) {
return false; return false;
} }
@Override @Override
public int size() { public boolean containsObject(CharSequence name, Object value, boolean ignoreCase) {
return 0; return false;
} }
@Override @Override
public boolean isEmpty() { public TextHeaders add(CharSequence name, CharSequence value) {
return true; super.add(name, value);
return this;
} }
@Override @Override
public Set<String> names() { public TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
return Collections.emptySet(); super.add(name, values);
return this;
} }
@Override @Override
public Set<CharSequence> unconvertedNames() { public TextHeaders add(CharSequence name, CharSequence... values) {
return Collections.emptySet(); super.add(name, values);
return this;
} }
@Override @Override
public TextHeaders add(CharSequence name, Object value) { public TextHeaders addObject(CharSequence name, Object value) {
throw new UnsupportedOperationException("read only"); super.addObject(name, value);
return this;
} }
@Override @Override
public TextHeaders add(CharSequence name, Iterable<?> values) { public TextHeaders addObject(CharSequence name, Iterable<?> values) {
throw new UnsupportedOperationException("read only"); super.addObject(name, values);
return this;
} }
@Override @Override
public TextHeaders add(CharSequence name, Object... values) { public TextHeaders addObject(CharSequence name, Object... values) {
throw new UnsupportedOperationException("read only"); super.addObject(name, values);
return this;
}
@Override
public TextHeaders addBoolean(CharSequence name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public TextHeaders addChar(CharSequence name, char value) {
super.addChar(name, value);
return this;
}
@Override
public TextHeaders addByte(CharSequence name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public TextHeaders addShort(CharSequence name, short value) {
super.addShort(name, value);
return this;
}
@Override
public TextHeaders addInt(CharSequence name, int value) {
super.addInt(name, value);
return this;
}
@Override
public TextHeaders addLong(CharSequence name, long value) {
super.addLong(name, value);
return this;
}
@Override
public TextHeaders addFloat(CharSequence name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public TextHeaders addDouble(CharSequence name, double value) {
super.addDouble(name, value);
return this;
} }
@Override @Override
public TextHeaders add(TextHeaders headers) { public TextHeaders add(TextHeaders headers) {
throw new UnsupportedOperationException("read only"); super.add(headers);
return this;
} }
@Override @Override
public TextHeaders set(CharSequence name, Object value) { public TextHeaders set(CharSequence name, CharSequence value) {
throw new UnsupportedOperationException("read only"); super.set(name, value);
return this;
} }
@Override @Override
public TextHeaders set(CharSequence name, Iterable<?> values) { public TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
throw new UnsupportedOperationException("read only"); super.set(name, values);
return this;
} }
@Override @Override
public TextHeaders set(CharSequence name, Object... values) { public TextHeaders set(CharSequence name, CharSequence... values) {
throw new UnsupportedOperationException("read only"); super.set(name, values);
return this;
}
@Override
public TextHeaders setObject(CharSequence name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public TextHeaders setObject(CharSequence name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public TextHeaders setObject(CharSequence name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public TextHeaders setBoolean(CharSequence name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public TextHeaders setChar(CharSequence name, char value) {
super.setChar(name, value);
return this;
}
@Override
public TextHeaders setByte(CharSequence name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public TextHeaders setShort(CharSequence name, short value) {
super.setShort(name, value);
return this;
}
@Override
public TextHeaders setInt(CharSequence name, int value) {
super.setInt(name, value);
return this;
}
@Override
public TextHeaders setLong(CharSequence name, long value) {
super.setLong(name, value);
return this;
}
@Override
public TextHeaders setFloat(CharSequence name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public TextHeaders setDouble(CharSequence name, double value) {
super.setDouble(name, value);
return this;
} }
@Override @Override
public TextHeaders set(TextHeaders headers) { public TextHeaders set(TextHeaders headers) {
throw new UnsupportedOperationException("read only"); super.set(headers);
return this;
} }
@Override @Override
public TextHeaders setAll(TextHeaders rhs) { public TextHeaders setAll(TextHeaders headers) {
throw new UnsupportedOperationException("read only"); super.setAll(headers);
} return this;
@Override
public boolean remove(CharSequence name) {
return false;
} }
@Override @Override
public TextHeaders clear() { public TextHeaders clear() {
return this; super.clear();
}
@Override
public boolean contains(CharSequence name, Object value) {
return false;
}
@Override
public boolean contains(CharSequence name, Object value, boolean ignoreCase) {
return false;
}
@Override
public Iterator<Entry<String, String>> iterator() {
return entries().iterator();
}
@Override
public Iterator<Entry<CharSequence, CharSequence>> unconvertedIterator() {
return unconvertedEntries().iterator();
}
@Override
public TextHeaders forEachEntry(TextHeaderProcessor processor) {
return this; return this;
} }
} }

View File

@ -1,839 +0,0 @@
/*
* Copyright 2014 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;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;
/**
* Basic map of header names to values. This is meant to be a central storage mechanism by all
* headers implementations. All keys and values are stored as {@link CharSequence}.
*/
public class HeaderMap implements Iterable<Entry<CharSequence, CharSequence>> {
private static final int BUCKET_SIZE = 17;
private static final int HASH_CODE_PRIME = 31;
public static final NameConverter IDENTITY_NAME_CONVERTER = new NameConverter() {
@Override
public CharSequence convertName(CharSequence name) {
return name;
}
};
public interface EntryVisitor {
boolean visit(Entry<CharSequence, CharSequence> entry);
}
public interface NameVisitor {
boolean visit(CharSequence name);
}
public interface NameConverter {
CharSequence convertName(CharSequence name);
}
public interface ValueMarshaller {
CharSequence marshal(Object value);
}
public interface ValueUnmarshaller<T> {
T unmarshal(CharSequence value);
}
private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE];
private final HeaderEntry head = new HeaderEntry();
private final NameConverter nameConverter;
private final boolean ignoreCase;
int size;
public HeaderMap() {
this(true);
}
public HeaderMap(boolean ignoreCase) {
this(ignoreCase, IDENTITY_NAME_CONVERTER);
}
public HeaderMap(boolean ignoreCase, NameConverter nameConverter) {
this.nameConverter = checkNotNull(nameConverter, "nameConverter");
head.before = head.after = head;
this.ignoreCase = ignoreCase;
}
public boolean isIgnoreCase() {
return ignoreCase;
}
public HeaderMap add(CharSequence name, CharSequence value) {
name = convertName(name);
checkNotNull(value, "value");
int h = hashCode(name);
int i = index(h);
add0(h, i, name, value);
return this;
}
public HeaderMap add(CharSequence name, Iterable<? extends CharSequence> values) {
name = convertName(name);
checkNotNull(values, "values");
int h = hashCode(name);
int i = index(h);
for (CharSequence v: values) {
if (v == null) {
break;
}
add0(h, i, name, v);
}
return this;
}
public HeaderMap add(CharSequence name, CharSequence... values) {
name = convertName(name);
checkNotNull(values, "values");
int h = hashCode(name);
int i = index(h);
for (CharSequence v: values) {
if (v == null) {
break;
}
add0(h, i, name, v);
}
return this;
}
public HeaderMap addConvertedValues(CharSequence name, ValueMarshaller converter, Iterable<?> values) {
name = convertName(name);
checkNotNull(values, "values");
checkNotNull(converter, "converter");
int h = hashCode(name);
int i = index(h);
for (Object v : values) {
if (v == null) {
break;
}
CharSequence convertedVal = converter.marshal(v);
add0(h, i, name, convertedVal);
}
return this;
}
public HeaderMap addConvertedValues(CharSequence name, ValueMarshaller converter, Object... values) {
name = convertName(name);
checkNotNull(values, "values");
checkNotNull(converter, "converter");
int h = hashCode(name);
int i = index(h);
for (Object v : values) {
if (v == null) {
break;
}
CharSequence convertedVal = converter.marshal(v);
add0(h, i, name, convertedVal);
}
return this;
}
private void add0(int h, int i, CharSequence name, CharSequence value) {
// Update the hash table.
HeaderEntry e = entries[i];
HeaderEntry newEntry;
entries[i] = newEntry = new HeaderEntry(h, name, value);
newEntry.next = e;
// Update the linked list.
newEntry.addBefore(head);
}
public HeaderMap add(HeaderMap headers) {
checkNotNull(headers, "headers");
add0(headers);
return this;
}
private void add0(HeaderMap headers) {
if (headers.isEmpty()) {
return;
}
HeaderMap m = (HeaderMap) headers;
HeaderEntry e = m.head.after;
while (e != m.head) {
add(e.name, e.value);
e = e.after;
}
}
public boolean remove(CharSequence name) {
checkNotNull(name, "name");
int h = hashCode(name);
int i = index(h);
return remove0(h, i, name);
}
private boolean remove0(int h, int i, CharSequence name) {
HeaderEntry e = entries[i];
if (e == null) {
return false;
}
boolean removed = false;
for (;;) {
if (e.hash == h && nameEquals(e.name, name)) {
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return true;
}
removed = true;
} else {
break;
}
}
for (;;) {
HeaderEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && nameEquals(next.name, name)) {
e.next = next.next;
next.remove();
removed = true;
} else {
e = next;
}
}
return removed;
}
public HeaderMap set(CharSequence name, CharSequence value) {
name = convertName(name);
checkNotNull(value, "value");
int h = hashCode(name);
int i = index(h);
remove0(h, i, name);
add0(h, i, name, value);
return this;
}
public HeaderMap set(CharSequence name, Iterable<? extends CharSequence> values) {
name = convertName(name);
checkNotNull(values, "values");
int h = hashCode(name);
int i = index(h);
remove0(h, i, name);
for (CharSequence v: values) {
if (v == null) {
break;
}
add0(h, i, name, v);
}
return this;
}
public HeaderMap set(CharSequence name, CharSequence... values) {
name = convertName(name);
checkNotNull(values, "values");
int h = hashCode(name);
int i = index(h);
remove0(h, i, name);
for (CharSequence v: values) {
if (v == null) {
break;
}
add0(h, i, name, v);
}
return this;
}
public HeaderMap set(CharSequence name, ValueMarshaller converter, Iterable<?> values) {
name = convertName(name);
checkNotNull(converter, "converter");
checkNotNull(values, "values");
int h = hashCode(name);
int i = index(h);
remove0(h, i, name);
for (Object v: values) {
if (v == null) {
break;
}
CharSequence convertedVal = converter.marshal(v);
add0(h, i, name, convertedVal);
}
return this;
}
public HeaderMap set(CharSequence name, ValueMarshaller converter, Object... values) {
name = convertName(name);
checkNotNull(converter, "converter");
checkNotNull(values, "values");
int h = hashCode(name);
int i = index(h);
remove0(h, i, name);
for (Object v: values) {
if (v == null) {
break;
}
CharSequence convertedVal = converter.marshal(v);
add0(h, i, name, convertedVal);
}
return this;
}
public HeaderMap set(HeaderMap headers) {
checkNotNull(headers, "headers");
clear();
add0(headers);
return this;
}
public HeaderMap setAll(HeaderMap headers) {
checkNotNull(headers, "headers");
HeaderEntry e = headers.head.after;
while (e != headers.head) {
set(e.name, e.value);
e = e.after;
}
return this;
}
public HeaderMap clear() {
Arrays.fill(entries, null);
head.before = head.after = head;
size = 0;
return this;
}
public CharSequence get(CharSequence name) {
checkNotNull(name, "name");
int h = hashCode(name);
int i = index(h);
HeaderEntry e = entries[i];
CharSequence value = null;
// loop until the first header was found
while (e != null) {
if (e.hash == h && nameEquals(e.name, name)) {
value = e.value;
}
e = e.next;
}
return value;
}
public CharSequence get(CharSequence name, CharSequence defaultValue) {
CharSequence v = get(name);
if (v == null) {
return defaultValue;
}
return v;
}
public CharSequence getAndRemove(CharSequence name) {
checkNotNull(name, "name");
int h = hashCode(name);
int i = index(h);
HeaderEntry e = entries[i];
if (e == null) {
return null;
}
CharSequence value = null;
for (;;) {
if (e.hash == h && nameEquals(e.name, name)) {
value = e.value;
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return value;
}
} else {
break;
}
}
for (;;) {
HeaderEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && nameEquals(next.name, name)) {
value = next.value;
e.next = next.next;
next.remove();
} else {
e = next;
}
}
return value;
}
public CharSequence getAndRemove(CharSequence name, CharSequence defaultValue) {
CharSequence v = getAndRemove(name);
if (v == null) {
return defaultValue;
}
return v;
}
public List<CharSequence> getAll(CharSequence name) {
checkNotNull(name, "name");
List<CharSequence> values = new ArrayList<CharSequence>(4);
int h = hashCode(name);
int i = index(h);
HeaderEntry e = entries[i];
while (e != null) {
if (e.hash == h && nameEquals(e.name, name)) {
values.add(e.getValue());
}
e = e.next;
}
Collections.reverse(values);
return values;
}
public List<CharSequence> getAllAndRemove(CharSequence name) {
checkNotNull(name, "name");
int h = hashCode(name);
int i = index(h);
HeaderEntry e = entries[i];
if (e == null) {
return null;
}
List<CharSequence> values = new ArrayList<CharSequence>(4);
for (;;) {
if (e.hash == h && nameEquals(e.name, name)) {
values.add(e.getValue());
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
Collections.reverse(values);
return values;
}
} else {
break;
}
}
for (;;) {
HeaderEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && nameEquals(next.name, name)) {
values.add(next.getValue());
e.next = next.next;
next.remove();
} else {
e = next;
}
}
Collections.reverse(values);
return values;
}
public <T> List<T> getAll(CharSequence name, ValueUnmarshaller<T> unmarshaller) {
checkNotNull(name, "name");
checkNotNull(unmarshaller, "unmarshaller");
List<T> values = new ArrayList<T>(4);
int h = hashCode(name);
int i = index(h);
HeaderEntry e = entries[i];
while (e != null) {
if (e.hash == h && nameEquals(e.name, name)) {
values.add(unmarshaller.unmarshal(e.value));
}
e = e.next;
}
Collections.reverse(values);
return values;
}
public <T> List<T> getAllAndRemove(CharSequence name, ValueUnmarshaller<T> unmarshaller) {
checkNotNull(name, "name");
checkNotNull(unmarshaller, "unmarshaller");
int h = hashCode(name);
int i = index(h);
HeaderEntry e = entries[i];
if (e == null) {
return null;
}
List<T> values = new ArrayList<T>(4);
for (;;) {
if (e.hash == h && nameEquals(e.name, name)) {
values.add(unmarshaller.unmarshal(e.value));
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
Collections.reverse(values);
return values;
}
} else {
break;
}
}
for (;;) {
HeaderEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && nameEquals(next.name, name)) {
values.add(unmarshaller.unmarshal(next.getValue()));
e.next = next.next;
next.remove();
} else {
e = next;
}
}
Collections.reverse(values);
return values;
}
public List<Map.Entry<CharSequence, CharSequence>> entries() {
int cnt = 0;
int size = size();
@SuppressWarnings("unchecked")
Map.Entry<CharSequence, CharSequence>[] all = new Map.Entry[size];
HeaderEntry e = head.after;
while (e != head) {
all[cnt ++] = e;
e = e.after;
}
assert size == cnt;
return Arrays.asList(all);
}
@Override
public Iterator<Entry<CharSequence, CharSequence>> iterator() {
return new HeaderIterator();
}
public boolean contains(CharSequence name) {
return get(name) != null;
}
public int size() {
return size;
}
public boolean isEmpty() {
return head == head.after;
}
public boolean contains(CharSequence name, CharSequence value) {
return contains(name, value, false);
}
public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
checkNotNull(name, "name");
checkNotNull(value, "value");
int h = hashCode(name);
int i = index(h);
HeaderEntry e = entries[i];
while (e != null) {
if (e.hash == h && nameEquals(e.name, name)) {
if (valueEquals(e.value, value, ignoreCase)) {
return true;
}
}
e = e.next;
}
return false;
}
public Set<CharSequence> names() {
return names(ignoreCase);
}
/**
* Get the set of names for all text headers
* @param caseInsensitive {@code true} if names should be added in a case insensitive
* @return The set of names for all text headers
*/
public Set<CharSequence> names(boolean caseInsensitive) {
final Set<CharSequence> names =
caseInsensitive ? new TreeSet<CharSequence>(
AsciiString.CHARSEQUENCE_CASE_INSENSITIVE_ORDER)
: new LinkedHashSet<CharSequence>(size());
forEachName(new NameVisitor() {
@Override
public boolean visit(CharSequence name) {
names.add(name);
return true;
}
});
return names;
}
public HeaderMap forEachEntry(EntryVisitor visitor) {
HeaderEntry e = head.after;
while (e != head) {
if (!visitor.visit(e)) {
break;
}
e = e.after;
}
return this;
}
public void forEachName(NameVisitor visitor) {
HeaderEntry e = head.after;
while (e != head) {
if (!visitor.visit(e.getKey())) {
return;
}
e = e.after;
}
}
@Override
public int hashCode() {
int result = 1;
for (CharSequence name : names()) {
result = HASH_CODE_PRIME * result + name.hashCode();
Set<CharSequence> values = new TreeSet<CharSequence>(getAll(name));
for (CharSequence value : values) {
result = HASH_CODE_PRIME * result + value.hashCode();
}
}
return result;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof HeaderMap)) {
return false;
}
// First, check that the set of names match.
HeaderMap h2 = (HeaderMap) o;
Set<CharSequence> names = names();
if (!names.equals(h2.names())) {
return false;
}
// Compare the values for each name.
for (CharSequence name : names) {
List<CharSequence> values = getAll(name);
List<CharSequence> otherValues = h2.getAll(name);
if (values.size() != otherValues.size()) {
return false;
}
// Convert the values to a set and remove values from the other object to see if
// they match.
Set<CharSequence> valueSet = new HashSet<CharSequence>(values);
valueSet.removeAll(otherValues);
if (!valueSet.isEmpty()) {
return false;
}
}
return true;
}
@Override
public String toString() {
StringBuilder builder =
new StringBuilder('[');
Set<CharSequence> names = names(true);
for (CharSequence name : names) {
Set<CharSequence> valueSet = new TreeSet<CharSequence>(getAll(name));
for (CharSequence value : valueSet) {
builder.append(name).append(": ").append(value).append(", ");
}
}
// Now remove the last ", " if there is one.
if (builder.length() >= 3) {
builder.setLength(builder.length() - 2);
}
return builder.append("]").toString();
}
private boolean nameEquals(CharSequence a, CharSequence b) {
return equals(a, b, ignoreCase);
}
private static boolean valueEquals(CharSequence a, CharSequence b, boolean ignoreCase) {
return equals(a, b, ignoreCase);
}
private static boolean equals(CharSequence a, CharSequence b, boolean ignoreCase) {
if (ignoreCase) {
return AsciiString.equalsIgnoreCase(a, b);
} else {
return AsciiString.equals(a, b);
}
}
private static int index(int hash) {
return Math.abs(hash % BUCKET_SIZE);
}
private CharSequence convertName(CharSequence name) {
return nameConverter.convertName(checkNotNull(name, "name"));
}
private static int hashCode(CharSequence name) {
return AsciiString.caseInsensitiveHashCode(name);
}
private final class HeaderEntry implements Map.Entry<CharSequence, CharSequence> {
final int hash;
final CharSequence name;
CharSequence value;
HeaderEntry next;
HeaderEntry before, after;
HeaderEntry(int hash, CharSequence name, CharSequence value) {
this.hash = hash;
this.name = name;
this.value = value;
}
HeaderEntry() {
hash = -1;
name = null;
value = null;
}
void remove() {
before.after = after;
after.before = before;
--size;
}
void addBefore(HeaderEntry e) {
after = e;
before = e.before;
before.after = this;
after.before = this;
++size;
}
@Override
public CharSequence getKey() {
return name;
}
@Override
public CharSequence getValue() {
return value;
}
@Override
public CharSequence setValue(CharSequence value) {
checkNotNull(value, "value");
checkNotNull(value, "value");
CharSequence oldValue = this.value;
this.value = value;
return oldValue;
}
@Override
public String toString() {
return new StringBuilder(name).append('=').append(value).toString();
}
}
protected final class HeaderIterator implements Iterator<Entry<CharSequence, CharSequence>> {
private HeaderEntry current = head;
@Override
public boolean hasNext() {
return current.after != head;
}
@Override
public Entry<CharSequence, CharSequence> next() {
current = current.after;
if (current == head) {
throw new NoSuchElementException();
}
return current;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +0,0 @@
/*
* Copyright 2014 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;
public interface TextHeaderProcessor {
boolean process(CharSequence name, CharSequence value) throws Exception;
}

View File

@ -16,451 +16,142 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/** /**
* A typical string multimap used by text protocols such as HTTP for the representation of arbitrary key-value data. * A typical string multimap used by text protocols such as HTTP for the representation of arbitrary key-value data. One
* One thing to note is that it uses {@link CharSequence} as its primary key and value type rather than {@link String}. * thing to note is that it uses {@link CharSequence} as its primary key and value type rather than {@link String}. When
* When you invoke the operations that produce {@link String}s such as {@link #get(CharSequence)}, * you invoke the operations that produce {@link String}s such as {@link #get(CharSequence)}, a {@link CharSequence} is
* a {@link CharSequence} is implicitly converted to a {@link String}. This is particularly useful for speed * implicitly converted to a {@link String}. This is particularly useful for speed optimization because this multimap
* optimization because this multimap can hold a special {@link CharSequence} implementation that a codec can * can hold a special {@link CharSequence} implementation that a codec can treat specially, such as {@link CharSequence}
* treat specially, such as {@link AsciiString}. * .
*/ */
public interface TextHeaders extends Iterable<Map.Entry<String, String>> { public interface TextHeaders extends ConvertibleHeaders<CharSequence, String> {
/** /**
* Returns the value of a header with the specified name. If there are * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}.
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @return the first header value if the header is found.
* {@code null} if there's no such header.
*/ */
String get(CharSequence name); public interface EntryVisitor extends Headers.EntryVisitor<CharSequence> {
}
/** /**
* Returns the value of a header with the specified name. If there are * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}.
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @param defaultValue the default value
* @return the first header value if the header is found.
* {@code defaultValue} if there's no such header.
*/ */
String get(CharSequence name, String defaultValue); public interface NameVisitor extends Headers.NameVisitor<CharSequence> {
}
/** /**
* Returns the integer value of a header with the specified name. If there are * Returns {@code true} if a header with the name and value exists.
* more than one values for the specified name, the first value is returned. * @param name the header name
* * @param value the header value
* @param name the name of the header to search * @return {@code true} if it contains it {@code false} otherwise
* @return the first header value if the header is found and its value is an integer.
* {@code null} if there's no such header or its value is not an integer.
*/ */
Integer getInt(CharSequence name); boolean contains(CharSequence name, CharSequence value, boolean ignoreCase);
/** /**
* Returns the integer value of a header with the specified name. If there are * Returns {@code true} if a header with the name and value exists.
* more than one values for the specified name, the first value is returned. * @param name the header name
* * @param value the header value
* @param name the name of the header to search * @return {@code true} if it contains it {@code false} otherwise
* @param defaultValue the default value
* @return the first header value if the header is found and its value is an integer.
* {@code defaultValue} if there's no such header or its value is not an integer.
*/ */
int getInt(CharSequence name, int defaultValue); boolean containsObject(CharSequence name, Object value, boolean ignoreCase);
@Override
TextHeaders add(CharSequence name, CharSequence value);
@Override
TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
@Override
TextHeaders add(CharSequence name, CharSequence... values);
@Override
TextHeaders addObject(CharSequence name, Object value);
@Override
TextHeaders addObject(CharSequence name, Iterable<?> values);
@Override
TextHeaders addObject(CharSequence name, Object... values);
@Override
TextHeaders addBoolean(CharSequence name, boolean value);
@Override
TextHeaders addByte(CharSequence name, byte value);
@Override
TextHeaders addChar(CharSequence name, char value);
@Override
TextHeaders addShort(CharSequence name, short value);
@Override
TextHeaders addInt(CharSequence name, int value);
@Override
TextHeaders addLong(CharSequence name, long value);
@Override
TextHeaders addFloat(CharSequence name, float value);
@Override
TextHeaders addDouble(CharSequence name, double value);
/** /**
* Returns the long integer value of a header with the specified name. If there are * See {@link Headers#add(Headers)}
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @return the first header value if the header is found and its value is a long integer.
* {@code null} if there's no such header or its value is not a long integer.
*/
Long getLong(CharSequence name);
/**
* Returns the long integer value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @param defaultValue the default value
* @return the first header value if the header is found and its value is a long integer.
* {@code defaultValue} if there's no such header or its value is not a long integer.
*/
long getLong(CharSequence name, long defaultValue);
/**
* Returns the date value of a header with the specified name as milliseconds. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return the first header value in milliseconds if the header is found and its value is a date.
* {@code null} if there's no such header or its value is not a date.
*/
Long getTimeMillis(CharSequence name);
/**
* Returns the date value of a header with the specified name as milliseconds. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @param defaultValue default value
* @return the first header value in milliseconds if the header is found and its value is a date.
* {@code defaultValue} if there's no such header or its value is not a date.
*/
long getTimeMillis(CharSequence name, long defaultValue);
/**
* Returns and removes the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @return the first header value or {@code null} if there is no such header
*/
String getAndRemove(CharSequence name);
/**
* Returns and removes the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @param defaultValue the default value
* @return the first header value or {@code defaultValue} if there is no such header
*/
String getAndRemove(CharSequence name, String defaultValue);
/**
* Returns and removes the integer value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @return the first header value if the header is found and its value is an integer.
* {@code null} if there's no such header or its value is not an integer.
*/
Integer getIntAndRemove(CharSequence name);
/**
* Returns and removes the integer value of a header with the specified name. If there are more than one values for
* the specified name, the first value is returned.
*
* @param name the name of the header to search
* @param defaultValue the default value
* @return the first header value if the header is found and its value is an integer.
* {@code defaultValue} if there is no such header or its value of header is not an integer.
*/
int getIntAndRemove(CharSequence name, int defaultValue);
/**
* Returns and removes the long value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name the name of the header to search
* @return the first header value if the header is found and its value is an integer.
* {@code null} if there's no such header or its value is not an integer.
*/
Long getLongAndRemove(CharSequence name);
/**
* Returns and removes the long value of a header with the specified name. If there are more than one values for
* the specified name, the first value is returned.
*
* @param name the name of the header to search
* @param defaultValue the default value
* @return the first header value if the header is found and its value is an integer.
* {@code defaultValue} if there's no such header or its value is not an integer.
*/
long getLongAndRemove(CharSequence name, long defaultValue);
/**
* Returns and removes the date value of a header with the specified name as milliseconds. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return the first header value in milliseconds if the header is found and its value is a date.
* {@code null} if there's no such header or its value is not a date.
*/
Long getTimeMillisAndRemove(CharSequence name);
/**
* Returns and removes the date value of a header with the specified name as milliseconds. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @param defaultValue default value
* @return the first header value in milliseconds if the header is found and its value is a date.
* {@code defaultValue} if there's no such header or its value is not a date.
*/
long getTimeMillisAndRemove(CharSequence name, long defaultValue);
/**
* Returns the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return The first header value or {@code null} if there is no such header
*/
CharSequence getUnconverted(CharSequence name);
/**
* Returns and Removes the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return The first header value or {@code null} if there is no such header
*/
CharSequence getUnconvertedAndRemove(CharSequence name);
/**
* Returns the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values are found
*/
List<String> getAll(CharSequence name);
/**
* Returns the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values are found
*/
List<CharSequence> getAllUnconverted(CharSequence name);
/**
* Returns and Removes the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values are found
*/
List<String> getAllAndRemove(CharSequence name);
/**
* Returns and Removes the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values are found
*/
List<CharSequence> getAllUnconvertedAndRemove(CharSequence name);
/**
* Returns a new {@link List} that contains all headers in this object. Note that modifying the
* returned {@link List} will not affect the state of this object. If you intend to enumerate over the header
* entries only, use {@link #iterator()} instead, which has much less overhead.
*/
List<Entry<String, String>> entries();
/**
* Returns a new {@link List} that contains all headers in this object. Note that modifying the
* returned {@link List} will not affect the state of this object. If you intend to enumerate over the header
* entries only, use {@link #iterator()} instead, which has much less overhead.
*/
List<Entry<CharSequence, CharSequence>> unconvertedEntries();
/**
* Returns {@code true} if and only if this collection contains the header with the specified name.
*
* @param name The name of the header to search for
* @return {@code true} if at least one header is found
*/
boolean contains(CharSequence name);
/**
* Returns the number of header entries in this collection.
*/
int size();
/**
* Returns {@code true} if and only if this collection contains no header entries.
*/
boolean isEmpty();
/**
* Returns a new {@link Set} that contains the names of all headers in this object. Note that modifying the
* returned {@link Set} will not affect the state of this object. If you intend to enumerate over the header
* entries only, use {@link #iterator()} instead, which has much less overhead.
*/
Set<String> names();
/**
* Returns a new {@link Set} that contains the names of all headers in this object. Note that modifying the
* returned {@link Set} will not affect the state of this object. If you intend to enumerate over the header
* entries only, use {@link #iterator()} instead, which has much less overhead.
*/
Set<CharSequence> unconvertedNames();
/**
* Adds a new header with the specified name and value.
*
* If the specified value is not a {@link String}, it is converted
* into a {@link String} by {@link Object#toString()}, except in the cases
* of {@link java.util.Date} and {@link java.util.Calendar}, which are formatted to the date
* format defined in <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
*
* @param name the name of the header being added
* @param value the value of the header being added
*
* @return {@code this}
*/
TextHeaders add(CharSequence name, Object value);
/**
* Adds a new header with the specified name and values.
*
* This getMethod can be represented approximately as the following code:
* <pre>
* for (Object v: values) {
* if (v == null) {
* break;
* }
* headers.add(name, v);
* }
* </pre>
*
* @param name the name of the headepublic abstract rs being set
* @param values the values of the headers being set
* @return {@code this}
*/
TextHeaders add(CharSequence name, Iterable<?> values);
/**
* Adds a new header with the specified name and values.
*
* This getMethod can be represented approximately as the following code:
* <pre>
* for (Object v: values) {
* if (v == null) {
* break;
* }
* headers.add(name, v);
* }
* </pre>
*
* @param name the name of the headepublic abstract rs being set
* @param values the values of the headers being set
* @return {@code this}
*/
TextHeaders add(CharSequence name, Object... values);
/**
* Adds all header entries of the specified {@code headers}.
*
* @return {@code this}
*/ */
TextHeaders add(TextHeaders headers); TextHeaders add(TextHeaders headers);
/** @Override
* Sets a header with the specified name and value. TextHeaders set(CharSequence name, CharSequence value);
*
* If there is an existing header with the same name, it is removed. @Override
* If the specified value is not a {@link String}, it is converted into a TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
* {@link String} by {@link Object#toString()}, except for {@link java.util.Date}
* and {@link java.util.Calendar}, which are formatted to the date format defined in @Override
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>. TextHeaders set(CharSequence name, CharSequence... values);
*
* @param name The name of the header being set @Override
* @param value The value of the header being set TextHeaders setObject(CharSequence name, Object value);
* @return {@code this}
*/ @Override
TextHeaders set(CharSequence name, Object value); TextHeaders setObject(CharSequence name, Iterable<?> values);
@Override
TextHeaders setObject(CharSequence name, Object... values);
@Override
TextHeaders setBoolean(CharSequence name, boolean value);
@Override
TextHeaders setByte(CharSequence name, byte value);
@Override
TextHeaders setChar(CharSequence name, char value);
@Override
TextHeaders setShort(CharSequence name, short value);
@Override
TextHeaders setInt(CharSequence name, int value);
@Override
TextHeaders setLong(CharSequence name, long value);
@Override
TextHeaders setFloat(CharSequence name, float value);
@Override
TextHeaders setDouble(CharSequence name, double value);
/** /**
* Sets a header with the specified name and values. * See {@link Headers#set(Headers)}
*
* If there is an existing header with the same name, it is removed.
* This getMethod can be represented approximately as the following code:
* <pre>
* headers.remove(name);
* for (Object v: values) {
* if (v == null) {
* break;
* }
* headers.add(name, v);
* }
* </pre>
*
* @param name the name of the headers being set
* @param values the values of the headers being set
* @return {@code this}
*/
TextHeaders set(CharSequence name, Iterable<?> values);
/**
* Sets a header with the specified name and values.
*
* If there is an existing header with the same name, it is removed.
* This getMethod can be represented approximately as the following code:
* <pre>
* headers.remove(name);
* for (Object v: values) {
* if (v == null) {
* break;
* }
* headers.add(name, v);
* }
* </pre>
*
* @param name the name of the headers being set
* @param values the values of the headers being set
* @return {@code this}
*/
TextHeaders set(CharSequence name, Object... values);
/**
* Cleans the current header entries and copies all header entries of the specified {@code headers}.
*
* @return {@code this}
*/ */
TextHeaders set(TextHeaders headers); TextHeaders set(TextHeaders headers);
/** /**
* Retains all current headers but calls {@link #set(CharSequence, Object)} for each entry in {@code headers} * See {@link Headers#setAll(Headers)}
* @param headers The headers used to {@link #set(CharSequence, Object)} values in this instance
* @return {@code this}
*/ */
TextHeaders setAll(TextHeaders headers); TextHeaders setAll(TextHeaders headers);
/**
* Removes the header with the specified name.
*
* @param name The name of the header to remove
* @return {@code true} if and only if at least one entry has been removed
*/
boolean remove(CharSequence name);
/**
* Removes all headers.
*
* @return {@code this}
*/
TextHeaders clear();
/**
* Returns {@code true} if a header with the name and value exists.
*
* @param name the header name
* @param value the header value
* @return {@code true} if it contains it {@code false} otherwise
*/
boolean contains(CharSequence name, Object value);
/**
* Returns {@code true} if a header with the name and value exists.
*
* @param name the header name
* @param value the header value
* @return {@code true} if it contains it {@code false} otherwise
*/
boolean contains(CharSequence name, Object value, boolean ignoreCase);
@Override @Override
Iterator<Entry<String, String>> iterator(); TextHeaders clear();
Iterator<Entry<CharSequence, CharSequence>> unconvertedIterator();
TextHeaders forEachEntry(TextHeaderProcessor processor);
} }

View File

@ -0,0 +1,88 @@
/*
* Copyright 2014 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;
import static org.junit.Assert.assertArrayEquals;
import io.netty.util.CharsetUtil;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import org.junit.Test;
/**
* Test for the {@link AsciiString} class
*/
public class AsciiStringTest {
@Test
public void testGetBytesStringBuilder() {
final StringBuilder b = new StringBuilder();
for (int i = 0; i < 1 << 16; ++i) {
b.append("eéaà");
}
final String bString = b.toString();
final Charset[] charsets = CharsetUtil.values();
for (int i = 0; i < charsets.length; ++i) {
final Charset charset = charsets[i];
byte[] expected = getBytesWithEncoder(bString, charset);
byte[] actual = AsciiString.getBytes(b, charset);
assertArrayEquals("failure for " + charset, expected, actual);
}
}
@Test
public void testGetBytesString() {
final StringBuilder b = new StringBuilder();
for (int i = 0; i < 1 << 16; ++i) {
b.append("eéaà");
}
final String bString = b.toString();
final Charset[] charsets = CharsetUtil.values();
for (int i = 0; i < charsets.length; ++i) {
final Charset charset = charsets[i];
byte[] expected = bString.getBytes(charset);
byte[] actual = AsciiString.getBytes(bString, charset);
assertArrayEquals("failure for " + charset, expected, actual);
}
}
@Test
public void testGetBytesAsciiString() {
final StringBuilder b = new StringBuilder();
for (int i = 0; i < 1 << 16; ++i) {
b.append("eéaà");
}
final String bString = b.toString();
// The AsciiString class actually limits the Charset to ISO_8859_1
byte[] expected = bString.getBytes(CharsetUtil.ISO_8859_1);
final Charset[] charsets = CharsetUtil.values();
for (int i = 0; i < charsets.length; ++i) {
final Charset charset = charsets[i];
byte[] actual = AsciiString.getBytes(new AsciiString(bString), charset);
assertArrayEquals("failure for " + charset, expected, actual);
}
}
private static byte[] getBytesWithEncoder(CharSequence value, Charset charset) {
final CharsetEncoder encoder = CharsetUtil.getEncoder(charset);
final ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * value.length()));
encoder.encode(CharBuffer.wrap(value), nativeBuffer, true);
return nativeBuffer.array();
}
}

View File

@ -29,9 +29,9 @@ import java.util.Set;
import org.junit.Test; import org.junit.Test;
/** /**
* Tests for {@link HeaderMap}. * Tests for {@link DefaultBinaryHeaders}.
*/ */
public class HeaderMapTest { public class DefaultBinaryHeadersTest {
@Test @Test
public void binaryHeadersWithSameValuesShouldBeEquivalent() { public void binaryHeadersWithSameValuesShouldBeEquivalent() {
@ -40,11 +40,11 @@ public class HeaderMapTest {
byte[] key2 = randomBytes(); byte[] key2 = randomBytes();
byte[] value2 = randomBytes(); byte[] value2 = randomBytes();
HeaderMap h1 = new HeaderMap(false); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false);
h1.set(as(key1), as(value1)); h1.set(as(key1), as(value1));
h1.set(as(key2), as(value2)); h1.set(as(key2), as(value2));
HeaderMap h2 = new HeaderMap(false); DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false);
h2.set(as(key1), as(value1)); h2.set(as(key1), as(value1));
h2.set(as(key2), as(value2)); h2.set(as(key2), as(value2));
@ -63,13 +63,13 @@ public class HeaderMapTest {
byte[] v3 = randomBytes(); byte[] v3 = randomBytes();
byte[] v4 = randomBytes(); byte[] v4 = randomBytes();
HeaderMap h1 = new HeaderMap(false); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false);
h1.set(as(k1), as(v1)); h1.set(as(k1), as(v1));
h1.set(as(k2), as(v2)); h1.set(as(k2), as(v2));
h1.add(as(k2), as(v3)); h1.add(as(k2), as(v3));
h1.add(as(k1), as(v4)); h1.add(as(k1), as(v4));
HeaderMap h2 = new HeaderMap(false); DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false);
h2.set(as(k1), as(v1)); h2.set(as(k1), as(v1));
h2.set(as(k2), as(v2)); h2.set(as(k2), as(v2));
h2.add(as(k1), as(v4)); h2.add(as(k1), as(v4));
@ -90,13 +90,13 @@ public class HeaderMapTest {
byte[] v3 = randomBytes(); byte[] v3 = randomBytes();
byte[] v4 = randomBytes(); byte[] v4 = randomBytes();
HeaderMap h1 = new HeaderMap(false); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false);
h1.set(as(k1), as(v1)); h1.set(as(k1), as(v1));
h1.set(as(k2), as(v2)); h1.set(as(k2), as(v2));
h1.add(as(k2), as(v3)); h1.add(as(k2), as(v3));
h1.add(as(k1), as(v4)); h1.add(as(k1), as(v4));
HeaderMap h2 = new HeaderMap(false); DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false);
h2.set(as(k1), as(v1)); h2.set(as(k1), as(v1));
h2.set(as(k2), as(v2)); h2.set(as(k2), as(v2));
h2.add(as(k1), as(v4)); h2.add(as(k1), as(v4));
@ -116,18 +116,18 @@ public class HeaderMapTest {
byte[] v3 = randomBytes(); byte[] v3 = randomBytes();
byte[] v4 = randomBytes(); byte[] v4 = randomBytes();
HeaderMap h1 = new HeaderMap(false); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false);
h1.set(as(k1), as(v1)); h1.set(as(k1), as(v1));
h1.set(as(k2), as(v2)); h1.set(as(k2), as(v2));
h1.add(as(k2), as(v3)); h1.add(as(k2), as(v3));
h1.add(as(k1), as(v4)); h1.add(as(k1), as(v4));
HeaderMap h2 = new HeaderMap(false); DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false);
h2.set(as(k1), as(v1)); h2.set(as(k1), as(v1));
h2.set(as(k2), as(v2)); h2.set(as(k2), as(v2));
h2.add(as(k1), as(v4)); h2.add(as(k1), as(v4));
HeaderMap expected = new HeaderMap(false); DefaultBinaryHeaders expected = new DefaultBinaryHeaders(false);
expected.set(as(k1), as(v1)); expected.set(as(k1), as(v1));
expected.set(as(k2), as(v2)); expected.set(as(k2), as(v2));
expected.add(as(k2), as(v3)); expected.add(as(k2), as(v3));
@ -148,27 +148,27 @@ public class HeaderMapTest {
byte[] v2 = randomBytes(); byte[] v2 = randomBytes();
byte[] v3 = randomBytes(); byte[] v3 = randomBytes();
HeaderMap h1 = new HeaderMap(false); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false);
h1.add(as(k1), as(v1)); h1.add(as(k1), as(v1));
h1.add(as(k1), as(v2)); h1.add(as(k1), as(v2));
assertEquals(2, h1.size()); assertEquals(2, h1.size());
h1.set(as(k1), as(v3)); h1.set(as(k1), as(v3));
assertEquals(1, h1.size()); assertEquals(1, h1.size());
List<CharSequence> list = h1.getAll(as(k1)); List<AsciiString> list = h1.getAll(as(k1));
assertEquals(1, list.size()); assertEquals(1, list.size());
assertEquals(as(v3), list.get(0)); assertEquals(as(v3), list.get(0));
} }
@Test @Test
public void headersWithSameValuesShouldBeEquivalent() { public void headersWithSameValuesShouldBeEquivalent() {
HeaderMap h1 = new HeaderMap(); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
h1.set("foo", "goo"); h1.set(as("foo"), as("goo"));
h1.set("foo2", "goo2"); h1.set(as("foo2"), as("goo2"));
HeaderMap h2 = new HeaderMap(); DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
h2.set("foo", "goo"); h2.set(as("foo"), as("goo"));
h2.set("foo2", "goo2"); h2.set(as("foo2"), as("goo2"));
assertTrue(h1.equals(h2)); assertTrue(h1.equals(h2));
assertTrue(h2.equals(h1)); assertTrue(h2.equals(h1));
@ -178,17 +178,17 @@ public class HeaderMapTest {
@Test @Test
public void headersWithSameDuplicateValuesShouldBeEquivalent() { public void headersWithSameDuplicateValuesShouldBeEquivalent() {
HeaderMap h1 = new HeaderMap(); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
h1.set("foo", "goo"); h1.set(as("foo"), as("goo"));
h1.set("foo2", "goo2"); h1.set(as("foo2"), as("goo2"));
h1.add("foo2", "goo3"); h1.add(as("foo2"), as("goo3"));
h1.add("foo", "goo4"); h1.add(as("foo"), as("goo4"));
HeaderMap h2 = new HeaderMap(); DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
h2.set("foo", "goo"); h2.set(as("foo"), as("goo"));
h2.set("foo2", "goo2"); h2.set(as("foo2"), as("goo2"));
h2.add("foo", "goo4"); h2.add(as("foo"), as("goo4"));
h2.add("foo2", "goo3"); h2.add(as("foo2"), as("goo3"));
assertTrue(h1.equals(h2)); assertTrue(h1.equals(h2));
assertTrue(h2.equals(h1)); assertTrue(h2.equals(h1));
@ -198,16 +198,16 @@ public class HeaderMapTest {
@Test @Test
public void headersWithDifferentValuesShouldNotBeEquivalent() { public void headersWithDifferentValuesShouldNotBeEquivalent() {
HeaderMap h1 = new HeaderMap(); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
h1.set("foo", "goo"); h1.set(as("foo"), as("goo"));
h1.set("foo2", "goo2"); h1.set(as("foo2"), as("goo2"));
h1.add("foo2", "goo3"); h1.add(as("foo2"), as("goo3"));
h1.add("foo", "goo4"); h1.add(as("foo"), as("goo4"));
HeaderMap h2 = new HeaderMap(); DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
h2.set("foo", "goo"); h2.set(as("foo"), as("goo"));
h2.set("foo2", "goo2"); h2.set(as("foo2"), as("goo2"));
h2.add("foo", "goo4"); h2.add(as("foo"), as("goo4"));
assertFalse(h1.equals(h2)); assertFalse(h1.equals(h2));
assertFalse(h2.equals(h1)); assertFalse(h2.equals(h1));
@ -217,25 +217,25 @@ public class HeaderMapTest {
@Test @Test
public void setAllShouldMergeHeaders() { public void setAllShouldMergeHeaders() {
HeaderMap h1 = new HeaderMap(); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
h1.set("foo", "goo"); h1.set(as("foo"), as("goo"));
h1.set("foo2", "goo2"); h1.set(as("foo2"), as("goo2"));
h1.add("foo2", "goo3"); h1.add(as("foo2"), as("goo3"));
h1.add("foo", "goo4"); h1.add(as("foo"), as("goo4"));
HeaderMap h2 = new HeaderMap(); DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
h2.set("foo", "goo"); h2.set(as("foo"), as("goo"));
h2.set("foo2", "goo2"); h2.set(as("foo2"), as("goo2"));
h2.add("foo", "goo4"); h2.add(as("foo"), as("goo4"));
HeaderMap expected = new HeaderMap(); DefaultBinaryHeaders expected = new DefaultBinaryHeaders();
expected.set("foo", "goo"); expected.set(as("foo"), as("goo"));
expected.set("foo2", "goo2"); expected.set(as("foo2"), as("goo2"));
expected.add("foo2", "goo3"); expected.add(as("foo2"), as("goo3"));
expected.add("foo", "goo4"); expected.add(as("foo"), as("goo4"));
expected.set("foo", "goo"); expected.set(as("foo"), as("goo"));
expected.set("foo2", "goo2"); expected.set(as("foo2"), as("goo2"));
expected.set("foo", "goo4"); expected.set(as("foo"), as("goo4"));
h1.setAll(h2); h1.setAll(h2);
@ -244,22 +244,21 @@ public class HeaderMapTest {
@Test @Test
public void setShouldReplacePreviousValues() { public void setShouldReplacePreviousValues() {
HeaderMap h1 = new HeaderMap(); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
h1.add("foo", "goo"); h1.add(as("foo"), as("goo"));
h1.add("foo", "goo2"); h1.add(as("foo"), as("goo2"));
assertEquals(2, h1.size()); assertEquals(2, h1.size());
h1.set("foo", "goo3"); h1.set(as("foo"), as("goo3"));
assertEquals(1, h1.size()); assertEquals(1, h1.size());
List<CharSequence> list = h1.getAll("foo"); List<AsciiString> list = h1.getAll(as("foo"));
assertEquals(1, list.size()); assertEquals(1, list.size());
assertEquals("goo3", list.get(0)); assertEquals(as("goo3"), list.get(0));
} }
@Test(expected = NoSuchElementException.class) @Test(expected = NoSuchElementException.class)
public void iterateEmptyHeadersShouldThrow() { public void iterateEmptyHeadersShouldThrow() {
Iterator<Map.Entry<CharSequence, CharSequence>> iterator = Iterator<Map.Entry<AsciiString, AsciiString>> iterator = new DefaultBinaryHeaders().iterator();
new HeaderMap().iterator();
assertFalse(iterator.hasNext()); assertFalse(iterator.hasNext());
iterator.next(); iterator.next();
} }
@ -275,16 +274,15 @@ public class HeaderMapTest {
headers.add("c:1"); headers.add("c:1");
// Build the headers from the input set. // Build the headers from the input set.
HeaderMap h1 = new HeaderMap(); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
for (String header : headers) { for (String header : headers) {
String[] parts = header.split(":"); String[] parts = header.split(":");
h1.add(parts[0], parts[1]); h1.add(as(parts[0]), as(parts[1]));
} }
// Now iterate through the headers, removing them from the original set. // Now iterate through the headers, removing them from the original set.
for (Map.Entry<CharSequence, CharSequence> entry : h1) { for (Map.Entry<AsciiString, AsciiString> entry : h1) {
assertTrue(headers assertTrue(headers.remove(entry.getKey().toString() + ':' + entry.getValue().toString()));
.remove(entry.getKey().toString() + ':' + entry.getValue().toString()));
} }
// Make sure we removed them all. // Make sure we removed them all.
@ -293,12 +291,12 @@ public class HeaderMapTest {
@Test @Test
public void getAndRemoveShouldReturnFirstEntry() { public void getAndRemoveShouldReturnFirstEntry() {
HeaderMap h1 = new HeaderMap(); DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
h1.add("foo", "goo"); h1.add(as("foo"), as("goo"));
h1.add("foo", "goo2"); h1.add(as("foo"), as("goo2"));
assertEquals("goo", h1.getAndRemove("foo")); assertEquals(as("goo"), h1.getAndRemove(as("foo")));
assertEquals(0, h1.size()); assertEquals(0, h1.size());
List<CharSequence> values = h1.getAll("foo"); List<AsciiString> values = h1.getAll(as("foo"));
assertEquals(0, values.size()); assertEquals(0, values.size());
} }
@ -308,7 +306,11 @@ public class HeaderMapTest {
return data; return data;
} }
private String as(byte[] bytes) { private AsciiString as(byte[] bytes) {
return new String(bytes); return new AsciiString(bytes);
}
private AsciiString as(String value) {
return new AsciiString(value);
} }
} }

View File

@ -61,6 +61,11 @@ public final class CharsetUtil {
*/ */
public static final Charset US_ASCII = Charset.forName("US-ASCII"); public static final Charset US_ASCII = Charset.forName("US-ASCII");
private static final Charset[] CHARSETS = new Charset[]
{ UTF_16, UTF_16BE, UTF_16LE, UTF_8, ISO_8859_1, US_ASCII };
public static Charset[] values() { return CHARSETS; }
/** /**
* Returns a cached thread-local {@link CharsetEncoder} for the specified * Returns a cached thread-local {@link CharsetEncoder} for the specified
* <tt>charset</tt>. * <tt>charset</tt>.
@ -111,7 +116,5 @@ public final class CharsetUtil {
return d; return d;
} }
private CharsetUtil() { private CharsetUtil() { }
// Unused
}
} }

View File

@ -0,0 +1,52 @@
/*
* Copyright 2014 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.util.collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Provides utilities for the primitive collection types that are not supplied by the JDK
*/
public final class CollectionUtils {
private CollectionUtils() { }
/**
* Compare two lists using the {@code comparator} for all comparisons (not using the equals() operator)
* @param lhs Left hand side
* @param rhs Right hand side
* @param comparator Comparator which will be used for all comparisons (equals() on objects will not be used)
* @return True if {@code lhs} == {@code rhs} according to {@code comparator}. False otherwise.
*/
public static <T> boolean equals(List<T> lhs, List<T> rhs, Comparator<? super T> comparator) {
final int lhsSize = lhs.size();
if (lhsSize != rhs.size()) {
return false;
}
// Don't use a TreeSet to do the comparison. We want to force the comparator
// to be used instead of the object's equals()
Collections.sort(lhs, comparator);
Collections.sort(rhs, comparator);
for (int i = 0; i < lhsSize; ++i) {
if (comparator.compare(lhs.get(i), rhs.get(i)) != 0) {
return false;
}
}
return true;
}
}

View File

@ -150,7 +150,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
} }
// Cache Validation // Cache Validation
String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE); String ifModifiedSince = request.headers().getAndConvert(IF_MODIFIED_SINCE);
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);

View File

@ -49,7 +49,7 @@ public class HttpHelloWorldServerHandler extends ChannelHandlerAdapter {
boolean keepAlive = HttpHeaderUtil.isKeepAlive(req); boolean keepAlive = HttpHeaderUtil.isKeepAlive(req);
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(CONTENT)); FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(CONTENT));
response.headers().set(CONTENT_TYPE, "text/plain"); response.headers().set(CONTENT_TYPE, "text/plain");
response.headers().set(CONTENT_LENGTH, response.content().readableBytes()); response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
if (!keepAlive) { if (!keepAlive) {
ctx.write(response).addListener(ChannelFutureListener.CLOSE); ctx.write(response).addListener(ChannelFutureListener.CLOSE);

View File

@ -36,8 +36,8 @@ public class HttpSnoopClientHandler extends SimpleChannelInboundHandler<HttpObje
System.err.println(); System.err.println();
if (!response.headers().isEmpty()) { if (!response.headers().isEmpty()) {
for (String name: response.headers().names()) { for (CharSequence name: response.headers().names()) {
for (String value: response.headers().getAll(name)) { for (CharSequence value: response.headers().getAll(name)) {
System.err.println("HEADER: " + name + " = " + value); System.err.println("HEADER: " + name + " = " + value);
} }
} }

View File

@ -74,9 +74,9 @@ public class HttpSnoopServerHandler extends SimpleChannelInboundHandler<Object>
HttpHeaders headers = request.headers(); HttpHeaders headers = request.headers();
if (!headers.isEmpty()) { if (!headers.isEmpty()) {
for (Map.Entry<String, String> h: headers) { for (Map.Entry<CharSequence, CharSequence> h: headers) {
String key = h.getKey(); CharSequence key = h.getKey();
String value = h.getValue(); CharSequence value = h.getValue();
buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n"); buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
} }
buf.append("\r\n"); buf.append("\r\n");
@ -115,8 +115,8 @@ public class HttpSnoopServerHandler extends SimpleChannelInboundHandler<Object>
LastHttpContent trailer = (LastHttpContent) msg; LastHttpContent trailer = (LastHttpContent) msg;
if (!trailer.trailingHeaders().isEmpty()) { if (!trailer.trailingHeaders().isEmpty()) {
buf.append("\r\n"); buf.append("\r\n");
for (String name: trailer.trailingHeaders().names()) { for (CharSequence name: trailer.trailingHeaders().names()) {
for (String value: trailer.trailingHeaders().getAll(name)) { for (CharSequence value: trailer.trailingHeaders().getAll(name)) {
buf.append("TRAILING HEADER: "); buf.append("TRAILING HEADER: ");
buf.append(name).append(" = ").append(value).append("\r\n"); buf.append(name).append(" = ").append(value).append("\r\n");
} }
@ -155,14 +155,14 @@ public class HttpSnoopServerHandler extends SimpleChannelInboundHandler<Object>
if (keepAlive) { if (keepAlive) {
// Add 'Content-Length' header only for a keep-alive connection. // Add 'Content-Length' header only for a keep-alive connection.
response.headers().set(CONTENT_LENGTH, response.content().readableBytes()); response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
// Add keep alive header as per: // Add keep alive header as per:
// - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection // - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection
response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
} }
// Encode the cookie. // Encode the cookie.
String cookieString = request.headers().get(COOKIE); String cookieString = request.headers().getAndConvert(COOKIE);
if (cookieString != null) { if (cookieString != null) {
Set<Cookie> cookies = CookieDecoder.decode(cookieString); Set<Cookie> cookies = CookieDecoder.decode(cookieString);
if (!cookies.isEmpty()) { if (!cookies.isEmpty()) {

View File

@ -182,7 +182,7 @@ public final class HttpUploadClient {
); );
// send request // send request
List<Entry<String, String>> entries = headers.entries(); List<Entry<String, String>> entries = headers.entriesConverted();
channel.writeAndFlush(request); channel.writeAndFlush(request);
// Wait for the server to close the connection. // Wait for the server to close the connection.

View File

@ -40,8 +40,8 @@ public class HttpUploadClientHandler extends SimpleChannelInboundHandler<HttpObj
System.err.println("VERSION: " + response.protocolVersion()); System.err.println("VERSION: " + response.protocolVersion());
if (!response.headers().isEmpty()) { if (!response.headers().isEmpty()) {
for (String name : response.headers().names()) { for (CharSequence name : response.headers().names()) {
for (String value : response.headers().getAll(name)) { for (CharSequence value : response.headers().getAll(name)) {
System.err.println("HEADER: " + name + " = " + value); System.err.println("HEADER: " + name + " = " + value);
} }
} }

View File

@ -113,14 +113,14 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
responseContent.append("\r\n\r\n"); responseContent.append("\r\n\r\n");
// new getMethod // new getMethod
for (Entry<String, String> entry : request.headers()) { for (Entry<CharSequence, CharSequence> entry : request.headers()) {
responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n"); responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n");
} }
responseContent.append("\r\n\r\n"); responseContent.append("\r\n\r\n");
// new getMethod // new getMethod
Set<Cookie> cookies; Set<Cookie> cookies;
String value = request.headers().get(COOKIE); String value = request.headers().getAndConvert(COOKIE);
if (value == null) { if (value == null) {
cookies = Collections.emptySet(); cookies = Collections.emptySet();
} else { } else {
@ -299,11 +299,11 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
if (!close) { if (!close) {
// There's no need to add 'Content-Length' header // There's no need to add 'Content-Length' header
// if this is the last response. // if this is the last response.
response.headers().set(CONTENT_LENGTH, buf.readableBytes()); response.headers().setInt(CONTENT_LENGTH, buf.readableBytes());
} }
Set<Cookie> cookies; Set<Cookie> cookies;
String value = request.headers().get(COOKIE); String value = request.headers().getAndConvert(COOKIE);
if (value == null) { if (value == null) {
cookies = Collections.emptySet(); cookies = Collections.emptySet();
} else { } else {
@ -401,7 +401,7 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8"); response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
response.headers().set(CONTENT_LENGTH, buf.readableBytes()); response.headers().setInt(CONTENT_LENGTH, buf.readableBytes());
// Write the response. // Write the response.
ctx.channel().writeAndFlush(response); ctx.channel().writeAndFlush(response);

View File

@ -97,7 +97,7 @@ public final class Http2Client {
if (URL != null) { if (URL != null) {
// Create a simple GET request. // Create a simple GET request.
FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, URL); FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, URL);
request.headers().add(HttpHeaders.Names.HOST, hostName); request.headers().addObject(HttpHeaders.Names.HOST, hostName);
request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP); request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.DEFLATE); request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.DEFLATE);
channel.writeAndFlush(request); channel.writeAndFlush(request);
@ -108,7 +108,7 @@ public final class Http2Client {
// Create a simple POST request with a body. // Create a simple POST request with a body.
FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, URL2, FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, URL2,
Unpooled.copiedBuffer(URL2DATA.getBytes(CharsetUtil.UTF_8))); Unpooled.copiedBuffer(URL2DATA.getBytes(CharsetUtil.UTF_8)));
request.headers().add(HttpHeaders.Names.HOST, hostName); request.headers().addObject(HttpHeaders.Names.HOST, hostName);
request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP); request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.DEFLATE); request.headers().add(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.DEFLATE);
channel.writeAndFlush(request); channel.writeAndFlush(request);

View File

@ -76,13 +76,12 @@ public class HttpResponseHandler extends SimpleChannelInboundHandler<FullHttpRes
@Override @Override
protected void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception { protected void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
String streamIdText = msg.headers().get(HttpUtil.ExtensionHeaderNames.STREAM_ID.text()); Integer streamId = msg.headers().getInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text());
if (streamIdText == null) { if (streamId == null) {
System.err.println("HttpResponseHandler unexpected message received: " + msg); System.err.println("HttpResponseHandler unexpected message received: " + msg);
return; return;
} }
int streamId = Integer.parseInt(streamIdText);
ChannelPromise promise = streamidPromiseMap.get(streamId); ChannelPromise promise = streamidPromiseMap.get(streamId);
if (promise == null) { if (promise == null) {
System.err.println("Message received for unknown stream id " + streamId); System.err.println("Message received for unknown stream id " + streamId);

Some files were not shown because too many files have changed in this diff Show More