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:
parent
6f35e608c3
commit
2374e17c6e
@ -17,7 +17,6 @@ package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
import io.netty.handler.codec.DefaultTextHeaders;
|
||||
import io.netty.handler.codec.TextHeaderProcessor;
|
||||
import io.netty.handler.codec.TextHeaders;
|
||||
|
||||
import java.util.Calendar;
|
||||
@ -45,75 +44,15 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
|
||||
LOOKUP_TABLE['='] = -1;
|
||||
}
|
||||
|
||||
protected final boolean validate;
|
||||
private static final class HttpHeadersValidationConverter extends DefaultTextValueTypeConverter {
|
||||
private final boolean validate;
|
||||
|
||||
public DefaultHttpHeaders() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public DefaultHttpHeaders(boolean validate) {
|
||||
public HttpHeadersValidationConverter(boolean validate) {
|
||||
this.validate = validate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CharSequence convertName(CharSequence name) {
|
||||
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) {
|
||||
public CharSequence convert(Object value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException("value");
|
||||
}
|
||||
@ -141,6 +80,87 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
|
||||
|
||||
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) {
|
||||
int state = 0;
|
||||
@ -148,46 +168,39 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
|
||||
final int start = seq.arrayOffset();
|
||||
final int end = start + seq.length();
|
||||
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));
|
||||
}
|
||||
|
||||
if (state != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"a header value must not end with '\\r' or '\\n':" + seq);
|
||||
throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateValue(CharSequence seq) {
|
||||
int state = 0;
|
||||
// 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));
|
||||
}
|
||||
|
||||
if (state != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"a header value must not end with '\\r' or '\\n':" + seq);
|
||||
throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq);
|
||||
}
|
||||
}
|
||||
|
||||
private static int validateValueChar(CharSequence seq, int state, char character) {
|
||||
/*
|
||||
* State:
|
||||
*
|
||||
* 0: Previous character was neither CR nor LF
|
||||
* 1: The previous character was CR
|
||||
* 2: The previous character was LF
|
||||
* State: 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) {
|
||||
// Check the absolutely prohibited characters.
|
||||
switch (character) {
|
||||
case 0x0b: // Vertical tab
|
||||
throw new IllegalArgumentException(
|
||||
"a header value contains a prohibited character '\\v': " + seq);
|
||||
throw new IllegalArgumentException("a header value contains a prohibited character '\\v': " + seq);
|
||||
case '\f':
|
||||
throw new IllegalArgumentException(
|
||||
"a header value contains a prohibited character '\\f': " + seq);
|
||||
throw new IllegalArgumentException("a header value contains a prohibited character '\\f': " + seq);
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,41 +222,106 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
|
||||
state = 2;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"only '\\n' is allowed after '\\r': " + seq);
|
||||
throw new IllegalArgumentException("only '\\n' is allowed after '\\r': " + seq);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (character) {
|
||||
case '\t': case ' ':
|
||||
case '\t':
|
||||
case ' ':
|
||||
state = 0;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"only ' ' and '\\t' are allowed after '\\n': " + seq);
|
||||
throw new IllegalArgumentException("only ' ' and '\\t' are allowed after '\\n': " + seq);
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders add(CharSequence name, Object value) {
|
||||
public HttpHeaders add(CharSequence name, CharSequence value) {
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders add(CharSequence name, Iterable<?> values) {
|
||||
public HttpHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders add(CharSequence name, Object... values) {
|
||||
public HttpHeaders add(CharSequence name, CharSequence... values) {
|
||||
super.add(name, values);
|
||||
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
|
||||
public HttpHeaders add(TextHeaders headers) {
|
||||
super.add(headers);
|
||||
@ -251,23 +329,89 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders set(CharSequence name, Object value) {
|
||||
public HttpHeaders set(CharSequence name, CharSequence value) {
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders set(CharSequence name, Object... values) {
|
||||
public HttpHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders set(CharSequence name, Iterable<?> values) {
|
||||
public HttpHeaders set(CharSequence name, CharSequence... values) {
|
||||
super.set(name, values);
|
||||
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
|
||||
public HttpHeaders set(TextHeaders headers) {
|
||||
super.set(headers);
|
||||
@ -275,14 +419,14 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders clear() {
|
||||
super.clear();
|
||||
public HttpHeaders setAll(TextHeaders headers) {
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders forEachEntry(TextHeaderProcessor processor) {
|
||||
super.forEachEntry(processor);
|
||||
public HttpHeaders clear() {
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ public abstract class DefaultHttpMessage extends DefaultHttpObject implements Ht
|
||||
}
|
||||
|
||||
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(": ");
|
||||
buf.append(e.getValue());
|
||||
|
@ -15,6 +15,9 @@
|
||||
*/
|
||||
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.Unpooled;
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
@ -99,7 +102,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
|
||||
}
|
||||
|
||||
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(": ");
|
||||
buf.append(e.getValue());
|
||||
@ -108,22 +111,32 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
|
||||
}
|
||||
|
||||
private static final class TrailingHttpHeaders extends DefaultHttpHeaders {
|
||||
TrailingHttpHeaders(boolean validate) {
|
||||
private static final class TrailingHttpHeadersNameConverter extends HttpHeadersNameConverter {
|
||||
public TrailingHttpHeadersNameConverter(boolean validate) {
|
||||
super(validate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CharSequence convertName(CharSequence name) {
|
||||
public CharSequence convertName(CharSequence name) {
|
||||
name = super.convertName(name);
|
||||
if (validate) {
|
||||
if (AsciiString.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH, name) ||
|
||||
AsciiString.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING, name) ||
|
||||
AsciiString.equalsIgnoreCase(HttpHeaders.Names.TRAILER, name)) {
|
||||
throw new IllegalArgumentException(
|
||||
"prohibited trailing header: " + name);
|
||||
if (AsciiString.equalsIgnoreCase(CONTENT_LENGTH, name)
|
||||
|| AsciiString.equalsIgnoreCase(TRANSFER_ENCODING, name)
|
||||
|| AsciiString.equalsIgnoreCase(TRAILER, name)) {
|
||||
throw new IllegalArgumentException("prohibited trailing header: " + 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,33 +17,99 @@
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.handler.codec.EmptyTextHeaders;
|
||||
import io.netty.handler.codec.TextHeaderProcessor;
|
||||
import io.netty.handler.codec.TextHeaders;
|
||||
|
||||
public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders {
|
||||
|
||||
public static final EmptyHttpHeaders INSTANCE = new EmptyHttpHeaders();
|
||||
|
||||
protected EmptyHttpHeaders() { }
|
||||
protected EmptyHttpHeaders() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders add(CharSequence name, Object value) {
|
||||
public HttpHeaders add(CharSequence name, CharSequence value) {
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders add(CharSequence name, Iterable<?> values) {
|
||||
public HttpHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders add(CharSequence name, Object... values) {
|
||||
public HttpHeaders add(CharSequence name, CharSequence... values) {
|
||||
super.add(name, values);
|
||||
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
|
||||
public HttpHeaders add(TextHeaders headers) {
|
||||
super.add(headers);
|
||||
@ -51,23 +117,89 @@ public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders set(CharSequence name, Object value) {
|
||||
public HttpHeaders set(CharSequence name, CharSequence value) {
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders set(CharSequence name, Object... values) {
|
||||
public HttpHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders set(CharSequence name, Iterable<?> values) {
|
||||
public HttpHeaders set(CharSequence name, CharSequence... values) {
|
||||
super.set(name, values);
|
||||
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
|
||||
public HttpHeaders set(TextHeaders headers) {
|
||||
super.set(headers);
|
||||
@ -75,14 +207,14 @@ public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders clear() {
|
||||
super.clear();
|
||||
public HttpHeaders setAll(TextHeaders headers) {
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders forEachEntry(TextHeaderProcessor processor) {
|
||||
super.forEachEntry(processor);
|
||||
public HttpHeaders clear() {
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
@ -177,12 +178,12 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator {
|
||||
return;
|
||||
}
|
||||
|
||||
String upgradeHeader = response.headers().get(UPGRADE);
|
||||
CharSequence upgradeHeader = response.headers().get(UPGRADE);
|
||||
if (upgradeHeader == null) {
|
||||
throw new IllegalStateException(
|
||||
"Switching Protocols response missing UPGRADE header");
|
||||
}
|
||||
if (!upgradeCodec.protocol().equalsIgnoreCase(upgradeHeader)) {
|
||||
if (AsciiString.equalsIgnoreCase(upgradeCodec.protocol(), upgradeHeader)) {
|
||||
throw new IllegalStateException(
|
||||
"Switching Protocols response with unexpected UPGRADE protocol: "
|
||||
+ upgradeHeader);
|
||||
|
@ -95,7 +95,7 @@ public class HttpContentCompressor extends HttpContentEncoder {
|
||||
|
||||
@Override
|
||||
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 &&
|
||||
!AsciiString.equalsIgnoreCase(HttpHeaders.Values.IDENTITY, contentEncoding)) {
|
||||
return null;
|
||||
|
@ -87,7 +87,7 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObj
|
||||
this.message = null;
|
||||
|
||||
// Determine the content encoding.
|
||||
String contentEncoding = headers.get(HttpHeaders.Names.CONTENT_ENCODING);
|
||||
String contentEncoding = headers.getAndConvert(HttpHeaders.Names.CONTENT_ENCODING);
|
||||
if (contentEncoding != null) {
|
||||
contentEncoding = contentEncoding.trim();
|
||||
} else {
|
||||
|
@ -33,7 +33,7 @@ public final class HttpHeaderUtil {
|
||||
* {@link HttpVersion#isKeepAliveDefault()}.
|
||||
*/
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
@ -94,9 +94,9 @@ public final class HttpHeaderUtil {
|
||||
* or its value is not a number
|
||||
*/
|
||||
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) {
|
||||
return Long.parseLong(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// We know the content length if it's a Web Socket message even if
|
||||
@ -121,13 +121,9 @@ public final class HttpHeaderUtil {
|
||||
* a number
|
||||
*/
|
||||
public static long getContentLength(HttpMessage message, long defaultValue) {
|
||||
String contentLength = message.headers().get(Names.CONTENT_LENGTH);
|
||||
if (contentLength != null) {
|
||||
try {
|
||||
return Long.parseLong(contentLength);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return defaultValue;
|
||||
}
|
||||
Long value = message.headers().getLong(Names.CONTENT_LENGTH);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 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.
|
||||
*/
|
||||
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) {
|
||||
@ -195,7 +191,7 @@ public final class HttpHeaderUtil {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return false;
|
||||
}
|
||||
@ -237,7 +233,7 @@ public final class HttpHeaderUtil {
|
||||
m.headers().add(Names.TRANSFER_ENCODING, Values.CHUNKED);
|
||||
m.headers().remove(Names.CONTENT_LENGTH);
|
||||
} else {
|
||||
List<CharSequence> values = m.headers().getAllUnconverted(Names.TRANSFER_ENCODING);
|
||||
List<CharSequence> values = m.headers().getAll(Names.TRANSFER_ENCODING);
|
||||
if (values.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
import io.netty.handler.codec.TextHeaderProcessor;
|
||||
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}.
|
||||
*/
|
||||
public interface HttpHeaders extends TextHeaders {
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
* {@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 =
|
||||
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 =
|
||||
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 =
|
||||
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 =
|
||||
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 =
|
||||
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 =
|
||||
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 =
|
||||
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
|
||||
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
|
||||
public static final AsciiString PROXY_CONNECTION = new AsciiString("Proxy-Connection");
|
||||
public static final AsciiString PROXY_CONNECTION = new AsciiString("proxy-connection");
|
||||
|
||||
private Names() {
|
||||
}
|
||||
@ -359,6 +364,14 @@ public interface HttpHeaders extends TextHeaders {
|
||||
*/
|
||||
public static final 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"}
|
||||
*/
|
||||
@ -435,6 +448,10 @@ public interface HttpHeaders extends TextHeaders {
|
||||
* {@code "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"}
|
||||
*/
|
||||
@ -491,38 +508,129 @@ public interface HttpHeaders extends TextHeaders {
|
||||
* {@code "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() {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
HttpHeaders add(CharSequence name, Object value);
|
||||
HttpHeaders add(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
HttpHeaders add(CharSequence name, Iterable<?> values);
|
||||
HttpHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@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
|
||||
HttpHeaders add(TextHeaders headers);
|
||||
|
||||
@Override
|
||||
HttpHeaders set(CharSequence name, Object value);
|
||||
HttpHeaders set(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
HttpHeaders set(CharSequence name, Iterable<?> values);
|
||||
HttpHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@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
|
||||
HttpHeaders set(TextHeaders headers);
|
||||
|
||||
@Override
|
||||
HttpHeaders clear();
|
||||
HttpHeaders setAll(TextHeaders headers);
|
||||
|
||||
@Override
|
||||
HttpHeaders forEachEntry(TextHeaderProcessor processor);
|
||||
HttpHeaders clear();
|
||||
}
|
||||
|
@ -18,9 +18,11 @@ package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
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;
|
||||
|
||||
@ -29,7 +31,9 @@ final class HttpHeadersEncoder implements TextHeaderProcessor {
|
||||
}
|
||||
|
||||
@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 int nameLen = name.length();
|
||||
final int valueLen = value.length();
|
||||
|
@ -58,7 +58,7 @@ public class HttpObjectAggregator
|
||||
HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.EMPTY_BUFFER);
|
||||
|
||||
static {
|
||||
TOO_LARGE.headers().set(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
TOO_LARGE.headers().setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -550,11 +550,15 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<State> {
|
||||
do {
|
||||
char firstChar = line.charAt(0);
|
||||
if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {
|
||||
List<String> current = trailer.trailingHeaders().getAll(lastHeader);
|
||||
List<CharSequence> current = trailer.trailingHeaders().getAll(lastHeader);
|
||||
if (!current.isEmpty()) {
|
||||
int lastPos = current.size() - 1;
|
||||
String newString = current.get(lastPos) + line.toString().trim();
|
||||
current.set(lastPos, newString);
|
||||
String lineTrimmed = line.toString().trim();
|
||||
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 {
|
||||
// Content-Length, Transfer-Encoding, or Trailer
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.FileRegion;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
@ -137,7 +138,12 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
|
||||
} else {
|
||||
ByteBuf buf = ctx.alloc().buffer();
|
||||
buf.writeBytes(ZERO_CRLF);
|
||||
try {
|
||||
headers.forEachEntry(new HttpHeadersEncoder(buf));
|
||||
} catch (Exception ex) {
|
||||
buf.release();
|
||||
PlatformDependent.throwException(ex);
|
||||
}
|
||||
buf.writeBytes(CRLF);
|
||||
out.add(buf);
|
||||
}
|
||||
|
@ -14,9 +14,15 @@
|
||||
*/
|
||||
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.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.ReferenceCounted;
|
||||
|
||||
@ -29,11 +35,6 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
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
|
||||
* 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) {
|
||||
// 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);
|
||||
if (upgradeCodec == null) {
|
||||
// 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.
|
||||
String connectionHeader = request.headers().get(CONNECTION);
|
||||
CharSequence connectionHeader = request.headers().get(CONNECTION);
|
||||
if (connectionHeader == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the CONNECTION header contains UPGRADE as well as all protocol-specific headers.
|
||||
Collection<String> requiredHeaders = upgradeCodec.requiredUpgradeHeaders();
|
||||
Set<String> values = splitHeader(connectionHeader);
|
||||
if (!values.contains(UPGRADE.toString()) || !values.containsAll(requiredHeaders)) {
|
||||
Set<CharSequence> values = splitHeader(connectionHeader);
|
||||
if (!values.contains(UPGRADE) || !values.containsAll(requiredHeaders)) {
|
||||
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
|
||||
* header. If no suitable codec was found, returns {@code null}.
|
||||
*/
|
||||
private UpgradeCodec selectUpgradeCodec(String upgradeHeader) {
|
||||
Set<String> requestedProtocols = splitHeader(upgradeHeader);
|
||||
private UpgradeCodec selectUpgradeCodec(CharSequence upgradeHeader) {
|
||||
Set<CharSequence> requestedProtocols = splitHeader(upgradeHeader);
|
||||
|
||||
// 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
|
||||
@ -345,9 +346,9 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator {
|
||||
* Splits a comma-separated header value. The returned set is case-insensitive and contains each
|
||||
* part with whitespace removed.
|
||||
*/
|
||||
private static Set<String> splitHeader(String header) {
|
||||
private static Set<CharSequence> splitHeader(CharSequence header) {
|
||||
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) {
|
||||
char c = header.charAt(i);
|
||||
if (Character.isWhitespace(c)) {
|
||||
|
@ -209,9 +209,9 @@ public final class CorsConfig {
|
||||
for (Entry<CharSequence, Callable<?>> entry : this.preflightHeaders.entrySet()) {
|
||||
final Object value = getValue(entry.getValue());
|
||||
if (value instanceof Iterable) {
|
||||
preflightHeaders.add(entry.getKey(), (Iterable<?>) value);
|
||||
preflightHeaders.addObject(entry.getKey(), (Iterable<?>) value);
|
||||
} else {
|
||||
preflightHeaders.add(entry.getKey(), value);
|
||||
preflightHeaders.addObject(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
return preflightHeaders;
|
||||
|
@ -86,7 +86,7 @@ public class CorsHandler extends ChannelHandlerAdapter {
|
||||
}
|
||||
|
||||
private boolean setOrigin(final HttpResponse response) {
|
||||
final String origin = request.headers().get(ORIGIN);
|
||||
final CharSequence origin = request.headers().get(ORIGIN);
|
||||
if (origin != null) {
|
||||
if ("null".equals(origin) && config.isNullOriginAllowed()) {
|
||||
setAnyOrigin(response);
|
||||
@ -116,7 +116,7 @@ public class CorsHandler extends ChannelHandlerAdapter {
|
||||
return true;
|
||||
}
|
||||
|
||||
final String origin = request.headers().get(ORIGIN);
|
||||
final CharSequence origin = request.headers().get(ORIGIN);
|
||||
if (origin == null) {
|
||||
// Not a CORS request so we cannot validate it. It may be a non CORS request.
|
||||
return true;
|
||||
@ -141,7 +141,7 @@ public class CorsHandler extends ChannelHandlerAdapter {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ public class CorsHandler extends ChannelHandlerAdapter {
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -173,7 +173,7 @@ public class CorsHandler extends ChannelHandlerAdapter {
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -16,6 +16,7 @@
|
||||
package io.netty.handler.codec.http.multipart;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
@ -29,31 +30,31 @@ final class HttpPostBodyUtil {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public static final String ATTACHMENT = "attachment";
|
||||
public static final String ATTACHMENT = HttpHeaders.Values.ATTACHMENT.toString();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static final String MULTIPART_MIXED = "multipart/mixed";
|
||||
public static final String MULTIPART_MIXED = HttpHeaders.Values.MULTIPART_MIXED.toString();
|
||||
|
||||
/**
|
||||
* Charset for 8BIT
|
||||
@ -68,12 +69,12 @@ final class HttpPostBodyUtil {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
|
@ -181,7 +181,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
|
||||
this.factory = factory;
|
||||
// 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) {
|
||||
// Offer automatically if the given request is als type of HttpContent
|
||||
// See #1089
|
||||
|
@ -140,7 +140,7 @@ public class HttpPostRequestDecoder implements InterfaceHttpPostRequestDecoder {
|
||||
*/
|
||||
public static boolean isMultipart(HttpRequest request) {
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
|
@ -711,8 +711,8 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
|
||||
}
|
||||
|
||||
HttpHeaders headers = request.headers();
|
||||
List<String> contentTypes = headers.getAll(HttpHeaders.Names.CONTENT_TYPE);
|
||||
List<String> transferEncoding = headers.getAll(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
List<String> contentTypes = headers.getAllAndConvert(HttpHeaders.Names.CONTENT_TYPE);
|
||||
List<CharSequence> transferEncoding = headers.getAll(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
if (contentTypes != null) {
|
||||
headers.remove(HttpHeaders.Names.CONTENT_TYPE);
|
||||
for (String contentType : contentTypes) {
|
||||
@ -747,7 +747,7 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
|
||||
isChunked = true;
|
||||
if (transferEncoding != null) {
|
||||
headers.remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
for (String v : transferEncoding) {
|
||||
for (CharSequence v : transferEncoding) {
|
||||
if (AsciiString.equalsIgnoreCase(v, HttpHeaders.Values.CHUNKED)) {
|
||||
// ignore
|
||||
} else {
|
||||
|
@ -203,7 +203,7 @@ public abstract class WebSocketClientHandshaker {
|
||||
|
||||
// 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
|
||||
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;
|
||||
String expectedProtocol = expectedSubprotocol != null ? expectedSubprotocol : "";
|
||||
boolean protocolValid = false;
|
||||
|
@ -163,7 +163,7 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
||||
|
||||
// Set Content-Length to workaround some known defect.
|
||||
// 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);
|
||||
return request;
|
||||
}
|
||||
@ -198,13 +198,13 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
||||
|
||||
HttpHeaders headers = response.headers();
|
||||
|
||||
String upgrade = headers.get(Names.UPGRADE);
|
||||
CharSequence upgrade = headers.get(Names.UPGRADE);
|
||||
if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, 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)) {
|
||||
throw new WebSocketHandshakeException("Invalid handshake response connection: "
|
||||
+ connection);
|
||||
|
@ -173,17 +173,17 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
|
||||
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)) {
|
||||
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)) {
|
||||
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)) {
|
||||
throw new WebSocketHandshakeException(String.format(
|
||||
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));
|
||||
|
@ -174,17 +174,17 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
||||
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)) {
|
||||
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)) {
|
||||
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)) {
|
||||
throw new WebSocketHandshakeException(String.format(
|
||||
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));
|
||||
|
@ -184,17 +184,17 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
||||
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)) {
|
||||
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)) {
|
||||
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)) {
|
||||
throw new WebSocketHandshakeException(String.format(
|
||||
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));
|
||||
|
@ -133,7 +133,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
||||
// New handshake getMethod with a challenge:
|
||||
res.headers().add(SEC_WEBSOCKET_ORIGIN, req.headers().get(ORIGIN));
|
||||
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) {
|
||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||
if (selectedSubprotocol == null) {
|
||||
@ -146,8 +146,8 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
||||
}
|
||||
|
||||
// Calculate the answer of the challenge.
|
||||
String key1 = req.headers().get(SEC_WEBSOCKET_KEY1);
|
||||
String key2 = req.headers().get(SEC_WEBSOCKET_KEY2);
|
||||
String key1 = req.headers().getAndConvert(SEC_WEBSOCKET_KEY1);
|
||||
String key2 = req.headers().getAndConvert(SEC_WEBSOCKET_KEY2);
|
||||
int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) /
|
||||
BEGINNING_SPACE.matcher(key1).replaceAll("").length());
|
||||
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:
|
||||
res.headers().add(WEBSOCKET_ORIGIN, req.headers().get(ORIGIN));
|
||||
res.headers().add(WEBSOCKET_LOCATION, uri());
|
||||
String protocol = req.headers().get(WEBSOCKET_PROTOCOL);
|
||||
String protocol = req.headers().getAndConvert(WEBSOCKET_PROTOCOL);
|
||||
if (protocol != null) {
|
||||
res.headers().add(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
|
||||
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) {
|
||||
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.CONNECTION, Names.UPGRADE);
|
||||
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) {
|
||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||
if (selectedSubprotocol == null) {
|
||||
|
@ -104,7 +104,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
||||
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) {
|
||||
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.CONNECTION, Names.UPGRADE);
|
||||
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) {
|
||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||
if (selectedSubprotocol == null) {
|
||||
|
@ -102,7 +102,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
||||
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) {
|
||||
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.CONNECTION, Names.UPGRADE);
|
||||
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) {
|
||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||
if (selectedSubprotocol == null) {
|
||||
|
@ -86,7 +86,7 @@ public class WebSocketServerHandshakerFactory {
|
||||
*/
|
||||
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.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
|
||||
// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
|
||||
|
@ -63,7 +63,7 @@ public class WebSocketClientExtensionHandler extends ChannelHandlerAdapter {
|
||||
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade((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) {
|
||||
WebSocketExtensionData extensionData = extentionHandshaker.newRequestData();
|
||||
@ -84,7 +84,7 @@ public class WebSocketClientExtensionHandler extends ChannelHandlerAdapter {
|
||||
HttpResponse response = (HttpResponse) msg;
|
||||
|
||||
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) {
|
||||
List<WebSocketExtensionData> extensions =
|
||||
|
@ -69,7 +69,7 @@ public class WebSocketServerExtensionHandler extends ChannelHandlerAdapter {
|
||||
HttpRequest request = (HttpRequest) msg;
|
||||
|
||||
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) {
|
||||
List<WebSocketExtensionData> extensions =
|
||||
@ -107,7 +107,7 @@ public class WebSocketServerExtensionHandler extends ChannelHandlerAdapter {
|
||||
if (msg instanceof HttpResponse &&
|
||||
WebSocketExtensionUtil.isWebsocketUpgrade((HttpResponse) msg) && validExtensions != null) {
|
||||
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) {
|
||||
WebSocketExtensionData extensionData = extension.newReponseData();
|
||||
|
@ -17,31 +17,16 @@ package io.netty.handler.codec.spdy;
|
||||
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
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 java.util.Locale;
|
||||
|
||||
|
||||
public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeaders {
|
||||
private static final Headers.ValueConverter<CharSequence> SPDY_VALUE_CONVERTER =
|
||||
new DefaultTextValueTypeConverter() {
|
||||
@Override
|
||||
protected CharSequence convertName(CharSequence name) {
|
||||
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");
|
||||
}
|
||||
|
||||
public CharSequence convert(Object value) {
|
||||
CharSequence seq;
|
||||
if (value instanceof CharSequence) {
|
||||
seq = (CharSequence) value;
|
||||
@ -52,25 +37,109 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
|
||||
SpdyCodecUtil.validateHeaderValue(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
|
||||
public SpdyHeaders add(CharSequence name, Object value) {
|
||||
public SpdyHeaders add(CharSequence name, CharSequence value) {
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders add(CharSequence name, Iterable<?> values) {
|
||||
public SpdyHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders add(CharSequence name, Object... values) {
|
||||
public SpdyHeaders add(CharSequence name, CharSequence... values) {
|
||||
super.add(name, values);
|
||||
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
|
||||
public SpdyHeaders add(TextHeaders headers) {
|
||||
super.add(headers);
|
||||
@ -78,23 +147,89 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders set(CharSequence name, Object value) {
|
||||
public SpdyHeaders set(CharSequence name, CharSequence value) {
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders set(CharSequence name, Object... values) {
|
||||
public SpdyHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders set(CharSequence name, Iterable<?> values) {
|
||||
public SpdyHeaders set(CharSequence name, CharSequence... values) {
|
||||
super.set(name, values);
|
||||
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
|
||||
public SpdyHeaders set(TextHeaders headers) {
|
||||
super.set(headers);
|
||||
@ -102,14 +237,14 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders clear() {
|
||||
super.clear();
|
||||
public SpdyHeaders setAll(TextHeaders headers) {
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders forEachEntry(TextHeaderProcessor processor) {
|
||||
super.forEachEntry(processor);
|
||||
public SpdyHeaders clear() {
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ public class DefaultSpdyHeadersFrame extends DefaultSpdyStreamFrame
|
||||
}
|
||||
|
||||
protected void appendHeaders(StringBuilder buf) {
|
||||
for (Map.Entry<String, String> e: headers()) {
|
||||
for (Map.Entry<CharSequence, CharSequence> e: headers()) {
|
||||
buf.append(" ");
|
||||
buf.append(e.getKey());
|
||||
buf.append(": ");
|
||||
|
@ -15,14 +15,15 @@
|
||||
*/
|
||||
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.ByteBufAllocator;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
|
||||
|
||||
public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
|
||||
|
||||
private final int version;
|
||||
@ -44,7 +45,7 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
|
||||
|
||||
@Override
|
||||
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();
|
||||
if (numHeaders == 0) {
|
||||
return Unpooled.EMPTY_BUFFER;
|
||||
@ -55,15 +56,15 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
|
||||
}
|
||||
ByteBuf headerBlock = alloc.heapBuffer();
|
||||
writeLengthField(headerBlock, numHeaders);
|
||||
for (String name: names) {
|
||||
byte[] nameBytes = name.getBytes("UTF-8");
|
||||
for (CharSequence name: names) {
|
||||
byte[] nameBytes = AsciiString.getBytes(name, CharsetUtil.UTF_8);
|
||||
writeLengthField(headerBlock, nameBytes.length);
|
||||
headerBlock.writeBytes(nameBytes);
|
||||
int savedIndex = headerBlock.writerIndex();
|
||||
int valueLength = 0;
|
||||
writeLengthField(headerBlock, valueLength);
|
||||
for (String value: frame.headers().getAll(name)) {
|
||||
byte[] valueBytes = value.getBytes("UTF-8");
|
||||
for (CharSequence value: frame.headers().getAll(name)) {
|
||||
byte[] valueBytes = AsciiString.getBytes(value, CharsetUtil.UTF_8);
|
||||
if (valueBytes.length > 0) {
|
||||
headerBlock.writeBytes(valueBytes);
|
||||
headerBlock.writeByte(0);
|
||||
|
@ -16,7 +16,6 @@
|
||||
package io.netty.handler.codec.spdy;
|
||||
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
import io.netty.handler.codec.TextHeaderProcessor;
|
||||
import io.netty.handler.codec.TextHeaders;
|
||||
|
||||
/**
|
||||
@ -58,32 +57,98 @@ public interface SpdyHeaders extends TextHeaders {
|
||||
}
|
||||
|
||||
@Override
|
||||
SpdyHeaders add(CharSequence name, Object value);
|
||||
SpdyHeaders add(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders add(CharSequence name, Iterable<?> values);
|
||||
SpdyHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@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
|
||||
SpdyHeaders add(TextHeaders headers);
|
||||
|
||||
@Override
|
||||
SpdyHeaders set(CharSequence name, Object value);
|
||||
SpdyHeaders set(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders set(CharSequence name, Iterable<?> values);
|
||||
SpdyHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@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
|
||||
SpdyHeaders set(TextHeaders headers);
|
||||
|
||||
@Override
|
||||
SpdyHeaders clear();
|
||||
SpdyHeaders setAll(TextHeaders headers);
|
||||
|
||||
@Override
|
||||
SpdyHeaders forEachEntry(TextHeaderProcessor processor);
|
||||
SpdyHeaders clear();
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
|
||||
return;
|
||||
}
|
||||
|
||||
String URL = spdySynStreamFrame.headers().get(PATH);
|
||||
CharSequence URL = spdySynStreamFrame.headers().get(PATH);
|
||||
spdySynStreamFrame.headers().remove(PATH);
|
||||
|
||||
// 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);
|
||||
|
||||
// Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers
|
||||
httpResponseWithEntity.headers().set(Names.STREAM_ID, streamId);
|
||||
httpResponseWithEntity.headers().set(Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId);
|
||||
httpResponseWithEntity.headers().set(Names.PRIORITY, spdySynStreamFrame.priority());
|
||||
httpResponseWithEntity.headers().setInt(Names.STREAM_ID, streamId);
|
||||
httpResponseWithEntity.headers().setInt(Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId);
|
||||
httpResponseWithEntity.headers().setByte(Names.PRIORITY, spdySynStreamFrame.priority());
|
||||
httpResponseWithEntity.headers().set(Names.URL, URL);
|
||||
|
||||
if (spdySynStreamFrame.isLast()) {
|
||||
@ -197,8 +197,8 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
|
||||
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
|
||||
spdySynReplyFrame.setLast(true);
|
||||
SpdyHeaders frameHeaders = spdySynReplyFrame.headers();
|
||||
frameHeaders.set(STATUS, HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE);
|
||||
frameHeaders.set(VERSION, HttpVersion.HTTP_1_0);
|
||||
frameHeaders.setInt(STATUS, HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.code());
|
||||
frameHeaders.setObject(VERSION, HttpVersion.HTTP_1_0);
|
||||
ctx.writeAndFlush(spdySynReplyFrame);
|
||||
return;
|
||||
}
|
||||
@ -207,7 +207,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
|
||||
FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame);
|
||||
|
||||
// Set the Stream-ID as a header
|
||||
httpRequestWithEntity.headers().set(Names.STREAM_ID, streamId);
|
||||
httpRequestWithEntity.headers().setInt(Names.STREAM_ID, streamId);
|
||||
|
||||
if (spdySynStreamFrame.isLast()) {
|
||||
out.add(httpRequestWithEntity);
|
||||
@ -222,8 +222,8 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
|
||||
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
|
||||
spdySynReplyFrame.setLast(true);
|
||||
SpdyHeaders frameHeaders = spdySynReplyFrame.headers();
|
||||
frameHeaders.set(STATUS, HttpResponseStatus.BAD_REQUEST);
|
||||
frameHeaders.set(VERSION, HttpVersion.HTTP_1_0);
|
||||
frameHeaders.setInt(STATUS, HttpResponseStatus.BAD_REQUEST.code());
|
||||
frameHeaders.setObject(VERSION, HttpVersion.HTTP_1_0);
|
||||
ctx.writeAndFlush(spdySynReplyFrame);
|
||||
}
|
||||
}
|
||||
@ -246,7 +246,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
|
||||
FullHttpResponse httpResponseWithEntity = createHttpResponse(ctx, spdySynReplyFrame, validateHeaders);
|
||||
|
||||
// Set the Stream-ID as a header
|
||||
httpResponseWithEntity.headers().set(Names.STREAM_ID, streamId);
|
||||
httpResponseWithEntity.headers().setInt(Names.STREAM_ID, streamId);
|
||||
|
||||
if (spdySynReplyFrame.isLast()) {
|
||||
HttpHeaderUtil.setContentLength(httpResponseWithEntity, 0);
|
||||
@ -276,7 +276,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
|
||||
|
||||
// Ignore trailers in a truncated HEADERS frame.
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -327,9 +327,9 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
|
||||
throws Exception {
|
||||
// Create the first line of the request from the name/value pairs
|
||||
SpdyHeaders headers = requestFrame.headers();
|
||||
HttpMethod method = HttpMethod.valueOf(headers.get(METHOD));
|
||||
String url = headers.get(PATH);
|
||||
HttpVersion httpVersion = HttpVersion.valueOf(headers.get(VERSION));
|
||||
HttpMethod method = HttpMethod.valueOf(headers.getAndConvert(METHOD));
|
||||
String url = headers.getAndConvert(PATH);
|
||||
HttpVersion httpVersion = HttpVersion.valueOf(headers.getAndConvert(VERSION));
|
||||
headers.remove(METHOD);
|
||||
headers.remove(PATH);
|
||||
headers.remove(VERSION);
|
||||
@ -340,11 +340,11 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
|
||||
headers.remove(SCHEME);
|
||||
|
||||
// Replace the SPDY host header with the HTTP host header
|
||||
String host = headers.get(HOST);
|
||||
CharSequence host = headers.get(HOST);
|
||||
headers.remove(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());
|
||||
}
|
||||
|
||||
@ -363,12 +363,12 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
|
||||
// Create the first line of the response from the name/value pairs
|
||||
SpdyHeaders headers = responseFrame.headers();
|
||||
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(VERSION);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -185,7 +185,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
||||
} else {
|
||||
// Create SPDY HEADERS frame out of trailers
|
||||
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());
|
||||
}
|
||||
|
||||
@ -212,8 +212,8 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
||||
int streamID = httpHeaders.getInt(Names.STREAM_ID);
|
||||
int associatedToStreamId = httpHeaders.getInt(Names.ASSOCIATED_TO_STREAM_ID, 0);
|
||||
byte priority = (byte) httpHeaders.getInt(Names.PRIORITY, 0);
|
||||
String URL = httpHeaders.get(Names.URL);
|
||||
String scheme = httpHeaders.get(Names.SCHEME);
|
||||
CharSequence URL = httpHeaders.get(Names.URL);
|
||||
CharSequence scheme = httpHeaders.get(Names.SCHEME);
|
||||
httpHeaders.remove(Names.STREAM_ID);
|
||||
httpHeaders.remove(Names.ASSOCIATED_TO_STREAM_ID);
|
||||
httpHeaders.remove(Names.PRIORITY);
|
||||
@ -234,21 +234,21 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
||||
SpdyHeaders frameHeaders = spdySynStreamFrame.headers();
|
||||
if (httpMessage instanceof FullHttpRequest) {
|
||||
HttpRequest httpRequest = (HttpRequest) httpMessage;
|
||||
frameHeaders.set(METHOD, httpRequest.method());
|
||||
frameHeaders.setObject(METHOD, httpRequest.method());
|
||||
frameHeaders.set(PATH, httpRequest.uri());
|
||||
frameHeaders.set(VERSION, httpMessage.protocolVersion());
|
||||
frameHeaders.setObject(VERSION, httpMessage.protocolVersion());
|
||||
}
|
||||
if (httpMessage instanceof HttpResponse) {
|
||||
HttpResponse httpResponse = (HttpResponse) httpMessage;
|
||||
frameHeaders.set(STATUS, httpResponse.status());
|
||||
frameHeaders.setInt(STATUS, httpResponse.status().code());
|
||||
frameHeaders.set(PATH, URL);
|
||||
frameHeaders.set(VERSION, httpMessage.protocolVersion());
|
||||
frameHeaders.setObject(VERSION, httpMessage.protocolVersion());
|
||||
spdySynStreamFrame.setUnidirectional(true);
|
||||
}
|
||||
|
||||
// Replace the HTTP host header with the SPDY host header
|
||||
if (spdyVersion >= 3) {
|
||||
CharSequence host = httpHeaders.getUnconverted(HttpHeaders.Names.HOST);
|
||||
CharSequence host = httpHeaders.get(HttpHeaders.Names.HOST);
|
||||
httpHeaders.remove(HttpHeaders.Names.HOST);
|
||||
frameHeaders.set(HOST, host);
|
||||
}
|
||||
@ -260,7 +260,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
||||
frameHeaders.set(SCHEME, scheme);
|
||||
|
||||
// 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());
|
||||
}
|
||||
currentStreamId = spdySynStreamFrame.streamId();
|
||||
@ -286,11 +286,11 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
|
||||
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
|
||||
SpdyHeaders frameHeaders = spdySynReplyFrame.headers();
|
||||
// Unfold the first line of the response into name/value pairs
|
||||
frameHeaders.set(STATUS, httpResponse.status());
|
||||
frameHeaders.set(VERSION, httpResponse.protocolVersion());
|
||||
frameHeaders.setInt(STATUS, httpResponse.status().code());
|
||||
frameHeaders.setObject(VERSION, httpResponse.protocolVersion());
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ public class SpdyHttpResponseStreamIdHandler extends
|
||||
protected void encode(ChannelHandlerContext ctx, HttpMessage msg, List<Object> out) throws Exception {
|
||||
Integer id = ids.poll();
|
||||
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));
|
||||
|
@ -197,7 +197,7 @@ public class HttpContentCompressorTest {
|
||||
FullHttpResponse res = new DefaultFullHttpResponse(
|
||||
HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
|
||||
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);
|
||||
|
||||
assertEncodedResponse(ch);
|
||||
@ -309,8 +309,8 @@ public class HttpContentCompressorTest {
|
||||
|
||||
HttpResponse res = (HttpResponse) o;
|
||||
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_ENCODING), is("gzip"));
|
||||
assertThat(res.headers().getAndConvert(Names.CONTENT_ENCODING), is("gzip"));
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ public class HttpContentEncoderTest {
|
||||
|
||||
FullHttpResponse res = new DefaultFullHttpResponse(
|
||||
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);
|
||||
|
||||
assertEncodedResponse(ch);
|
||||
@ -260,8 +260,8 @@ public class HttpContentEncoderTest {
|
||||
|
||||
HttpResponse res = (HttpResponse) o;
|
||||
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_ENCODING), is("test"));
|
||||
assertThat(res.headers().getAndConvert(Names.CONTENT_ENCODING), is("test"));
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ public class HttpHeaderUtilTest {
|
||||
|
||||
assertEquals("1", headers.get("Foo"));
|
||||
|
||||
List<String> values = headers.getAll("Foo");
|
||||
List<CharSequence> values = headers.getAll("Foo");
|
||||
assertEquals(2, values.size());
|
||||
assertEquals("1", values.get(0));
|
||||
assertEquals("2", values.get(1));
|
||||
|
@ -42,7 +42,7 @@ public class HttpObjectAggregatorTest {
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(aggr);
|
||||
|
||||
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 chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
|
||||
HttpContent chunk3 = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER);
|
||||
@ -80,12 +80,12 @@ public class HttpObjectAggregatorTest {
|
||||
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(aggr);
|
||||
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);
|
||||
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
|
||||
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
|
||||
LastHttpContent trailer = new DefaultLastHttpContent();
|
||||
trailer.trailingHeaders().set("X-Trailer", true);
|
||||
trailer.trailingHeaders().setObject("X-Trailer", true);
|
||||
|
||||
assertFalse(embedder.writeInbound(message));
|
||||
assertFalse(embedder.writeInbound(chunk1));
|
||||
@ -281,7 +281,7 @@ public class HttpObjectAggregatorTest {
|
||||
EmbeddedChannel embedder = new EmbeddedChannel(aggr);
|
||||
|
||||
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");
|
||||
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
|
||||
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
|
||||
|
@ -98,7 +98,7 @@ public class HttpRequestDecoderTest {
|
||||
}
|
||||
|
||||
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(value, header1.get(0));
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ public class HttpResponseDecoderTest {
|
||||
HttpResponse res = ch.readInbound();
|
||||
assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
|
||||
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()));
|
||||
|
||||
// Close the connection without sending anything.
|
||||
@ -205,7 +205,7 @@ public class HttpResponseDecoderTest {
|
||||
HttpResponse res = ch.readInbound();
|
||||
assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
|
||||
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.
|
||||
HttpContent content = ch.readInbound();
|
||||
@ -275,7 +275,7 @@ public class HttpResponseDecoderTest {
|
||||
HttpResponse res = ch.readInbound();
|
||||
assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
|
||||
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()));
|
||||
|
||||
ch.writeInbound(Unpooled.wrappedBuffer(new byte[1024]));
|
||||
@ -313,7 +313,7 @@ public class HttpResponseDecoderTest {
|
||||
assertThat(lastContent.content().isReadable(), is(false));
|
||||
HttpHeaders headers = lastContent.trailingHeaders();
|
||||
assertEquals(1, headers.names().size());
|
||||
List<String> values = headers.getAll("Set-Cookie");
|
||||
List<CharSequence> values = headers.getAll("Set-Cookie");
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.contains("t1=t1v1"));
|
||||
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));
|
||||
HttpHeaders headers = lastContent.trailingHeaders();
|
||||
assertEquals(1, headers.names().size());
|
||||
List<String> values = headers.getAll("Set-Cookie");
|
||||
List<CharSequence> values = headers.getAll("Set-Cookie");
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.contains("t1=t1v1"));
|
||||
assertTrue(values.contains("t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT"));
|
||||
|
@ -39,7 +39,8 @@ public class HttpResponseEncoderTest {
|
||||
|
||||
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();
|
||||
assertTrue(channel.writeOutbound(FILE_REGION));
|
||||
buffer = channel.readOutbound();
|
||||
|
@ -92,7 +92,7 @@ public class HttpServerCodecTest {
|
||||
|
||||
// Ensure the aggregator generates a full request.
|
||||
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().readByte(), is((byte) 42));
|
||||
req.release();
|
||||
@ -103,12 +103,13 @@ public class HttpServerCodecTest {
|
||||
// Send the actual response.
|
||||
FullHttpResponse res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CREATED);
|
||||
res.content().writeBytes("OK".getBytes(CharsetUtil.UTF_8));
|
||||
res.headers().set(CONTENT_LENGTH, 2);
|
||||
res.headers().setInt(CONTENT_LENGTH, 2);
|
||||
ch.writeOutbound(res);
|
||||
|
||||
// Ensure the encoder handles the response after handling 100 Continue.
|
||||
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();
|
||||
|
||||
ch.finish();
|
||||
|
@ -95,20 +95,20 @@ public class CorsConfigTest {
|
||||
@Test
|
||||
public void preflightResponseHeadersSingleValue() {
|
||||
final CorsConfig cors = withAnyOrigin().preflightResponseHeader("SingleValue", "value").build();
|
||||
assertThat(cors.preflightResponseHeaders().get("SingleValue"), equalTo("value"));
|
||||
assertThat(cors.preflightResponseHeaders().getAndConvert("SingleValue"), equalTo("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preflightResponseHeadersMultipleValues() {
|
||||
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
|
||||
public void defaultPreflightResponseHeaders() {
|
||||
final CorsConfig cors = withAnyOrigin().build();
|
||||
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
|
||||
|
@ -47,14 +47,14 @@ public class CorsHandlerTest {
|
||||
@Test
|
||||
public void simpleRequestWithAnyOrigin() {
|
||||
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
|
||||
public void simpleRequestWithOrigin() {
|
||||
final String origin = "http://localhost:8888";
|
||||
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
|
||||
@ -63,9 +63,9 @@ public class CorsHandlerTest {
|
||||
final String origin2 = "https://localhost:8888";
|
||||
final String[] origins = {origin1, origin2};
|
||||
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);
|
||||
assertThat(response2.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin2));
|
||||
assertThat(response2.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin2));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -81,9 +81,9 @@ public class CorsHandlerTest {
|
||||
.allowedRequestMethods(GET, DELETE)
|
||||
.build();
|
||||
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().getAll(ACCESS_CONTROL_ALLOW_METHODS), hasItems("GET", "DELETE"));
|
||||
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
|
||||
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888"));
|
||||
assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_ALLOW_METHODS), hasItems("GET", "DELETE"));
|
||||
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -93,19 +93,20 @@ public class CorsHandlerTest {
|
||||
.allowedRequestHeaders("content-type", "xheader1")
|
||||
.build();
|
||||
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().getAll(ACCESS_CONTROL_ALLOW_METHODS), hasItems("OPTIONS", "GET"));
|
||||
assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_HEADERS), hasItems("content-type", "xheader1"));
|
||||
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
|
||||
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888"));
|
||||
assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_ALLOW_METHODS), hasItems("OPTIONS", "GET"));
|
||||
assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_ALLOW_HEADERS),
|
||||
hasItems("content-type", "xheader1"));
|
||||
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preflightRequestWithDefaultHeaders() {
|
||||
final CorsConfig config = CorsConfig.withOrigin("http://localhost:8888").build();
|
||||
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(VARY), equalTo(ORIGIN.toString()));
|
||||
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -114,8 +115,8 @@ public class CorsHandlerTest {
|
||||
.preflightResponseHeader("CustomHeader", "somevalue")
|
||||
.build();
|
||||
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
|
||||
assertThat(response.headers().get("CustomHeader"), equalTo("somevalue"));
|
||||
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
|
||||
assertThat(response.headers().getAndConvert("CustomHeader"), equalTo("somevalue"));
|
||||
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -124,8 +125,8 @@ public class CorsHandlerTest {
|
||||
.preflightResponseHeader("CustomHeader", "value1", "value2")
|
||||
.build();
|
||||
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
|
||||
assertThat(response.headers().getAll("CustomHeader"), hasItems("value1", "value2"));
|
||||
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
|
||||
assertThat(response.headers().getAllAndConvert("CustomHeader"), hasItems("value1", "value2"));
|
||||
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -134,8 +135,8 @@ public class CorsHandlerTest {
|
||||
.preflightResponseHeader("CustomHeader", Arrays.asList("value1", "value2"))
|
||||
.build();
|
||||
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
|
||||
assertThat(response.headers().getAll("CustomHeader"), hasItems("value1", "value2"));
|
||||
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
|
||||
assertThat(response.headers().getAllAndConvert("CustomHeader"), hasItems("value1", "value2"));
|
||||
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -148,8 +149,8 @@ public class CorsHandlerTest {
|
||||
}
|
||||
}).build();
|
||||
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
|
||||
assertThat(response.headers().get("GenHeader"), equalTo("generatedValue"));
|
||||
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
|
||||
assertThat(response.headers().getAndConvert("GenHeader"), equalTo("generatedValue"));
|
||||
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -157,7 +158,7 @@ public class CorsHandlerTest {
|
||||
final String origin = "null";
|
||||
final CorsConfig config = CorsConfig.withOrigin(origin).allowNullOrigin().build();
|
||||
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
|
||||
@ -165,7 +166,7 @@ public class CorsHandlerTest {
|
||||
final String origin = "null";
|
||||
final CorsConfig config = CorsConfig.withOrigin(origin).allowCredentials().build();
|
||||
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
|
||||
@ -180,15 +181,15 @@ public class CorsHandlerTest {
|
||||
public void simpleRequestCustomHeaders() {
|
||||
final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("custom1", "custom2").build();
|
||||
final HttpResponse response = simpleRequest(config, "http://localhost:7777");
|
||||
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("*"));
|
||||
assertThat(response.headers().getAll(ACCESS_CONTROL_EXPOSE_HEADERS), hasItems("custom1", "custom1"));
|
||||
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("*"));
|
||||
assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_EXPOSE_HEADERS), hasItems("custom1", "custom1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleRequestAllowCredentials() {
|
||||
final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build();
|
||||
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
|
||||
@ -202,16 +203,16 @@ public class CorsHandlerTest {
|
||||
public void anyOriginAndAllowCredentialsShouldEchoRequestOrigin() {
|
||||
final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build();
|
||||
final HttpResponse response = simpleRequest(config, "http://localhost:7777");
|
||||
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true"));
|
||||
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777"));
|
||||
assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
|
||||
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true"));
|
||||
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777"));
|
||||
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleRequestExposeHeaders() {
|
||||
final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("one", "two").build();
|
||||
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
|
||||
|
@ -18,11 +18,13 @@ package io.netty.handler.codec.http.multipart;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
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.HttpVersion;
|
||||
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder.EncoderMode;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
@ -47,15 +49,15 @@ public class HttpPostRequestEncoderTest {
|
||||
String content = getRequestBody(encoder);
|
||||
|
||||
String expected = "--" + multipartDataBoundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"foo\"" + "\r\n" +
|
||||
"Content-Type: text/plain; charset=UTF-8" + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" +
|
||||
"\r\n" +
|
||||
"bar" +
|
||||
"\r\n" +
|
||||
"--" + multipartDataBoundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" +
|
||||
"Content-Type: text/plain" + "\r\n" +
|
||||
"Content-Transfer-Encoding: binary" + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain" + "\r\n" +
|
||||
CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
|
||||
"\r\n" +
|
||||
"File 01" + StringUtil.NEWLINE +
|
||||
"\r\n" +
|
||||
@ -83,25 +85,25 @@ public class HttpPostRequestEncoderTest {
|
||||
String content = getRequestBody(encoder);
|
||||
|
||||
String expected = "--" + multipartDataBoundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"foo\"" + "\r\n" +
|
||||
"Content-Type: text/plain; charset=UTF-8" + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" +
|
||||
"\r\n" +
|
||||
"bar" + "\r\n" +
|
||||
"--" + multipartDataBoundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"quux\"" + "\r\n" +
|
||||
"Content-Type: multipart/mixed; boundary=" + multipartMixedBoundary + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"quux\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": multipart/mixed; boundary=" + multipartMixedBoundary + "\r\n" +
|
||||
"\r\n" +
|
||||
"--" + multipartMixedBoundary + "\r\n" +
|
||||
"Content-Disposition: attachment; filename=\"file-02.txt\"" + "\r\n" +
|
||||
"Content-Type: text/plain" + "\r\n" +
|
||||
"Content-Transfer-Encoding: binary" + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": attachment; filename=\"file-02.txt\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain" + "\r\n" +
|
||||
CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
|
||||
"\r\n" +
|
||||
"File 01" + StringUtil.NEWLINE +
|
||||
"\r\n" +
|
||||
"--" + multipartMixedBoundary + "\r\n" +
|
||||
"Content-Disposition: attachment; filename=\"file-02.txt\"" + "\r\n" +
|
||||
"Content-Type: text/plain" + "\r\n" +
|
||||
"Content-Transfer-Encoding: binary" + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": attachment; filename=\"file-02.txt\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain" + "\r\n" +
|
||||
CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
|
||||
"\r\n" +
|
||||
"File 02" + StringUtil.NEWLINE +
|
||||
"\r\n" +
|
||||
@ -130,20 +132,20 @@ public class HttpPostRequestEncoderTest {
|
||||
String content = getRequestBody(encoder);
|
||||
|
||||
String expected = "--" + multipartDataBoundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"foo\"" + "\r\n" +
|
||||
"Content-Type: text/plain; charset=UTF-8" + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" +
|
||||
"\r\n" +
|
||||
"bar" + "\r\n" +
|
||||
"--" + multipartDataBoundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" +
|
||||
"Content-Type: text/plain" + "\r\n" +
|
||||
"Content-Transfer-Encoding: binary" + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain" + "\r\n" +
|
||||
CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
|
||||
"\r\n" +
|
||||
"File 01" + StringUtil.NEWLINE + "\r\n" +
|
||||
"--" + multipartDataBoundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"quux\"; filename=\"file-02.txt\"" + "\r\n" +
|
||||
"Content-Type: text/plain" + "\r\n" +
|
||||
"Content-Transfer-Encoding: binary" + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-02.txt\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain" + "\r\n" +
|
||||
CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
|
||||
"\r\n" +
|
||||
"File 02" + StringUtil.NEWLINE +
|
||||
"\r\n" +
|
||||
@ -169,15 +171,15 @@ public class HttpPostRequestEncoderTest {
|
||||
String content = getRequestBody(encoder);
|
||||
|
||||
String expected = "--" + multipartDataBoundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"foo\"" + "\r\n" +
|
||||
"Content-Type: text/plain; charset=UTF-8" + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" +
|
||||
"\r\n" +
|
||||
"bar" +
|
||||
"\r\n" +
|
||||
"--" + multipartDataBoundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" +
|
||||
"Content-Type: text/plain" + "\r\n" +
|
||||
"Content-Transfer-Encoding: binary" + "\r\n" +
|
||||
CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" +
|
||||
CONTENT_TYPE + ": text/plain" + "\r\n" +
|
||||
CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" +
|
||||
"\r\n" +
|
||||
"File 01" + StringUtil.NEWLINE +
|
||||
"\r\n" +
|
||||
|
@ -69,14 +69,14 @@ public class WebSocketClientExtensionHandlerTest {
|
||||
|
||||
HttpRequest req2 = ch.readOutbound();
|
||||
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
|
||||
req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
|
||||
HttpResponse res = newUpgradeResponse("main");
|
||||
ch.writeInbound(res);
|
||||
|
||||
HttpResponse res2 = ch.readInbound();
|
||||
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
|
||||
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
|
||||
// test
|
||||
assertEquals(2, reqExts.size());
|
||||
@ -119,14 +119,14 @@ public class WebSocketClientExtensionHandlerTest {
|
||||
|
||||
HttpRequest req2 = ch.readOutbound();
|
||||
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
|
||||
req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
|
||||
HttpResponse res = newUpgradeResponse("fallback");
|
||||
ch.writeInbound(res);
|
||||
|
||||
HttpResponse res2 = ch.readInbound();
|
||||
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
|
||||
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
|
||||
// test
|
||||
assertEquals(2, reqExts.size());
|
||||
@ -182,14 +182,14 @@ public class WebSocketClientExtensionHandlerTest {
|
||||
|
||||
HttpRequest req2 = ch.readOutbound();
|
||||
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
|
||||
req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
|
||||
HttpResponse res = newUpgradeResponse("main, fallback");
|
||||
ch.writeInbound(res);
|
||||
|
||||
HttpResponse res2 = ch.readInbound();
|
||||
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
|
||||
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
|
||||
// test
|
||||
assertEquals(2, reqExts.size());
|
||||
@ -239,7 +239,7 @@ public class WebSocketClientExtensionHandlerTest {
|
||||
|
||||
HttpRequest req2 = ch.readOutbound();
|
||||
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
|
||||
req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
|
||||
HttpResponse res = newUpgradeResponse("main, fallback");
|
||||
ch.writeInbound(res);
|
||||
|
@ -79,7 +79,7 @@ public class WebSocketServerExtensionHandlerTest {
|
||||
|
||||
HttpResponse res2 = ch.readOutbound();
|
||||
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
|
||||
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
|
||||
// test
|
||||
assertEquals(1, resExts.size());
|
||||
@ -130,7 +130,7 @@ public class WebSocketServerExtensionHandlerTest {
|
||||
|
||||
HttpResponse res2 = ch.readOutbound();
|
||||
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
|
||||
res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS));
|
||||
|
||||
// test
|
||||
assertEquals(2, resExts.size());
|
||||
|
@ -45,7 +45,7 @@ public class WebSocketServerCompressionHandlerTest {
|
||||
|
||||
HttpResponse res2 = ch.readOutbound();
|
||||
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.assertTrue(exts.get(0).parameters().isEmpty());
|
||||
@ -66,7 +66,7 @@ public class WebSocketServerCompressionHandlerTest {
|
||||
|
||||
HttpResponse res2 = ch.readOutbound();
|
||||
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("10", exts.get(0).parameters().get(CLIENT_MAX_WINDOW));
|
||||
@ -87,7 +87,7 @@ public class WebSocketServerCompressionHandlerTest {
|
||||
|
||||
HttpResponse res2 = ch.readOutbound();
|
||||
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.assertTrue(exts.get(0).parameters().isEmpty());
|
||||
@ -108,7 +108,7 @@ public class WebSocketServerCompressionHandlerTest {
|
||||
|
||||
HttpResponse res2 = ch.readOutbound();
|
||||
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("10", exts.get(0).parameters().get(SERVER_MAX_WINDOW));
|
||||
@ -163,7 +163,7 @@ public class WebSocketServerCompressionHandlerTest {
|
||||
|
||||
HttpResponse res2 = ch.readOutbound();
|
||||
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.assertTrue(exts.get(0).parameters().isEmpty());
|
||||
@ -185,7 +185,7 @@ public class WebSocketServerCompressionHandlerTest {
|
||||
|
||||
HttpResponse res2 = ch.readOutbound();
|
||||
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.assertTrue(exts.get(0).parameters().isEmpty());
|
||||
|
@ -81,9 +81,9 @@ public class SpdySessionHandlerTest {
|
||||
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
|
||||
assertEquals(streamId, spdyHeadersFrame.streamId());
|
||||
assertEquals(last, spdyHeadersFrame.isLast());
|
||||
for (String name: headers.names()) {
|
||||
List<String> expectedValues = headers.getAll(name);
|
||||
List<String> receivedValues = spdyHeadersFrame.headers().getAll(name);
|
||||
for (CharSequence name: headers.names()) {
|
||||
List<CharSequence> expectedValues = headers.getAll(name);
|
||||
List<CharSequence> receivedValues = spdyHeadersFrame.headers().getAll(name);
|
||||
assertTrue(receivedValues.containsAll(expectedValues));
|
||||
receivedValues.removeAll(expectedValues);
|
||||
assertTrue(receivedValues.isEmpty());
|
||||
@ -357,7 +357,7 @@ public class SpdySessionHandlerTest {
|
||||
int streamId = spdySynStreamFrame.streamId();
|
||||
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -19,10 +19,6 @@ import io.netty.handler.codec.BinaryHeaders;
|
||||
import io.netty.handler.codec.DefaultBinaryHeaders;
|
||||
|
||||
public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2Headers {
|
||||
|
||||
public DefaultHttp2Headers() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers add(AsciiString name, AsciiString value) {
|
||||
super.add(name, value);
|
||||
@ -30,7 +26,7 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers add(AsciiString name, Iterable<AsciiString> values) {
|
||||
public Http2Headers add(AsciiString name, Iterable<? extends AsciiString> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
@ -41,6 +37,72 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
|
||||
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
|
||||
public Http2Headers add(BinaryHeaders headers) {
|
||||
super.add(headers);
|
||||
@ -54,7 +116,7 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers set(AsciiString name, Iterable<AsciiString> values) {
|
||||
public Http2Headers set(AsciiString name, Iterable<? extends AsciiString> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
@ -65,6 +127,72 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
|
||||
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
|
||||
public Http2Headers set(BinaryHeaders headers) {
|
||||
super.set(headers);
|
||||
@ -83,26 +211,6 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
|
||||
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
|
||||
public Http2Headers method(AsciiString value) {
|
||||
set(PseudoHeaderName.METHOD.value(), value);
|
||||
|
@ -20,12 +20,13 @@ import static io.netty.handler.codec.http2.Http2Exception.protocolError;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
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.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@ -71,18 +72,21 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea
|
||||
encodeHeader(name, value, stream);
|
||||
}
|
||||
}
|
||||
headers.forEachEntry(new BinaryHeaders.BinaryHeaderVisitor() {
|
||||
|
||||
headers.forEachEntry(new EntryVisitor() {
|
||||
@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)) {
|
||||
encodeHeader(name, value, stream);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw Http2Exception.format(Http2Error.COMPRESSION_ERROR,
|
||||
"Failed encoding headers block: %s", e.getMessage());
|
||||
} catch (Exception e) {
|
||||
throw Http2Exception.format(Http2Error.COMPRESSION_ERROR, "Failed encoding headers block: %s",
|
||||
e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
stream.close();
|
||||
|
@ -35,8 +35,6 @@ import io.netty.handler.codec.compression.ZlibWrapper;
|
||||
* stream.
|
||||
*/
|
||||
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() {
|
||||
@Override
|
||||
public void streamRemoved(Http2Stream stream) {
|
||||
@ -171,7 +169,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
|
||||
if (decompressor == null) {
|
||||
if (!endOfStream) {
|
||||
// Determine the content encoding.
|
||||
AsciiString contentEncoding = headers.get(CONTENT_ENCODING_LOWER_CASE);
|
||||
AsciiString contentEncoding = headers.get(CONTENT_ENCODING);
|
||||
if (contentEncoding == null) {
|
||||
contentEncoding = IDENTITY;
|
||||
}
|
||||
@ -182,9 +180,9 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
|
||||
// so that the message looks like a decoded message.
|
||||
AsciiString targetContentEncoding = getTargetContentEncoding(contentEncoding);
|
||||
if (IDENTITY.equalsIgnoreCase(targetContentEncoding)) {
|
||||
headers.remove(CONTENT_ENCODING_LOWER_CASE);
|
||||
headers.remove(CONTENT_ENCODING);
|
||||
} 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
|
||||
// this content-length will not be correct. Instead of queuing messages or delaying sending
|
||||
// header frames...just remove the content-length header
|
||||
headers.remove(CONTENT_LENGTH_LOWER_CASE);
|
||||
headers.remove(CONTENT_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,67 +26,194 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers add(AsciiString name, AsciiString value) {
|
||||
public Http2Headers add(AsciiString name, AsciiString value) {
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers add(AsciiString name, Iterable<AsciiString> values) {
|
||||
public Http2Headers add(AsciiString name, Iterable<? extends AsciiString> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers add(AsciiString name, AsciiString... values) {
|
||||
public Http2Headers add(AsciiString name, AsciiString... values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@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);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers set(AsciiString name, AsciiString value) {
|
||||
public Http2Headers set(AsciiString name, AsciiString value) {
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers set(AsciiString name, Iterable<AsciiString> values) {
|
||||
public Http2Headers set(AsciiString name, Iterable<? extends AsciiString> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers set(AsciiString name, AsciiString... values) {
|
||||
public Http2Headers set(AsciiString name, AsciiString... values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@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);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers setAll(BinaryHeaders headers) {
|
||||
public Http2Headers setAll(BinaryHeaders headers) {
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers clear() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers forEachEntry(BinaryHeaderVisitor visitor) {
|
||||
super.forEachEntry(visitor);
|
||||
public Http2Headers clear() {
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -84,11 +84,44 @@ public interface Http2Headers extends BinaryHeaders {
|
||||
Http2Headers add(AsciiString name, AsciiString value);
|
||||
|
||||
@Override
|
||||
Http2Headers add(AsciiString name, Iterable<AsciiString> values);
|
||||
Http2Headers add(AsciiString name, Iterable<? extends AsciiString> values);
|
||||
|
||||
@Override
|
||||
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
|
||||
Http2Headers add(BinaryHeaders headers);
|
||||
|
||||
@ -96,11 +129,44 @@ public interface Http2Headers extends BinaryHeaders {
|
||||
Http2Headers set(AsciiString name, AsciiString value);
|
||||
|
||||
@Override
|
||||
Http2Headers set(AsciiString name, Iterable<AsciiString> values);
|
||||
Http2Headers set(AsciiString name, Iterable<? extends AsciiString> values);
|
||||
|
||||
@Override
|
||||
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
|
||||
Http2Headers set(BinaryHeaders headers);
|
||||
|
||||
@ -110,9 +176,6 @@ public interface Http2Headers extends BinaryHeaders {
|
||||
@Override
|
||||
Http2Headers clear();
|
||||
|
||||
@Override
|
||||
Http2Headers forEachEntry(BinaryHeaderVisitor visitor);
|
||||
|
||||
/**
|
||||
* Sets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header
|
||||
*/
|
||||
|
@ -25,6 +25,7 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
import io.netty.handler.codec.base64.Base64;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
@ -86,8 +87,7 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade
|
||||
try {
|
||||
// Decode the HTTP2-Settings header and set the settings on the handler to make
|
||||
// sure everything is fine with the request.
|
||||
String settingsHeader = upgradeRequest.headers().get(HTTP_UPGRADE_SETTINGS_HEADER);
|
||||
settings = decodeSettingsHeader(ctx, settingsHeader);
|
||||
settings = decodeSettingsHeader(ctx, upgradeRequest.headers().get(HTTP_UPGRADE_SETTINGS_HEADER));
|
||||
connectionHandler.onHttpServerUpgrade(settings);
|
||||
// Everything looks good, no need to modify the response.
|
||||
} catch (Throwable e) {
|
||||
@ -108,9 +108,9 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade
|
||||
/**
|
||||
* 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 {
|
||||
ByteBuf header = Unpooled.wrappedBuffer(settingsHeader.getBytes(CharsetUtil.UTF_8));
|
||||
ByteBuf header = Unpooled.wrappedBuffer(AsciiString.getBytes(settingsHeader, CharsetUtil.UTF_8));
|
||||
try {
|
||||
// Decode the SETTINGS payload.
|
||||
ByteBuf payload = Base64.decode(header, URL_SAFE);
|
||||
|
@ -51,23 +51,10 @@ public class Http2ToHttpConnectionHandler extends Http2ConnectionHandler {
|
||||
*
|
||||
* @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
|
||||
* @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 {
|
||||
int streamId = 0;
|
||||
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;
|
||||
private int getStreamId(HttpHeaders httpHeaders) throws Exception {
|
||||
return httpHeaders.getInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), connection().local().nextStreamId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,7 +70,7 @@ public class Http2ToHttpConnectionHandler extends Http2ConnectionHandler {
|
||||
int streamId = 0;
|
||||
try {
|
||||
streamId = getStreamId(httpMsg.headers());
|
||||
} catch (Http2Exception e) {
|
||||
} catch (Exception e) {
|
||||
httpMsg.release();
|
||||
promise.setFailure(e);
|
||||
return;
|
||||
@ -91,16 +78,17 @@ public class Http2ToHttpConnectionHandler extends Http2ConnectionHandler {
|
||||
|
||||
// Convert and write the headers.
|
||||
Http2Headers http2Headers = HttpUtil.toHttp2Headers(httpMsg);
|
||||
Http2ConnectionEncoder encoder = encoder();
|
||||
|
||||
if (hasData) {
|
||||
ChannelPromiseAggregator promiseAggregator = new ChannelPromiseAggregator(promise);
|
||||
ChannelPromise headerPromise = ctx.newPromise();
|
||||
ChannelPromise dataPromise = ctx.newPromise();
|
||||
promiseAggregator.add(headerPromise, dataPromise);
|
||||
encoder().writeHeaders(ctx, streamId, http2Headers, 0, false, headerPromise);
|
||||
encoder().writeData(ctx, streamId, httpMsg.content(), 0, true, dataPromise);
|
||||
encoder.writeHeaders(ctx, streamId, http2Headers, 0, false, headerPromise);
|
||||
encoder.writeData(ctx, streamId, httpMsg.content(), 0, true, dataPromise);
|
||||
} else {
|
||||
encoder().writeHeaders(ctx, streamId, http2Headers, 0, true, promise);
|
||||
encoder.writeHeaders(ctx, streamId, http2Headers, 0, true, promise);
|
||||
}
|
||||
} else {
|
||||
ctx.write(msg, promise);
|
||||
|
@ -16,7 +16,7 @@ package io.netty.handler.codec.http2;
|
||||
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
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.DefaultFullHttpResponse;
|
||||
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.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides utility methods and constants for the HTTP/2 to HTTP conversion
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public final class HttpUtil {
|
||||
/**
|
||||
* 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>();
|
||||
static {
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.CONNECTION.toLowerCase());
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.KEEP_ALIVE.toLowerCase());
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.PROXY_CONNECTION.toLowerCase());
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.TRANSFER_ENCODING.toLowerCase());
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.HOST.toLowerCase());
|
||||
// These are already defined as lower-case.
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.STREAM_ID.text());
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.AUTHORITY.text());
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.SCHEME.text());
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.PATH.text());
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final Set<CharSequence> HTTP_TO_HTTP2_HEADER_BLACKLIST = new HashSet<CharSequence>() {
|
||||
private static final long serialVersionUID = -5678614530214167043L;
|
||||
{
|
||||
add(HttpHeaders.Names.CONNECTION);
|
||||
add(HttpHeaders.Names.KEEP_ALIVE);
|
||||
add(HttpHeaders.Names.PROXY_CONNECTION);
|
||||
add(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
add(HttpHeaders.Names.HOST);
|
||||
add(ExtensionHeaderNames.STREAM_ID.text());
|
||||
add(ExtensionHeaderNames.AUTHORITY.text());
|
||||
add(ExtensionHeaderNames.SCHEME.text());
|
||||
add(ExtensionHeaderNames.PATH.text());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This will be the method used for {@link HttpRequest} objects generated
|
||||
* out of the HTTP message flow defined in
|
||||
* <a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.">HTTP/2 Spec Message Flow</a>
|
||||
* This will be the method used for {@link HttpRequest} objects generated out of the HTTP message flow defined in <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;
|
||||
|
||||
/**
|
||||
* This will be the path used for {@link HttpRequest} objects generated
|
||||
* out of the HTTP message flow defined in
|
||||
* <a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.">HTTP/2 Spec Message Flow</a>
|
||||
* This will be the path used for {@link HttpRequest} objects generated out of the HTTP message flow defined in <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 = "";
|
||||
|
||||
/**
|
||||
* This will be the status code used for {@link HttpResponse} objects generated
|
||||
* out of the HTTP message flow defined in
|
||||
* <a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.">HTTP/2 Spec Message Flow</a>
|
||||
* This will be the status code used for {@link HttpResponse} objects generated out of the HTTP message flow defined
|
||||
* in <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;
|
||||
|
||||
private HttpUtil() { }
|
||||
private HttpUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the HTTP header extensions used to carry HTTP/2 information in HTTP objects
|
||||
*/
|
||||
public enum ExtensionHeaderNames {
|
||||
/**
|
||||
* HTTP extension header which will identify the stream id from the HTTP/2 event(s)
|
||||
* responsible for generating a {@code HttpObject}
|
||||
* HTTP extension header which will identify the stream id from the HTTP/2 event(s) responsible for generating a
|
||||
* {@code HttpObject}
|
||||
* <p>
|
||||
* {@code "x-http2-stream-id"}
|
||||
*/
|
||||
STREAM_ID("x-http2-stream-id"),
|
||||
|
||||
/**
|
||||
* HTTP extension header which will identify the authority pseudo header from the HTTP/2
|
||||
* event(s) responsible for generating a {@code HttpObject}
|
||||
* HTTP extension header which will identify the authority pseudo header from the HTTP/2 event(s) responsible
|
||||
* for generating a {@code HttpObject}
|
||||
* <p>
|
||||
* {@code "x-http2-authority"}
|
||||
*/
|
||||
AUTHORITY("x-http2-authority"),
|
||||
/**
|
||||
* HTTP extension header which will identify the scheme pseudo header from the HTTP/2
|
||||
* event(s) responsible for generating a {@code HttpObject}
|
||||
* HTTP extension header which will identify the scheme pseudo header from the HTTP/2 event(s) responsible for
|
||||
* generating a {@code HttpObject}
|
||||
* <p>
|
||||
* {@code "x-http2-scheme"}
|
||||
*/
|
||||
SCHEME("x-http2-scheme"),
|
||||
/**
|
||||
* HTTP extension header which will identify the path pseudo header from the HTTP/2 event(s)
|
||||
* responsible for generating a {@code HttpObject}
|
||||
* HTTP extension header which will identify the path pseudo header from the HTTP/2 event(s) responsible for
|
||||
* generating a {@code HttpObject}
|
||||
* <p>
|
||||
* {@code "x-http2-path"}
|
||||
*/
|
||||
PATH("x-http2-path"),
|
||||
/**
|
||||
* HTTP extension header which will identify the stream id used to create this stream in a
|
||||
* HTTP/2 push promise frame
|
||||
* HTTP extension header which will identify the stream id used to create this stream in a HTTP/2 push promise
|
||||
* frame
|
||||
* <p>
|
||||
* {@code "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
|
||||
* on. This stream will be a child node of the stream id associated with this header value.
|
||||
* HTTP extension header which will identify the stream id which this stream is dependent on. This stream will
|
||||
* be a child node of the stream id associated with this header value.
|
||||
* <p>
|
||||
* {@code "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
|
||||
* not on the default stream) of the associated HTTP/2 stream responsible responsible for
|
||||
* generating a {@code HttpObject}
|
||||
* HTTP extension header which will identify the weight (if non-default and the priority is not on the default
|
||||
* stream) of the associated HTTP/2 stream responsible responsible for generating a {@code HttpObject}
|
||||
* <p>
|
||||
* {@code "x-http2-stream-weight"}
|
||||
*/
|
||||
@ -176,21 +176,19 @@ public final class HttpUtil {
|
||||
*
|
||||
* @param streamId The stream associated with the response
|
||||
* @param http2Headers The initial set of HTTP/2 headers to create the response with
|
||||
* @param validateHttpHeaders
|
||||
* <ul>
|
||||
* @param validateHttpHeaders <ul>
|
||||
* <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>
|
||||
* </ul>
|
||||
* @return A new response object which represents headers/data
|
||||
* @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, Map)}
|
||||
*/
|
||||
public static FullHttpResponse toHttpResponse(int streamId, Http2Headers http2Headers,
|
||||
boolean validateHttpHeaders) throws Http2Exception {
|
||||
public static FullHttpResponse toHttpResponse(int streamId, Http2Headers http2Headers, boolean validateHttpHeaders)
|
||||
throws Http2Exception {
|
||||
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/1.1 status line.
|
||||
FullHttpResponse msg =
|
||||
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, validateHttpHeaders);
|
||||
FullHttpResponse msg = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, validateHttpHeaders);
|
||||
addHttp2ToHttpHeaders(streamId, http2Headers, msg, false);
|
||||
return msg;
|
||||
}
|
||||
@ -200,8 +198,7 @@ public final class HttpUtil {
|
||||
*
|
||||
* @param streamId The stream associated with the request
|
||||
* @param http2Headers The initial set of HTTP/2 headers to create the request with
|
||||
* @param validateHttpHeaders
|
||||
* <ul>
|
||||
* @param validateHttpHeaders <ul>
|
||||
* <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>
|
||||
* </ul>
|
||||
@ -212,9 +209,8 @@ public final class HttpUtil {
|
||||
throws Http2Exception {
|
||||
// HTTP/2 does not define a way to carry the version identifier that is
|
||||
// included in the HTTP/1.1 request line.
|
||||
FullHttpRequest msg =
|
||||
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(http2Headers
|
||||
.method().toString()), http2Headers.path().toString(), validateHttpHeaders);
|
||||
FullHttpRequest msg = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(http2Headers.method()
|
||||
.toString()), http2Headers.path().toString(), validateHttpHeaders);
|
||||
addHttp2ToHttpHeaders(streamId, http2Headers, msg, false);
|
||||
return msg;
|
||||
}
|
||||
@ -225,25 +221,26 @@ public final class HttpUtil {
|
||||
* @param streamId The stream associated with {@code sourceHeaders}
|
||||
* @param sourceHeaders The HTTP/2 headers to convert
|
||||
* @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
|
||||
* headers.
|
||||
* @param addToTrailer {@code true} to add to trailing headers. {@code false} to add to initial headers.
|
||||
* @throws Http2Exception If not all HTTP/2 headers can be translated to HTTP/1.x
|
||||
*/
|
||||
public static void addHttp2ToHttpHeaders(int streamId, Http2Headers sourceHeaders,
|
||||
FullHttpMessage destinationMessage, boolean addToTrailer)
|
||||
throws Http2Exception {
|
||||
FullHttpMessage destinationMessage, boolean addToTrailer) throws Http2Exception {
|
||||
HttpHeaders headers = addToTrailer ? destinationMessage.trailingHeaders() : destinationMessage.headers();
|
||||
boolean request = destinationMessage instanceof HttpRequest;
|
||||
Http2ToHttpHeaderTranslator visitor = new Http2ToHttpHeaderTranslator(headers, request);
|
||||
try {
|
||||
sourceHeaders.forEachEntry(visitor);
|
||||
if (visitor.cause() != null) {
|
||||
throw visitor.cause();
|
||||
} catch (Http2Exception ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
PlatformDependent.throwException(ex);
|
||||
}
|
||||
|
||||
headers.remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
headers.remove(HttpHeaders.Names.TRAILER);
|
||||
if (!addToTrailer) {
|
||||
headers.set(ExtensionHeaderNames.STREAM_ID.text(), streamId);
|
||||
headers.setInt(ExtensionHeaderNames.STREAM_ID.text(), streamId);
|
||||
HttpHeaderUtil.setKeepAlive(destinationMessage, true);
|
||||
}
|
||||
}
|
||||
@ -259,7 +256,7 @@ public final class HttpUtil {
|
||||
out.path(new AsciiString(request.uri()));
|
||||
out.method(new AsciiString(request.method().toString()));
|
||||
|
||||
String value = inHeaders.get(HttpHeaders.Names.HOST);
|
||||
String value = inHeaders.getAndConvert(HttpHeaders.Names.HOST);
|
||||
if (value != null) {
|
||||
URI hostUri = URI.create(value);
|
||||
// The authority MUST NOT include the deprecated "userinfo" subcomponent
|
||||
@ -274,15 +271,15 @@ public final class HttpUtil {
|
||||
}
|
||||
|
||||
// Consume the Authority extension header if present
|
||||
value = inHeaders.get(ExtensionHeaderNames.AUTHORITY.text());
|
||||
if (value != null) {
|
||||
out.authority(new AsciiString(value));
|
||||
CharSequence cValue = inHeaders.get(ExtensionHeaderNames.AUTHORITY.text());
|
||||
if (cValue != null) {
|
||||
out.authority(AsciiString.of(cValue));
|
||||
}
|
||||
|
||||
// Consume the Scheme extension header if present
|
||||
value = inHeaders.get(ExtensionHeaderNames.SCHEME.text());
|
||||
if (value != null) {
|
||||
out.scheme(new AsciiString(value));
|
||||
cValue = inHeaders.get(ExtensionHeaderNames.SCHEME.text());
|
||||
if (cValue != null) {
|
||||
out.scheme(AsciiString.of(cValue));
|
||||
}
|
||||
} else if (in instanceof HttpResponse) {
|
||||
HttpResponse response = (HttpResponse) in;
|
||||
@ -290,90 +287,81 @@ public final class HttpUtil {
|
||||
}
|
||||
|
||||
// Add the HTTP headers which have not been consumed above
|
||||
inHeaders.forEachEntry(new TextHeaderProcessor() {
|
||||
try {
|
||||
inHeaders.forEachEntry(new EntryVisitor() {
|
||||
@Override
|
||||
public boolean process(CharSequence name, CharSequence value) throws Exception {
|
||||
AsciiString aName = AsciiString.of(name);
|
||||
if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName.toLowerCase())) {
|
||||
AsciiString aValue = AsciiString.of(value);
|
||||
public boolean visit(Entry<CharSequence, CharSequence> entry) throws Exception {
|
||||
final AsciiString aName = AsciiString.of(entry.getKey()).toLowerCase();
|
||||
if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName)) {
|
||||
AsciiString aValue = AsciiString.of(entry.getValue());
|
||||
out.add(aName, aValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
PlatformDependent.throwException(ex);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private static final Map<AsciiString, String> REQUEST_HEADER_TRANSLATIONS =
|
||||
new HashMap<AsciiString, String>();
|
||||
private static final Map<AsciiString, String> RESPONSE_HEADER_TRANSLATIONS =
|
||||
new HashMap<AsciiString, String>();
|
||||
private static final Map<AsciiString, AsciiString>
|
||||
REQUEST_HEADER_TRANSLATIONS = new HashMap<AsciiString, AsciiString>();
|
||||
private static final Map<AsciiString, AsciiString>
|
||||
RESPONSE_HEADER_TRANSLATIONS = new HashMap<AsciiString, AsciiString>();
|
||||
static {
|
||||
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.AUTHORITY.value(),
|
||||
ExtensionHeaderNames.AUTHORITY.text().toString());
|
||||
ExtensionHeaderNames.AUTHORITY.text());
|
||||
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.SCHEME.value(),
|
||||
ExtensionHeaderNames.SCHEME.text().toString());
|
||||
ExtensionHeaderNames.SCHEME.text());
|
||||
REQUEST_HEADER_TRANSLATIONS.putAll(RESPONSE_HEADER_TRANSLATIONS);
|
||||
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.PATH.value(),
|
||||
ExtensionHeaderNames.PATH.text().toString());
|
||||
ExtensionHeaderNames.PATH.text());
|
||||
}
|
||||
|
||||
private final HttpHeaders output;
|
||||
private final Map<AsciiString, String> translations;
|
||||
private Http2Exception e;
|
||||
private final Map<AsciiString, AsciiString> translations;
|
||||
|
||||
/**
|
||||
* Create a new instance
|
||||
*
|
||||
* @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.
|
||||
* Otherwise uses the response translation map.
|
||||
* @param request if {@code true}, translates headers using the request translation map. Otherwise uses the
|
||||
* response translation map.
|
||||
*/
|
||||
public Http2ToHttpHeaderTranslator(HttpHeaders output, boolean request) {
|
||||
this.output = output;
|
||||
translations = request? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS;
|
||||
translations = request ? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(AsciiString name, AsciiString value) {
|
||||
String translatedName = translations.get(name);
|
||||
public boolean visit(Entry<AsciiString, AsciiString> entry) throws Http2Exception {
|
||||
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) {
|
||||
translatedName = name.toString();
|
||||
translatedName = name;
|
||||
}
|
||||
|
||||
// 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
|
||||
if (translatedName.isEmpty() || translatedName.charAt(0) == ':') {
|
||||
e = Http2Exception
|
||||
throw Http2Exception
|
||||
.protocolError("Unknown HTTP/2 header '%s' encountered in translation to HTTP/1.x",
|
||||
translatedName);
|
||||
return false;
|
||||
} else {
|
||||
output.add(translatedName, value.toString());
|
||||
output.add(translatedName, value);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ public class InboundHttp2ToHttpAdapter extends Http2EventAdapter {
|
||||
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);
|
||||
}
|
||||
|
@ -14,15 +14,18 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
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.http.DefaultHttpHeaders;
|
||||
import io.netty.handler.codec.http.FullHttpMessage;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.util.collection.IntObjectHashMap;
|
||||
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}
|
||||
@ -179,13 +182,17 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA
|
||||
* @param http2Headers The target HTTP/2 headers
|
||||
*/
|
||||
private void addHttpHeadersToHttp2Headers(HttpHeaders httpHeaders, final Http2Headers http2Headers) {
|
||||
httpHeaders.forEachEntry(new TextHeaderProcessor() {
|
||||
try {
|
||||
httpHeaders.forEachEntry(new EntryVisitor() {
|
||||
@Override
|
||||
public boolean process(CharSequence name, CharSequence value) throws Exception {
|
||||
http2Headers.add(new AsciiString(name), new AsciiString(value));
|
||||
public boolean visit(Entry<CharSequence, CharSequence> entry) throws Exception {
|
||||
http2Headers.add(AsciiString.of(entry.getKey()), AsciiString.of(entry.getValue()));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
PlatformDependent.throwException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -215,7 +222,7 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA
|
||||
// and the HTTP message flow exists in OPEN.
|
||||
if (parent != null && !parent.equals(connection.connectionStream())) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
@ -224,7 +231,7 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA
|
||||
removePriorityRelatedHeaders(msg.trailingHeaders());
|
||||
} else if (!parent.equals(connection.connectionStream())) {
|
||||
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 {
|
||||
headers = getActiveHeaders(msg);
|
||||
}
|
||||
headers.set(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), stream.weight());
|
||||
headers.setShort(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), stream.weight());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -106,7 +106,7 @@ public class DefaultHttp2ToHttpConnectionHandlerTest {
|
||||
final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/example");
|
||||
try {
|
||||
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,
|
||||
"http://my-user_name@www.example.org:5555/example");
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "www.example.org:5555");
|
||||
|
@ -169,8 +169,8 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.SCHEME.text(), "https");
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org");
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
final Http2Headers http2Headers =
|
||||
new DefaultHttp2Headers().method(as("GET")).scheme(as("https"))
|
||||
.authority(as("example.org"))
|
||||
@ -200,8 +200,8 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
"/some/path/resource2", content, true);
|
||||
try {
|
||||
HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET"))
|
||||
.path(as("/some/path/resource2"));
|
||||
runInChannel(clientChannel, new Http2Runnable() {
|
||||
@ -230,8 +230,8 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
"/some/path/resource2", content, true);
|
||||
try {
|
||||
HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET"))
|
||||
.path(as("/some/path/resource2"));
|
||||
final int midPoint = text.length() / 2;
|
||||
@ -264,8 +264,8 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
"/some/path/resource2", content, true);
|
||||
try {
|
||||
HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET"))
|
||||
.path(as("/some/path/resource2"));
|
||||
runInChannel(clientChannel, new Http2Runnable() {
|
||||
@ -298,8 +298,8 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
"/some/path/resource2", content, true);
|
||||
try {
|
||||
HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
HttpHeaders trailingHeaders = request.trailingHeaders();
|
||||
trailingHeaders.set("FoO", "goo");
|
||||
trailingHeaders.set("foO2", "goo2");
|
||||
@ -338,8 +338,8 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
"/some/path/resource2", content, true);
|
||||
try {
|
||||
HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
HttpHeaders trailingHeaders = request.trailingHeaders();
|
||||
trailingHeaders.set("Foo", "goo");
|
||||
trailingHeaders.set("fOo2", "goo2");
|
||||
@ -383,13 +383,13 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
"/some/path/resource2", content2, true);
|
||||
try {
|
||||
HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
HttpHeaders httpHeaders2 = request2.headers();
|
||||
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
|
||||
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3);
|
||||
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 123);
|
||||
httpHeaders2.set(HttpHeaders.Names.CONTENT_LENGTH, text2.length());
|
||||
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
|
||||
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3);
|
||||
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 123);
|
||||
httpHeaders2.setInt(HttpHeaders.Names.CONTENT_LENGTH, text2.length());
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT"))
|
||||
.path(as("/some/path/resource"));
|
||||
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);
|
||||
try {
|
||||
HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
HttpHeaders httpHeaders2 = request2.headers();
|
||||
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
|
||||
httpHeaders2.set(HttpHeaders.Names.CONTENT_LENGTH, text2.length());
|
||||
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
|
||||
httpHeaders2.setInt(HttpHeaders.Names.CONTENT_LENGTH, text2.length());
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT"))
|
||||
.path(as("/some/path/resource"));
|
||||
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT"))
|
||||
.path(as("/some/path/resource2"));
|
||||
HttpHeaders httpHeaders3 = request3.headers();
|
||||
httpHeaders3.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
|
||||
httpHeaders3.set(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3);
|
||||
httpHeaders3.set(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 222);
|
||||
httpHeaders3.set(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
|
||||
httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3);
|
||||
httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 222);
|
||||
httpHeaders3.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
runInChannel(clientChannel, new Http2Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -486,18 +486,18 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
HttpMethod.GET, "/push/test", true);
|
||||
try {
|
||||
HttpHeaders httpHeaders = response.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
HttpHeaders httpHeaders2 = response2.headers();
|
||||
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.SCHEME.text(), "https");
|
||||
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org");
|
||||
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
|
||||
httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), 3);
|
||||
httpHeaders2.set(HttpHeaders.Names.CONTENT_LENGTH, text2.length());
|
||||
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
|
||||
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), 3);
|
||||
httpHeaders2.setInt(HttpHeaders.Names.CONTENT_LENGTH, text2.length());
|
||||
|
||||
httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(as("GET"))
|
||||
.path(as("/push/test"));
|
||||
runInChannel(clientChannel, new Http2Runnable() {
|
||||
@ -545,9 +545,9 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
final FullHttpMessage request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "/info/test",
|
||||
true);
|
||||
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.CONTENT_LENGTH, 0);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
final Http2Headers http2Headers =
|
||||
new DefaultHttp2Headers()
|
||||
.method(as("PUT"))
|
||||
@ -577,8 +577,8 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
reset(serverListener);
|
||||
|
||||
httpHeaders = response.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(as("100"));
|
||||
runInChannel(serverConnectedChannel, new Http2Runnable() {
|
||||
@Override
|
||||
@ -597,7 +597,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
|
||||
setServerLatch(1);
|
||||
httpHeaders = request2.headers();
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.remove(HttpHeaders.Names.EXPECT);
|
||||
runInChannel(clientChannel, new Http2Runnable() {
|
||||
@Override
|
||||
@ -614,8 +614,8 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
|
||||
setClientLatch(1);
|
||||
httpHeaders = response2.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
|
||||
httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0);
|
||||
final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(as("200"));
|
||||
runInChannel(serverConnectedChannel, new Http2Runnable() {
|
||||
@Override
|
||||
|
@ -17,29 +17,94 @@
|
||||
package io.netty.handler.codec.stomp;
|
||||
|
||||
import io.netty.handler.codec.DefaultTextHeaders;
|
||||
import io.netty.handler.codec.TextHeaderProcessor;
|
||||
import io.netty.handler.codec.TextHeaders;
|
||||
|
||||
public class DefaultStompHeaders extends DefaultTextHeaders implements StompHeaders {
|
||||
|
||||
@Override
|
||||
public StompHeaders add(CharSequence name, Object value) {
|
||||
public StompHeaders add(CharSequence name, CharSequence value) {
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders add(CharSequence name, Iterable<?> values) {
|
||||
public StompHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders add(CharSequence name, Object... values) {
|
||||
public StompHeaders add(CharSequence name, CharSequence... values) {
|
||||
super.add(name, values);
|
||||
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
|
||||
public StompHeaders add(TextHeaders headers) {
|
||||
super.add(headers);
|
||||
@ -47,23 +112,89 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders set(CharSequence name, Object value) {
|
||||
public StompHeaders set(CharSequence name, CharSequence value) {
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders set(CharSequence name, Object... values) {
|
||||
public StompHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders set(CharSequence name, Iterable<?> values) {
|
||||
public StompHeaders set(CharSequence name, CharSequence... values) {
|
||||
super.set(name, values);
|
||||
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
|
||||
public StompHeaders set(TextHeaders headers) {
|
||||
super.set(headers);
|
||||
@ -71,14 +202,14 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders clear() {
|
||||
super.clear();
|
||||
public StompHeaders setAll(TextHeaders headers) {
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders forEachEntry(TextHeaderProcessor processor) {
|
||||
super.forEachEntry(processor);
|
||||
public StompHeaders clear() {
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
package io.netty.handler.codec.stomp;
|
||||
|
||||
import io.netty.handler.codec.AsciiString;
|
||||
import io.netty.handler.codec.TextHeaderProcessor;
|
||||
import io.netty.handler.codec.TextHeaders;
|
||||
|
||||
/**
|
||||
@ -46,32 +45,98 @@ public interface StompHeaders extends TextHeaders {
|
||||
AsciiString CONTENT_TYPE = new AsciiString("content-type");
|
||||
|
||||
@Override
|
||||
StompHeaders add(CharSequence name, Object value);
|
||||
StompHeaders add(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
StompHeaders add(CharSequence name, Iterable<?> values);
|
||||
StompHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@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
|
||||
StompHeaders add(TextHeaders headers);
|
||||
|
||||
@Override
|
||||
StompHeaders set(CharSequence name, Object value);
|
||||
StompHeaders set(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
StompHeaders set(CharSequence name, Iterable<?> values);
|
||||
StompHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@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
|
||||
StompHeaders set(TextHeaders headers);
|
||||
|
||||
@Override
|
||||
StompHeaders clear();
|
||||
StompHeaders setAll(TextHeaders headers);
|
||||
|
||||
@Override
|
||||
StompHeaders forEachEntry(TextHeaderProcessor processor);
|
||||
StompHeaders clear();
|
||||
}
|
||||
|
@ -69,8 +69,7 @@ public class StompSubframeAggregator
|
||||
|
||||
@Override
|
||||
protected long contentLength(StompHeadersSubframe start) throws Exception {
|
||||
String value = start.headers().get(StompHeaders.CONTENT_LENGTH);
|
||||
return Long.parseLong(value);
|
||||
return start.headers().getLong(StompHeaders.CONTENT_LENGTH, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -219,15 +219,7 @@ public class StompSubframeDecoder extends ReplayingDecoder<State> {
|
||||
}
|
||||
|
||||
private static long getContentLength(StompHeaders headers, long defaultValue) {
|
||||
String contentLength = headers.get(StompHeaders.CONTENT_LENGTH);
|
||||
if (contentLength != null) {
|
||||
try {
|
||||
return Long.parseLong(contentLength);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
return headers.getLong(StompHeaders.CONTENT_LENGTH, defaultValue);
|
||||
}
|
||||
|
||||
private static void skipNullCharacter(ByteBuf buffer) {
|
||||
|
@ -22,6 +22,7 @@ import io.netty.handler.codec.AsciiHeadersEncoder.NewlineType;
|
||||
import io.netty.handler.codec.AsciiHeadersEncoder.SeparatorType;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -65,7 +66,12 @@ public class StompSubframeEncoder extends MessageToMessageEncoder<StompSubframe>
|
||||
|
||||
buf.writeBytes(frame.command().toString().getBytes(CharsetUtil.US_ASCII));
|
||||
buf.writeByte(StompConstants.LF);
|
||||
try {
|
||||
frame.headers().forEachEntry(new AsciiHeadersEncoder(buf, SeparatorType.COLON, NewlineType.LF));
|
||||
} catch (Exception ex) {
|
||||
buf.release();
|
||||
PlatformDependent.throwException(ex);
|
||||
}
|
||||
buf.writeByte(StompConstants.LF);
|
||||
return buf;
|
||||
}
|
||||
|
@ -17,9 +17,12 @@
|
||||
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.
|
||||
@ -74,7 +77,9 @@ public final class AsciiHeadersEncoder implements TextHeaderProcessor {
|
||||
}
|
||||
|
||||
@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 int nameLen = name.length();
|
||||
final int valueLen = value.length();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,354 +16,123 @@
|
||||
|
||||
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)
|
||||
* for the representation of arbitrary key-value data. {@link AsciiString} is just a wrapper around
|
||||
* a byte array but provides some additional utility when handling text data.
|
||||
* A typical {@code AsciiString} multimap used by protocols that use binary headers (such as HTTP/2) for the
|
||||
* representation of arbitrary key-value data. {@link AsciiString} is just a wrapper around a byte array but provides
|
||||
* 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 {
|
||||
/**
|
||||
* @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;
|
||||
public interface EntryVisitor extends Headers.EntryVisitor<AsciiString> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 if the header is found.
|
||||
* {@code null} if there's no such header.
|
||||
* A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}.
|
||||
*/
|
||||
AsciiString get(AsciiString name);
|
||||
public interface NameVisitor extends Headers.NameVisitor<AsciiString> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
@Override
|
||||
BinaryHeaders add(AsciiString name, AsciiString 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 headers being set
|
||||
* @param values the values of the headers being set
|
||||
* @return {@code this}
|
||||
*/
|
||||
BinaryHeaders add(AsciiString name, Iterable<AsciiString> values);
|
||||
@Override
|
||||
BinaryHeaders add(AsciiString name, Iterable<? extends AsciiString> 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 headers being set
|
||||
* @param values the values of the headers being set
|
||||
* @return {@code this}
|
||||
*/
|
||||
@Override
|
||||
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}.
|
||||
*
|
||||
* @return {@code this}
|
||||
* See {@link Headers#add(Headers)}
|
||||
*/
|
||||
BinaryHeaders add(BinaryHeaders headers);
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
@Override
|
||||
BinaryHeaders set(AsciiString name, AsciiString value);
|
||||
|
||||
/**
|
||||
* 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, Iterable<AsciiString> values);
|
||||
@Override
|
||||
BinaryHeaders set(AsciiString name, Iterable<? extends AsciiString> 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}
|
||||
*/
|
||||
@Override
|
||||
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}.
|
||||
*
|
||||
* @return {@code this}
|
||||
* See {@link Headers#set(Headers)}
|
||||
*/
|
||||
BinaryHeaders set(BinaryHeaders headers);
|
||||
|
||||
/**
|
||||
* Retains all current headers but calls {@link #set(AsciiString, Object)} for each entry in {@code headers}
|
||||
* @param headers The headers used to {@link #set(AsciiString, Object)} values in this instance
|
||||
* @return {@code this}
|
||||
* See {@link Headers#setAll(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
|
||||
Iterator<Entry<AsciiString, AsciiString>> iterator();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
BinaryHeaders clear();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -14,329 +14,334 @@
|
||||
*/
|
||||
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 java.util.Arrays;
|
||||
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;
|
||||
import java.text.ParseException;
|
||||
|
||||
public class DefaultBinaryHeaders implements BinaryHeaders {
|
||||
private final HeaderMap.ValueUnmarshaller<AsciiString> VALUE_UNMARSHALLER =
|
||||
new HeaderMap.ValueUnmarshaller<AsciiString>() {
|
||||
public class DefaultBinaryHeaders extends DefaultHeaders<AsciiString> implements BinaryHeaders {
|
||||
private static final HashCodeGenerator<AsciiString> ASCII_HASH_CODE_GENERATOR =
|
||||
new HashCodeGenerator<AsciiString>() {
|
||||
@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;
|
||||
} 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
|
||||
public boolean visit(AsciiString name, AsciiString value) throws Exception {
|
||||
add(name, value);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
private final BinaryHeaderVisitor setAll = new BinaryHeaderVisitor() {
|
||||
@Override
|
||||
public boolean visit(AsciiString name, AsciiString value) throws Exception {
|
||||
set(name, value);
|
||||
return true;
|
||||
public AsciiString convertName(AsciiString name) {
|
||||
return name.toLowerCase();
|
||||
}
|
||||
};
|
||||
|
||||
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() {
|
||||
// Binary headers are case-sensitive. It's up the HTTP/1 translation layer to convert headers to
|
||||
// lowercase.
|
||||
headers = new HeaderMap(false);
|
||||
this(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
|
||||
public BinaryHeaders add(AsciiString name, AsciiString value) {
|
||||
headers.add(name, value);
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders add(AsciiString name, Iterable<AsciiString> values) {
|
||||
headers.add(name, values);
|
||||
public BinaryHeaders add(AsciiString name, Iterable<? extends AsciiString> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders add(BinaryHeaders headers) {
|
||||
checkNotNull(headers, "headers");
|
||||
|
||||
add0(headers);
|
||||
super.add(headers);
|
||||
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
|
||||
public BinaryHeaders set(AsciiString name, AsciiString value) {
|
||||
headers.set(name, value);
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders set(AsciiString name, Iterable<AsciiString> values) {
|
||||
headers.set(name, values);
|
||||
public BinaryHeaders set(AsciiString name, Iterable<? extends AsciiString> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders set(BinaryHeaders headers) {
|
||||
checkNotNull(headers, "headers");
|
||||
clear();
|
||||
add0(headers);
|
||||
super.set(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders setAll(BinaryHeaders headers) {
|
||||
checkNotNull(headers, "headers");
|
||||
|
||||
if (headers instanceof DefaultBinaryHeaders) {
|
||||
this.headers.setAll(((DefaultBinaryHeaders) headers).headers);
|
||||
} else {
|
||||
forEachEntry(setAll);
|
||||
}
|
||||
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders clear() {
|
||||
headers.clear();
|
||||
super.clear();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
1459
codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java
Normal file
1459
codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -16,154 +16,199 @@
|
||||
|
||||
package io.netty.handler.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
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();
|
||||
public class EmptyBinaryHeaders extends EmptyHeaders<AsciiString> implements BinaryHeaders {
|
||||
protected EmptyBinaryHeaders() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders add(AsciiString name, AsciiString value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders add(AsciiString name, Iterable<AsciiString> values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
public BinaryHeaders add(AsciiString name, Iterable<? extends AsciiString> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public BinaryHeaders add(BinaryHeaders headers) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
super.add(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders set(AsciiString name, AsciiString value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders set(AsciiString name, Iterable<AsciiString> values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
public BinaryHeaders set(AsciiString name, Iterable<? extends AsciiString> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public BinaryHeaders set(BinaryHeaders headers) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
super.set(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders setAll(BinaryHeaders headers) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(AsciiString name) {
|
||||
return false;
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryHeaders clear() {
|
||||
super.clear();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
536
codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java
Normal file
536
codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java
Normal 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();
|
||||
}
|
||||
}
|
@ -16,238 +16,209 @@
|
||||
|
||||
package io.netty.handler.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
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;
|
||||
public class EmptyTextHeaders extends EmptyConvertibleHeaders<CharSequence, String> implements TextHeaders {
|
||||
protected EmptyTextHeaders() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(CharSequence name, String defaultValue) {
|
||||
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) {
|
||||
public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
public boolean containsObject(CharSequence name, Object value, boolean ignoreCase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
public TextHeaders add(CharSequence name, CharSequence value) {
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> names() {
|
||||
return Collections.emptySet();
|
||||
public TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CharSequence> unconvertedNames() {
|
||||
return Collections.emptySet();
|
||||
public TextHeaders add(CharSequence name, CharSequence... values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextHeaders add(CharSequence name, Object value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
public TextHeaders addObject(CharSequence name, Object value) {
|
||||
super.addObject(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextHeaders add(CharSequence name, Iterable<?> values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
public TextHeaders addObject(CharSequence name, Iterable<?> values) {
|
||||
super.addObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextHeaders add(CharSequence name, Object... values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
public TextHeaders addObject(CharSequence name, Object... values) {
|
||||
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
|
||||
public TextHeaders add(TextHeaders headers) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
super.add(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextHeaders set(CharSequence name, Object value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
public TextHeaders set(CharSequence name, CharSequence value) {
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextHeaders set(CharSequence name, Iterable<?> values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
public TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextHeaders set(CharSequence name, Object... values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
public TextHeaders set(CharSequence name, CharSequence... values) {
|
||||
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
|
||||
public TextHeaders set(TextHeaders headers) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
super.set(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextHeaders setAll(TextHeaders rhs) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(CharSequence name) {
|
||||
return false;
|
||||
public TextHeaders setAll(TextHeaders headers) {
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextHeaders clear() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@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) {
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
1085
codec/src/main/java/io/netty/handler/codec/Headers.java
Normal file
1085
codec/src/main/java/io/netty/handler/codec/Headers.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
@ -16,451 +16,142 @@
|
||||
|
||||
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.
|
||||
* One thing to note is that it uses {@link CharSequence} as its primary key and value type rather than {@link String}.
|
||||
* When you invoke the operations that produce {@link String}s such as {@link #get(CharSequence)},
|
||||
* a {@link CharSequence} is implicitly converted to a {@link String}. This is particularly useful for speed
|
||||
* optimization because this multimap can hold a special {@link CharSequence} implementation that a codec can
|
||||
* treat specially, such as {@link AsciiString}.
|
||||
* A typical string multimap used by text protocols such as HTTP for the representation of arbitrary key-value data. One
|
||||
* thing to note is that it uses {@link CharSequence} as its primary key and value type rather than {@link String}. When
|
||||
* you invoke the operations that produce {@link String}s such as {@link #get(CharSequence)}, a {@link CharSequence} is
|
||||
* implicitly converted to a {@link String}. This is particularly useful for speed optimization because this multimap
|
||||
* can hold a special {@link CharSequence} implementation that a codec can treat specially, such as {@link CharSequence}
|
||||
* .
|
||||
*/
|
||||
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
|
||||
* 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.
|
||||
* A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}.
|
||||
*/
|
||||
String get(CharSequence name);
|
||||
public interface EntryVisitor extends Headers.EntryVisitor<CharSequence> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}.
|
||||
*/
|
||||
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
|
||||
* 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.
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
* 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.
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
* 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}
|
||||
* See {@link Headers#add(Headers)}
|
||||
*/
|
||||
TextHeaders add(TextHeaders headers);
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
TextHeaders set(CharSequence name, Object value);
|
||||
@Override
|
||||
TextHeaders set(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@Override
|
||||
TextHeaders set(CharSequence name, CharSequence... values);
|
||||
|
||||
@Override
|
||||
TextHeaders setObject(CharSequence name, Object value);
|
||||
|
||||
@Override
|
||||
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.
|
||||
*
|
||||
* 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}
|
||||
* See {@link Headers#set(Headers)}
|
||||
*/
|
||||
TextHeaders set(TextHeaders headers);
|
||||
|
||||
/**
|
||||
* Retains all current headers but calls {@link #set(CharSequence, Object)} for each entry in {@code headers}
|
||||
* @param headers The headers used to {@link #set(CharSequence, Object)} values in this instance
|
||||
* @return {@code this}
|
||||
* See {@link Headers#setAll(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
|
||||
Iterator<Entry<String, String>> iterator();
|
||||
|
||||
Iterator<Entry<CharSequence, CharSequence>> unconvertedIterator();
|
||||
|
||||
TextHeaders forEachEntry(TextHeaderProcessor processor);
|
||||
TextHeaders clear();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -29,9 +29,9 @@ import java.util.Set;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link HeaderMap}.
|
||||
* Tests for {@link DefaultBinaryHeaders}.
|
||||
*/
|
||||
public class HeaderMapTest {
|
||||
public class DefaultBinaryHeadersTest {
|
||||
|
||||
@Test
|
||||
public void binaryHeadersWithSameValuesShouldBeEquivalent() {
|
||||
@ -40,11 +40,11 @@ public class HeaderMapTest {
|
||||
byte[] key2 = randomBytes();
|
||||
byte[] value2 = randomBytes();
|
||||
|
||||
HeaderMap h1 = new HeaderMap(false);
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false);
|
||||
h1.set(as(key1), as(value1));
|
||||
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(key2), as(value2));
|
||||
|
||||
@ -63,13 +63,13 @@ public class HeaderMapTest {
|
||||
byte[] v3 = randomBytes();
|
||||
byte[] v4 = randomBytes();
|
||||
|
||||
HeaderMap h1 = new HeaderMap(false);
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false);
|
||||
h1.set(as(k1), as(v1));
|
||||
h1.set(as(k2), as(v2));
|
||||
h1.add(as(k2), as(v3));
|
||||
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(k2), as(v2));
|
||||
h2.add(as(k1), as(v4));
|
||||
@ -90,13 +90,13 @@ public class HeaderMapTest {
|
||||
byte[] v3 = randomBytes();
|
||||
byte[] v4 = randomBytes();
|
||||
|
||||
HeaderMap h1 = new HeaderMap(false);
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false);
|
||||
h1.set(as(k1), as(v1));
|
||||
h1.set(as(k2), as(v2));
|
||||
h1.add(as(k2), as(v3));
|
||||
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(k2), as(v2));
|
||||
h2.add(as(k1), as(v4));
|
||||
@ -116,18 +116,18 @@ public class HeaderMapTest {
|
||||
byte[] v3 = randomBytes();
|
||||
byte[] v4 = randomBytes();
|
||||
|
||||
HeaderMap h1 = new HeaderMap(false);
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false);
|
||||
h1.set(as(k1), as(v1));
|
||||
h1.set(as(k2), as(v2));
|
||||
h1.add(as(k2), as(v3));
|
||||
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(k2), as(v2));
|
||||
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(k2), as(v2));
|
||||
expected.add(as(k2), as(v3));
|
||||
@ -148,27 +148,27 @@ public class HeaderMapTest {
|
||||
byte[] v2 = 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(v2));
|
||||
assertEquals(2, h1.size());
|
||||
|
||||
h1.set(as(k1), as(v3));
|
||||
assertEquals(1, h1.size());
|
||||
List<CharSequence> list = h1.getAll(as(k1));
|
||||
List<AsciiString> list = h1.getAll(as(k1));
|
||||
assertEquals(1, list.size());
|
||||
assertEquals(as(v3), list.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWithSameValuesShouldBeEquivalent() {
|
||||
HeaderMap h1 = new HeaderMap();
|
||||
h1.set("foo", "goo");
|
||||
h1.set("foo2", "goo2");
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
|
||||
h1.set(as("foo"), as("goo"));
|
||||
h1.set(as("foo2"), as("goo2"));
|
||||
|
||||
HeaderMap h2 = new HeaderMap();
|
||||
h2.set("foo", "goo");
|
||||
h2.set("foo2", "goo2");
|
||||
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
|
||||
h2.set(as("foo"), as("goo"));
|
||||
h2.set(as("foo2"), as("goo2"));
|
||||
|
||||
assertTrue(h1.equals(h2));
|
||||
assertTrue(h2.equals(h1));
|
||||
@ -178,17 +178,17 @@ public class HeaderMapTest {
|
||||
|
||||
@Test
|
||||
public void headersWithSameDuplicateValuesShouldBeEquivalent() {
|
||||
HeaderMap h1 = new HeaderMap();
|
||||
h1.set("foo", "goo");
|
||||
h1.set("foo2", "goo2");
|
||||
h1.add("foo2", "goo3");
|
||||
h1.add("foo", "goo4");
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
|
||||
h1.set(as("foo"), as("goo"));
|
||||
h1.set(as("foo2"), as("goo2"));
|
||||
h1.add(as("foo2"), as("goo3"));
|
||||
h1.add(as("foo"), as("goo4"));
|
||||
|
||||
HeaderMap h2 = new HeaderMap();
|
||||
h2.set("foo", "goo");
|
||||
h2.set("foo2", "goo2");
|
||||
h2.add("foo", "goo4");
|
||||
h2.add("foo2", "goo3");
|
||||
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
|
||||
h2.set(as("foo"), as("goo"));
|
||||
h2.set(as("foo2"), as("goo2"));
|
||||
h2.add(as("foo"), as("goo4"));
|
||||
h2.add(as("foo2"), as("goo3"));
|
||||
|
||||
assertTrue(h1.equals(h2));
|
||||
assertTrue(h2.equals(h1));
|
||||
@ -198,16 +198,16 @@ public class HeaderMapTest {
|
||||
|
||||
@Test
|
||||
public void headersWithDifferentValuesShouldNotBeEquivalent() {
|
||||
HeaderMap h1 = new HeaderMap();
|
||||
h1.set("foo", "goo");
|
||||
h1.set("foo2", "goo2");
|
||||
h1.add("foo2", "goo3");
|
||||
h1.add("foo", "goo4");
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
|
||||
h1.set(as("foo"), as("goo"));
|
||||
h1.set(as("foo2"), as("goo2"));
|
||||
h1.add(as("foo2"), as("goo3"));
|
||||
h1.add(as("foo"), as("goo4"));
|
||||
|
||||
HeaderMap h2 = new HeaderMap();
|
||||
h2.set("foo", "goo");
|
||||
h2.set("foo2", "goo2");
|
||||
h2.add("foo", "goo4");
|
||||
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
|
||||
h2.set(as("foo"), as("goo"));
|
||||
h2.set(as("foo2"), as("goo2"));
|
||||
h2.add(as("foo"), as("goo4"));
|
||||
|
||||
assertFalse(h1.equals(h2));
|
||||
assertFalse(h2.equals(h1));
|
||||
@ -217,25 +217,25 @@ public class HeaderMapTest {
|
||||
|
||||
@Test
|
||||
public void setAllShouldMergeHeaders() {
|
||||
HeaderMap h1 = new HeaderMap();
|
||||
h1.set("foo", "goo");
|
||||
h1.set("foo2", "goo2");
|
||||
h1.add("foo2", "goo3");
|
||||
h1.add("foo", "goo4");
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
|
||||
h1.set(as("foo"), as("goo"));
|
||||
h1.set(as("foo2"), as("goo2"));
|
||||
h1.add(as("foo2"), as("goo3"));
|
||||
h1.add(as("foo"), as("goo4"));
|
||||
|
||||
HeaderMap h2 = new HeaderMap();
|
||||
h2.set("foo", "goo");
|
||||
h2.set("foo2", "goo2");
|
||||
h2.add("foo", "goo4");
|
||||
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
|
||||
h2.set(as("foo"), as("goo"));
|
||||
h2.set(as("foo2"), as("goo2"));
|
||||
h2.add(as("foo"), as("goo4"));
|
||||
|
||||
HeaderMap expected = new HeaderMap();
|
||||
expected.set("foo", "goo");
|
||||
expected.set("foo2", "goo2");
|
||||
expected.add("foo2", "goo3");
|
||||
expected.add("foo", "goo4");
|
||||
expected.set("foo", "goo");
|
||||
expected.set("foo2", "goo2");
|
||||
expected.set("foo", "goo4");
|
||||
DefaultBinaryHeaders expected = new DefaultBinaryHeaders();
|
||||
expected.set(as("foo"), as("goo"));
|
||||
expected.set(as("foo2"), as("goo2"));
|
||||
expected.add(as("foo2"), as("goo3"));
|
||||
expected.add(as("foo"), as("goo4"));
|
||||
expected.set(as("foo"), as("goo"));
|
||||
expected.set(as("foo2"), as("goo2"));
|
||||
expected.set(as("foo"), as("goo4"));
|
||||
|
||||
h1.setAll(h2);
|
||||
|
||||
@ -244,22 +244,21 @@ public class HeaderMapTest {
|
||||
|
||||
@Test
|
||||
public void setShouldReplacePreviousValues() {
|
||||
HeaderMap h1 = new HeaderMap();
|
||||
h1.add("foo", "goo");
|
||||
h1.add("foo", "goo2");
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
|
||||
h1.add(as("foo"), as("goo"));
|
||||
h1.add(as("foo"), as("goo2"));
|
||||
assertEquals(2, h1.size());
|
||||
|
||||
h1.set("foo", "goo3");
|
||||
h1.set(as("foo"), as("goo3"));
|
||||
assertEquals(1, h1.size());
|
||||
List<CharSequence> list = h1.getAll("foo");
|
||||
List<AsciiString> list = h1.getAll(as("foo"));
|
||||
assertEquals(1, list.size());
|
||||
assertEquals("goo3", list.get(0));
|
||||
assertEquals(as("goo3"), list.get(0));
|
||||
}
|
||||
|
||||
@Test(expected = NoSuchElementException.class)
|
||||
public void iterateEmptyHeadersShouldThrow() {
|
||||
Iterator<Map.Entry<CharSequence, CharSequence>> iterator =
|
||||
new HeaderMap().iterator();
|
||||
Iterator<Map.Entry<AsciiString, AsciiString>> iterator = new DefaultBinaryHeaders().iterator();
|
||||
assertFalse(iterator.hasNext());
|
||||
iterator.next();
|
||||
}
|
||||
@ -275,16 +274,15 @@ public class HeaderMapTest {
|
||||
headers.add("c:1");
|
||||
|
||||
// Build the headers from the input set.
|
||||
HeaderMap h1 = new HeaderMap();
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
|
||||
for (String header : headers) {
|
||||
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.
|
||||
for (Map.Entry<CharSequence, CharSequence> entry : h1) {
|
||||
assertTrue(headers
|
||||
.remove(entry.getKey().toString() + ':' + entry.getValue().toString()));
|
||||
for (Map.Entry<AsciiString, AsciiString> entry : h1) {
|
||||
assertTrue(headers.remove(entry.getKey().toString() + ':' + entry.getValue().toString()));
|
||||
}
|
||||
|
||||
// Make sure we removed them all.
|
||||
@ -293,12 +291,12 @@ public class HeaderMapTest {
|
||||
|
||||
@Test
|
||||
public void getAndRemoveShouldReturnFirstEntry() {
|
||||
HeaderMap h1 = new HeaderMap();
|
||||
h1.add("foo", "goo");
|
||||
h1.add("foo", "goo2");
|
||||
assertEquals("goo", h1.getAndRemove("foo"));
|
||||
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
|
||||
h1.add(as("foo"), as("goo"));
|
||||
h1.add(as("foo"), as("goo2"));
|
||||
assertEquals(as("goo"), h1.getAndRemove(as("foo")));
|
||||
assertEquals(0, h1.size());
|
||||
List<CharSequence> values = h1.getAll("foo");
|
||||
List<AsciiString> values = h1.getAll(as("foo"));
|
||||
assertEquals(0, values.size());
|
||||
}
|
||||
|
||||
@ -308,7 +306,11 @@ public class HeaderMapTest {
|
||||
return data;
|
||||
}
|
||||
|
||||
private String as(byte[] bytes) {
|
||||
return new String(bytes);
|
||||
private AsciiString as(byte[] bytes) {
|
||||
return new AsciiString(bytes);
|
||||
}
|
||||
|
||||
private AsciiString as(String value) {
|
||||
return new AsciiString(value);
|
||||
}
|
||||
}
|
@ -61,6 +61,11 @@ public final class CharsetUtil {
|
||||
*/
|
||||
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
|
||||
* <tt>charset</tt>.
|
||||
@ -111,7 +116,5 @@ public final class CharsetUtil {
|
||||
return d;
|
||||
}
|
||||
|
||||
private CharsetUtil() {
|
||||
// Unused
|
||||
}
|
||||
private CharsetUtil() { }
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -150,7 +150,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
|
||||
}
|
||||
|
||||
// Cache Validation
|
||||
String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE);
|
||||
String ifModifiedSince = request.headers().getAndConvert(IF_MODIFIED_SINCE);
|
||||
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
||||
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
|
||||
|
@ -49,7 +49,7 @@ public class HttpHelloWorldServerHandler extends ChannelHandlerAdapter {
|
||||
boolean keepAlive = HttpHeaderUtil.isKeepAlive(req);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(CONTENT));
|
||||
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) {
|
||||
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
|
@ -36,8 +36,8 @@ public class HttpSnoopClientHandler extends SimpleChannelInboundHandler<HttpObje
|
||||
System.err.println();
|
||||
|
||||
if (!response.headers().isEmpty()) {
|
||||
for (String name: response.headers().names()) {
|
||||
for (String value: response.headers().getAll(name)) {
|
||||
for (CharSequence name: response.headers().names()) {
|
||||
for (CharSequence value: response.headers().getAll(name)) {
|
||||
System.err.println("HEADER: " + name + " = " + value);
|
||||
}
|
||||
}
|
||||
|
@ -74,9 +74,9 @@ public class HttpSnoopServerHandler extends SimpleChannelInboundHandler<Object>
|
||||
|
||||
HttpHeaders headers = request.headers();
|
||||
if (!headers.isEmpty()) {
|
||||
for (Map.Entry<String, String> h: headers) {
|
||||
String key = h.getKey();
|
||||
String value = h.getValue();
|
||||
for (Map.Entry<CharSequence, CharSequence> h: headers) {
|
||||
CharSequence key = h.getKey();
|
||||
CharSequence value = h.getValue();
|
||||
buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
|
||||
}
|
||||
buf.append("\r\n");
|
||||
@ -115,8 +115,8 @@ public class HttpSnoopServerHandler extends SimpleChannelInboundHandler<Object>
|
||||
LastHttpContent trailer = (LastHttpContent) msg;
|
||||
if (!trailer.trailingHeaders().isEmpty()) {
|
||||
buf.append("\r\n");
|
||||
for (String name: trailer.trailingHeaders().names()) {
|
||||
for (String value: trailer.trailingHeaders().getAll(name)) {
|
||||
for (CharSequence name: trailer.trailingHeaders().names()) {
|
||||
for (CharSequence value: trailer.trailingHeaders().getAll(name)) {
|
||||
buf.append("TRAILING HEADER: ");
|
||||
buf.append(name).append(" = ").append(value).append("\r\n");
|
||||
}
|
||||
@ -155,14 +155,14 @@ public class HttpSnoopServerHandler extends SimpleChannelInboundHandler<Object>
|
||||
|
||||
if (keepAlive) {
|
||||
// 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:
|
||||
// - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection
|
||||
response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
|
||||
}
|
||||
|
||||
// Encode the cookie.
|
||||
String cookieString = request.headers().get(COOKIE);
|
||||
String cookieString = request.headers().getAndConvert(COOKIE);
|
||||
if (cookieString != null) {
|
||||
Set<Cookie> cookies = CookieDecoder.decode(cookieString);
|
||||
if (!cookies.isEmpty()) {
|
||||
|
@ -182,7 +182,7 @@ public final class HttpUploadClient {
|
||||
);
|
||||
|
||||
// send request
|
||||
List<Entry<String, String>> entries = headers.entries();
|
||||
List<Entry<String, String>> entries = headers.entriesConverted();
|
||||
channel.writeAndFlush(request);
|
||||
|
||||
// Wait for the server to close the connection.
|
||||
|
@ -40,8 +40,8 @@ public class HttpUploadClientHandler extends SimpleChannelInboundHandler<HttpObj
|
||||
System.err.println("VERSION: " + response.protocolVersion());
|
||||
|
||||
if (!response.headers().isEmpty()) {
|
||||
for (String name : response.headers().names()) {
|
||||
for (String value : response.headers().getAll(name)) {
|
||||
for (CharSequence name : response.headers().names()) {
|
||||
for (CharSequence value : response.headers().getAll(name)) {
|
||||
System.err.println("HEADER: " + name + " = " + value);
|
||||
}
|
||||
}
|
||||
|
@ -113,14 +113,14 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
|
||||
responseContent.append("\r\n\r\n");
|
||||
|
||||
// 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("\r\n\r\n");
|
||||
|
||||
// new getMethod
|
||||
Set<Cookie> cookies;
|
||||
String value = request.headers().get(COOKIE);
|
||||
String value = request.headers().getAndConvert(COOKIE);
|
||||
if (value == null) {
|
||||
cookies = Collections.emptySet();
|
||||
} else {
|
||||
@ -299,11 +299,11 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
|
||||
if (!close) {
|
||||
// There's no need to add 'Content-Length' header
|
||||
// if this is the last response.
|
||||
response.headers().set(CONTENT_LENGTH, buf.readableBytes());
|
||||
response.headers().setInt(CONTENT_LENGTH, buf.readableBytes());
|
||||
}
|
||||
|
||||
Set<Cookie> cookies;
|
||||
String value = request.headers().get(COOKIE);
|
||||
String value = request.headers().getAndConvert(COOKIE);
|
||||
if (value == null) {
|
||||
cookies = Collections.emptySet();
|
||||
} else {
|
||||
@ -401,7 +401,7 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
|
||||
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
|
||||
|
||||
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.
|
||||
ctx.channel().writeAndFlush(response);
|
||||
|
@ -97,7 +97,7 @@ public final class Http2Client {
|
||||
if (URL != null) {
|
||||
// Create a simple GET request.
|
||||
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.DEFLATE);
|
||||
channel.writeAndFlush(request);
|
||||
@ -108,7 +108,7 @@ public final class Http2Client {
|
||||
// Create a simple POST request with a body.
|
||||
FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, URL2,
|
||||
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.DEFLATE);
|
||||
channel.writeAndFlush(request);
|
||||
|
@ -76,13 +76,12 @@ public class HttpResponseHandler extends SimpleChannelInboundHandler<FullHttpRes
|
||||
|
||||
@Override
|
||||
protected void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
|
||||
String streamIdText = msg.headers().get(HttpUtil.ExtensionHeaderNames.STREAM_ID.text());
|
||||
if (streamIdText == null) {
|
||||
Integer streamId = msg.headers().getInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text());
|
||||
if (streamId == null) {
|
||||
System.err.println("HttpResponseHandler unexpected message received: " + msg);
|
||||
return;
|
||||
}
|
||||
|
||||
int streamId = Integer.parseInt(streamIdText);
|
||||
ChannelPromise promise = streamidPromiseMap.get(streamId);
|
||||
if (promise == null) {
|
||||
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
Loading…
Reference in New Issue
Block a user