Backport header improvements from 5.0

Motivation:
The header class hierarchy and algorithm was improved on the master branch for versions 5.x. These improvments should be backported to the 4.1 baseline.

Modifications:
- cherry-pick the following commits from the master branch: 2374e17, 36b4157, 222d258

Result:
Header improvements in master branch are available in 4.1 branch.
This commit is contained in:
Scott Mitchell 2014-09-18 19:04:35 -04:00 committed by Trustin Lee
parent f2678a31ff
commit 50e06442c3
69 changed files with 7001 additions and 2685 deletions

View File

@ -17,7 +17,9 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.DefaultHeaders.NameConverter;
import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.DefaultTextHeaders;
import io.netty.handler.codec.DefaultTextHeaders.DefaultTextValueTypeConverter;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.TextHeaders;
import java.util.Calendar; import java.util.Calendar;
@ -27,20 +29,228 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
/**
* Default implementation of {@link HttpHeaders}.
*/
public class DefaultHttpHeaders extends HttpHeaders { public class DefaultHttpHeaders extends HttpHeaders {
private static final int HIGHEST_INVALID_NAME_CHAR_MASK = ~63;
private static final int HIGHEST_INVALID_VALUE_CHAR_MASK = ~15;
/**
* A look-up table used for checking if a character in a header name is prohibited.
*/
private static final byte[] LOOKUP_TABLE = new byte[~HIGHEST_INVALID_NAME_CHAR_MASK + 1];
static {
LOOKUP_TABLE['\t'] = -1;
LOOKUP_TABLE['\n'] = -1;
LOOKUP_TABLE[0x0b] = -1;
LOOKUP_TABLE['\f'] = -1;
LOOKUP_TABLE[' '] = -1;
LOOKUP_TABLE[','] = -1;
LOOKUP_TABLE[':'] = -1;
LOOKUP_TABLE[';'] = -1;
LOOKUP_TABLE['='] = -1;
}
private final TextHeaders headers; private final TextHeaders headers;
private static final class HttpHeadersValidationConverter extends DefaultTextValueTypeConverter {
private final boolean validate;
HttpHeadersValidationConverter(boolean validate) {
this.validate = validate;
}
@Override
public CharSequence convertObject(Object value) {
if (value == null) {
throw new NullPointerException("value");
}
CharSequence seq;
if (value instanceof CharSequence) {
seq = (CharSequence) value;
} else if (value instanceof Number) {
seq = value.toString();
} else if (value instanceof Date) {
seq = HttpHeaderDateFormat.get().format((Date) value);
} else if (value instanceof Calendar) {
seq = HttpHeaderDateFormat.get().format(((Calendar) value).getTime());
} else {
seq = value.toString();
}
if (validate) {
if (value instanceof AsciiString) {
validateValue((AsciiString) seq);
} else {
validateValue(seq);
}
}
return seq;
}
private static void validateValue(AsciiString seq) {
int state = 0;
// Start looping through each of the character
final int start = seq.arrayOffset();
final int end = start + seq.length();
final byte[] array = seq.array();
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);
}
}
private static void validateValue(CharSequence seq) {
int state = 0;
// Start looping through each of the character
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);
}
}
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
*/
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);
case '\f':
throw new IllegalArgumentException("a header value contains a prohibited character '\\f': " + seq);
}
}
// Check the CRLF (HT | SP) pattern
switch (state) {
case 0:
switch (character) {
case '\r':
state = 1;
break;
case '\n':
state = 2;
break;
}
break;
case 1:
switch (character) {
case '\n':
state = 2;
break;
default:
throw new IllegalArgumentException("only '\\n' is allowed after '\\r': " + seq);
}
break;
case 2:
switch (character) {
case '\t':
case ' ':
state = 0;
break;
default:
throw new IllegalArgumentException("only ' ' and '\\t' are allowed after '\\n': " + seq);
}
}
return state;
}
}
static class HttpHeadersNameConverter implements NameConverter<CharSequence> {
protected final boolean validate;
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 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 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() { public DefaultHttpHeaders() {
this(true); this(true);
} }
public DefaultHttpHeaders(boolean validate) { public DefaultHttpHeaders(boolean validate) {
headers = validate? new ValidatingTextHeaders() : new NonValidatingTextHeaders(); this(true, validate? VALIDATE_NAME_CONVERTER : NO_VALIDATE_NAME_CONVERTER);
} }
DefaultHttpHeaders(TextHeaders headers) { protected DefaultHttpHeaders(boolean validate, NameConverter<CharSequence> nameConverter) {
this.headers = headers; headers = new DefaultTextHeaders(true,
validate ? VALIDATE_OBJECT_CONVERTER : NO_VALIDATE_OBJECT_CONVERTER, nameConverter);
} }
@Override @Override
@ -65,25 +275,25 @@ public class DefaultHttpHeaders extends HttpHeaders {
@Override @Override
public HttpHeaders add(String name, Object value) { public HttpHeaders add(String name, Object value) {
headers.add(name, value); headers.addObject(name, value);
return this; return this;
} }
@Override @Override
public HttpHeaders add(CharSequence name, Object value) { public HttpHeaders add(CharSequence name, Object value) {
headers.add(name, value); headers.addObject(name, value);
return this; return this;
} }
@Override @Override
public HttpHeaders add(String name, Iterable<?> values) { public HttpHeaders add(String name, Iterable<?> values) {
headers.add(name, values); headers.addObject(name, values);
return this; return this;
} }
@Override @Override
public HttpHeaders add(CharSequence name, Iterable<?> values) { public HttpHeaders add(CharSequence name, Iterable<?> values) {
headers.add(name, values); headers.addObject(name, values);
return this; return this;
} }
@ -101,25 +311,25 @@ public class DefaultHttpHeaders extends HttpHeaders {
@Override @Override
public HttpHeaders set(String name, Object value) { public HttpHeaders set(String name, Object value) {
headers.set(name, value); headers.setObject(name, value);
return this; return this;
} }
@Override @Override
public HttpHeaders set(CharSequence name, Object value) { public HttpHeaders set(CharSequence name, Object value) {
headers.set(name, value); headers.setObject(name, value);
return this; return this;
} }
@Override @Override
public HttpHeaders set(String name, Iterable<?> values) { public HttpHeaders set(String name, Iterable<?> values) {
headers.set(name, values); headers.setObject(name, values);
return this; return this;
} }
@Override @Override
public HttpHeaders set(CharSequence name, Iterable<?> values) { public HttpHeaders set(CharSequence name, Iterable<?> values) {
headers.set(name, values); headers.setObject(name, values);
return this; return this;
} }
@ -131,32 +341,32 @@ public class DefaultHttpHeaders extends HttpHeaders {
@Override @Override
public String get(String name) { public String get(String name) {
return headers.get(name); return headers.getAndConvert(name);
} }
@Override @Override
public String get(CharSequence name) { public String get(CharSequence name) {
return headers.get(name); return headers.getAndConvert(name);
} }
@Override @Override
public List<String> getAll(String name) { public List<String> getAll(String name) {
return headers.getAll(name); return headers.getAllAndConvert(name);
} }
@Override @Override
public List<String> getAll(CharSequence name) { public List<String> getAll(CharSequence name) {
return headers.getAll(name); return headers.getAllAndConvert(name);
} }
@Override @Override
public List<Map.Entry<String, String>> entries() { public List<Map.Entry<String, String>> entries() {
return headers.entries(); return headers.entriesConverted();
} }
@Override @Override
public Iterator<Map.Entry<String, String>> iterator() { public Iterator<Map.Entry<String, String>> iterator() {
return headers.iterator(); return headers.iteratorConverted();
} }
@Override @Override
@ -186,207 +396,10 @@ public class DefaultHttpHeaders extends HttpHeaders {
@Override @Override
public Set<String> names() { public Set<String> names() {
return headers.names(); return headers.namesAndConvert(String.CASE_INSENSITIVE_ORDER);
} }
void encode(ByteBuf buf) { void encode(ByteBuf buf) throws Exception {
headers.forEachEntry(new HttpHeadersEncoder(buf)); headers.forEachEntry(new HttpHeadersEncoder(buf));
} }
static class NonValidatingTextHeaders extends DefaultTextHeaders {
@Override
protected CharSequence convertValue(Object value) {
if (value == null) {
throw new NullPointerException("value");
}
CharSequence seq;
if (value instanceof CharSequence) {
seq = (CharSequence) value;
} else if (value instanceof Number) {
seq = value.toString();
} else if (value instanceof Date) {
seq = HttpHeaderDateFormat.get().format((Date) value);
} else if (value instanceof Calendar) {
seq = HttpHeaderDateFormat.get().format(((Calendar) value).getTime());
} else {
seq = value.toString();
}
return seq;
}
}
static class ValidatingTextHeaders extends NonValidatingTextHeaders {
private static final int HIGHEST_INVALID_NAME_CHAR_MASK = ~63;
private static final int HIGHEST_INVALID_VALUE_CHAR_MASK = ~15;
/**
* A look-up table used for checking if a character in a header name is prohibited.
*/
private static final byte[] LOOKUP_TABLE = new byte[~HIGHEST_INVALID_NAME_CHAR_MASK + 1];
static {
LOOKUP_TABLE['\t'] = -1;
LOOKUP_TABLE['\n'] = -1;
LOOKUP_TABLE[0x0b] = -1;
LOOKUP_TABLE['\f'] = -1;
LOOKUP_TABLE[' '] = -1;
LOOKUP_TABLE[','] = -1;
LOOKUP_TABLE[':'] = -1;
LOOKUP_TABLE[';'] = -1;
LOOKUP_TABLE['='] = -1;
}
@Override
protected CharSequence convertName(CharSequence name) {
name = super.convertName(name);
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) {
CharSequence seq = super.convertValue(value);
if (value instanceof AsciiString) {
validateValue((AsciiString) seq);
} else {
validateValue(seq);
}
return seq;
}
private static void validateValue(AsciiString seq) {
int state = 0;
// Start looping through each of the character
final int start = seq.arrayOffset();
final int end = start + seq.length();
final byte[] array = seq.array();
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);
}
}
private static void validateValue(CharSequence seq) {
int state = 0;
// Start looping through each of the character
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);
}
}
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
*/
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);
case '\f':
throw new IllegalArgumentException(
"a header value contains a prohibited character '\\f': " + seq);
}
}
// Check the CRLF (HT | SP) pattern
switch (state) {
case 0:
switch (character) {
case '\r':
state = 1;
break;
case '\n':
state = 2;
break;
}
break;
case 1:
switch (character) {
case '\n':
state = 2;
break;
default:
throw new IllegalArgumentException(
"only '\\n' is allowed after '\\r': " + seq);
}
break;
case 2:
switch (character) {
case '\t': case ' ':
state = 0;
break;
default:
throw new IllegalArgumentException(
"only ' ' and '\\t' are allowed after '\\n': " + seq);
}
}
return state;
}
}
} }

View File

@ -17,7 +17,7 @@ package io.netty.handler.codec.http;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import java.util.Map; import java.util.Map.Entry;
/** /**
* The default {@link HttpMessage} implementation. * The default {@link HttpMessage} implementation.
@ -88,7 +88,11 @@ public abstract class DefaultHttpMessage extends DefaultHttpObject implements Ht
} }
void appendHeaders(StringBuilder buf) { void appendHeaders(StringBuilder buf) {
for (Map.Entry<String, String> e: headers()) { appendHeaders(buf, headers());
}
void appendHeaders(StringBuilder buf, HttpHeaders headers) {
for (Entry<String, String> e: headers) {
buf.append(e.getKey()); buf.append(e.getKey());
buf.append(": "); buf.append(": ");
buf.append(e.getValue()); buf.append(e.getValue());

View File

@ -18,11 +18,11 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.DefaultHttpHeaders.NonValidatingTextHeaders;
import io.netty.handler.codec.http.DefaultHttpHeaders.ValidatingTextHeaders;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import java.util.Map; import java.util.Map.Entry;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
/** /**
* The default {@link LastHttpContent} implementation. * The default {@link LastHttpContent} implementation.
@ -42,8 +42,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
public DefaultLastHttpContent(ByteBuf content, boolean validateHeaders) { public DefaultLastHttpContent(ByteBuf content, boolean validateHeaders) {
super(content); super(content);
trailingHeaders = new DefaultHttpHeaders( trailingHeaders = new TrailingHttpHeaders(validateHeaders);
validateHeaders? new ValidatingTrailingTextHeaders() : new NonValidatingTextHeaders());
this.validateHeaders = validateHeaders; this.validateHeaders = validateHeaders;
} }
@ -102,7 +101,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
} }
private void appendHeaders(StringBuilder buf) { private void appendHeaders(StringBuilder buf) {
for (Map.Entry<String, String> e: trailingHeaders()) { for (Entry<String, String> e : trailingHeaders()) {
buf.append(e.getKey()); buf.append(e.getKey());
buf.append(": "); buf.append(": ");
buf.append(e.getValue()); buf.append(e.getValue());
@ -110,17 +109,33 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
} }
} }
private static final class ValidatingTrailingTextHeaders extends ValidatingTextHeaders { private static final class TrailingHttpHeaders extends DefaultHttpHeaders {
@Override private static final class TrailingHttpHeadersNameConverter extends HttpHeadersNameConverter {
protected CharSequence convertName(CharSequence name) { TrailingHttpHeadersNameConverter(boolean validate) {
name = super.convertName(name); super(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);
} }
return name;
@Override
public CharSequence convertName(CharSequence name) {
name = super.convertName(name);
if (validate) {
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);
} }
} }
} }

View File

@ -0,0 +1,263 @@
/*
* 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.http;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpHeaders.Values;
import java.util.Iterator;
import java.util.List;
public final class HttpHeaderUtil {
/**
* Returns {@code true} if and only if the connection can remain open and
* thus 'kept alive'. This methods respects the value of the
* {@code "Connection"} header first and then the return value of
* {@link HttpVersion#isKeepAliveDefault()}.
*/
public static boolean isKeepAlive(HttpMessage message) {
CharSequence connection = message.headers().get(Names.CONNECTION);
if (connection != null && AsciiString.equalsIgnoreCase(Values.CLOSE, connection)) {
return false;
}
if (message.protocolVersion().isKeepAliveDefault()) {
return !AsciiString.equalsIgnoreCase(Values.CLOSE, connection);
} else {
return AsciiString.equalsIgnoreCase(Values.KEEP_ALIVE, connection);
}
}
/**
* Sets the value of the {@code "Connection"} header depending on the
* protocol version of the specified message. This getMethod sets or removes
* the {@code "Connection"} header depending on what the default keep alive
* mode of the message's protocol version is, as specified by
* {@link HttpVersion#isKeepAliveDefault()}.
* <ul>
* <li>If the connection is kept alive by default:
* <ul>
* <li>set to {@code "close"} if {@code keepAlive} is {@code false}.</li>
* <li>remove otherwise.</li>
* </ul></li>
* <li>If the connection is closed by default:
* <ul>
* <li>set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.</li>
* <li>remove otherwise.</li>
* </ul></li>
* </ul>
*/
public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
HttpHeaders h = message.headers();
if (message.protocolVersion().isKeepAliveDefault()) {
if (keepAlive) {
h.remove(Names.CONNECTION);
} else {
h.set(Names.CONNECTION, Values.CLOSE);
}
} else {
if (keepAlive) {
h.set(Names.CONNECTION, Values.KEEP_ALIVE);
} else {
h.remove(Names.CONNECTION);
}
}
}
/**
* Returns the length of the content. Please note that this value is
* not retrieved from {@link HttpContent#content()} but from the
* {@code "Content-Length"} header, and thus they are independent from each
* other.
*
* @return the content length
*
* @throws NumberFormatException
* if the message does not have the {@code "Content-Length"} header
* or its value is not a number
*/
public static long getContentLength(HttpMessage message) {
String value = message.headers().get(Names.CONTENT_LENGTH);
if (value != null) {
return Long.parseLong(value);
}
// We know the content length if it's a Web Socket message even if
// Content-Length header is missing.
long webSocketContentLength = getWebSocketContentLength(message);
if (webSocketContentLength >= 0) {
return webSocketContentLength;
}
// Otherwise we don't.
throw new NumberFormatException("header not found: " + Names.CONTENT_LENGTH);
}
/**
* Returns the length of the content. Please note that this value is
* not retrieved from {@link HttpContent#content()} but from the
* {@code "Content-Length"} header, and thus they are independent from each
* other.
*
* @return the content length or {@code defaultValue} if this message does
* not have the {@code "Content-Length"} header or its value is not
* a number
*/
public static long getContentLength(HttpMessage message, long defaultValue) {
String value = message.headers().get(Names.CONTENT_LENGTH);
if (value != null) {
return Long.parseLong(value);
}
// We know the content length if it's a Web Socket message even if
// Content-Length header is missing.
long webSocketContentLength = getWebSocketContentLength(message);
if (webSocketContentLength >= 0) {
return webSocketContentLength;
}
// Otherwise we don't.
return defaultValue;
}
/**
* Returns the content length of the specified web socket message. If the
* specified message is not a web socket message, {@code -1} is returned.
*/
private static int getWebSocketContentLength(HttpMessage message) {
// WebSockset messages have constant content-lengths.
HttpHeaders h = message.headers();
if (message instanceof HttpRequest) {
HttpRequest req = (HttpRequest) message;
if (HttpMethod.GET.equals(req.method()) &&
h.contains(Names.SEC_WEBSOCKET_KEY1) &&
h.contains(Names.SEC_WEBSOCKET_KEY2)) {
return 8;
}
} else if (message instanceof HttpResponse) {
HttpResponse res = (HttpResponse) message;
if (res.status().code() == 101 &&
h.contains(Names.SEC_WEBSOCKET_ORIGIN) &&
h.contains(Names.SEC_WEBSOCKET_LOCATION)) {
return 16;
}
}
// Not a web socket message
return -1;
}
/**
* Sets the {@code "Content-Length"} header.
*/
public static void setContentLength(HttpMessage message, long length) {
message.headers().set(Names.CONTENT_LENGTH, length);
}
public static boolean isContentLengthSet(HttpMessage m) {
return m.headers().contains(Names.CONTENT_LENGTH);
}
/**
* Returns {@code true} if and only if the specified message contains the
* {@code "Expect: 100-continue"} header.
*/
public static boolean is100ContinueExpected(HttpMessage message) {
// Expect: 100-continue is for requests only.
if (!(message instanceof HttpRequest)) {
return false;
}
// It works only on HTTP/1.1 or later.
if (message.protocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
return false;
}
// In most cases, there will be one or zero 'Expect' header.
CharSequence value = message.headers().get(Names.EXPECT);
if (value == null) {
return false;
}
if (AsciiString.equalsIgnoreCase(Values.CONTINUE, value)) {
return true;
}
// Multiple 'Expect' headers. Search through them.
return message.headers().contains(Names.EXPECT, Values.CONTINUE, true);
}
/**
* Sets or removes the {@code "Expect: 100-continue"} header to / from the
* specified message. If the specified {@code value} is {@code true},
* the {@code "Expect: 100-continue"} header is set and all other previous
* {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"}
* headers are removed completely.
*/
public static void set100ContinueExpected(HttpMessage message, boolean expected) {
if (expected) {
message.headers().set(Names.EXPECT, Values.CONTINUE);
} else {
message.headers().remove(Names.EXPECT);
}
}
/**
* Checks to see if the transfer encoding in a specified {@link HttpMessage} is chunked
*
* @param message The message to check
* @return True if transfer encoding is chunked, otherwise false
*/
public static boolean isTransferEncodingChunked(HttpMessage message) {
return message.headers().contains(Names.TRANSFER_ENCODING, Values.CHUNKED, true);
}
public static void setTransferEncodingChunked(HttpMessage m, boolean chunked) {
if (chunked) {
m.headers().add(Names.TRANSFER_ENCODING, Values.CHUNKED);
m.headers().remove(Names.CONTENT_LENGTH);
} else {
List<String> values = m.headers().getAll(Names.TRANSFER_ENCODING);
if (values.isEmpty()) {
return;
}
Iterator<String> valuesIt = values.iterator();
while (valuesIt.hasNext()) {
CharSequence value = valuesIt.next();
if (AsciiString.equalsIgnoreCase(value, Values.CHUNKED)) {
valuesIt.remove();
}
}
if (values.isEmpty()) {
m.headers().remove(Names.TRANSFER_ENCODING);
} else {
m.headers().set(Names.TRANSFER_ENCODING, values);
}
}
}
static void encodeAscii0(CharSequence seq, ByteBuf buf) {
int length = seq.length();
for (int i = 0 ; i < length; i++) {
buf.writeByte((byte) seq.charAt(i));
}
}
private HttpHeaderUtil() { }
}

View File

@ -15,6 +15,10 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.HttpConstants.COLON;
import static io.netty.handler.codec.http.HttpConstants.CR;
import static io.netty.handler.codec.http.HttpConstants.LF;
import static io.netty.handler.codec.http.HttpConstants.SP;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.AsciiString;
@ -28,8 +32,6 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import static io.netty.handler.codec.http.HttpConstants.*;
/** /**
* Provides the constants for the standard HTTP header names and values and * Provides the constants for the standard HTTP header names and values and
* commonly used utility methods that accesses an {@link HttpMessage}. * commonly used utility methods that accesses an {@link HttpMessage}.
@ -38,20 +40,20 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
private static final byte[] HEADER_SEPERATOR = { COLON, SP }; private static final byte[] HEADER_SEPERATOR = { COLON, SP };
private static final byte[] CRLF = { CR, LF }; private static final byte[] CRLF = { CR, LF };
private static final CharSequence CONTENT_LENGTH_ENTITY = newEntity(Names.CONTENT_LENGTH); private static final CharSequence CONTENT_LENGTH_ENTITY = Names.CONTENT_LENGTH;
private static final CharSequence CONNECTION_ENTITY = newEntity(Names.CONNECTION); private static final CharSequence CONNECTION_ENTITY = Names.CONNECTION;
private static final CharSequence CLOSE_ENTITY = newEntity(Values.CLOSE); private static final CharSequence CLOSE_ENTITY = Values.CLOSE;
private static final CharSequence KEEP_ALIVE_ENTITY = newEntity(Values.KEEP_ALIVE); private static final CharSequence KEEP_ALIVE_ENTITY = Values.KEEP_ALIVE;
private static final CharSequence HOST_ENTITY = newEntity(Names.HOST); private static final CharSequence HOST_ENTITY = Names.HOST;
private static final CharSequence DATE_ENTITY = newEntity(Names.DATE); private static final CharSequence DATE_ENTITY = Names.DATE;
private static final CharSequence EXPECT_ENTITY = newEntity(Names.EXPECT); private static final CharSequence EXPECT_ENTITY = Names.EXPECT;
private static final CharSequence CONTINUE_ENTITY = newEntity(Values.CONTINUE); private static final CharSequence CONTINUE_ENTITY = Values.CONTINUE;
private static final CharSequence TRANSFER_ENCODING_ENTITY = newEntity(Names.TRANSFER_ENCODING); private static final CharSequence TRANSFER_ENCODING_ENTITY = Names.TRANSFER_ENCODING;
private static final CharSequence CHUNKED_ENTITY = newEntity(Values.CHUNKED); private static final CharSequence CHUNKED_ENTITY = Values.CHUNKED;
private static final CharSequence SEC_WEBSOCKET_KEY1_ENTITY = newEntity(Names.SEC_WEBSOCKET_KEY1); private static final CharSequence SEC_WEBSOCKET_KEY1_ENTITY = Names.SEC_WEBSOCKET_KEY1;
private static final CharSequence SEC_WEBSOCKET_KEY2_ENTITY = newEntity(Names.SEC_WEBSOCKET_KEY2); private static final CharSequence SEC_WEBSOCKET_KEY2_ENTITY = Names.SEC_WEBSOCKET_KEY2;
private static final CharSequence SEC_WEBSOCKET_ORIGIN_ENTITY = newEntity(Names.SEC_WEBSOCKET_ORIGIN); private static final CharSequence SEC_WEBSOCKET_ORIGIN_ENTITY = Names.SEC_WEBSOCKET_ORIGIN;
private static final CharSequence SEC_WEBSOCKET_LOCATION_ENTITY = newEntity(Names.SEC_WEBSOCKET_LOCATION); private static final CharSequence SEC_WEBSOCKET_LOCATION_ENTITY = Names.SEC_WEBSOCKET_LOCATION;
public static final HttpHeaders EMPTY_HEADERS = new HttpHeaders() { public static final HttpHeaders EMPTY_HEADERS = new HttpHeaders() {
@Override @Override
@ -122,300 +124,330 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
/** /**
* Standard HTTP header names. * Standard HTTP header names.
* <p>
* These are all defined as lowercase to support HTTP/2 requirements while also not
* violating HTTP/1.x requirements. New header names should always be lowercase.
*/ */
public static final class Names { public static final class Names {
/** /**
* {@code "Accept"} * {@code "accept"}
*/ */
public static final String ACCEPT = "Accept"; public static final AsciiString ACCEPT = new AsciiString("accept");
/** /**
* {@code "Accept-Charset"} * {@code "accept-charset"}
*/ */
public static final String ACCEPT_CHARSET = "Accept-Charset"; public static final AsciiString ACCEPT_CHARSET = new AsciiString("accept-charset");
/** /**
* {@code "Accept-Encoding"} * {@code "accept-encoding"}
*/ */
public static final String ACCEPT_ENCODING = "Accept-Encoding"; public static final AsciiString ACCEPT_ENCODING = new AsciiString("accept-encoding");
/** /**
* {@code "Accept-Language"} * {@code "accept-language"}
*/ */
public static final String ACCEPT_LANGUAGE = "Accept-Language"; public static final AsciiString ACCEPT_LANGUAGE = new AsciiString("accept-language");
/** /**
* {@code "Accept-Ranges"} * {@code "accept-ranges"}
*/ */
public static final String ACCEPT_RANGES = "Accept-Ranges"; public static final AsciiString ACCEPT_RANGES = new AsciiString("accept-ranges");
/** /**
* {@code "Accept-Patch"} * {@code "accept-patch"}
*/ */
public static final String ACCEPT_PATCH = "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 String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; public static final AsciiString ACCESS_CONTROL_ALLOW_CREDENTIALS =
new AsciiString("access-control-allow-credentials");
/** /**
* {@code "Access-Control-Allow-Headers"} * {@code "access-control-allow-headers"}
*/ */
public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; public static final AsciiString ACCESS_CONTROL_ALLOW_HEADERS =
new AsciiString("access-control-allow-headers");
/** /**
* {@code "Access-Control-Allow-Methods"} * {@code "access-control-allow-methods"}
*/ */
public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; public static final AsciiString ACCESS_CONTROL_ALLOW_METHODS =
new AsciiString("access-control-allow-methods");
/** /**
* {@code "Access-Control-Allow-Origin"} * {@code "access-control-allow-origin"}
*/ */
public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; public static final AsciiString ACCESS_CONTROL_ALLOW_ORIGIN =
new AsciiString("access-control-allow-origin");
/** /**
* {@code "Access-Control-Expose-Headers"} * {@code "access-control-expose-headers"}
*/ */
public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"; public static final AsciiString ACCESS_CONTROL_EXPOSE_HEADERS =
new AsciiString("access-control-expose-headers");
/** /**
* {@code "Access-Control-Max-Age"} * {@code "access-control-max-age"}
*/ */
public static final String ACCESS_CONTROL_MAX_AGE = "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 String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; public static final AsciiString ACCESS_CONTROL_REQUEST_HEADERS =
new AsciiString("access-control-request-headers");
/** /**
* {@code "Access-Control-Request-Method"} * {@code "access-control-request-method"}
*/ */
public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; public static final AsciiString ACCESS_CONTROL_REQUEST_METHOD =
new AsciiString("access-control-request-method");
/** /**
* {@code "Age"} * {@code "age"}
*/ */
public static final String AGE = "Age"; public static final AsciiString AGE = new AsciiString("age");
/** /**
* {@code "Allow"} * {@code "allow"}
*/ */
public static final String ALLOW = "Allow"; public static final AsciiString ALLOW = new AsciiString("allow");
/** /**
* {@code "Authorization"} * {@code "authorization"}
*/ */
public static final String AUTHORIZATION = "Authorization"; public static final AsciiString AUTHORIZATION = new AsciiString("authorization");
/** /**
* {@code "Cache-Control"} * {@code "cache-control"}
*/ */
public static final String CACHE_CONTROL = "Cache-Control"; public static final AsciiString CACHE_CONTROL = new AsciiString("cache-control");
/** /**
* {@code "Connection"} * {@code "connection"}
*/ */
public static final String CONNECTION = "Connection"; public static final AsciiString CONNECTION = new AsciiString("connection");
/** /**
* {@code "Content-Base"} * {@code "content-base"}
*/ */
public static final String CONTENT_BASE = "Content-Base"; public static final AsciiString CONTENT_BASE = new AsciiString("content-base");
/** /**
* {@code "Content-Encoding"} * {@code "content-encoding"}
*/ */
public static final String CONTENT_ENCODING = "Content-Encoding"; public static final AsciiString CONTENT_ENCODING = new AsciiString("content-encoding");
/** /**
* {@code "Content-Language"} * {@code "content-language"}
*/ */
public static final String CONTENT_LANGUAGE = "Content-Language"; public static final AsciiString CONTENT_LANGUAGE = new AsciiString("content-language");
/** /**
* {@code "Content-Length"} * {@code "content-length"}
*/ */
public static final String CONTENT_LENGTH = "Content-Length"; public static final AsciiString CONTENT_LENGTH = new AsciiString("content-length");
/** /**
* {@code "Content-Location"} * {@code "content-location"}
*/ */
public static final String CONTENT_LOCATION = "Content-Location"; public static final AsciiString CONTENT_LOCATION = new AsciiString("content-location");
/** /**
* {@code "Content-Transfer-Encoding"} * {@code "content-transfer-encoding"}
*/ */
public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding"; public static final AsciiString CONTENT_TRANSFER_ENCODING = new AsciiString("content-transfer-encoding");
/** /**
* {@code "Content-MD5"} * {@code "content-disposition"}
*/ */
public static final String CONTENT_MD5 = "Content-MD5"; public static final AsciiString CONTENT_DISPOSITION = new AsciiString("content-disposition");
/** /**
* {@code "Content-Range"} * {@code "content-md5"}
*/ */
public static final String CONTENT_RANGE = "Content-Range"; public static final AsciiString CONTENT_MD5 = new AsciiString("content-md5");
/** /**
* {@code "Content-Type"} * {@code "content-range"}
*/ */
public static final String CONTENT_TYPE = "Content-Type"; public static final AsciiString CONTENT_RANGE = new AsciiString("content-range");
/** /**
* {@code "Cookie"} * {@code "content-type"}
*/ */
public static final String COOKIE = "Cookie"; public static final AsciiString CONTENT_TYPE = new AsciiString("content-type");
/** /**
* {@code "Date"} * {@code "cookie"}
*/ */
public static final String DATE = "Date"; public static final AsciiString COOKIE = new AsciiString("cookie");
/** /**
* {@code "ETag"} * {@code "date"}
*/ */
public static final String ETAG = "ETag"; public static final AsciiString DATE = new AsciiString("date");
/** /**
* {@code "Expect"} * {@code "etag"}
*/ */
public static final String EXPECT = "Expect"; public static final AsciiString ETAG = new AsciiString("etag");
/** /**
* {@code "Expires"} * {@code "expect"}
*/ */
public static final String EXPIRES = "Expires"; public static final AsciiString EXPECT = new AsciiString("expect");
/** /**
* {@code "From"} * {@code "expires"}
*/ */
public static final String FROM = "From"; public static final AsciiString EXPIRES = new AsciiString("expires");
/** /**
* {@code "Host"} * {@code "from"}
*/ */
public static final String HOST = "Host"; public static final AsciiString FROM = new AsciiString("from");
/** /**
* {@code "If-Match"} * {@code "host"}
*/ */
public static final String IF_MATCH = "If-Match"; public static final AsciiString HOST = new AsciiString("host");
/** /**
* {@code "If-Modified-Since"} * {@code "if-match"}
*/ */
public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; public static final AsciiString IF_MATCH = new AsciiString("if-match");
/** /**
* {@code "If-None-Match"} * {@code "if-modified-since"}
*/ */
public static final String IF_NONE_MATCH = "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 String IF_RANGE = "If-Range"; public static final AsciiString IF_NONE_MATCH = new AsciiString("if-none-match");
/** /**
* {@code "If-Unmodified-Since"} * {@code "if-range"}
*/ */
public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; public static final AsciiString IF_RANGE = new AsciiString("if-range");
/** /**
* {@code "Last-Modified"} * {@code "if-unmodified-since"}
*/ */
public static final String LAST_MODIFIED = "Last-Modified"; public static final AsciiString IF_UNMODIFIED_SINCE = new AsciiString("if-unmodified-since");
/** /**
* {@code "Location"} * {@code "last-modified"}
*/ */
public static final String LOCATION = "Location"; public static final AsciiString LAST_MODIFIED = new AsciiString("last-modified");
/** /**
* {@code "Max-Forwards"} * {@code "location"}
*/ */
public static final String MAX_FORWARDS = "Max-Forwards"; public static final AsciiString LOCATION = new AsciiString("location");
/** /**
* {@code "Origin"} * {@code "max-forwards"}
*/ */
public static final String ORIGIN = "Origin"; public static final AsciiString MAX_FORWARDS = new AsciiString("max-forwards");
/** /**
* {@code "Pragma"} * {@code "origin"}
*/ */
public static final String PRAGMA = "Pragma"; public static final AsciiString ORIGIN = new AsciiString("origin");
/** /**
* {@code "Proxy-Authenticate"} * {@code "pragma"}
*/ */
public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; public static final AsciiString PRAGMA = new AsciiString("pragma");
/** /**
* {@code "Proxy-Authorization"} * {@code "proxy-authenticate"}
*/ */
public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; public static final AsciiString PROXY_AUTHENTICATE = new AsciiString("proxy-authenticate");
/** /**
* {@code "Range"} * {@code "proxy-authorization"}
*/ */
public static final String RANGE = "Range"; public static final AsciiString PROXY_AUTHORIZATION = new AsciiString("proxy-authorization");
/** /**
* {@code "Referer"} * {@code "range"}
*/ */
public static final String REFERER = "Referer"; public static final AsciiString RANGE = new AsciiString("range");
/** /**
* {@code "Retry-After"} * {@code "referer"}
*/ */
public static final String RETRY_AFTER = "Retry-After"; public static final AsciiString REFERER = new AsciiString("referer");
/** /**
* {@code "Sec-WebSocket-Key1"} * {@code "retry-after"}
*/ */
public static final String SEC_WEBSOCKET_KEY1 = "Sec-WebSocket-Key1"; public static final AsciiString RETRY_AFTER = new AsciiString("retry-after");
/** /**
* {@code "Sec-WebSocket-Key2"} * {@code "sec-websocket-key1"}
*/ */
public static final String SEC_WEBSOCKET_KEY2 = "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 String SEC_WEBSOCKET_LOCATION = "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 String SEC_WEBSOCKET_ORIGIN = "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 String SEC_WEBSOCKET_PROTOCOL = "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 String SEC_WEBSOCKET_VERSION = "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 String SEC_WEBSOCKET_KEY = "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 String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept"; public static final AsciiString SEC_WEBSOCKET_KEY = new AsciiString("sec-websocket-key");
/** /**
* {@code "Server"} * {@code "sec-websocket-accept"}
*/ */
public static final String SERVER = "Server"; public static final AsciiString SEC_WEBSOCKET_ACCEPT = new AsciiString("sec-websocket-accept");
/** /**
* {@code "Set-Cookie"} * {@code "sec-websocket-protocol"}
*/ */
public static final String SET_COOKIE = "Set-Cookie"; public static final AsciiString SEC_WEBSOCKET_EXTENSIONS = new AsciiString("sec-websocket-extensions");
/** /**
* {@code "Set-Cookie2"} * {@code "server"}
*/ */
public static final String SET_COOKIE2 = "Set-Cookie2"; public static final AsciiString SERVER = new AsciiString("server");
/** /**
* {@code "TE"} * {@code "set-cookie"}
*/ */
public static final String TE = "TE"; public static final AsciiString SET_COOKIE = new AsciiString("set-cookie");
/** /**
* {@code "Trailer"} * {@code "set-cookie2"}
*/ */
public static final String TRAILER = "Trailer"; public static final AsciiString SET_COOKIE2 = new AsciiString("set-cookie2");
/** /**
* {@code "Transfer-Encoding"} * {@code "te"}
*/ */
public static final String TRANSFER_ENCODING = "Transfer-Encoding"; public static final AsciiString TE = new AsciiString("te");
/** /**
* {@code "Upgrade"} * {@code "trailer"}
*/ */
public static final String UPGRADE = "Upgrade"; public static final AsciiString TRAILER = new AsciiString("trailer");
/** /**
* {@code "User-Agent"} * {@code "transfer-encoding"}
*/ */
public static final String USER_AGENT = "User-Agent"; public static final AsciiString TRANSFER_ENCODING = new AsciiString("transfer-encoding");
/** /**
* {@code "Vary"} * {@code "upgrade"}
*/ */
public static final String VARY = "Vary"; public static final AsciiString UPGRADE = new AsciiString("upgrade");
/** /**
* {@code "Via"} * {@code "user-agent"}
*/ */
public static final String VIA = "Via"; public static final AsciiString USER_AGENT = new AsciiString("user-agent");
/** /**
* {@code "Warning"} * {@code "vary"}
*/ */
public static final String WARNING = "Warning"; public static final AsciiString VARY = new AsciiString("vary");
/** /**
* {@code "WebSocket-Location"} * {@code "via"}
*/ */
public static final String WEBSOCKET_LOCATION = "WebSocket-Location"; public static final AsciiString VIA = new AsciiString("via");
/** /**
* {@code "WebSocket-Origin"} * {@code "warning"}
*/ */
public static final String WEBSOCKET_ORIGIN = "WebSocket-Origin"; public static final AsciiString WARNING = new AsciiString("warning");
/** /**
* {@code "WebSocket-Protocol"} * {@code "websocket-location"}
*/ */
public static final String WEBSOCKET_PROTOCOL = "WebSocket-Protocol"; public static final AsciiString WEBSOCKET_LOCATION = new AsciiString("websocket-location");
/** /**
* {@code "WWW-Authenticate"} * {@code "websocket-origin"}
*/ */
public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; public static final AsciiString WEBSOCKET_ORIGIN = new AsciiString("websocket-origin");
/**
* {@code "websocket-protocol"}
*/
public static final AsciiString WEBSOCKET_PROTOCOL = new AsciiString("websocket-protocol");
/**
* {@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");
/**
* {@code "proxy-connection"}
* @deprecated use {@link #CONNECTION}
*/
@Deprecated
public static final AsciiString PROXY_CONNECTION = new AsciiString("proxy-connection");
private Names() { private Names() {
} }
@ -428,8 +460,16 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
/** /**
* {@code "application/x-www-form-urlencoded"} * {@code "application/x-www-form-urlencoded"}
*/ */
public static final String APPLICATION_X_WWW_FORM_URLENCODED = public static final AsciiString APPLICATION_X_WWW_FORM_URLENCODED =
"application/x-www-form-urlencoded"; new AsciiString("application/x-www-form-urlencoded");
/**
* {@code "application/octet-stream"}
*/
public static final AsciiString APPLICATION_OCTET_STREAM = new AsciiString("application/octet-stream");
/**
* {@code "text/plain"}
*/
public static final AsciiString TEXT_PLAIN = new AsciiString("text/plain");
/** /**
* {@code "base64"} * {@code "base64"}
*/ */
@ -498,6 +538,10 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
* {@code "multipart/form-data"} * {@code "multipart/form-data"}
*/ */
public static final String MULTIPART_FORM_DATA = "multipart/form-data"; public static final String MULTIPART_FORM_DATA = "multipart/form-data";
/**
* {@code "multipart/mixed"}
*/
public static final AsciiString MULTIPART_MIXED = new AsciiString("multipart/mixed");
/** /**
* {@code "must-revalidate"} * {@code "must-revalidate"}
*/ */
@ -553,7 +597,32 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
/** /**
* {@code "WebSocket"} * {@code "WebSocket"}
*/ */
public static final String WEBSOCKET = "WebSocket"; public static final AsciiString WEBSOCKET = new AsciiString("WebSocket");
/**
* {@code "name"}
* See {@link Names#CONTENT_DISPOSITION}
*/
public static final AsciiString NAME = new AsciiString("name");
/**
* {@code "filename"}
* See {@link Names#CONTENT_DISPOSITION}
*/
public static final AsciiString FILENAME = new AsciiString("filename");
/**
* {@code "form-data"}
* See {@link Names#CONTENT_DISPOSITION}
*/
public static final AsciiString FORM_DATA = new AsciiString("form-data");
/**
* {@code "attachment"}
* See {@link Names#CONTENT_DISPOSITION}
*/
public static final AsciiString ATTACHMENT = new AsciiString("attachment");
/**
* {@code "file"}
* See {@link Names#CONTENT_DISPOSITION}
*/
public static final AsciiString FILE = new AsciiString("file");
private Values() { private Values() {
} }
@ -1193,7 +1262,7 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
return AsciiString.equalsIgnoreCase(name1, name2); return AsciiString.equalsIgnoreCase(name1, name2);
} }
static void encode(HttpHeaders headers, ByteBuf buf) { static void encode(HttpHeaders headers, ByteBuf buf) throws Exception {
if (headers instanceof DefaultHttpHeaders) { if (headers instanceof DefaultHttpHeaders) {
((DefaultHttpHeaders) headers).encode(buf); ((DefaultHttpHeaders) headers).encode(buf);
} else { } else {

View File

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

View File

@ -559,8 +559,12 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<State> {
List<String> current = trailer.trailingHeaders().getAll(lastHeader); List<String> current = trailer.trailingHeaders().getAll(lastHeader);
if (!current.isEmpty()) { if (!current.isEmpty()) {
int lastPos = current.size() - 1; int lastPos = current.size() - 1;
String newString = current.get(lastPos) + line.toString().trim(); String lineTrimmed = line.toString().trim();
current.set(lastPos, newString); CharSequence currentLastPos = current.get(lastPos);
StringBuilder b = new StringBuilder(currentLastPos.length() + lineTrimmed.length());
b.append(currentLastPos);
b.append(lineTrimmed);
current.set(lastPos, b.toString());
} else { } else {
// Content-Length, Transfer-Encoding, or Trailer // Content-Length, Transfer-Encoding, or Trailer
} }

View File

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

View File

@ -88,7 +88,7 @@ public class CorsHandler extends ChannelDuplexHandler {
} }
private boolean setOrigin(final HttpResponse response) { private boolean setOrigin(final HttpResponse response) {
final String origin = request.headers().get(ORIGIN); final CharSequence origin = request.headers().get(ORIGIN);
if (origin != null) { if (origin != null) {
if ("null".equals(origin) && config.isNullOriginAllowed()) { if ("null".equals(origin) && config.isNullOriginAllowed()) {
setAnyOrigin(response); setAnyOrigin(response);
@ -118,7 +118,7 @@ public class CorsHandler extends ChannelDuplexHandler {
return true; return true;
} }
final String origin = request.headers().get(ORIGIN); final CharSequence origin = request.headers().get(ORIGIN);
if (origin == null) { if (origin == null) {
// Not a CORS request so we cannot validate it. It may be a non CORS request. // Not a CORS request so we cannot validate it. It may be a non CORS request.
return true; return true;
@ -143,7 +143,7 @@ public class CorsHandler extends ChannelDuplexHandler {
setOrigin(response, "*"); setOrigin(response, "*");
} }
private static void setOrigin(final HttpResponse response, final String origin) { private static void setOrigin(final HttpResponse response, final CharSequence origin) {
response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, origin); response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
} }

View File

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

View File

@ -16,6 +16,7 @@
package io.netty.handler.codec.http.multipart; package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.HttpConstants; import io.netty.handler.codec.http.HttpConstants;
import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
@ -695,10 +696,10 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
currentFieldAttributes.put(attribute.getName(), attribute); currentFieldAttributes.put(attribute.getName(), attribute);
} }
} }
} else if (contents[0].equalsIgnoreCase(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING)) { } else if (AsciiString.equalsIgnoreCase(contents[0], HttpHeaders.Names.CONTENT_TRANSFER_ENCODING)) {
Attribute attribute; Attribute attribute;
try { try {
attribute = factory.createAttribute(request, HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, attribute = factory.createAttribute(request, HttpHeaders.Names.CONTENT_TRANSFER_ENCODING.toString(),
cleanString(contents[1])); cleanString(contents[1]));
} catch (NullPointerException e) { } catch (NullPointerException e) {
throw new ErrorDataDecoderException(e); throw new ErrorDataDecoderException(e);
@ -706,10 +707,10 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
throw new ErrorDataDecoderException(e); throw new ErrorDataDecoderException(e);
} }
currentFieldAttributes.put(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, attribute); currentFieldAttributes.put(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, attribute);
} else if (contents[0].equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH)) { } else if (AsciiString.equalsIgnoreCase(contents[0], HttpHeaders.Names.CONTENT_LENGTH)) {
Attribute attribute; Attribute attribute;
try { try {
attribute = factory.createAttribute(request, HttpHeaders.Names.CONTENT_LENGTH, attribute = factory.createAttribute(request, HttpHeaders.Names.CONTENT_LENGTH.toString(),
cleanString(contents[1])); cleanString(contents[1]));
} catch (NullPointerException e) { } catch (NullPointerException e) {
throw new ErrorDataDecoderException(e); throw new ErrorDataDecoderException(e);
@ -717,7 +718,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
throw new ErrorDataDecoderException(e); throw new ErrorDataDecoderException(e);
} }
currentFieldAttributes.put(HttpHeaders.Names.CONTENT_LENGTH, attribute); currentFieldAttributes.put(HttpHeaders.Names.CONTENT_LENGTH, attribute);
} else if (contents[0].equalsIgnoreCase(HttpHeaders.Names.CONTENT_TYPE)) { } else if (AsciiString.equalsIgnoreCase(contents[0], HttpHeaders.Names.CONTENT_TYPE)) {
// Take care of possible "multipart/mixed" // Take care of possible "multipart/mixed"
if (contents[1].equalsIgnoreCase(HttpPostBodyUtil.MULTIPART_MIXED)) { if (contents[1].equalsIgnoreCase(HttpPostBodyUtil.MULTIPART_MIXED)) {
if (currentStatus == MultiPartStatus.DISPOSITION) { if (currentStatus == MultiPartStatus.DISPOSITION) {

View File

@ -716,7 +716,7 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
// "multipart/form-data; boundary=--89421926422648" // "multipart/form-data; boundary=--89421926422648"
String lowercased = contentType.toLowerCase(); String lowercased = contentType.toLowerCase();
if (lowercased.startsWith(HttpHeaders.Values.MULTIPART_FORM_DATA) || if (lowercased.startsWith(HttpHeaders.Values.MULTIPART_FORM_DATA) ||
lowercased.startsWith(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) { lowercased.startsWith(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED.toString())) {
// ignore // ignore
} else { } else {
headers.add(HttpHeaders.Names.CONTENT_TYPE, contentType); headers.add(HttpHeaders.Names.CONTENT_TYPE, contentType);
@ -744,7 +744,7 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
isChunked = true; isChunked = true;
if (transferEncoding != null) { if (transferEncoding != null) {
headers.remove(HttpHeaders.Names.TRANSFER_ENCODING); headers.remove(HttpHeaders.Names.TRANSFER_ENCODING);
for (String v : transferEncoding) { for (CharSequence v : transferEncoding) {
if (AsciiString.equalsIgnoreCase(v, HttpHeaders.Values.CHUNKED)) { if (AsciiString.equalsIgnoreCase(v, HttpHeaders.Values.CHUNKED)) {
// ignore // ignore
} else { } else {

View File

@ -198,13 +198,13 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
HttpHeaders headers = response.headers(); HttpHeaders headers = response.headers();
String upgrade = headers.get(Names.UPGRADE); CharSequence upgrade = headers.get(Names.UPGRADE);
if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
+ upgrade); + upgrade);
} }
String connection = headers.get(Names.CONNECTION); CharSequence connection = headers.get(Names.CONNECTION);
if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " throw new WebSocketHandshakeException("Invalid handshake response connection: "
+ connection); + connection);

View File

@ -41,7 +41,7 @@ import java.net.URI;
public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker { public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker07.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker07.class);
private static final CharSequence WEBSOCKET = HttpHeaders.newEntity(Values.WEBSOCKET.toLowerCase()); private static final CharSequence WEBSOCKET = Values.WEBSOCKET.toLowerCase();
public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private String expectedChallengeResponseString; private String expectedChallengeResponseString;
@ -207,17 +207,17 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status()); throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status());
} }
String upgrade = headers.get(Names.UPGRADE); CharSequence upgrade = headers.get(Names.UPGRADE);
if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
} }
String connection = headers.get(Names.CONNECTION); CharSequence connection = headers.get(Names.CONNECTION);
if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
} }
String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); CharSequence accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) { if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format( throw new WebSocketHandshakeException(String.format(
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString)); "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));

View File

@ -41,7 +41,7 @@ import java.net.URI;
public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker08.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker08.class);
private static final CharSequence WEBSOCKET = HttpHeaders.newEntity(Values.WEBSOCKET.toLowerCase()); private static final CharSequence WEBSOCKET = Values.WEBSOCKET.toLowerCase();
public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
@ -208,17 +208,17 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status()); throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status());
} }
String upgrade = headers.get(Names.UPGRADE); CharSequence upgrade = headers.get(Names.UPGRADE);
if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
} }
String connection = headers.get(Names.CONNECTION); CharSequence connection = headers.get(Names.CONNECTION);
if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
} }
String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); CharSequence accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) { if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format( throw new WebSocketHandshakeException(String.format(
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString)); "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));

View File

@ -41,7 +41,7 @@ import java.net.URI;
public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker13.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker13.class);
private static final CharSequence WEBSOCKET = HttpHeaders.newEntity(Values.WEBSOCKET.toLowerCase()); private static final CharSequence WEBSOCKET = Values.WEBSOCKET.toLowerCase();
public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
@ -218,17 +218,17 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status()); throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status());
} }
String upgrade = headers.get(Names.UPGRADE); CharSequence upgrade = headers.get(Names.UPGRADE);
if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
} }
String connection = headers.get(Names.CONNECTION); CharSequence connection = headers.get(Names.CONNECTION);
if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
} }
String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); CharSequence accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) { if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format( throw new WebSocketHandshakeException(String.format(
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString)); "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));

View File

@ -35,7 +35,7 @@ import static io.netty.handler.codec.http.HttpVersion.*;
*/ */
public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker { public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
private static final CharSequence WEBSOCKET = HttpHeaders.newEntity(Values.WEBSOCKET.toLowerCase()); private static final CharSequence WEBSOCKET = Values.WEBSOCKET.toLowerCase();
public static final String WEBSOCKET_07_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; public static final String WEBSOCKET_07_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
@ -129,7 +129,7 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
res.headers().add(headers); res.headers().add(headers);
} }
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); CharSequence key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) { if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key"); throw new WebSocketHandshakeException("not a WebSocket request: missing key");
} }

View File

@ -35,7 +35,7 @@ import static io.netty.handler.codec.http.HttpVersion.*;
*/ */
public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
private static final CharSequence WEBSOCKET = HttpHeaders.newEntity(Values.WEBSOCKET.toLowerCase()); private static final CharSequence WEBSOCKET = Values.WEBSOCKET.toLowerCase();
public static final String WEBSOCKET_08_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; public static final String WEBSOCKET_08_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
@ -128,7 +128,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
res.headers().add(headers); res.headers().add(headers);
} }
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); CharSequence key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) { if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key"); throw new WebSocketHandshakeException("not a WebSocket request: missing key");
} }

View File

@ -34,7 +34,7 @@ import static io.netty.handler.codec.http.HttpVersion.*;
*/ */
public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
private static final CharSequence WEBSOCKET = HttpHeaders.newEntity(Values.WEBSOCKET.toLowerCase()); private static final CharSequence WEBSOCKET = Values.WEBSOCKET.toLowerCase();
public static final String WEBSOCKET_13_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; public static final String WEBSOCKET_13_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
@ -126,7 +126,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
res.headers().add(headers); res.headers().add(headers);
} }
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); CharSequence key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) { if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key"); throw new WebSocketHandshakeException("not a WebSocket request: missing key");
} }

View File

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

View File

@ -15,6 +15,7 @@
*/ */
package io.netty.handler.codec.rtsp; package io.netty.handler.codec.rtsp;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
@ -30,179 +31,179 @@ public final class RtspHeaders {
/** /**
* {@code "Accept"} * {@code "Accept"}
*/ */
public static final String ACCEPT = HttpHeaders.Names.ACCEPT; public static final AsciiString ACCEPT = HttpHeaders.Names.ACCEPT;
/** /**
* {@code "Accept-Encoding"} * {@code "Accept-Encoding"}
*/ */
public static final String ACCEPT_ENCODING = HttpHeaders.Names.ACCEPT_ENCODING; public static final AsciiString ACCEPT_ENCODING = HttpHeaders.Names.ACCEPT_ENCODING;
/** /**
* {@code "Accept-Lanugage"} * {@code "Accept-Lanugage"}
*/ */
public static final String ACCEPT_LANGUAGE = HttpHeaders.Names.ACCEPT_LANGUAGE; public static final AsciiString ACCEPT_LANGUAGE = HttpHeaders.Names.ACCEPT_LANGUAGE;
/** /**
* {@code "Allow"} * {@code "Allow"}
*/ */
public static final String ALLOW = "Allow"; public static final AsciiString ALLOW = new AsciiString("Allow");
/** /**
* {@code "Authorization"} * {@code "Authorization"}
*/ */
public static final String AUTHORIZATION = HttpHeaders.Names.AUTHORIZATION; public static final AsciiString AUTHORIZATION = HttpHeaders.Names.AUTHORIZATION;
/** /**
* {@code "Bandwidth"} * {@code "Bandwidth"}
*/ */
public static final String BANDWIDTH = "Bandwidth"; public static final AsciiString BANDWIDTH = new AsciiString("Bandwidth");
/** /**
* {@code "Blocksize"} * {@code "Blocksize"}
*/ */
public static final String BLOCKSIZE = "Blocksize"; public static final AsciiString BLOCKSIZE = new AsciiString("Blocksize");
/** /**
* {@code "Cache-Control"} * {@code "Cache-Control"}
*/ */
public static final String CACHE_CONTROL = HttpHeaders.Names.CACHE_CONTROL; public static final AsciiString CACHE_CONTROL = HttpHeaders.Names.CACHE_CONTROL;
/** /**
* {@code "Conference"} * {@code "Conference"}
*/ */
public static final String CONFERENCE = "Conference"; public static final AsciiString CONFERENCE = new AsciiString("Conference");
/** /**
* {@code "Connection"} * {@code "Connection"}
*/ */
public static final String CONNECTION = HttpHeaders.Names.CONNECTION; public static final AsciiString CONNECTION = HttpHeaders.Names.CONNECTION;
/** /**
* {@code "Content-Base"} * {@code "Content-Base"}
*/ */
public static final String CONTENT_BASE = HttpHeaders.Names.CONTENT_BASE; public static final AsciiString CONTENT_BASE = HttpHeaders.Names.CONTENT_BASE;
/** /**
* {@code "Content-Encoding"} * {@code "Content-Encoding"}
*/ */
public static final String CONTENT_ENCODING = HttpHeaders.Names.CONTENT_ENCODING; public static final AsciiString CONTENT_ENCODING = HttpHeaders.Names.CONTENT_ENCODING;
/** /**
* {@code "Content-Language"} * {@code "Content-Language"}
*/ */
public static final String CONTENT_LANGUAGE = HttpHeaders.Names.CONTENT_LANGUAGE; public static final AsciiString CONTENT_LANGUAGE = HttpHeaders.Names.CONTENT_LANGUAGE;
/** /**
* {@code "Content-Length"} * {@code "Content-Length"}
*/ */
public static final String CONTENT_LENGTH = HttpHeaders.Names.CONTENT_LENGTH; public static final AsciiString CONTENT_LENGTH = HttpHeaders.Names.CONTENT_LENGTH;
/** /**
* {@code "Content-Location"} * {@code "Content-Location"}
*/ */
public static final String CONTENT_LOCATION = HttpHeaders.Names.CONTENT_LOCATION; public static final AsciiString CONTENT_LOCATION = HttpHeaders.Names.CONTENT_LOCATION;
/** /**
* {@code "Content-Type"} * {@code "Content-Type"}
*/ */
public static final String CONTENT_TYPE = HttpHeaders.Names.CONTENT_TYPE; public static final AsciiString CONTENT_TYPE = HttpHeaders.Names.CONTENT_TYPE;
/** /**
* {@code "CSeq"} * {@code "CSeq"}
*/ */
public static final String CSEQ = "CSeq"; public static final AsciiString CSEQ = new AsciiString("CSeq");
/** /**
* {@code "Date"} * {@code "Date"}
*/ */
public static final String DATE = HttpHeaders.Names.DATE; public static final AsciiString DATE = HttpHeaders.Names.DATE;
/** /**
* {@code "Expires"} * {@code "Expires"}
*/ */
public static final String EXPIRES = HttpHeaders.Names.EXPIRES; public static final AsciiString EXPIRES = HttpHeaders.Names.EXPIRES;
/** /**
* {@code "From"} * {@code "From"}
*/ */
public static final String FROM = HttpHeaders.Names.FROM; public static final AsciiString FROM = HttpHeaders.Names.FROM;
/** /**
* {@code "Host"} * {@code "Host"}
*/ */
public static final String HOST = HttpHeaders.Names.HOST; public static final AsciiString HOST = HttpHeaders.Names.HOST;
/** /**
* {@code "If-Match"} * {@code "If-Match"}
*/ */
public static final String IF_MATCH = HttpHeaders.Names.IF_MATCH; public static final AsciiString IF_MATCH = HttpHeaders.Names.IF_MATCH;
/** /**
* {@code "If-Modified-Since"} * {@code "If-Modified-Since"}
*/ */
public static final String IF_MODIFIED_SINCE = HttpHeaders.Names.IF_MODIFIED_SINCE; public static final AsciiString IF_MODIFIED_SINCE = HttpHeaders.Names.IF_MODIFIED_SINCE;
/** /**
* {@code "KeyMgmt"} * {@code "KeyMgmt"}
*/ */
public static final String KEYMGMT = "KeyMgmt"; public static final AsciiString KEYMGMT = new AsciiString("KeyMgmt");
/** /**
* {@code "Last-Modified"} * {@code "Last-Modified"}
*/ */
public static final String LAST_MODIFIED = HttpHeaders.Names.LAST_MODIFIED; public static final AsciiString LAST_MODIFIED = HttpHeaders.Names.LAST_MODIFIED;
/** /**
* {@code "Proxy-Authenticate"} * {@code "Proxy-Authenticate"}
*/ */
public static final String PROXY_AUTHENTICATE = HttpHeaders.Names.PROXY_AUTHENTICATE; public static final AsciiString PROXY_AUTHENTICATE = HttpHeaders.Names.PROXY_AUTHENTICATE;
/** /**
* {@code "Proxy-Require"} * {@code "Proxy-Require"}
*/ */
public static final String PROXY_REQUIRE = "Proxy-Require"; public static final AsciiString PROXY_REQUIRE = new AsciiString("Proxy-Require");
/** /**
* {@code "Public"} * {@code "Public"}
*/ */
public static final String PUBLIC = "Public"; public static final AsciiString PUBLIC = new AsciiString("Public");
/** /**
* {@code "Range"} * {@code "Range"}
*/ */
public static final String RANGE = HttpHeaders.Names.RANGE; public static final AsciiString RANGE = HttpHeaders.Names.RANGE;
/** /**
* {@code "Referer"} * {@code "Referer"}
*/ */
public static final String REFERER = HttpHeaders.Names.REFERER; public static final AsciiString REFERER = HttpHeaders.Names.REFERER;
/** /**
* {@code "Require"} * {@code "Require"}
*/ */
public static final String REQUIRE = "Require"; public static final AsciiString REQUIRE = new AsciiString("Require");
/** /**
* {@code "Retry-After"} * {@code "Retry-After"}
*/ */
public static final String RETRT_AFTER = HttpHeaders.Names.RETRY_AFTER; public static final AsciiString RETRT_AFTER = HttpHeaders.Names.RETRY_AFTER;
/** /**
* {@code "RTP-Info"} * {@code "RTP-Info"}
*/ */
public static final String RTP_INFO = "RTP-Info"; public static final AsciiString RTP_INFO = new AsciiString("RTP-Info");
/** /**
* {@code "Scale"} * {@code "Scale"}
*/ */
public static final String SCALE = "Scale"; public static final AsciiString SCALE = new AsciiString("Scale");
/** /**
* {@code "Session"} * {@code "Session"}
*/ */
public static final String SESSION = "Session"; public static final AsciiString SESSION = new AsciiString("Session");
/** /**
* {@code "Server"} * {@code "Server"}
*/ */
public static final String SERVER = HttpHeaders.Names.SERVER; public static final AsciiString SERVER = HttpHeaders.Names.SERVER;
/** /**
* {@code "Speed"} * {@code "Speed"}
*/ */
public static final String SPEED = "Speed"; public static final AsciiString SPEED = new AsciiString("Speed");
/** /**
* {@code "Timestamp"} * {@code "Timestamp"}
*/ */
public static final String TIMESTAMP = "Timestamp"; public static final AsciiString TIMESTAMP = new AsciiString("Timestamp");
/** /**
* {@code "Transport"} * {@code "Transport"}
*/ */
public static final String TRANSPORT = "Transport"; public static final AsciiString TRANSPORT = new AsciiString("Transport");
/** /**
* {@code "Unsupported"} * {@code "Unsupported"}
*/ */
public static final String UNSUPPORTED = "Unsupported"; public static final AsciiString UNSUPPORTED = new AsciiString("Unsupported");
/** /**
* {@code "User-Agent"} * {@code "User-Agent"}
*/ */
public static final String USER_AGENT = HttpHeaders.Names.USER_AGENT; public static final AsciiString USER_AGENT = HttpHeaders.Names.USER_AGENT;
/** /**
* {@code "Vary"} * {@code "Vary"}
*/ */
public static final String VARY = HttpHeaders.Names.VARY; public static final AsciiString VARY = HttpHeaders.Names.VARY;
/** /**
* {@code "Via"} * {@code "Via"}
*/ */
public static final String VIA = HttpHeaders.Names.VIA; public static final AsciiString VIA = HttpHeaders.Names.VIA;
/** /**
* {@code "WWW-Authenticate"} * {@code "WWW-Authenticate"}
*/ */
public static final String WWW_AUTHENTICATE = HttpHeaders.Names.WWW_AUTHENTICATE; public static final AsciiString WWW_AUTHENTICATE = HttpHeaders.Names.WWW_AUTHENTICATE;
private Names() { private Names() {
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -234,15 +234,15 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
SpdyHeaders frameHeaders = spdySynStreamFrame.headers(); SpdyHeaders frameHeaders = spdySynStreamFrame.headers();
if (httpMessage instanceof FullHttpRequest) { if (httpMessage instanceof FullHttpRequest) {
HttpRequest httpRequest = (HttpRequest) httpMessage; HttpRequest httpRequest = (HttpRequest) httpMessage;
frameHeaders.set(METHOD, httpRequest.method()); frameHeaders.setObject(METHOD, httpRequest.method());
frameHeaders.set(PATH, httpRequest.uri()); frameHeaders.set(PATH, httpRequest.uri());
frameHeaders.set(VERSION, httpMessage.protocolVersion()); frameHeaders.setObject(VERSION, httpMessage.protocolVersion());
} }
if (httpMessage instanceof HttpResponse) { if (httpMessage instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) httpMessage; HttpResponse httpResponse = (HttpResponse) httpMessage;
frameHeaders.set(STATUS, httpResponse.status()); frameHeaders.setInt(STATUS, httpResponse.status().code());
frameHeaders.set(PATH, URL); frameHeaders.set(PATH, URL);
frameHeaders.set(VERSION, httpMessage.protocolVersion()); frameHeaders.setObject(VERSION, httpMessage.protocolVersion());
spdySynStreamFrame.setUnidirectional(true); spdySynStreamFrame.setUnidirectional(true);
} }
@ -286,8 +286,8 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
SpdyHeaders frameHeaders = spdySynReplyFrame.headers(); SpdyHeaders frameHeaders = spdySynReplyFrame.headers();
// Unfold the first line of the response into name/value pairs // Unfold the first line of the response into name/value pairs
frameHeaders.set(STATUS, httpResponse.status()); frameHeaders.setInt(STATUS, httpResponse.status().code());
frameHeaders.set(VERSION, httpResponse.protocolVersion()); frameHeaders.setObject(VERSION, httpResponse.protocolVersion());
// Transfer the remaining HTTP headers // Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpHeaders) { for (Map.Entry<String, String> entry: httpHeaders) {

View File

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

View File

@ -108,7 +108,8 @@ public class HttpServerCodecTest {
// Ensure the encoder handles the response after handling 100 Continue. // Ensure the encoder handles the response after handling 100 Continue.
ByteBuf encodedRes = ch.readOutbound(); ByteBuf encodedRes = ch.readOutbound();
assertThat(encodedRes.toString(CharsetUtil.UTF_8), is("HTTP/1.1 201 Created\r\nContent-Length: 2\r\n\r\nOK")); assertThat(encodedRes.toString(CharsetUtil.UTF_8), is("HTTP/1.1 201 Created\r\n" +
CONTENT_LENGTH + ": 2\r\n\r\nOK"));
encodedRes.release(); encodedRes.release();
ch.finish(); ch.finish();

View File

@ -83,7 +83,7 @@ public class CorsHandlerTest {
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888"));
assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_METHODS), hasItems("GET", "DELETE")); assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_METHODS), hasItems("GET", "DELETE"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN)); assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -96,7 +96,7 @@ public class CorsHandlerTest {
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); 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_METHODS), hasItems("OPTIONS", "GET"));
assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_HEADERS), hasItems("content-type", "xheader1")); assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_HEADERS), hasItems("content-type", "xheader1"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN)); assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -105,7 +105,7 @@ public class CorsHandlerTest {
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().get(CONTENT_LENGTH), is("0")); assertThat(response.headers().get(CONTENT_LENGTH), is("0"));
assertThat(response.headers().get(DATE), is(notNullValue())); assertThat(response.headers().get(DATE), is(notNullValue()));
assertThat(response.headers().get(VARY), equalTo(ORIGIN)); assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -115,7 +115,7 @@ public class CorsHandlerTest {
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().get("CustomHeader"), equalTo("somevalue")); assertThat(response.headers().get("CustomHeader"), equalTo("somevalue"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN)); assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -125,7 +125,7 @@ public class CorsHandlerTest {
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().getAll("CustomHeader"), hasItems("value1", "value2")); assertThat(response.headers().getAll("CustomHeader"), hasItems("value1", "value2"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN)); assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -135,7 +135,7 @@ public class CorsHandlerTest {
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().getAll("CustomHeader"), hasItems("value1", "value2")); assertThat(response.headers().getAll("CustomHeader"), hasItems("value1", "value2"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN)); assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -149,7 +149,7 @@ public class CorsHandlerTest {
}).build(); }).build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().get("GenHeader"), equalTo("generatedValue")); assertThat(response.headers().get("GenHeader"), equalTo("generatedValue"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN)); assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -204,7 +204,7 @@ public class CorsHandlerTest {
final HttpResponse response = simpleRequest(config, "http://localhost:7777"); final HttpResponse response = simpleRequest(config, "http://localhost:7777");
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true"));
assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777")); assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777"));
assertThat(response.headers().get(VARY), equalTo(ORIGIN)); assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test

View File

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

View File

@ -127,7 +127,7 @@ public class WebSocketRequestBuilder {
.method(HttpMethod.GET) .method(HttpMethod.GET)
.uri("/test") .uri("/test")
.host("server.example.com") .host("server.example.com")
.upgrade(WEBSOCKET.toLowerCase()) .upgrade(WEBSOCKET.toLowerCase().toString())
.key("dGhlIHNhbXBsZSBub25jZQ==") .key("dGhlIHNhbXBsZSBub25jZQ==")
.origin("http://example.com") .origin("http://example.com")
.version13() .version13()

View File

@ -95,7 +95,7 @@ public class WebSocketServerProtocolHandlerTest {
.uri("/test") .uri("/test")
.key(null) .key(null)
.connection("Upgrade") .connection("Upgrade")
.upgrade(WEBSOCKET.toLowerCase()) .upgrade(WEBSOCKET.toLowerCase().toString())
.version13() .version13()
.build(); .build();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,138 @@
/*
* 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;
/**
* 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 Headers<AsciiString> {
/**
* A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}.
*/
interface EntryVisitor extends Headers.EntryVisitor<AsciiString> {
}
/**
* A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}.
*/
interface NameVisitor extends Headers.NameVisitor<AsciiString> {
}
@Override
BinaryHeaders add(AsciiString name, AsciiString value);
@Override
BinaryHeaders add(AsciiString name, Iterable<? extends AsciiString> values);
@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);
/**
* See {@link Headers#add(Headers)}
*/
BinaryHeaders add(BinaryHeaders headers);
@Override
BinaryHeaders set(AsciiString name, AsciiString value);
@Override
BinaryHeaders set(AsciiString name, Iterable<? extends AsciiString> values);
@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);
/**
* See {@link Headers#set(Headers)}
*/
BinaryHeaders set(BinaryHeaders headers);
/**
* See {@link Headers#setAll(Headers)}
*/
BinaryHeaders setAll(BinaryHeaders headers);
@Override
BinaryHeaders clear();
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Extension to the {@link Headers} interface to provide methods which convert the
* native {@code UnconvertedType} to the not-native {@code ConvertedType}
*/
public interface ConvertibleHeaders<UnconvertedType, ConvertedType> extends Headers<UnconvertedType> {
/**
* Interface to do conversions to and from the two generic type parameters
*/
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
*
* @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
*
* @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
*
* @return The values corresponding to {@code name} and then converted
*/
Set<ConvertedType> namesAndConvert(Comparator<ConvertedType> comparator);
}

View File

@ -0,0 +1,349 @@
/*
* 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 io.netty.util.internal.PlatformDependent;
import java.text.ParseException;
import static io.netty.handler.codec.AsciiString.*;
public class DefaultBinaryHeaders extends DefaultHeaders<AsciiString> implements BinaryHeaders {
private static final HashCodeGenerator<AsciiString> ASCII_HASH_CODE_GENERATOR =
new HashCodeGenerator<AsciiString>() {
@Override
public int generateHashCode(AsciiString name) {
return AsciiString.caseInsensitiveHashCode(name);
}
};
private static final ValueConverter<AsciiString> OBJECT_TO_ASCII = new ValueConverter<AsciiString>() {
@Override
public AsciiString convertObject(Object value) {
if (value instanceof AsciiString) {
return (AsciiString) value;
}
if (value instanceof CharSequence) {
return new AsciiString((CharSequence) value);
}
return new AsciiString(value.toString());
}
@Override
public AsciiString convertInt(int value) {
return new AsciiString(String.valueOf(value));
}
@Override
public AsciiString convertLong(long value) {
return new AsciiString(String.valueOf(value));
}
@Override
public AsciiString convertDouble(double value) {
return new AsciiString(String.valueOf(value));
}
@Override
public AsciiString convertChar(char value) {
return new AsciiString(String.valueOf(value));
}
@Override
public AsciiString convertBoolean(boolean value) {
return new AsciiString(String.valueOf(value));
}
@Override
public AsciiString convertFloat(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 convertShort(short value) {
return new AsciiString(String.valueOf(value));
}
@Override
public short convertToShort(AsciiString value) {
return value.parseShort();
}
@Override
public AsciiString convertByte(byte value) {
return new AsciiString(String.valueOf(value));
}
@Override
public byte convertToByte(AsciiString value) {
return value.byteAt(0);
}
};
private static final NameConverter<AsciiString> ASCII_TO_LOWER_CONVERTER = new NameConverter<AsciiString>() {
@Override
public AsciiString convertName(AsciiString name) {
return name.toLowerCase();
}
};
private static final NameConverter<AsciiString> ASCII_IDENTITY_CONVERTER = new NameConverter<AsciiString>() {
@Override
public AsciiString convertName(AsciiString name) {
return name;
}
};
public DefaultBinaryHeaders() {
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) {
super.add(name, value);
return this;
}
@Override
public BinaryHeaders add(AsciiString name, Iterable<? extends AsciiString> values) {
super.add(name, values);
return this;
}
@Override
public BinaryHeaders add(AsciiString name, AsciiString... 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) {
super.add(headers);
return this;
}
@Override
public BinaryHeaders set(AsciiString name, AsciiString value) {
super.set(name, value);
return this;
}
@Override
public BinaryHeaders set(AsciiString name, Iterable<? extends AsciiString> values) {
super.set(name, values);
return this;
}
@Override
public BinaryHeaders set(AsciiString name, AsciiString... 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) {
super.set(headers);
return this;
}
@Override
public BinaryHeaders setAll(BinaryHeaders headers) {
super.setAll(headers);
return this;
}
@Override
public BinaryHeaders clear() {
super.clear();
return this;
}
}

View File

@ -0,0 +1,181 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
public class DefaultConvertibleHeaders<UnconvertedType, ConvertedType> extends DefaultHeaders<UnconvertedType>
implements ConvertibleHeaders<UnconvertedType, ConvertedType> {
private final TypeConverter<UnconvertedType, ConvertedType> typeConverter;
public DefaultConvertibleHeaders(Comparator<? super UnconvertedType> keyComparator,
Comparator<? super UnconvertedType> valueComparator,
HashCodeGenerator<UnconvertedType> hashCodeGenerator,
ValueConverter<UnconvertedType> valueConverter,
TypeConverter<UnconvertedType, ConvertedType> typeConverter) {
super(keyComparator, valueComparator, hashCodeGenerator, valueConverter);
this.typeConverter = typeConverter;
}
public DefaultConvertibleHeaders(Comparator<? super UnconvertedType> keyComparator,
Comparator<? super UnconvertedType> valueComparator,
HashCodeGenerator<UnconvertedType> hashCodeGenerator,
ValueConverter<UnconvertedType> valueConverter,
TypeConverter<UnconvertedType, ConvertedType> typeConverter,
NameConverter<UnconvertedType> nameConverter) {
super(keyComparator, valueComparator, hashCodeGenerator, valueConverter, nameConverter);
this.typeConverter = typeConverter;
}
@Override
public ConvertedType getAndConvert(UnconvertedType name) {
return getAndConvert(name, null);
}
@Override
public ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue) {
UnconvertedType v = get(name);
if (v == null) {
return defaultValue;
}
return typeConverter.toConvertedType(v);
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name) {
return getAndRemoveAndConvert(name, null);
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue) {
UnconvertedType v = getAndRemove(name);
if (v == null) {
return defaultValue;
}
return typeConverter.toConvertedType(v);
}
@Override
public List<ConvertedType> getAllAndConvert(UnconvertedType name) {
List<UnconvertedType> all = getAll(name);
List<ConvertedType> allConverted = new ArrayList<ConvertedType>(all.size());
for (int i = 0; i < all.size(); ++i) {
allConverted.add(typeConverter.toConvertedType(all.get(i)));
}
return allConverted;
}
@Override
public List<ConvertedType> getAllAndRemoveAndConvert(UnconvertedType name) {
List<UnconvertedType> all = getAllAndRemove(name);
List<ConvertedType> allConverted = new ArrayList<ConvertedType>(all.size());
for (int i = 0; i < all.size(); ++i) {
allConverted.add(typeConverter.toConvertedType(all.get(i)));
}
return allConverted;
}
@Override
public List<Entry<ConvertedType, ConvertedType>> entriesConverted() {
List<Entry<UnconvertedType, UnconvertedType>> entries = entries();
List<Entry<ConvertedType, ConvertedType>> entriesConverted = new ArrayList<Entry<ConvertedType, ConvertedType>>(
entries.size());
for (int i = 0; i < entries.size(); ++i) {
entriesConverted.add(new ConvertedEntry(entries.get(i)));
}
return entriesConverted;
}
@Override
public Iterator<Entry<ConvertedType, ConvertedType>> iteratorConverted() {
return new ConvertedIterator();
}
@Override
public Set<ConvertedType> namesAndConvert(Comparator<ConvertedType> comparator) {
Set<UnconvertedType> names = names();
Set<ConvertedType> namesConverted = new TreeSet<ConvertedType>(comparator);
for (UnconvertedType unconverted : names) {
namesConverted.add(typeConverter.toConvertedType(unconverted));
}
return namesConverted;
}
private final class ConvertedIterator implements Iterator<Entry<ConvertedType, ConvertedType>> {
private final 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;
ConvertedEntry(Entry<UnconvertedType, UnconvertedType> entry) {
this.entry = entry;
}
@Override
public ConvertedType getKey() {
if (name == null) {
name = typeConverter.toConvertedType(entry.getKey());
}
return name;
}
@Override
public ConvertedType getValue() {
if (value == null) {
value = typeConverter.toConvertedType(entry.getValue());
}
return value;
}
@Override
public ConvertedType setValue(ConvertedType value) {
ConvertedType old = getValue();
entry.setValue(typeConverter.toUnconvertedType(value));
return old;
}
@Override
public String toString() {
return entry.toString();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,214 @@
/*
* 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 class EmptyBinaryHeaders extends EmptyHeaders<AsciiString> implements BinaryHeaders {
protected EmptyBinaryHeaders() {
}
@Override
public BinaryHeaders add(AsciiString name, AsciiString value) {
super.add(name, value);
return this;
}
@Override
public BinaryHeaders add(AsciiString name, Iterable<? extends AsciiString> values) {
super.add(name, values);
return this;
}
@Override
public BinaryHeaders add(AsciiString name, AsciiString... 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) {
super.add(headers);
return this;
}
@Override
public BinaryHeaders set(AsciiString name, AsciiString value) {
super.set(name, value);
return this;
}
@Override
public BinaryHeaders set(AsciiString name, Iterable<? extends AsciiString> values) {
super.set(name, values);
return this;
}
@Override
public BinaryHeaders set(AsciiString name, AsciiString... 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) {
super.set(headers);
return this;
}
@Override
public BinaryHeaders setAll(BinaryHeaders headers) {
super.setAll(headers);
return this;
}
@Override
public BinaryHeaders clear() {
super.clear();
return this;
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
public class EmptyConvertibleHeaders<UnconvertedType, ConvertedType> extends
EmptyHeaders<UnconvertedType> implements ConvertibleHeaders<UnconvertedType, ConvertedType> {
@Override
public ConvertedType getAndConvert(UnconvertedType name) {
return null;
}
@Override
public ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue) {
return defaultValue;
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name) {
return null;
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue) {
return defaultValue;
}
@Override
public List<ConvertedType> getAllAndConvert(UnconvertedType name) {
return Collections.emptyList();
}
@Override
public List<ConvertedType> getAllAndRemoveAndConvert(UnconvertedType name) {
return Collections.emptyList();
}
@Override
public List<Entry<ConvertedType, ConvertedType>> entriesConverted() {
return Collections.emptyList();
}
@Override
public Iterator<Entry<ConvertedType, ConvertedType>> iteratorConverted() {
return entriesConverted().iterator();
}
@Override
public Set<ConvertedType> namesAndConvert(Comparator<ConvertedType> comparator) {
return Collections.emptySet();
}
}

View File

@ -0,0 +1,536 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
public class EmptyHeaders<T> implements Headers<T> {
@Override
public T get(T name) {
return null;
}
@Override
public T get(T name, T defaultValue) {
return null;
}
@Override
public T getAndRemove(T name) {
return null;
}
@Override
public T getAndRemove(T name, T defaultValue) {
return null;
}
@Override
public List<T> getAll(T name) {
return Collections.emptyList();
}
@Override
public List<T> getAllAndRemove(T name) {
return Collections.emptyList();
}
@Override
public Boolean getBoolean(T name) {
return null;
}
@Override
public boolean getBoolean(T name, boolean defaultValue) {
return defaultValue;
}
@Override
public Byte getByte(T name) {
return null;
}
@Override
public byte getByte(T name, byte defaultValue) {
return defaultValue;
}
@Override
public Character getChar(T name) {
return null;
}
@Override
public char getChar(T name, char defaultValue) {
return defaultValue;
}
@Override
public Short getShort(T name) {
return null;
}
@Override
public short getInt(T name, short defaultValue) {
return defaultValue;
}
@Override
public Integer getInt(T name) {
return null;
}
@Override
public int getInt(T name, int defaultValue) {
return defaultValue;
}
@Override
public Long getLong(T name) {
return null;
}
@Override
public long getLong(T name, long defaultValue) {
return defaultValue;
}
@Override
public Float getFloat(T name) {
return null;
}
@Override
public float getFloat(T name, float defaultValue) {
return defaultValue;
}
@Override
public Double getDouble(T name) {
return null;
}
@Override
public double getDouble(T name, double defaultValue) {
return defaultValue;
}
@Override
public Long getTimeMillis(T name) {
return null;
}
@Override
public long getTimeMillis(T name, long defaultValue) {
return defaultValue;
}
@Override
public Boolean getBooleanAndRemove(T name) {
return null;
}
@Override
public boolean getBooleanAndRemove(T name, boolean defaultValue) {
return defaultValue;
}
@Override
public Byte getByteAndRemove(T name) {
return null;
}
@Override
public byte getByteAndRemove(T name, byte defaultValue) {
return defaultValue;
}
@Override
public Character getCharAndRemove(T name) {
return null;
}
@Override
public char getCharAndRemove(T name, char defaultValue) {
return defaultValue;
}
@Override
public Short getShortAndRemove(T name) {
return null;
}
@Override
public short getShortAndRemove(T name, short defaultValue) {
return defaultValue;
}
@Override
public Integer getIntAndRemove(T name) {
return null;
}
@Override
public int getIntAndRemove(T name, int defaultValue) {
return defaultValue;
}
@Override
public Long getLongAndRemove(T name) {
return null;
}
@Override
public long getLongAndRemove(T name, long defaultValue) {
return defaultValue;
}
@Override
public Float getFloatAndRemove(T name) {
return null;
}
@Override
public float getFloatAndRemove(T name, float defaultValue) {
return defaultValue;
}
@Override
public Double getDoubleAndRemove(T name) {
return null;
}
@Override
public double getDoubleAndRemove(T name, double defaultValue) {
return defaultValue;
}
@Override
public Long getTimeMillisAndRemove(T name) {
return null;
}
@Override
public long getTimeMillisAndRemove(T name, long defaultValue) {
return defaultValue;
}
@Override
public List<Entry<T, T>> entries() {
return Collections.emptyList();
}
@Override
public boolean contains(T name) {
return false;
}
@Override
public boolean contains(T name, T value) {
return false;
}
@Override
public boolean containsObject(T name, Object value) {
return false;
}
@Override
public boolean containsBoolean(T name, boolean 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, short 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(Headers.EntryVisitor<T> visitor) throws Exception {
return null;
}
@Override
public T forEachName(Headers.NameVisitor<T> visitor) throws Exception {
return null;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Headers)) {
return false;
}
Headers<?> rhs = (Headers<?>) o;
return isEmpty() && rhs.isEmpty();
}
@Override
public int hashCode() {
return 1;
}
@Override
public String toString() {
return new StringBuilder(getClass().getSimpleName()).append('[').append(']').toString();
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec;
public interface TextHeaderProcessor {
boolean process(CharSequence name, CharSequence value) throws Exception;
}

View File

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

View File

@ -0,0 +1,88 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec;
import static org.junit.Assert.assertArrayEquals;
import io.netty.util.CharsetUtil;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import org.junit.Test;
/**
* Test for the {@link AsciiString} class
*/
public class AsciiStringTest {
@Test
public void testGetBytesStringBuilder() {
final StringBuilder b = new StringBuilder();
for (int i = 0; i < 1 << 16; ++i) {
b.append("eéaà");
}
final String bString = b.toString();
final Charset[] charsets = CharsetUtil.values();
for (int i = 0; i < charsets.length; ++i) {
final Charset charset = charsets[i];
byte[] expected = getBytesWithEncoder(bString, charset);
byte[] actual = AsciiString.getBytes(b, charset);
assertArrayEquals("failure for " + charset, expected, actual);
}
}
@Test
public void testGetBytesString() {
final StringBuilder b = new StringBuilder();
for (int i = 0; i < 1 << 16; ++i) {
b.append("eéaà");
}
final String bString = b.toString();
final Charset[] charsets = CharsetUtil.values();
for (int i = 0; i < charsets.length; ++i) {
final Charset charset = charsets[i];
byte[] expected = bString.getBytes(charset);
byte[] actual = AsciiString.getBytes(bString, charset);
assertArrayEquals("failure for " + charset, expected, actual);
}
}
@Test
public void testGetBytesAsciiString() {
final StringBuilder b = new StringBuilder();
for (int i = 0; i < 1 << 16; ++i) {
b.append("eéaà");
}
final String bString = b.toString();
// The AsciiString class actually limits the Charset to ISO_8859_1
byte[] expected = bString.getBytes(CharsetUtil.ISO_8859_1);
final Charset[] charsets = CharsetUtil.values();
for (int i = 0; i < charsets.length; ++i) {
final Charset charset = charsets[i];
byte[] actual = AsciiString.getBytes(new AsciiString(bString), charset);
assertArrayEquals("failure for " + charset, expected, actual);
}
}
private static byte[] getBytesWithEncoder(CharSequence value, Charset charset) {
final CharsetEncoder encoder = CharsetUtil.getEncoder(charset);
final ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * value.length()));
encoder.encode(CharBuffer.wrap(value), nativeBuffer, true);
return nativeBuffer.array();
}
}

View File

@ -0,0 +1,316 @@
/*
* 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.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
import org.junit.Test;
/**
* Tests for {@link DefaultBinaryHeaders}.
*/
public class DefaultBinaryHeadersTest {
@Test
public void binaryHeadersWithSameValuesShouldBeEquivalent() {
byte[] key1 = randomBytes();
byte[] value1 = randomBytes();
byte[] key2 = randomBytes();
byte[] value2 = randomBytes();
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false);
h1.set(as(key1), as(value1));
h1.set(as(key2), as(value2));
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false);
h2.set(as(key1), as(value1));
h2.set(as(key2), as(value2));
assertTrue(h1.equals(h2));
assertTrue(h2.equals(h1));
assertTrue(h2.equals(h2));
assertTrue(h1.equals(h1));
}
@Test
public void binaryHeadersWithSameDuplicateValuesShouldBeEquivalent() {
byte[] k1 = randomBytes();
byte[] k2 = randomBytes();
byte[] v1 = randomBytes();
byte[] v2 = randomBytes();
byte[] v3 = randomBytes();
byte[] v4 = randomBytes();
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));
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false);
h2.set(as(k1), as(v1));
h2.set(as(k2), as(v2));
h2.add(as(k1), as(v4));
h2.add(as(k2), as(v3));
assertTrue(h1.equals(h2));
assertTrue(h2.equals(h1));
assertTrue(h2.equals(h2));
assertTrue(h1.equals(h1));
}
@Test
public void binaryHeadersWithDifferentValuesShouldNotBeEquivalent() {
byte[] k1 = randomBytes();
byte[] k2 = randomBytes();
byte[] v1 = randomBytes();
byte[] v2 = randomBytes();
byte[] v3 = randomBytes();
byte[] v4 = randomBytes();
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));
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false);
h2.set(as(k1), as(v1));
h2.set(as(k2), as(v2));
h2.add(as(k1), as(v4));
assertFalse(h1.equals(h2));
assertFalse(h2.equals(h1));
assertTrue(h2.equals(h2));
assertTrue(h1.equals(h1));
}
@Test
public void binarySetAllShouldMergeHeaders() {
byte[] k1 = randomBytes();
byte[] k2 = randomBytes();
byte[] v1 = randomBytes();
byte[] v2 = randomBytes();
byte[] v3 = randomBytes();
byte[] v4 = randomBytes();
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));
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false);
h2.set(as(k1), as(v1));
h2.set(as(k2), as(v2));
h2.add(as(k1), as(v4));
DefaultBinaryHeaders expected = new DefaultBinaryHeaders(false);
expected.set(as(k1), as(v1));
expected.set(as(k2), as(v2));
expected.add(as(k2), as(v3));
expected.add(as(k1), as(v4));
expected.set(as(k1), as(v1));
expected.set(as(k2), as(v2));
expected.set(as(k1), as(v4));
h1.setAll(h2);
assertEquals(expected, h1);
}
@Test
public void binarySetShouldReplacePreviousValues() {
byte[] k1 = randomBytes();
byte[] v1 = randomBytes();
byte[] v2 = randomBytes();
byte[] v3 = randomBytes();
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<AsciiString> list = h1.getAll(as(k1));
assertEquals(1, list.size());
assertEquals(as(v3), list.get(0));
}
@Test
public void headersWithSameValuesShouldBeEquivalent() {
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
h1.set(as("foo"), as("goo"));
h1.set(as("foo2"), as("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));
assertTrue(h2.equals(h2));
assertTrue(h1.equals(h1));
}
@Test
public void headersWithSameDuplicateValuesShouldBeEquivalent() {
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"));
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));
assertTrue(h2.equals(h2));
assertTrue(h1.equals(h1));
}
@Test
public void headersWithDifferentValuesShouldNotBeEquivalent() {
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"));
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));
assertTrue(h2.equals(h2));
assertTrue(h1.equals(h1));
}
@Test
public void setAllShouldMergeHeaders() {
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"));
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
h2.set(as("foo"), as("goo"));
h2.set(as("foo2"), as("goo2"));
h2.add(as("foo"), as("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);
assertEquals(expected, h1);
}
@Test
public void setShouldReplacePreviousValues() {
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
h1.add(as("foo"), as("goo"));
h1.add(as("foo"), as("goo2"));
assertEquals(2, h1.size());
h1.set(as("foo"), as("goo3"));
assertEquals(1, h1.size());
List<AsciiString> list = h1.getAll(as("foo"));
assertEquals(1, list.size());
assertEquals(as("goo3"), list.get(0));
}
@Test(expected = NoSuchElementException.class)
public void iterateEmptyHeadersShouldThrow() {
Iterator<Map.Entry<AsciiString, AsciiString>> iterator = new DefaultBinaryHeaders().iterator();
assertFalse(iterator.hasNext());
iterator.next();
}
@Test
public void iterateHeadersShouldReturnAllValues() {
Set<String> headers = new HashSet<String>();
headers.add("a:1");
headers.add("a:2");
headers.add("a:3");
headers.add("b:1");
headers.add("b:2");
headers.add("c:1");
// Build the headers from the input set.
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
for (String header : headers) {
String[] parts = header.split(":");
h1.add(as(parts[0]), as(parts[1]));
}
// Now iterate through the headers, removing them from the original set.
for (Map.Entry<AsciiString, AsciiString> entry : h1) {
assertTrue(headers.remove(entry.getKey().toString() + ':' + entry.getValue().toString()));
}
// Make sure we removed them all.
assertTrue(headers.isEmpty());
}
@Test
public void getAndRemoveShouldReturnFirstEntry() {
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<AsciiString> values = h1.getAll(as("foo"));
assertEquals(0, values.size());
}
private static byte[] randomBytes() {
byte[] data = new byte[100];
new Random().nextBytes(data);
return data;
}
private AsciiString as(byte[] bytes) {
return new AsciiString(bytes);
}
private AsciiString as(String value) {
return new AsciiString(value);
}
}

View File

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

View File

@ -0,0 +1,52 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.util.collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Provides utilities for the primitive collection types that are not supplied by the JDK
*/
public final class CollectionUtils {
private CollectionUtils() { }
/**
* Compare two lists using the {@code comparator} for all comparisons (not using the equals() operator)
* @param lhs Left hand side
* @param rhs Right hand side
* @param comparator Comparator which will be used for all comparisons (equals() on objects will not be used)
* @return True if {@code lhs} == {@code rhs} according to {@code comparator}. False otherwise.
*/
public static <T> boolean equals(List<T> lhs, List<T> rhs, Comparator<? super T> comparator) {
final int lhsSize = lhs.size();
if (lhsSize != rhs.size()) {
return false;
}
// Don't use a TreeSet to do the comparison. We want to force the comparator
// to be used instead of the object's equals()
Collections.sort(lhs, comparator);
Collections.sort(rhs, comparator);
for (int i = 0; i < lhsSize; ++i) {
if (comparator.compare(lhs.get(i), rhs.get(i)) != 0) {
return false;
}
}
return true;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -51,7 +51,7 @@ public class SpdyServerHandler extends SimpleChannelInboundHandler<Object> {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content); FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
response.headers().set(CONTENT_LENGTH, response.content().readableBytes()); response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
if (!keepAlive) { if (!keepAlive) {
ctx.write(response).addListener(ChannelFutureListener.CLOSE); ctx.write(response).addListener(ChannelFutureListener.CLOSE);

View File

@ -62,7 +62,7 @@ public class StompClientHandler extends SimpleChannelInboundHandler<StompFrame>
ctx.writeAndFlush(subscribeFrame); ctx.writeAndFlush(subscribeFrame);
break; break;
case RECEIPT: case RECEIPT:
String receiptHeader = frame.headers().get(StompHeaders.RECEIPT_ID); String receiptHeader = frame.headers().getAndConvert(StompHeaders.RECEIPT_ID);
if (state == ClientState.AUTHENTICATED && receiptHeader.equals(subscrReceiptId)) { if (state == ClientState.AUTHENTICATED && receiptHeader.equals(subscrReceiptId)) {
StompFrame msgFrame = new DefaultStompFrame(StompCommand.SEND); StompFrame msgFrame = new DefaultStompFrame(StompCommand.SEND);
msgFrame.headers().set(StompHeaders.DESTINATION, StompClient.TOPIC); msgFrame.headers().set(StompHeaders.DESTINATION, StompClient.TOPIC);

View File

@ -86,7 +86,7 @@ final class HttpProxyServer extends ProxyServer {
boolean authzSuccess = false; boolean authzSuccess = false;
if (username != null) { if (username != null) {
String authz = req.headers().get(Names.AUTHORIZATION); CharSequence authz = req.headers().get(Names.AUTHORIZATION);
if (authz != null) { if (authz != null) {
ByteBuf authzBuf64 = Unpooled.copiedBuffer(authz, CharsetUtil.US_ASCII); ByteBuf authzBuf64 = Unpooled.copiedBuffer(authz, CharsetUtil.US_ASCII);
ByteBuf authzBuf = Base64.decode(authzBuf64); ByteBuf authzBuf = Base64.decode(authzBuf64);