HTTP/2 Headers Type Updates
Motivation: The HTTP/2 RFC (https://tools.ietf.org/html/rfc7540#section-8.1.2) indicates that header names consist of ASCII characters. We currently use ByteString to represent HTTP/2 header names. The HTTP/2 RFC (https://tools.ietf.org/html/rfc7540#section-10.3) also eludes to header values inheriting the same validity characteristics as HTTP/1.x. Using AsciiString for the value type of HTTP/2 headers would allow for re-use of predefined HTTP/1.x values, and make comparisons more intuitive. The Headers<T> interface could also be expanded to allow for easier use of header types which do not have the same Key and Value type. Motivation: - Change Headers<T> to Headers<K, V> - Change Http2Headers<ByteString> to Http2Headers<CharSequence, CharSequence> - Remove ByteString. Having AsciiString extend ByteString complicates equality comparisons when the hash code algorithm is no longer shared. Result: Http2Header types are more representative of the HTTP/2 RFC, and relationship between HTTP/2 header name/values more directly relates to HTTP/1.x header names/values.
This commit is contained in:
parent
07d861c5ff
commit
19658e9cd8
@ -33,6 +33,7 @@ import java.nio.channels.GatheringByteChannel;
|
||||
import java.nio.channels.ScatteringByteChannel;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import static io.netty.util.internal.MathUtil.isOutOfBounds;
|
||||
|
||||
/**
|
||||
* A skeletal implementation of a buffer.
|
||||
@ -1110,7 +1111,7 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
||||
}
|
||||
|
||||
final void checkIndex0(int index, int fieldLength) {
|
||||
if (isInvalid(index, fieldLength, capacity())) {
|
||||
if (isOutOfBounds(index, fieldLength, capacity())) {
|
||||
throw new IndexOutOfBoundsException(String.format(
|
||||
"index: %d, length: %d (expected: range(0, %d))", index, fieldLength, capacity()));
|
||||
}
|
||||
@ -1118,7 +1119,7 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
||||
|
||||
protected final void checkSrcIndex(int index, int length, int srcIndex, int srcCapacity) {
|
||||
checkIndex(index, length);
|
||||
if (isInvalid(srcIndex, length, srcCapacity)) {
|
||||
if (isOutOfBounds(srcIndex, length, srcCapacity)) {
|
||||
throw new IndexOutOfBoundsException(String.format(
|
||||
"srcIndex: %d, length: %d (expected: range(0, %d))", srcIndex, length, srcCapacity));
|
||||
}
|
||||
@ -1126,16 +1127,12 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
||||
|
||||
protected final void checkDstIndex(int index, int length, int dstIndex, int dstCapacity) {
|
||||
checkIndex(index, length);
|
||||
if (isInvalid(dstIndex, length, dstCapacity)) {
|
||||
if (isOutOfBounds(dstIndex, length, dstCapacity)) {
|
||||
throw new IndexOutOfBoundsException(String.format(
|
||||
"dstIndex: %d, length: %d (expected: range(0, %d))", dstIndex, length, dstCapacity));
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isInvalid(int index, int length, int capacity) {
|
||||
return (index | length | (index + length) | (capacity - (index + length))) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an {@link IndexOutOfBoundsException} if the current
|
||||
* {@linkplain #readableBytes() readable bytes} of this buffer is less
|
||||
|
@ -19,6 +19,8 @@ import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;
|
||||
|
||||
/**
|
||||
* Special {@link SwappedByteBuf} for {@link ByteBuf}s that is using unsafe.
|
||||
*/
|
||||
@ -30,7 +32,7 @@ abstract class AbstractUnsafeSwappedByteBuf extends SwappedByteBuf {
|
||||
super(buf);
|
||||
assert PlatformDependent.isUnaligned();
|
||||
wrapped = buf;
|
||||
nativeByteOrder = UnsafeByteBufUtil.BIG_ENDIAN_NATIVE_ORDER == (order() == ByteOrder.BIG_ENDIAN);
|
||||
nativeByteOrder = BIG_ENDIAN_NATIVE_ORDER == (order() == ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,11 +15,8 @@
|
||||
*/
|
||||
package io.netty.buffer;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.ByteProcessor;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.Recycler;
|
||||
import io.netty.util.Recycler.Handle;
|
||||
@ -41,6 +38,9 @@ import java.nio.charset.CoderResult;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import static io.netty.util.internal.MathUtil.isOutOfBounds;
|
||||
|
||||
/**
|
||||
* A collection of utility methods that is related with handling {@link ByteBuf},
|
||||
* such as the generation of hex dump and swapping an integer's byte order.
|
||||
@ -661,7 +661,7 @@ public final class ByteBufUtil {
|
||||
* The copy will start at {@link ByteBuf#readerIndex()} and copy {@link ByteBuf#readableBytes()} bytes.
|
||||
*/
|
||||
public static byte[] getBytes(ByteBuf buf) {
|
||||
return getBytes(buf, checkNotNull(buf, "buf").readerIndex(), buf.readableBytes());
|
||||
return getBytes(buf, buf.readerIndex(), buf.readableBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -679,7 +679,7 @@ public final class ByteBufUtil {
|
||||
* If {@code copy} is false the underlying storage will be shared, if possible.
|
||||
*/
|
||||
public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) {
|
||||
if (start < 0 || length > checkNotNull(buf, "buf").capacity() - start) {
|
||||
if (isOutOfBounds(start, length, buf.capacity())) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
|
||||
+ ") <= " + "buf.capacity(" + buf.capacity() + ')');
|
||||
}
|
||||
@ -706,12 +706,10 @@ public final class ByteBufUtil {
|
||||
* @param dstIdx the starting offset in the destination byte array.
|
||||
* @param length the number of characters to copy.
|
||||
*/
|
||||
public static void copy(ByteString src, int srcIdx, ByteBuf dst, int dstIdx, int length) {
|
||||
final int thisLen = src.length();
|
||||
|
||||
if (srcIdx < 0 || length > thisLen - srcIdx) {
|
||||
public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int dstIdx, int length) {
|
||||
if (isOutOfBounds(srcIdx, length, src.length())) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
|
||||
+ length + ") <= srcLen(" + thisLen + ')');
|
||||
+ length + ") <= srcLen(" + src.length() + ')');
|
||||
}
|
||||
|
||||
checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx + src.arrayOffset(), length);
|
||||
@ -724,12 +722,10 @@ public final class ByteBufUtil {
|
||||
* @param dst the destination byte array.
|
||||
* @param length the number of characters to copy.
|
||||
*/
|
||||
public static void copy(ByteString src, int srcIdx, ByteBuf dst, int length) {
|
||||
final int thisLen = src.length();
|
||||
|
||||
if (srcIdx < 0 || length > thisLen - srcIdx) {
|
||||
public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int length) {
|
||||
if (isOutOfBounds(srcIdx, length, src.length())) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
|
||||
+ length + ") <= srcLen(" + thisLen + ')');
|
||||
+ length + ") <= srcLen(" + src.length() + ')');
|
||||
}
|
||||
|
||||
checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx + src.arrayOffset(), length);
|
||||
@ -771,7 +767,7 @@ public final class ByteBufUtil {
|
||||
* the given {@code length}.
|
||||
*/
|
||||
public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
|
||||
if (offset < 0 || length > checkNotNull(buf, "buf").capacity() - offset) {
|
||||
if (isOutOfBounds(offset, length, buf.capacity())) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length
|
||||
+ ") <= " + "buf.capacity(" + buf.capacity() + ')');
|
||||
|
@ -23,14 +23,14 @@ import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static io.netty.util.internal.MathUtil.isOutOfBounds;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;
|
||||
|
||||
/**
|
||||
* All operations get and set as {@link ByteOrder#BIG_ENDIAN}.
|
||||
*/
|
||||
final class UnsafeByteBufUtil {
|
||||
|
||||
static final boolean BIG_ENDIAN_NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
|
||||
private static final boolean UNALIGNED = PlatformDependent.isUnaligned();
|
||||
|
||||
static byte getByte(long address) {
|
||||
@ -282,7 +282,7 @@ final class UnsafeByteBufUtil {
|
||||
static void getBytes(AbstractByteBuf buf, long addr, int index, ByteBuf dst, int dstIndex, int length) {
|
||||
buf.checkIndex(index, length);
|
||||
checkNotNull(dst, "dst");
|
||||
if (AbstractByteBuf.isInvalid(dstIndex, length, dst.capacity())) {
|
||||
if (isOutOfBounds(dstIndex, length, dst.capacity())) {
|
||||
throw new IndexOutOfBoundsException("dstIndex: " + dstIndex);
|
||||
}
|
||||
|
||||
@ -298,7 +298,7 @@ final class UnsafeByteBufUtil {
|
||||
static void getBytes(AbstractByteBuf buf, long addr, int index, byte[] dst, int dstIndex, int length) {
|
||||
buf.checkIndex(index, length);
|
||||
checkNotNull(dst, "dst");
|
||||
if (AbstractByteBuf.isInvalid(dstIndex, length, dst.length)) {
|
||||
if (isOutOfBounds(dstIndex, length, dst.length)) {
|
||||
throw new IndexOutOfBoundsException("dstIndex: " + dstIndex);
|
||||
}
|
||||
if (length != 0) {
|
||||
@ -328,7 +328,7 @@ final class UnsafeByteBufUtil {
|
||||
static void setBytes(AbstractByteBuf buf, long addr, int index, ByteBuf src, int srcIndex, int length) {
|
||||
buf.checkIndex(index, length);
|
||||
checkNotNull(src, "src");
|
||||
if (AbstractByteBuf.isInvalid(srcIndex, length, src.capacity())) {
|
||||
if (isOutOfBounds(srcIndex, length, src.capacity())) {
|
||||
throw new IndexOutOfBoundsException("srcIndex: " + srcIndex);
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,8 @@ public class CombinedHttpHeaders extends DefaultHttpHeaders {
|
||||
super(new CombinedHttpHeadersImpl(CASE_INSENSITIVE_HASHER, valueConverter(validate), nameValidator(validate)));
|
||||
}
|
||||
|
||||
private static final class CombinedHttpHeadersImpl extends DefaultHeaders<CharSequence> {
|
||||
private static final class CombinedHttpHeadersImpl
|
||||
extends DefaultHeaders<CharSequence, CharSequence, CombinedHttpHeadersImpl> {
|
||||
/**
|
||||
* An estimate of the size of a header value.
|
||||
*/
|
||||
|
@ -18,6 +18,7 @@ package io.netty.handler.codec.http;
|
||||
import io.netty.handler.codec.CharSequenceValueConverter;
|
||||
import io.netty.handler.codec.DefaultHeaders;
|
||||
import io.netty.handler.codec.DefaultHeaders.NameValidator;
|
||||
import io.netty.handler.codec.DefaultHeadersImpl;
|
||||
import io.netty.handler.codec.HeadersUtils;
|
||||
import io.netty.handler.codec.ValueConverter;
|
||||
import io.netty.util.AsciiString;
|
||||
@ -36,7 +37,6 @@ import java.util.Set;
|
||||
|
||||
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
|
||||
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link HttpHeaders}.
|
||||
@ -46,7 +46,7 @@ public class DefaultHttpHeaders extends HttpHeaders {
|
||||
private static final ByteProcessor HEADER_NAME_VALIDATOR = new ByteProcessor() {
|
||||
@Override
|
||||
public boolean process(byte value) throws Exception {
|
||||
validateChar((char) (value & 0xFF));
|
||||
validateHeaderNameElement(value);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -60,16 +60,15 @@ public class DefaultHttpHeaders extends HttpHeaders {
|
||||
PlatformDependent.throwException(e);
|
||||
}
|
||||
} else {
|
||||
checkNotNull(name, "name");
|
||||
// Go through each character in the name
|
||||
for (int index = 0; index < name.length(); ++index) {
|
||||
validateChar(name.charAt(index));
|
||||
validateHeaderNameElement(name.charAt(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final DefaultHeaders<CharSequence> headers;
|
||||
private final DefaultHeaders<CharSequence, CharSequence, ?> headers;
|
||||
|
||||
public DefaultHttpHeaders() {
|
||||
this(true);
|
||||
@ -80,10 +79,12 @@ public class DefaultHttpHeaders extends HttpHeaders {
|
||||
}
|
||||
|
||||
protected DefaultHttpHeaders(boolean validate, NameValidator<CharSequence> nameValidator) {
|
||||
this(new DefaultHeaders<CharSequence>(CASE_INSENSITIVE_HASHER, valueConverter(validate), nameValidator));
|
||||
this(new DefaultHeadersImpl<CharSequence, CharSequence>(CASE_INSENSITIVE_HASHER,
|
||||
valueConverter(validate),
|
||||
nameValidator));
|
||||
}
|
||||
|
||||
protected DefaultHttpHeaders(DefaultHeaders<CharSequence> headers) {
|
||||
protected DefaultHttpHeaders(DefaultHeaders<CharSequence, CharSequence, ?> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
@ -319,8 +320,9 @@ public class DefaultHttpHeaders extends HttpHeaders {
|
||||
return headers.hashCode(CASE_SENSITIVE_HASHER);
|
||||
}
|
||||
|
||||
private static void validateChar(char character) {
|
||||
switch (character) {
|
||||
private static void validateHeaderNameElement(byte value) {
|
||||
switch (value) {
|
||||
case 0x00:
|
||||
case '\t':
|
||||
case '\n':
|
||||
case 0x0b:
|
||||
@ -333,12 +335,37 @@ public class DefaultHttpHeaders extends HttpHeaders {
|
||||
case '=':
|
||||
throw new IllegalArgumentException(
|
||||
"a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " +
|
||||
character);
|
||||
value);
|
||||
default:
|
||||
// Check to see if the character is not an ASCII character, or invalid
|
||||
if (character > 127) {
|
||||
if (value < 0) {
|
||||
throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " +
|
||||
character);
|
||||
value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateHeaderNameElement(char value) {
|
||||
switch (value) {
|
||||
case 0x00:
|
||||
case '\t':
|
||||
case '\n':
|
||||
case 0x0b:
|
||||
case '\f':
|
||||
case '\r':
|
||||
case ' ':
|
||||
case ',':
|
||||
case ':':
|
||||
case ';':
|
||||
case '=':
|
||||
throw new IllegalArgumentException(
|
||||
"a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " +
|
||||
value);
|
||||
default:
|
||||
// Check to see if the character is not an ASCII character, or invalid
|
||||
if (value > 127) {
|
||||
throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " +
|
||||
value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -357,13 +384,9 @@ public class DefaultHttpHeaders extends HttpHeaders {
|
||||
|
||||
@Override
|
||||
public CharSequence convertObject(Object value) {
|
||||
checkNotNull(value, "value");
|
||||
if (value instanceof CharSequence) {
|
||||
return (CharSequence) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return value.toString();
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
return HttpHeaderDateFormat.get().format((Date) value);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package io.netty.handler.codec.http;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.util.AsciiString;
|
||||
import static io.netty.util.AsciiString.c2b;
|
||||
|
||||
final class HttpHeadersEncoder {
|
||||
|
||||
@ -29,8 +30,8 @@ final class HttpHeadersEncoder {
|
||||
final int nameLen = name.length();
|
||||
final int valueLen = value.length();
|
||||
final int entryLen = nameLen + valueLen + 4;
|
||||
int offset = buf.writerIndex();
|
||||
buf.ensureWritable(entryLen);
|
||||
int offset = buf.writerIndex();
|
||||
writeAscii(buf, offset, name, nameLen);
|
||||
offset += nameLen;
|
||||
buf.setByte(offset ++, ':');
|
||||
@ -44,23 +45,15 @@ final class HttpHeadersEncoder {
|
||||
|
||||
private static void writeAscii(ByteBuf buf, int offset, CharSequence value, int valueLen) {
|
||||
if (value instanceof AsciiString) {
|
||||
writeAsciiString(buf, offset, (AsciiString) value, valueLen);
|
||||
ByteBufUtil.copy((AsciiString) value, 0, buf, offset, valueLen);
|
||||
} else {
|
||||
writeCharSequence(buf, offset, value, valueLen);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeAsciiString(ByteBuf buf, int offset, AsciiString value, int valueLen) {
|
||||
ByteBufUtil.copy(value, 0, buf, offset, valueLen);
|
||||
}
|
||||
|
||||
private static void writeCharSequence(ByteBuf buf, int offset, CharSequence value, int valueLen) {
|
||||
for (int i = 0; i < valueLen; i ++) {
|
||||
for (int i = 0; i < valueLen; ++i) {
|
||||
buf.setByte(offset ++, c2b(value.charAt(i)));
|
||||
}
|
||||
}
|
||||
|
||||
private static int c2b(char ch) {
|
||||
return ch < 256? (byte) ch : '?';
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import static io.netty.handler.codec.http.HttpConstants.SP;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.ByteProcessor;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
/**
|
||||
@ -471,7 +470,7 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
|
||||
|
||||
private static final class HttpStatusLineProcessor implements ByteProcessor {
|
||||
private static final byte ASCII_SPACE = (byte) ' ';
|
||||
private final ByteString string;
|
||||
private final AsciiString string;
|
||||
private int i;
|
||||
/**
|
||||
* 0 = New or havn't seen {@link ASCII_SPACE}.
|
||||
@ -482,7 +481,7 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
|
||||
private int state;
|
||||
private HttpResponseStatus status;
|
||||
|
||||
public HttpStatusLineProcessor(ByteString string) {
|
||||
public HttpStatusLineProcessor(AsciiString string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
@ -506,7 +505,7 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
|
||||
}
|
||||
|
||||
private void parseStatus(int codeEnd) {
|
||||
int code = string.parseAsciiInt(0, codeEnd);
|
||||
int code = string.parseInt(0, codeEnd);
|
||||
status = valueOf(code);
|
||||
if (codeEnd < string.length()) {
|
||||
String actualReason = string.toString(codeEnd + 1, string.length());
|
||||
@ -534,7 +533,7 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
|
||||
*
|
||||
* @throws IllegalArgumentException if the specified status line is malformed
|
||||
*/
|
||||
public static HttpResponseStatus parseLine(ByteString line) {
|
||||
public static HttpResponseStatus parseLine(AsciiString line) {
|
||||
try {
|
||||
HttpStatusLineProcessor processor = new HttpStatusLineProcessor(line);
|
||||
line.forEachByte(processor);
|
||||
|
@ -17,7 +17,6 @@ package io.netty.handler.codec.spdy;
|
||||
|
||||
import io.netty.handler.codec.CharSequenceValueConverter;
|
||||
import io.netty.handler.codec.DefaultHeaders;
|
||||
import io.netty.handler.codec.Headers;
|
||||
import io.netty.handler.codec.HeadersUtils;
|
||||
|
||||
import java.util.Iterator;
|
||||
@ -27,7 +26,7 @@ import java.util.Map.Entry;
|
||||
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
|
||||
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
|
||||
|
||||
public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence> implements SpdyHeaders {
|
||||
public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence, CharSequence, SpdyHeaders> implements SpdyHeaders {
|
||||
private static final NameValidator<CharSequence> SpydNameValidator = new NameValidator<CharSequence>() {
|
||||
@Override
|
||||
public void validateName(CharSequence name) {
|
||||
@ -42,214 +41,10 @@ public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence> implements
|
||||
@SuppressWarnings("unchecked")
|
||||
public DefaultSpdyHeaders(boolean validate) {
|
||||
super(CASE_INSENSITIVE_HASHER,
|
||||
validate ? HeaderValueConverterAndValidator.INSTANCE : HeaderValueConverter.INSTANCE,
|
||||
validate ? HeaderValueConverterAndValidator.INSTANCE : CharSequenceValueConverter.INSTANCE,
|
||||
validate ? SpydNameValidator : NameValidator.NOT_NULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders add(CharSequence name, CharSequence value) {
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders add(CharSequence name, CharSequence... values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addObject(CharSequence name, Object value) {
|
||||
super.addObject(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addObject(CharSequence name, Iterable<?> values) {
|
||||
super.addObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addObject(CharSequence name, Object... values) {
|
||||
super.addObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addBoolean(CharSequence name, boolean value) {
|
||||
super.addBoolean(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addChar(CharSequence name, char value) {
|
||||
super.addChar(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addByte(CharSequence name, byte value) {
|
||||
super.addByte(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addShort(CharSequence name, short value) {
|
||||
super.addShort(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addInt(CharSequence name, int value) {
|
||||
super.addInt(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addLong(CharSequence name, long value) {
|
||||
super.addLong(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addFloat(CharSequence name, float value) {
|
||||
super.addFloat(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addDouble(CharSequence name, double value) {
|
||||
super.addDouble(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders addTimeMillis(CharSequence name, long value) {
|
||||
super.addTimeMillis(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders add(Headers<? extends CharSequence> headers) {
|
||||
super.add(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders set(CharSequence name, CharSequence value) {
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders set(CharSequence name, CharSequence... values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setObject(CharSequence name, Object value) {
|
||||
super.setObject(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setObject(CharSequence name, Iterable<?> values) {
|
||||
super.setObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setObject(CharSequence name, Object... values) {
|
||||
super.setObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setBoolean(CharSequence name, boolean value) {
|
||||
super.setBoolean(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setChar(CharSequence name, char value) {
|
||||
super.setChar(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setByte(CharSequence name, byte value) {
|
||||
super.setByte(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setShort(CharSequence name, short value) {
|
||||
super.setShort(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setInt(CharSequence name, int value) {
|
||||
super.setInt(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setLong(CharSequence name, long value) {
|
||||
super.setLong(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setFloat(CharSequence name, float value) {
|
||||
super.setFloat(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setDouble(CharSequence name, double value) {
|
||||
super.setDouble(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setTimeMillis(CharSequence name, long value) {
|
||||
super.setTimeMillis(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders set(Headers<? extends CharSequence> headers) {
|
||||
super.set(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders setAll(Headers<? extends CharSequence> headers) {
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpdyHeaders clear() {
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsString(CharSequence name) {
|
||||
return HeadersUtils.getAsString(this, name);
|
||||
@ -276,23 +71,7 @@ public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence> implements
|
||||
ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
|
||||
}
|
||||
|
||||
private static class HeaderValueConverter extends CharSequenceValueConverter {
|
||||
public static final HeaderValueConverter INSTANCE = new HeaderValueConverter();
|
||||
|
||||
@Override
|
||||
public CharSequence convertObject(Object value) {
|
||||
final CharSequence seq;
|
||||
if (value instanceof CharSequence) {
|
||||
seq = (CharSequence) value;
|
||||
} else {
|
||||
seq = value.toString();
|
||||
}
|
||||
|
||||
return seq;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class HeaderValueConverterAndValidator extends HeaderValueConverter {
|
||||
private static final class HeaderValueConverterAndValidator extends CharSequenceValueConverter {
|
||||
public static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator();
|
||||
|
||||
@Override
|
||||
|
@ -15,17 +15,15 @@
|
||||
*/
|
||||
package io.netty.handler.codec.spdy;
|
||||
|
||||
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_MAX_NV_LENGTH;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_MAX_NV_LENGTH;
|
||||
|
||||
public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
|
||||
|
||||
private final int version;
|
||||
@ -59,18 +57,15 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
|
||||
ByteBuf headerBlock = alloc.heapBuffer();
|
||||
writeLengthField(headerBlock, numHeaders);
|
||||
for (CharSequence name: names) {
|
||||
final ByteString nameBytes = new ByteString(name, CharsetUtil.UTF_8);
|
||||
int length = nameBytes.length();
|
||||
writeLengthField(headerBlock, length);
|
||||
ByteBufUtil.copy(nameBytes, 0, headerBlock, length);
|
||||
writeLengthField(headerBlock, name.length());
|
||||
ByteBufUtil.writeAscii(headerBlock, name);
|
||||
int savedIndex = headerBlock.writerIndex();
|
||||
int valueLength = 0;
|
||||
writeLengthField(headerBlock, valueLength);
|
||||
for (CharSequence value: frame.headers().getAll(name)) {
|
||||
final ByteString valueBytes = new ByteString(value, CharsetUtil.UTF_8);
|
||||
length = valueBytes.length();
|
||||
int length = value.length();
|
||||
if (length > 0) {
|
||||
ByteBufUtil.copy(valueBytes, 0, headerBlock, length);
|
||||
ByteBufUtil.writeAscii(headerBlock, value);
|
||||
headerBlock.writeByte(0);
|
||||
valueLength += length + 1;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import java.util.Map.Entry;
|
||||
* Provides the constants for the standard SPDY HTTP header names and commonly
|
||||
* used utility methods that access a {@link SpdyHeadersFrame}.
|
||||
*/
|
||||
public interface SpdyHeaders extends Headers<CharSequence> {
|
||||
public interface SpdyHeaders extends Headers<CharSequence, CharSequence, SpdyHeaders> {
|
||||
|
||||
/**
|
||||
* SPDY HTTP header names
|
||||
@ -60,108 +60,6 @@ public interface SpdyHeaders extends Headers<CharSequence> {
|
||||
private HttpNames() { }
|
||||
}
|
||||
|
||||
@Override
|
||||
SpdyHeaders add(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@Override
|
||||
SpdyHeaders add(CharSequence name, CharSequence... values);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addObject(CharSequence name, Object value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addObject(CharSequence name, Iterable<?> values);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addObject(CharSequence name, Object... values);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addBoolean(CharSequence name, boolean value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addByte(CharSequence name, byte value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addChar(CharSequence name, char value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addShort(CharSequence name, short value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addInt(CharSequence name, int value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addLong(CharSequence name, long value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addFloat(CharSequence name, float value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addDouble(CharSequence name, double value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders addTimeMillis(CharSequence name, long value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders add(Headers<? extends CharSequence> headers);
|
||||
|
||||
@Override
|
||||
SpdyHeaders set(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@Override
|
||||
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 setTimeMillis(CharSequence name, long value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders setObject(CharSequence name, Object value);
|
||||
|
||||
@Override
|
||||
SpdyHeaders setObject(CharSequence name, Iterable<?> values);
|
||||
|
||||
@Override
|
||||
SpdyHeaders setObject(CharSequence name, Object... values);
|
||||
|
||||
@Override
|
||||
SpdyHeaders set(Headers<? extends CharSequence> headers);
|
||||
|
||||
@Override
|
||||
SpdyHeaders setAll(Headers<? extends CharSequence> headers);
|
||||
|
||||
@Override
|
||||
SpdyHeaders clear();
|
||||
|
||||
/**
|
||||
* {@link Headers#get(Object)} and convert the result to a {@link String}.
|
||||
* @param name the name of the header to retrieve
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2015 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.http2;
|
||||
|
||||
import io.netty.handler.codec.DefaultHeaders;
|
||||
import io.netty.handler.codec.UnsupportedValueConverter;
|
||||
import io.netty.handler.codec.ValueConverter;
|
||||
|
||||
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
|
||||
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
|
||||
|
||||
final class CharSequenceMap<V> extends DefaultHeaders<CharSequence, V, CharSequenceMap<V>> {
|
||||
public CharSequenceMap() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public CharSequenceMap(boolean caseSensative) {
|
||||
this(caseSensative, UnsupportedValueConverter.<V>instance());
|
||||
}
|
||||
|
||||
public CharSequenceMap(boolean caseSensative, ValueConverter<V> valueConverter) {
|
||||
super(caseSensative ? CASE_SENSITIVE_HASHER : CASE_INSENSITIVE_HASHER, valueConverter);
|
||||
}
|
||||
}
|
@ -14,13 +14,6 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_ENCODING;
|
||||
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.GZIP;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.IDENTITY;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
@ -31,7 +24,14 @@ import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.compression.ZlibCodecFactory;
|
||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||
import io.netty.util.ByteString;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_ENCODING;
|
||||
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.GZIP;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.IDENTITY;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP;
|
||||
|
||||
/**
|
||||
* A decorating HTTP2 encoder that will compress data frames according to the {@code content-encoding} header for each
|
||||
@ -195,11 +195,11 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
||||
* (alternatively, you can throw a {@link Http2Exception} to block unknown encoding).
|
||||
* @throws Http2Exception If the specified encoding is not not supported and warrants an exception
|
||||
*/
|
||||
protected EmbeddedChannel newContentCompressor(ByteString contentEncoding) throws Http2Exception {
|
||||
if (GZIP.equals(contentEncoding) || X_GZIP.equals(contentEncoding)) {
|
||||
protected EmbeddedChannel newContentCompressor(CharSequence contentEncoding) throws Http2Exception {
|
||||
if (GZIP.contentEqualsIgnoreCase(contentEncoding) || X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
|
||||
return newCompressionChannel(ZlibWrapper.GZIP);
|
||||
}
|
||||
if (DEFLATE.equals(contentEncoding) || X_DEFLATE.equals(contentEncoding)) {
|
||||
if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
|
||||
return newCompressionChannel(ZlibWrapper.ZLIB);
|
||||
}
|
||||
// 'identity' or unsupported
|
||||
@ -214,7 +214,7 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
||||
* @return the expected content encoding of the new content.
|
||||
* @throws Http2Exception if the {@code contentEncoding} is not supported and warrants an exception
|
||||
*/
|
||||
protected ByteString getTargetContentEncoding(ByteString contentEncoding) throws Http2Exception {
|
||||
protected CharSequence getTargetContentEncoding(CharSequence contentEncoding) throws Http2Exception {
|
||||
return contentEncoding;
|
||||
}
|
||||
|
||||
@ -241,14 +241,14 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
||||
return null;
|
||||
}
|
||||
|
||||
ByteString encoding = headers.get(CONTENT_ENCODING);
|
||||
CharSequence encoding = headers.get(CONTENT_ENCODING);
|
||||
if (encoding == null) {
|
||||
encoding = IDENTITY;
|
||||
}
|
||||
final EmbeddedChannel compressor = newContentCompressor(encoding);
|
||||
if (compressor != null) {
|
||||
ByteString targetContentEncoding = getTargetContentEncoding(encoding);
|
||||
if (IDENTITY.equals(targetContentEncoding)) {
|
||||
CharSequence targetContentEncoding = getTargetContentEncoding(encoding);
|
||||
if (IDENTITY.contentEqualsIgnoreCase(targetContentEncoding)) {
|
||||
headers.remove(CONTENT_ENCODING);
|
||||
} else {
|
||||
headers.set(CONTENT_ENCODING, targetContentEncoding);
|
||||
|
@ -14,9 +14,6 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
||||
|
||||
/**
|
||||
* Provides common functionality for {@link Http2HeaderTable}
|
||||
*/
|
||||
|
@ -14,28 +14,32 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
||||
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
||||
import io.netty.handler.codec.ByteStringValueConverter;
|
||||
import io.netty.handler.codec.CharSequenceValueConverter;
|
||||
import io.netty.handler.codec.DefaultHeaders;
|
||||
import io.netty.handler.codec.Headers;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.ByteProcessor;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
public class DefaultHttp2Headers extends DefaultHeaders<ByteString> implements Http2Headers {
|
||||
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
||||
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
|
||||
import static io.netty.util.AsciiString.isUpperCase;
|
||||
|
||||
public class DefaultHttp2Headers
|
||||
extends DefaultHeaders<CharSequence, CharSequence, Http2Headers> implements Http2Headers {
|
||||
private static final ByteProcessor HTTP2_NAME_VALIDATOR_PROCESSOR = new ByteProcessor() {
|
||||
@Override
|
||||
public boolean process(byte value) throws Exception {
|
||||
return value < 'A' || value > 'Z';
|
||||
return !isUpperCase(value);
|
||||
}
|
||||
};
|
||||
private static final NameValidator<ByteString> HTTP2_NAME_VALIDATOR = new NameValidator<ByteString>() {
|
||||
private static final NameValidator<CharSequence> HTTP2_NAME_VALIDATOR = new NameValidator<CharSequence>() {
|
||||
@Override
|
||||
public void validateName(ByteString name) {
|
||||
public void validateName(CharSequence name) {
|
||||
if (name instanceof AsciiString) {
|
||||
final int index;
|
||||
try {
|
||||
index = name.forEachByte(HTTP2_NAME_VALIDATOR_PROCESSOR);
|
||||
index = ((AsciiString) name).forEachByte(HTTP2_NAME_VALIDATOR_PROCESSOR);
|
||||
} catch (Http2Exception e) {
|
||||
PlatformDependent.throwException(e);
|
||||
return;
|
||||
@ -46,11 +50,21 @@ public class DefaultHttp2Headers extends DefaultHeaders<ByteString> implements H
|
||||
}
|
||||
|
||||
if (index != -1) {
|
||||
PlatformDependent.throwException(connectionError(PROTOCOL_ERROR, "invalid header name [%s]", name));
|
||||
PlatformDependent.throwException(connectionError(PROTOCOL_ERROR,
|
||||
"invalid header name [%s]", name));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < name.length(); ++i) {
|
||||
if (isUpperCase(name.charAt(i))) {
|
||||
PlatformDependent.throwException(connectionError(PROTOCOL_ERROR,
|
||||
"invalid header name [%s]", name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
private HeaderEntry<ByteString> firstNonPseudo = head;
|
||||
|
||||
private HeaderEntry<CharSequence, CharSequence> firstNonPseudo = head;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
@ -69,282 +83,83 @@ public class DefaultHttp2Headers extends DefaultHeaders<ByteString> implements H
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public DefaultHttp2Headers(boolean validate) {
|
||||
super(ByteStringValueConverter.INSTANCE, validate ? HTTP2_NAME_VALIDATOR : NameValidator.NOT_NULL);
|
||||
// Case sensitive compare is used because it is cheaper, and header validation can be used to catch invalid
|
||||
// headers.
|
||||
super(CASE_SENSITIVE_HASHER,
|
||||
CharSequenceValueConverter.INSTANCE,
|
||||
validate ? HTTP2_NAME_VALIDATOR : NameValidator.NOT_NULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers add(ByteString name, ByteString value) {
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers add(ByteString name, Iterable<? extends ByteString> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers add(ByteString name, ByteString... values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addObject(ByteString name, Object value) {
|
||||
super.addObject(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addObject(ByteString name, Iterable<?> values) {
|
||||
super.addObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addObject(ByteString name, Object... values) {
|
||||
super.addObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addBoolean(ByteString name, boolean value) {
|
||||
super.addBoolean(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addChar(ByteString name, char value) {
|
||||
super.addChar(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addByte(ByteString name, byte value) {
|
||||
super.addByte(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addShort(ByteString name, short value) {
|
||||
super.addShort(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addInt(ByteString name, int value) {
|
||||
super.addInt(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addLong(ByteString name, long value) {
|
||||
super.addLong(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addFloat(ByteString name, float value) {
|
||||
super.addFloat(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addDouble(ByteString name, double value) {
|
||||
super.addDouble(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addTimeMillis(ByteString name, long value) {
|
||||
super.addTimeMillis(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers add(Headers<? extends ByteString> headers) {
|
||||
super.add(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers set(ByteString name, ByteString value) {
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers set(ByteString name, Iterable<? extends ByteString> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers set(ByteString name, ByteString... values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setObject(ByteString name, Object value) {
|
||||
super.setObject(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setObject(ByteString name, Iterable<?> values) {
|
||||
super.setObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setObject(ByteString name, Object... values) {
|
||||
super.setObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setBoolean(ByteString name, boolean value) {
|
||||
super.setBoolean(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setChar(ByteString name, char value) {
|
||||
super.setChar(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setByte(ByteString name, byte value) {
|
||||
super.setByte(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setShort(ByteString name, short value) {
|
||||
super.setShort(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setInt(ByteString name, int value) {
|
||||
super.setInt(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setLong(ByteString name, long value) {
|
||||
super.setLong(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setFloat(ByteString name, float value) {
|
||||
super.setFloat(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setDouble(ByteString name, double value) {
|
||||
super.setDouble(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setTimeMillis(ByteString name, long value) {
|
||||
super.setTimeMillis(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers set(Headers<? extends ByteString> headers) {
|
||||
super.set(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setAll(Headers<? extends ByteString> headers) {
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers clear() {
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers method(ByteString value) {
|
||||
public Http2Headers method(CharSequence value) {
|
||||
set(PseudoHeaderName.METHOD.value(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers scheme(ByteString value) {
|
||||
public Http2Headers scheme(CharSequence value) {
|
||||
set(PseudoHeaderName.SCHEME.value(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers authority(ByteString value) {
|
||||
public Http2Headers authority(CharSequence value) {
|
||||
set(PseudoHeaderName.AUTHORITY.value(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers path(ByteString value) {
|
||||
public Http2Headers path(CharSequence value) {
|
||||
set(PseudoHeaderName.PATH.value(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers status(ByteString value) {
|
||||
public Http2Headers status(CharSequence value) {
|
||||
set(PseudoHeaderName.STATUS.value(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString method() {
|
||||
public CharSequence method() {
|
||||
return get(PseudoHeaderName.METHOD.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString scheme() {
|
||||
public CharSequence scheme() {
|
||||
return get(PseudoHeaderName.SCHEME.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString authority() {
|
||||
public CharSequence authority() {
|
||||
return get(PseudoHeaderName.AUTHORITY.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString path() {
|
||||
public CharSequence path() {
|
||||
return get(PseudoHeaderName.PATH.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString status() {
|
||||
public CharSequence status() {
|
||||
return get(PseudoHeaderName.STATUS.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HeaderEntry<ByteString> newHeaderEntry(int h, ByteString name, ByteString value,
|
||||
HeaderEntry<ByteString> next) {
|
||||
protected final HeaderEntry<CharSequence, CharSequence> newHeaderEntry(int h, CharSequence name, CharSequence value,
|
||||
HeaderEntry<CharSequence, CharSequence> next) {
|
||||
return new Http2HeaderEntry(h, name, value, next);
|
||||
}
|
||||
|
||||
private final class Http2HeaderEntry extends HeaderEntry<ByteString> {
|
||||
protected Http2HeaderEntry(int hash, ByteString key, ByteString value, HeaderEntry<ByteString> next) {
|
||||
private final class Http2HeaderEntry extends HeaderEntry<CharSequence, CharSequence> {
|
||||
protected Http2HeaderEntry(int hash, CharSequence key, CharSequence value,
|
||||
HeaderEntry<CharSequence, CharSequence> next) {
|
||||
super(hash, key);
|
||||
this.value = value;
|
||||
this.next = next;
|
||||
|
||||
// Make sure the pseudo headers fields are first in iteration order
|
||||
if (!key.isEmpty() && key.byteAt(0) == ':') {
|
||||
if (key.length() != 0 && key.charAt(0) == ':') {
|
||||
after = firstNonPseudo;
|
||||
before = firstNonPseudo.before();
|
||||
} else {
|
||||
|
@ -15,6 +15,15 @@
|
||||
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import com.twitter.hpack.Decoder;
|
||||
import com.twitter.hpack.HeaderListener;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import io.netty.util.AsciiString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE;
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_HEADER_SIZE;
|
||||
import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR;
|
||||
@ -22,15 +31,6 @@ import static io.netty.handler.codec.http2.Http2Error.ENHANCE_YOUR_CALM;
|
||||
import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import io.netty.util.ByteString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.twitter.hpack.Decoder;
|
||||
import com.twitter.hpack.HeaderListener;
|
||||
|
||||
public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2HeadersDecoder.Configuration {
|
||||
private final int maxHeaderSize;
|
||||
@ -91,7 +91,7 @@ public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2Hea
|
||||
HeaderListener listener = new HeaderListener() {
|
||||
@Override
|
||||
public void addHeader(byte[] key, byte[] value, boolean sensitive) {
|
||||
headers.add(new ByteString(key, false), new ByteString(value, false));
|
||||
headers.add(new AsciiString(key, false), new AsciiString(value, false));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -15,22 +15,22 @@
|
||||
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE;
|
||||
import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import com.twitter.hpack.Encoder;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.AsciiString;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.twitter.hpack.Encoder;
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE;
|
||||
import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2HeadersEncoder.Configuration {
|
||||
private final Encoder encoder;
|
||||
@ -64,7 +64,7 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea
|
||||
tableSizeChangeOutput.reset();
|
||||
}
|
||||
|
||||
for (Entry<ByteString, ByteString> header : headers) {
|
||||
for (Entry<CharSequence, CharSequence> header : headers) {
|
||||
encodeHeader(header.getKey(), header.getValue(), stream);
|
||||
}
|
||||
} catch (Http2Exception e) {
|
||||
@ -90,11 +90,13 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea
|
||||
return this;
|
||||
}
|
||||
|
||||
private void encodeHeader(ByteString key, ByteString value, OutputStream stream) throws IOException {
|
||||
encoder.encodeHeader(stream,
|
||||
key.isEntireArrayUsed() ? key.array() : new ByteString(key, true).array(),
|
||||
value.isEntireArrayUsed() ? value.array() : new ByteString(value, true).array(),
|
||||
sensitivityDetector.isSensitive(key, value));
|
||||
private byte[] toBytes(CharSequence chars) {
|
||||
AsciiString aString = AsciiString.of(chars);
|
||||
return aString.isEntireArrayUsed() ? aString.array() : aString.toByteArray();
|
||||
}
|
||||
|
||||
private void encodeHeader(CharSequence key, CharSequence value, OutputStream stream) throws IOException {
|
||||
encoder.encodeHeader(stream, toBytes(key), toBytes(value), sensitivityDetector.isSensitive(key, value));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,6 +14,14 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.compression.ZlibCodecFactory;
|
||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_ENCODING;
|
||||
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE;
|
||||
@ -24,14 +32,6 @@ import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP;
|
||||
import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.streamError;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.compression.ZlibCodecFactory;
|
||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||
import io.netty.util.ByteString;
|
||||
|
||||
/**
|
||||
* A HTTP2 frame listener that will decompress data frames according to the {@code content-encoding} header for each
|
||||
@ -160,11 +160,11 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
|
||||
* (alternatively, you can throw a {@link Http2Exception} to block unknown encoding).
|
||||
* @throws Http2Exception If the specified encoding is not not supported and warrants an exception
|
||||
*/
|
||||
protected EmbeddedChannel newContentDecompressor(ByteString contentEncoding) throws Http2Exception {
|
||||
if (GZIP.equals(contentEncoding) || X_GZIP.equals(contentEncoding)) {
|
||||
protected EmbeddedChannel newContentDecompressor(CharSequence contentEncoding) throws Http2Exception {
|
||||
if (GZIP.contentEqualsIgnoreCase(contentEncoding) || X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
|
||||
return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
|
||||
}
|
||||
if (DEFLATE.equals(contentEncoding) || X_DEFLATE.equals(contentEncoding)) {
|
||||
if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
|
||||
final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE;
|
||||
// To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly.
|
||||
return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(wrapper));
|
||||
@ -181,7 +181,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
|
||||
* @return the expected content encoding of the new content.
|
||||
* @throws Http2Exception if the {@code contentEncoding} is not supported and warrants an exception
|
||||
*/
|
||||
protected ByteString getTargetContentEncoding(@SuppressWarnings("UnusedParameters") ByteString contentEncoding)
|
||||
protected CharSequence getTargetContentEncoding(@SuppressWarnings("UnusedParameters") CharSequence contentEncoding)
|
||||
throws Http2Exception {
|
||||
return IDENTITY;
|
||||
}
|
||||
@ -204,7 +204,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
|
||||
Http2Decompressor decompressor = decompressor(stream);
|
||||
if (decompressor == null && !endOfStream) {
|
||||
// Determine the content encoding.
|
||||
ByteString contentEncoding = headers.get(CONTENT_ENCODING);
|
||||
CharSequence contentEncoding = headers.get(CONTENT_ENCODING);
|
||||
if (contentEncoding == null) {
|
||||
contentEncoding = IDENTITY;
|
||||
}
|
||||
@ -214,8 +214,8 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
|
||||
stream.setProperty(propertyKey, decompressor);
|
||||
// Decode the content and remove or replace the existing headers
|
||||
// so that the message looks like a decoded message.
|
||||
ByteString targetContentEncoding = getTargetContentEncoding(contentEncoding);
|
||||
if (IDENTITY.equals(targetContentEncoding)) {
|
||||
CharSequence targetContentEncoding = getTargetContentEncoding(contentEncoding);
|
||||
if (IDENTITY.contentEqualsIgnoreCase(targetContentEncoding)) {
|
||||
headers.remove(CONTENT_ENCODING);
|
||||
} else {
|
||||
headers.set(CONTENT_ENCODING, targetContentEncoding);
|
||||
|
@ -16,266 +16,61 @@
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import io.netty.handler.codec.EmptyHeaders;
|
||||
import io.netty.handler.codec.Headers;
|
||||
import io.netty.util.ByteString;
|
||||
|
||||
public final class EmptyHttp2Headers extends EmptyHeaders<ByteString> implements Http2Headers {
|
||||
public final class EmptyHttp2Headers
|
||||
extends EmptyHeaders<CharSequence, CharSequence, Http2Headers> implements Http2Headers {
|
||||
public static final EmptyHttp2Headers INSTANCE = new EmptyHttp2Headers();
|
||||
|
||||
private EmptyHttp2Headers() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers add(ByteString name, ByteString value) {
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers add(ByteString name, Iterable<? extends ByteString> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers add(ByteString name, ByteString... values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addObject(ByteString name, Object value) {
|
||||
super.addObject(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addObject(ByteString name, Iterable<?> values) {
|
||||
super.addObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addObject(ByteString name, Object... values) {
|
||||
super.addObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addBoolean(ByteString name, boolean value) {
|
||||
super.addBoolean(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addChar(ByteString name, char value) {
|
||||
super.addChar(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addByte(ByteString name, byte value) {
|
||||
super.addByte(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addShort(ByteString name, short value) {
|
||||
super.addShort(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addInt(ByteString name, int value) {
|
||||
super.addInt(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addLong(ByteString name, long value) {
|
||||
super.addLong(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addFloat(ByteString name, float value) {
|
||||
super.addFloat(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addDouble(ByteString name, double value) {
|
||||
super.addDouble(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers addTimeMillis(ByteString name, long value) {
|
||||
super.addTimeMillis(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers add(Headers<? extends ByteString> headers) {
|
||||
super.add(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers set(ByteString name, ByteString value) {
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers set(ByteString name, Iterable<? extends ByteString> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers set(ByteString name, ByteString... values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setObject(ByteString name, Object value) {
|
||||
super.setObject(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setObject(ByteString name, Iterable<?> values) {
|
||||
super.setObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setObject(ByteString name, Object... values) {
|
||||
super.setObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setBoolean(ByteString name, boolean value) {
|
||||
super.setBoolean(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setChar(ByteString name, char value) {
|
||||
super.setChar(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setByte(ByteString name, byte value) {
|
||||
super.setByte(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setShort(ByteString name, short value) {
|
||||
super.setShort(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setInt(ByteString name, int value) {
|
||||
super.setInt(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setLong(ByteString name, long value) {
|
||||
super.setLong(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setFloat(ByteString name, float value) {
|
||||
super.setFloat(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setDouble(ByteString name, double value) {
|
||||
super.setDouble(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setTimeMillis(ByteString name, long value) {
|
||||
super.setTimeMillis(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers set(Headers<? extends ByteString> headers) {
|
||||
super.set(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers setAll(Headers<? extends ByteString> headers) {
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Http2Headers clear() {
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers method(ByteString method) {
|
||||
public EmptyHttp2Headers method(CharSequence method) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers scheme(ByteString status) {
|
||||
public EmptyHttp2Headers scheme(CharSequence status) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers authority(ByteString authority) {
|
||||
public EmptyHttp2Headers authority(CharSequence authority) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers path(ByteString path) {
|
||||
public EmptyHttp2Headers path(CharSequence path) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmptyHttp2Headers status(ByteString status) {
|
||||
public EmptyHttp2Headers status(CharSequence status) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString method() {
|
||||
public CharSequence method() {
|
||||
return get(PseudoHeaderName.METHOD.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString scheme() {
|
||||
public CharSequence scheme() {
|
||||
return get(PseudoHeaderName.SCHEME.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString authority() {
|
||||
public CharSequence authority() {
|
||||
return get(PseudoHeaderName.AUTHORITY.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString path() {
|
||||
public CharSequence path() {
|
||||
return get(PseudoHeaderName.PATH.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString status() {
|
||||
public CharSequence status() {
|
||||
return get(PseudoHeaderName.STATUS.value());
|
||||
}
|
||||
}
|
||||
|
@ -15,19 +15,16 @@
|
||||
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import java.util.HashSet;
|
||||
import io.netty.handler.codec.Headers;
|
||||
import io.netty.util.AsciiString;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import io.netty.handler.codec.Headers;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
/**
|
||||
* A collection of headers sent or received via HTTP/2.
|
||||
*/
|
||||
public interface Http2Headers extends Headers<ByteString> {
|
||||
public interface Http2Headers extends Headers<CharSequence, CharSequence, Http2Headers> {
|
||||
|
||||
/**
|
||||
* HTTP/2 pseudo-headers names.
|
||||
@ -58,19 +55,19 @@ public interface Http2Headers extends Headers<ByteString> {
|
||||
*/
|
||||
STATUS(":status");
|
||||
|
||||
private final ByteString value;
|
||||
private static final Set<ByteString> PSEUDO_HEADERS = new HashSet<ByteString>();
|
||||
private final AsciiString value;
|
||||
private static final CharSequenceMap<AsciiString> PSEUDO_HEADERS = new CharSequenceMap<AsciiString>();
|
||||
static {
|
||||
for (PseudoHeaderName pseudoHeader : PseudoHeaderName.values()) {
|
||||
PSEUDO_HEADERS.add(pseudoHeader.value());
|
||||
PSEUDO_HEADERS.add(pseudoHeader.value(), AsciiString.EMPTY_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
PseudoHeaderName(String value) {
|
||||
this.value = new ByteString(value, CharsetUtil.UTF_8);
|
||||
this.value = new AsciiString(value);
|
||||
}
|
||||
|
||||
public ByteString value() {
|
||||
public AsciiString value() {
|
||||
// Return a slice so that the buffer gets its own reader index.
|
||||
return value;
|
||||
}
|
||||
@ -78,168 +75,66 @@ public interface Http2Headers extends Headers<ByteString> {
|
||||
/**
|
||||
* Indicates whether the given header name is a valid HTTP/2 pseudo header.
|
||||
*/
|
||||
public static boolean isPseudoHeader(ByteString header) {
|
||||
public static boolean isPseudoHeader(CharSequence header) {
|
||||
return PSEUDO_HEADERS.contains(header);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Http2Headers add(ByteString name, ByteString value);
|
||||
|
||||
@Override
|
||||
Http2Headers add(ByteString name, Iterable<? extends ByteString> values);
|
||||
|
||||
@Override
|
||||
Http2Headers add(ByteString name, ByteString... values);
|
||||
|
||||
@Override
|
||||
Http2Headers addObject(ByteString name, Object value);
|
||||
|
||||
@Override
|
||||
Http2Headers addObject(ByteString name, Iterable<?> values);
|
||||
|
||||
@Override
|
||||
Http2Headers addObject(ByteString name, Object... values);
|
||||
|
||||
@Override
|
||||
Http2Headers addBoolean(ByteString name, boolean value);
|
||||
|
||||
@Override
|
||||
Http2Headers addByte(ByteString name, byte value);
|
||||
|
||||
@Override
|
||||
Http2Headers addChar(ByteString name, char value);
|
||||
|
||||
@Override
|
||||
Http2Headers addShort(ByteString name, short value);
|
||||
|
||||
@Override
|
||||
Http2Headers addInt(ByteString name, int value);
|
||||
|
||||
@Override
|
||||
Http2Headers addLong(ByteString name, long value);
|
||||
|
||||
@Override
|
||||
Http2Headers addFloat(ByteString name, float value);
|
||||
|
||||
@Override
|
||||
Http2Headers addDouble(ByteString name, double value);
|
||||
|
||||
@Override
|
||||
Http2Headers addTimeMillis(ByteString name, long value);
|
||||
|
||||
@Override
|
||||
Http2Headers add(Headers<? extends ByteString> headers);
|
||||
|
||||
@Override
|
||||
Http2Headers set(ByteString name, ByteString value);
|
||||
|
||||
@Override
|
||||
Http2Headers set(ByteString name, Iterable<? extends ByteString> values);
|
||||
|
||||
@Override
|
||||
Http2Headers set(ByteString name, ByteString... values);
|
||||
|
||||
@Override
|
||||
Http2Headers setObject(ByteString name, Object value);
|
||||
|
||||
@Override
|
||||
Http2Headers setObject(ByteString name, Iterable<?> values);
|
||||
|
||||
@Override
|
||||
Http2Headers setObject(ByteString name, Object... values);
|
||||
|
||||
@Override
|
||||
Http2Headers setBoolean(ByteString name, boolean value);
|
||||
|
||||
@Override
|
||||
Http2Headers setByte(ByteString name, byte value);
|
||||
|
||||
@Override
|
||||
Http2Headers setChar(ByteString name, char value);
|
||||
|
||||
@Override
|
||||
Http2Headers setShort(ByteString name, short value);
|
||||
|
||||
@Override
|
||||
Http2Headers setInt(ByteString name, int value);
|
||||
|
||||
@Override
|
||||
Http2Headers setLong(ByteString name, long value);
|
||||
|
||||
@Override
|
||||
Http2Headers setFloat(ByteString name, float value);
|
||||
|
||||
@Override
|
||||
Http2Headers setDouble(ByteString name, double value);
|
||||
|
||||
@Override
|
||||
Http2Headers setTimeMillis(ByteString name, long value);
|
||||
|
||||
@Override
|
||||
Http2Headers set(Headers<? extends ByteString> headers);
|
||||
|
||||
@Override
|
||||
Http2Headers setAll(Headers<? extends ByteString> headers);
|
||||
|
||||
@Override
|
||||
Http2Headers clear();
|
||||
|
||||
/**
|
||||
* Returns an iterator over all HTTP/2 headers. The iteration order is as follows:
|
||||
* 1. All pseudo headers (order not specified).
|
||||
* 2. All non-pseudo headers (in insertion order).
|
||||
*/
|
||||
@Override
|
||||
Iterator<Entry<ByteString, ByteString>> iterator();
|
||||
Iterator<Entry<CharSequence, CharSequence>> iterator();
|
||||
|
||||
/**
|
||||
* Sets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header
|
||||
*/
|
||||
Http2Headers method(ByteString value);
|
||||
Http2Headers method(CharSequence value);
|
||||
|
||||
/**
|
||||
* Sets the {@link PseudoHeaderName#SCHEME} header if there is no such header
|
||||
*/
|
||||
Http2Headers scheme(ByteString value);
|
||||
Http2Headers scheme(CharSequence value);
|
||||
|
||||
/**
|
||||
* Sets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header
|
||||
*/
|
||||
Http2Headers authority(ByteString value);
|
||||
Http2Headers authority(CharSequence value);
|
||||
|
||||
/**
|
||||
* Sets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header
|
||||
*/
|
||||
Http2Headers path(ByteString value);
|
||||
Http2Headers path(CharSequence value);
|
||||
|
||||
/**
|
||||
* Sets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header
|
||||
*/
|
||||
Http2Headers status(ByteString value);
|
||||
Http2Headers status(CharSequence value);
|
||||
|
||||
/**
|
||||
* Gets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header
|
||||
*/
|
||||
ByteString method();
|
||||
CharSequence method();
|
||||
|
||||
/**
|
||||
* Gets the {@link PseudoHeaderName#SCHEME} header or {@code null} if there is no such header
|
||||
*/
|
||||
ByteString scheme();
|
||||
CharSequence scheme();
|
||||
|
||||
/**
|
||||
* Gets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header
|
||||
*/
|
||||
ByteString authority();
|
||||
CharSequence authority();
|
||||
|
||||
/**
|
||||
* Gets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header
|
||||
*/
|
||||
ByteString path();
|
||||
CharSequence path();
|
||||
|
||||
/**
|
||||
* Gets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header
|
||||
*/
|
||||
ByteString status();
|
||||
CharSequence status();
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.ByteString;
|
||||
|
||||
/**
|
||||
* Encodes {@link Http2Headers} into HPACK-encoded headers blocks.
|
||||
@ -47,7 +46,7 @@ public interface Http2HeadersEncoder {
|
||||
* <a href="http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-7.1.3">sensitive</a>.
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
boolean isSensitive(ByteString name, ByteString value);
|
||||
boolean isSensitive(CharSequence name, CharSequence value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,7 +67,7 @@ public interface Http2HeadersEncoder {
|
||||
*/
|
||||
SensitivityDetector NEVER_SENSITIVE = new SensitivityDetector() {
|
||||
@Override
|
||||
public boolean isSensitive(ByteString name, ByteString value) {
|
||||
public boolean isSensitive(CharSequence name, CharSequence value) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -20,7 +20,6 @@ import io.netty.handler.codec.http.FullHttpMessage;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpUtil;
|
||||
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpMessage;
|
||||
@ -28,17 +27,13 @@ import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpUtil;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.ByteString;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpScheme.HTTP;
|
||||
import static io.netty.handler.codec.http.HttpScheme.HTTPS;
|
||||
@ -47,6 +42,7 @@ import static io.netty.handler.codec.http.HttpUtil.isOriginForm;
|
||||
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.streamError;
|
||||
import static io.netty.util.AsciiString.EMPTY_STRING;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import static io.netty.util.internal.StringUtil.isNullOrEmpty;
|
||||
import static io.netty.util.internal.StringUtil.length;
|
||||
@ -58,21 +54,23 @@ public final class HttpConversionUtil {
|
||||
/**
|
||||
* The set of headers that should not be directly copied when converting headers from HTTP to HTTP/2.
|
||||
*/
|
||||
private static final CharSequenceMap<AsciiString> HTTP_TO_HTTP2_HEADER_BLACKLIST =
|
||||
new CharSequenceMap<AsciiString>();
|
||||
static {
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.CONNECTION, EMPTY_STRING);
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final Set<CharSequence> HTTP_TO_HTTP2_HEADER_BLACKLIST = new HashSet<CharSequence>() {
|
||||
private static final long serialVersionUID = -5678614530214167043L;
|
||||
{
|
||||
add(HttpHeaderNames.CONNECTION);
|
||||
add(HttpHeaderNames.KEEP_ALIVE);
|
||||
add(HttpHeaderNames.PROXY_CONNECTION);
|
||||
add(HttpHeaderNames.TRANSFER_ENCODING);
|
||||
add(HttpHeaderNames.HOST);
|
||||
add(HttpHeaderNames.UPGRADE);
|
||||
add(ExtensionHeaderNames.STREAM_ID.text());
|
||||
add(ExtensionHeaderNames.SCHEME.text());
|
||||
add(ExtensionHeaderNames.PATH.text());
|
||||
AsciiString keepAlive = HttpHeaderNames.KEEP_ALIVE;
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(keepAlive, EMPTY_STRING);
|
||||
@SuppressWarnings("deprecation")
|
||||
AsciiString proxyConnection = HttpHeaderNames.PROXY_CONNECTION;
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(proxyConnection, EMPTY_STRING);
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.TRANSFER_ENCODING, EMPTY_STRING);
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.HOST, EMPTY_STRING);
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.UPGRADE, EMPTY_STRING);
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.STREAM_ID.text(), EMPTY_STRING);
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.SCHEME.text(), EMPTY_STRING);
|
||||
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.PATH.text(), EMPTY_STRING);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This will be the method used for {@link HttpRequest} objects generated out of the HTTP message flow defined in <a
|
||||
@ -166,7 +164,7 @@ public final class HttpConversionUtil {
|
||||
* @return The HTTP/1.x status
|
||||
* @throws Http2Exception If there is a problem translating from HTTP/2 to HTTP/1.x
|
||||
*/
|
||||
public static HttpResponseStatus parseStatus(ByteString status) throws Http2Exception {
|
||||
public static HttpResponseStatus parseStatus(CharSequence status) throws Http2Exception {
|
||||
HttpResponseStatus result;
|
||||
try {
|
||||
result = HttpResponseStatus.parseLine(status);
|
||||
@ -219,9 +217,9 @@ public final class HttpConversionUtil {
|
||||
public static FullHttpRequest toHttpRequest(int streamId, Http2Headers http2Headers, boolean validateHttpHeaders)
|
||||
throws Http2Exception {
|
||||
// HTTP/2 does not define a way to carry the version identifier that is included in the HTTP/1.1 request line.
|
||||
final ByteString method = checkNotNull(http2Headers.method(),
|
||||
final CharSequence method = checkNotNull(http2Headers.method(),
|
||||
"method header cannot be null in conversion to HTTP/1.x");
|
||||
final ByteString path = checkNotNull(http2Headers.path(),
|
||||
final CharSequence path = checkNotNull(http2Headers.path(),
|
||||
"path header cannot be null in conversion to HTTP/1.x");
|
||||
FullHttpRequest msg = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method
|
||||
.toString()), path.toString(), validateHttpHeaders);
|
||||
@ -263,7 +261,7 @@ public final class HttpConversionUtil {
|
||||
HttpVersion httpVersion, boolean isTrailer, boolean isRequest) throws Http2Exception {
|
||||
Http2ToHttpHeaderTranslator translator = new Http2ToHttpHeaderTranslator(streamId, outputHeaders, isRequest);
|
||||
try {
|
||||
for (Entry<ByteString, ByteString> entry : inputHeaders) {
|
||||
for (Entry<CharSequence, CharSequence> entry : inputHeaders) {
|
||||
translator.translate(entry);
|
||||
}
|
||||
} catch (Http2Exception ex) {
|
||||
@ -326,12 +324,10 @@ public final class HttpConversionUtil {
|
||||
Entry<CharSequence, CharSequence> entry = iter.next();
|
||||
final AsciiString aName = AsciiString.of(entry.getKey()).toLowerCase();
|
||||
if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName)) {
|
||||
AsciiString aValue = AsciiString.of(entry.getValue());
|
||||
// https://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.2
|
||||
// makes a special exception for TE
|
||||
// https://tools.ietf.org/html/rfc7540#section-8.1.2.2 makes a special exception for TE
|
||||
if (!aName.contentEqualsIgnoreCase(HttpHeaderNames.TE) ||
|
||||
aValue.contentEqualsIgnoreCase(HttpHeaderValues.TRAILERS)) {
|
||||
out.add(aName, aValue);
|
||||
AsciiString.contentEqualsIgnoreCase(entry.getValue(), HttpHeaderValues.TRAILERS)) {
|
||||
out.add(aName, AsciiString.of(entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -405,23 +401,23 @@ public final class HttpConversionUtil {
|
||||
/**
|
||||
* Translations from HTTP/2 header name to the HTTP/1.x equivalent.
|
||||
*/
|
||||
private static final Map<ByteString, ByteString>
|
||||
REQUEST_HEADER_TRANSLATIONS = new HashMap<ByteString, ByteString>();
|
||||
private static final Map<ByteString, ByteString>
|
||||
RESPONSE_HEADER_TRANSLATIONS = new HashMap<ByteString, ByteString>();
|
||||
private static final CharSequenceMap<AsciiString>
|
||||
REQUEST_HEADER_TRANSLATIONS = new CharSequenceMap<AsciiString>();
|
||||
private static final CharSequenceMap<AsciiString>
|
||||
RESPONSE_HEADER_TRANSLATIONS = new CharSequenceMap<AsciiString>();
|
||||
static {
|
||||
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.AUTHORITY.value(),
|
||||
RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.AUTHORITY.value(),
|
||||
HttpHeaderNames.HOST);
|
||||
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.SCHEME.value(),
|
||||
RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.SCHEME.value(),
|
||||
ExtensionHeaderNames.SCHEME.text());
|
||||
REQUEST_HEADER_TRANSLATIONS.putAll(RESPONSE_HEADER_TRANSLATIONS);
|
||||
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.PATH.value(),
|
||||
REQUEST_HEADER_TRANSLATIONS.add(RESPONSE_HEADER_TRANSLATIONS);
|
||||
RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.PATH.value(),
|
||||
ExtensionHeaderNames.PATH.text());
|
||||
}
|
||||
|
||||
private final int streamId;
|
||||
private final HttpHeaders output;
|
||||
private final Map<ByteString, ByteString> translations;
|
||||
private final CharSequenceMap<AsciiString> translations;
|
||||
|
||||
/**
|
||||
* Create a new instance
|
||||
@ -436,23 +432,20 @@ public final class HttpConversionUtil {
|
||||
translations = request ? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS;
|
||||
}
|
||||
|
||||
public void translate(Entry<ByteString, ByteString> entry) throws Http2Exception {
|
||||
final ByteString name = entry.getKey();
|
||||
final ByteString value = entry.getValue();
|
||||
ByteString translatedName = translations.get(name);
|
||||
if (translatedName != null || !Http2Headers.PseudoHeaderName.isPseudoHeader(name)) {
|
||||
if (translatedName == null) {
|
||||
translatedName = name;
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.3
|
||||
public void translate(Entry<CharSequence, CharSequence> entry) throws Http2Exception {
|
||||
final CharSequence name = entry.getKey();
|
||||
final CharSequence value = entry.getValue();
|
||||
AsciiString translatedName = translations.get(name);
|
||||
if (translatedName != null) {
|
||||
output.add(translatedName, AsciiString.of(value));
|
||||
} else if (!Http2Headers.PseudoHeaderName.isPseudoHeader(name)) {
|
||||
// https://tools.ietf.org/html/rfc7540#section-8.1.2.3
|
||||
// All headers that start with ':' are only valid in HTTP/2 context
|
||||
if (translatedName.isEmpty() || translatedName.byteAt(0) == ':') {
|
||||
if (name.length() == 0 || name.charAt(0) == ':') {
|
||||
throw streamError(streamId, PROTOCOL_ERROR,
|
||||
"Invalid HTTP/2 header '%s' encountered in translation to HTTP/1.x", translatedName);
|
||||
} else {
|
||||
output.add(new AsciiString(translatedName, false), new AsciiString(value, false));
|
||||
}
|
||||
"Invalid HTTP/2 header '%s' encountered in translation to HTTP/1.x", name);
|
||||
}
|
||||
output.add(AsciiString.of(name), AsciiString.of(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,16 +15,11 @@
|
||||
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.SMALLEST_MAX_CONCURRENT_STREAMS;
|
||||
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
@ -33,6 +28,10 @@ import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.SMALLEST_MAX_CONCURRENT_STREAMS;
|
||||
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
||||
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
||||
|
||||
/**
|
||||
* Implementation of a {@link Http2ConnectionEncoder} that dispatches all method call to another
|
||||
* {@link Http2ConnectionEncoder}, until {@code SETTINGS_MAX_CONCURRENT_STREAMS} is reached.
|
||||
@ -72,9 +71,9 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
||||
private static final long serialVersionUID = 1326785622777291198L;
|
||||
private final int lastStreamId;
|
||||
private final long errorCode;
|
||||
private final ByteString debugData;
|
||||
private final byte[] debugData;
|
||||
|
||||
public Http2GoAwayException(int lastStreamId, long errorCode, ByteString debugData) {
|
||||
public Http2GoAwayException(int lastStreamId, long errorCode, byte[] debugData) {
|
||||
super(Http2Error.STREAM_CLOSED);
|
||||
this.lastStreamId = lastStreamId;
|
||||
this.errorCode = errorCode;
|
||||
@ -89,7 +88,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public ByteString debugData() {
|
||||
public byte[] debugData() {
|
||||
return debugData;
|
||||
}
|
||||
}
|
||||
@ -241,8 +240,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
||||
|
||||
private void cancelGoAwayStreams(int lastStreamId, long errorCode, ByteBuf debugData) {
|
||||
Iterator<PendingStream> iter = pendingStreams.values().iterator();
|
||||
Exception e = new Http2GoAwayException(lastStreamId, errorCode,
|
||||
new ByteString(ByteBufUtil.getBytes(debugData), false));
|
||||
Exception e = new Http2GoAwayException(lastStreamId, errorCode, ByteBufUtil.getBytes(debugData));
|
||||
while (iter.hasNext()) {
|
||||
PendingStream stream = iter.next();
|
||||
if (stream.streamId > lastStreamId) {
|
||||
|
@ -25,7 +25,6 @@ import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.concurrent.EventExecutor;
|
||||
import org.junit.After;
|
||||
@ -381,7 +380,7 @@ public class DefaultHttp2FrameIOTest {
|
||||
}
|
||||
|
||||
private Http2Headers headersOfSize(final int minSize) {
|
||||
final ByteString singleByte = new ByteString(new byte[]{0});
|
||||
final AsciiString singleByte = new AsciiString(new byte[]{0}, false);
|
||||
DefaultHttp2Headers headers = new DefaultHttp2Headers(false);
|
||||
for (int size = 0; size < minSize; size += 2) {
|
||||
headers.add(singleByte, singleByte);
|
||||
|
@ -17,17 +17,16 @@
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.AsciiString;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.netty.util.ByteString.fromAscii;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static io.netty.util.AsciiString.of;
|
||||
|
||||
public class DefaultHttp2HeadersTest {
|
||||
|
||||
@ -43,23 +42,26 @@ public class DefaultHttp2HeadersTest {
|
||||
public void pseudoHeadersWithRemovePreservesPseudoIterationOrder() {
|
||||
Http2Headers headers = newHeaders();
|
||||
|
||||
Set<ByteString> nonPseudoHeaders = new HashSet<ByteString>(headers.size());
|
||||
for (Entry<ByteString, ByteString> entry : headers) {
|
||||
if (entry.getKey().isEmpty() || entry.getKey().byteAt(0) != ':') {
|
||||
nonPseudoHeaders.add(entry.getKey());
|
||||
Http2Headers nonPseudoHeaders = new DefaultHttp2Headers();
|
||||
for (Entry<CharSequence, CharSequence> entry : headers) {
|
||||
if (entry.getKey().length() == 0 || entry.getKey().charAt(0) != ':' &&
|
||||
!nonPseudoHeaders.contains(entry.getKey())) {
|
||||
nonPseudoHeaders.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
assertFalse(nonPseudoHeaders.isEmpty());
|
||||
|
||||
// Remove all the non-pseudo headers and verify
|
||||
for (ByteString nonPseudoHeader : nonPseudoHeaders) {
|
||||
assertTrue(headers.remove(nonPseudoHeader));
|
||||
for (Entry<CharSequence, CharSequence> nonPseudoHeaderEntry : nonPseudoHeaders) {
|
||||
assertTrue(headers.remove(nonPseudoHeaderEntry.getKey()));
|
||||
verifyPseudoHeadersFirst(headers);
|
||||
verifyAllPseudoHeadersPresent(headers);
|
||||
}
|
||||
|
||||
// Add back all non-pseudo headers
|
||||
for (ByteString nonPseudoHeader : nonPseudoHeaders) {
|
||||
headers.add(nonPseudoHeader, fromAscii("goo"));
|
||||
for (Entry<CharSequence, CharSequence> nonPseudoHeaderEntry : nonPseudoHeaders) {
|
||||
headers.add(nonPseudoHeaderEntry.getKey(), of("goo"));
|
||||
verifyPseudoHeadersFirst(headers);
|
||||
verifyAllPseudoHeadersPresent(headers);
|
||||
}
|
||||
@ -69,7 +71,7 @@ public class DefaultHttp2HeadersTest {
|
||||
public void testHeaderNameValidation() {
|
||||
Http2Headers headers = newHeaders();
|
||||
|
||||
headers.add(fromAscii("Foo"), fromAscii("foo"));
|
||||
headers.add(of("Foo"), of("foo"));
|
||||
}
|
||||
|
||||
private static void verifyAllPseudoHeadersPresent(Http2Headers headers) {
|
||||
@ -79,9 +81,9 @@ public class DefaultHttp2HeadersTest {
|
||||
}
|
||||
|
||||
private static void verifyPseudoHeadersFirst(Http2Headers headers) {
|
||||
ByteString lastNonPseudoName = null;
|
||||
for (Entry<ByteString, ByteString> entry: headers) {
|
||||
if (entry.getKey().isEmpty() || entry.getKey().byteAt(0) != ':') {
|
||||
CharSequence lastNonPseudoName = null;
|
||||
for (Entry<CharSequence, CharSequence> entry: headers) {
|
||||
if (entry.getKey().length() == 0 || entry.getKey().charAt(0) != ':') {
|
||||
lastNonPseudoName = entry.getKey();
|
||||
} else if (lastNonPseudoName != null) {
|
||||
fail("All pseudo headers must be fist in iteration. Pseudo header " + entry.getKey() +
|
||||
@ -92,14 +94,14 @@ public class DefaultHttp2HeadersTest {
|
||||
|
||||
private static Http2Headers newHeaders() {
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.add(fromAscii("name1"), fromAscii("value1"), fromAscii("value2"));
|
||||
headers.method(fromAscii("POST"));
|
||||
headers.add(fromAscii("2name"), fromAscii("value3"));
|
||||
headers.path(fromAscii("/index.html"));
|
||||
headers.status(fromAscii("200"));
|
||||
headers.authority(fromAscii("netty.io"));
|
||||
headers.add(fromAscii("name3"), fromAscii("value4"));
|
||||
headers.scheme(fromAscii("https"));
|
||||
headers.add(of("name1"), of("value1"), of("value2"));
|
||||
headers.method(of("POST"));
|
||||
headers.add(of("2name"), of("value3"));
|
||||
headers.path(of("/index.html"));
|
||||
headers.status(of("200"));
|
||||
headers.authority(of("netty.io"));
|
||||
headers.add(of("name3"), of("value4"));
|
||||
headers.scheme(of("https"));
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.ByteString;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
@ -71,8 +70,8 @@ final class Http2TestUtil {
|
||||
/**
|
||||
* Returns an {@link AsciiString} that wraps a randomly-filled byte array.
|
||||
*/
|
||||
public static ByteString randomString() {
|
||||
return new ByteString(randomBytes());
|
||||
public static AsciiString randomString() {
|
||||
return new AsciiString(randomBytes());
|
||||
}
|
||||
|
||||
public static CharSequence of(String s) {
|
||||
|
@ -18,7 +18,6 @@ package io.netty.handler.codec.stomp;
|
||||
|
||||
import io.netty.handler.codec.CharSequenceValueConverter;
|
||||
import io.netty.handler.codec.DefaultHeaders;
|
||||
import io.netty.handler.codec.Headers;
|
||||
import io.netty.handler.codec.HeadersUtils;
|
||||
|
||||
import java.util.Iterator;
|
||||
@ -28,215 +27,12 @@ import java.util.Map.Entry;
|
||||
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
|
||||
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
|
||||
|
||||
public class DefaultStompHeaders extends DefaultHeaders<CharSequence> implements StompHeaders {
|
||||
public class DefaultStompHeaders
|
||||
extends DefaultHeaders<CharSequence, CharSequence, StompHeaders> implements StompHeaders {
|
||||
public DefaultStompHeaders() {
|
||||
super(CASE_SENSITIVE_HASHER, CharSequenceValueConverter.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders add(CharSequence name, CharSequence value) {
|
||||
super.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders add(CharSequence name, CharSequence... values) {
|
||||
super.add(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addObject(CharSequence name, Object value) {
|
||||
super.addObject(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addObject(CharSequence name, Iterable<?> values) {
|
||||
super.addObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addObject(CharSequence name, Object... values) {
|
||||
super.addObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addBoolean(CharSequence name, boolean value) {
|
||||
super.addBoolean(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addChar(CharSequence name, char value) {
|
||||
super.addChar(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addByte(CharSequence name, byte value) {
|
||||
super.addByte(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addShort(CharSequence name, short value) {
|
||||
super.addShort(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addInt(CharSequence name, int value) {
|
||||
super.addInt(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addLong(CharSequence name, long value) {
|
||||
super.addLong(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addFloat(CharSequence name, float value) {
|
||||
super.addFloat(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addDouble(CharSequence name, double value) {
|
||||
super.addDouble(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders addTimeMillis(CharSequence name, long value) {
|
||||
super.addTimeMillis(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders add(Headers<? extends CharSequence> headers) {
|
||||
super.add(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders set(CharSequence name, CharSequence value) {
|
||||
super.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders set(CharSequence name, CharSequence... values) {
|
||||
super.set(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setObject(CharSequence name, Object value) {
|
||||
super.setObject(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setObject(CharSequence name, Iterable<?> values) {
|
||||
super.setObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setObject(CharSequence name, Object... values) {
|
||||
super.setObject(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setBoolean(CharSequence name, boolean value) {
|
||||
super.setBoolean(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setChar(CharSequence name, char value) {
|
||||
super.setChar(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setByte(CharSequence name, byte value) {
|
||||
super.setByte(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setShort(CharSequence name, short value) {
|
||||
super.setShort(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setInt(CharSequence name, int value) {
|
||||
super.setInt(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setLong(CharSequence name, long value) {
|
||||
super.setLong(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setFloat(CharSequence name, float value) {
|
||||
super.setFloat(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setDouble(CharSequence name, double value) {
|
||||
super.setDouble(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setTimeMillis(CharSequence name, long value) {
|
||||
super.setTimeMillis(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders set(Headers<? extends CharSequence> headers) {
|
||||
super.set(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders setAll(Headers<? extends CharSequence> headers) {
|
||||
super.setAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StompHeaders clear() {
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsString(CharSequence name) {
|
||||
return HeadersUtils.getAsString(this, name);
|
||||
|
@ -26,7 +26,7 @@ import java.util.Map.Entry;
|
||||
* The multimap data structure for the STOMP header names and values. It also provides the constants for the standard
|
||||
* STOMP header names and values.
|
||||
*/
|
||||
public interface StompHeaders extends Headers<CharSequence> {
|
||||
public interface StompHeaders extends Headers<CharSequence, CharSequence, StompHeaders> {
|
||||
|
||||
AsciiString ACCEPT_VERSION = new AsciiString("accept-version");
|
||||
AsciiString HOST = new AsciiString("host");
|
||||
@ -48,108 +48,6 @@ public interface StompHeaders extends Headers<CharSequence> {
|
||||
AsciiString CONTENT_LENGTH = new AsciiString("content-length");
|
||||
AsciiString CONTENT_TYPE = new AsciiString("content-type");
|
||||
|
||||
@Override
|
||||
StompHeaders add(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
StompHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@Override
|
||||
StompHeaders add(CharSequence name, CharSequence... values);
|
||||
|
||||
@Override
|
||||
StompHeaders addObject(CharSequence name, Object value);
|
||||
|
||||
@Override
|
||||
StompHeaders addObject(CharSequence name, Iterable<?> values);
|
||||
|
||||
@Override
|
||||
StompHeaders addObject(CharSequence name, Object... values);
|
||||
|
||||
@Override
|
||||
StompHeaders addBoolean(CharSequence name, boolean value);
|
||||
|
||||
@Override
|
||||
StompHeaders addByte(CharSequence name, byte value);
|
||||
|
||||
@Override
|
||||
StompHeaders addChar(CharSequence name, char value);
|
||||
|
||||
@Override
|
||||
StompHeaders addShort(CharSequence name, short value);
|
||||
|
||||
@Override
|
||||
StompHeaders addInt(CharSequence name, int value);
|
||||
|
||||
@Override
|
||||
StompHeaders addLong(CharSequence name, long value);
|
||||
|
||||
@Override
|
||||
StompHeaders addFloat(CharSequence name, float value);
|
||||
|
||||
@Override
|
||||
StompHeaders addDouble(CharSequence name, double value);
|
||||
|
||||
@Override
|
||||
StompHeaders addTimeMillis(CharSequence name, long value);
|
||||
|
||||
@Override
|
||||
StompHeaders add(Headers<? extends CharSequence> headers);
|
||||
|
||||
@Override
|
||||
StompHeaders set(CharSequence name, CharSequence value);
|
||||
|
||||
@Override
|
||||
StompHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
|
||||
|
||||
@Override
|
||||
StompHeaders set(CharSequence name, CharSequence... values);
|
||||
|
||||
@Override
|
||||
StompHeaders setObject(CharSequence name, Object value);
|
||||
|
||||
@Override
|
||||
StompHeaders setObject(CharSequence name, Iterable<?> values);
|
||||
|
||||
@Override
|
||||
StompHeaders setObject(CharSequence name, Object... values);
|
||||
|
||||
@Override
|
||||
StompHeaders setBoolean(CharSequence name, boolean value);
|
||||
|
||||
@Override
|
||||
StompHeaders setByte(CharSequence name, byte value);
|
||||
|
||||
@Override
|
||||
StompHeaders setChar(CharSequence name, char value);
|
||||
|
||||
@Override
|
||||
StompHeaders setShort(CharSequence name, short value);
|
||||
|
||||
@Override
|
||||
StompHeaders setInt(CharSequence name, int value);
|
||||
|
||||
@Override
|
||||
StompHeaders setLong(CharSequence name, long value);
|
||||
|
||||
@Override
|
||||
StompHeaders setFloat(CharSequence name, float value);
|
||||
|
||||
@Override
|
||||
StompHeaders setDouble(CharSequence name, double value);
|
||||
|
||||
@Override
|
||||
StompHeaders setTimeMillis(CharSequence name, long value);
|
||||
|
||||
@Override
|
||||
StompHeaders set(Headers<? extends CharSequence> headers);
|
||||
|
||||
@Override
|
||||
StompHeaders setAll(Headers<? extends CharSequence> headers);
|
||||
|
||||
@Override
|
||||
StompHeaders clear();
|
||||
|
||||
/**
|
||||
* {@link Headers#get(Object)} and convert the result to a {@link String}.
|
||||
* @param name the name of the header to retrieve
|
||||
|
@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 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.nio.charset.Charset;
|
||||
import java.text.ParseException;
|
||||
|
||||
import io.netty.handler.codec.DefaultHeaders.HeaderDateFormat;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
/**
|
||||
* Converts to/from native types, general {@link Object}, and {@link ByteString}s.
|
||||
*/
|
||||
public final class ByteStringValueConverter implements ValueConverter<ByteString> {
|
||||
public static final ByteStringValueConverter INSTANCE = new ByteStringValueConverter();
|
||||
private static final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8;
|
||||
|
||||
private ByteStringValueConverter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString convertObject(Object value) {
|
||||
if (value instanceof ByteString) {
|
||||
return (ByteString) value;
|
||||
}
|
||||
if (value instanceof CharSequence) {
|
||||
return new ByteString((CharSequence) value, DEFAULT_CHARSET);
|
||||
}
|
||||
return new ByteString(value.toString(), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString convertInt(int value) {
|
||||
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString convertLong(long value) {
|
||||
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString convertDouble(double value) {
|
||||
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString convertChar(char value) {
|
||||
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString convertBoolean(boolean value) {
|
||||
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString convertFloat(float value) {
|
||||
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertToInt(ByteString value) {
|
||||
return value.parseAsciiInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long convertToLong(ByteString value) {
|
||||
return value.parseAsciiLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString convertTimeMillis(long value) {
|
||||
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long convertToTimeMillis(ByteString value) {
|
||||
try {
|
||||
return HeaderDateFormat.get().parse(value.toString());
|
||||
} catch (ParseException e) {
|
||||
PlatformDependent.throwException(e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double convertToDouble(ByteString value) {
|
||||
return value.parseAsciiDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char convertToChar(ByteString value) {
|
||||
return value.parseChar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean convertToBoolean(ByteString value) {
|
||||
return value.byteAt(0) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float convertToFloat(ByteString value) {
|
||||
return value.parseAsciiFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString convertShort(short value) {
|
||||
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short convertToShort(ByteString value) {
|
||||
return value.parseAsciiShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString convertByte(byte value) {
|
||||
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte convertToByte(ByteString value) {
|
||||
return value.byteAt(0);
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
package io.netty.handler.codec;
|
||||
|
||||
import io.netty.handler.codec.DefaultHeaders.HeaderDateFormat;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import java.text.ParseException;
|
||||
@ -65,6 +66,9 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
|
||||
|
||||
@Override
|
||||
public boolean convertToBoolean(CharSequence value) {
|
||||
if (value instanceof AsciiString) {
|
||||
return ((AsciiString) value).parseBoolean();
|
||||
}
|
||||
return Boolean.parseBoolean(value.toString());
|
||||
}
|
||||
|
||||
@ -75,14 +79,14 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
|
||||
|
||||
@Override
|
||||
public byte convertToByte(CharSequence value) {
|
||||
if (value instanceof AsciiString) {
|
||||
return ((AsciiString) value).byteAt(0);
|
||||
}
|
||||
return Byte.valueOf(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public char convertToChar(CharSequence value) {
|
||||
if (value.length() == 0) {
|
||||
throw new IllegalArgumentException("'value' is empty.");
|
||||
}
|
||||
return value.charAt(0);
|
||||
}
|
||||
|
||||
@ -93,16 +97,25 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
|
||||
|
||||
@Override
|
||||
public short convertToShort(CharSequence value) {
|
||||
if (value instanceof AsciiString) {
|
||||
return ((AsciiString) value).parseShort();
|
||||
}
|
||||
return Short.valueOf(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertToInt(CharSequence value) {
|
||||
if (value instanceof AsciiString) {
|
||||
return ((AsciiString) value).parseInt();
|
||||
}
|
||||
return Integer.parseInt(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long convertToLong(CharSequence value) {
|
||||
if (value instanceof AsciiString) {
|
||||
return ((AsciiString) value).parseLong();
|
||||
}
|
||||
return Long.parseLong(value.toString());
|
||||
}
|
||||
|
||||
@ -117,17 +130,23 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
|
||||
return HeaderDateFormat.get().parse(value.toString());
|
||||
} catch (ParseException e) {
|
||||
PlatformDependent.throwException(e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float convertToFloat(CharSequence value) {
|
||||
if (value instanceof AsciiString) {
|
||||
return ((AsciiString) value).parseFloat();
|
||||
}
|
||||
return Float.valueOf(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double convertToDouble(CharSequence value) {
|
||||
if (value instanceof AsciiString) {
|
||||
return ((AsciiString) value).parseDouble();
|
||||
}
|
||||
return Double.valueOf(value.toString());
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2015 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.HashingStrategy;
|
||||
|
||||
/**
|
||||
* A concrete implementation of {@link DefaultHeaders} that allows for direct instantiation.
|
||||
* @param <K> the type of the header name.
|
||||
* @param <V> the type of the header value.
|
||||
*/
|
||||
public final class DefaultHeadersImpl<K, V> extends DefaultHeaders<K, V, DefaultHeadersImpl<K, V>> {
|
||||
public DefaultHeadersImpl(HashingStrategy<K> nameHashingStrategy,
|
||||
ValueConverter<V> valueConverter, NameValidator<K> nameValidator) {
|
||||
super(nameHashingStrategy, valueConverter, nameValidator);
|
||||
}
|
||||
}
|
@ -20,274 +20,276 @@ import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
public class EmptyHeaders<T> implements Headers<T> {
|
||||
import static io.netty.handler.codec.DefaultHeaders.HASH_CODE_SEED;
|
||||
|
||||
public class EmptyHeaders<K, V, T extends Headers<K, V, T>> implements Headers<K, V, T> {
|
||||
@Override
|
||||
public T get(T name) {
|
||||
public V get(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(T name, T defaultValue) {
|
||||
public V get(K name, V defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getAndRemove(T name) {
|
||||
public V getAndRemove(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getAndRemove(T name, T defaultValue) {
|
||||
public V getAndRemove(K name, V defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getAll(T name) {
|
||||
public List<V> getAll(K name) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getAllAndRemove(T name) {
|
||||
public List<V> getAllAndRemove(K name) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBoolean(T name) {
|
||||
public Boolean getBoolean(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(T name, boolean defaultValue) {
|
||||
public boolean getBoolean(K name, boolean defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte getByte(T name) {
|
||||
public Byte getByte(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(T name, byte defaultValue) {
|
||||
public byte getByte(K name, byte defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Character getChar(T name) {
|
||||
public Character getChar(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getChar(T name, char defaultValue) {
|
||||
public char getChar(K name, char defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short getShort(T name) {
|
||||
public Short getShort(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(T name, short defaultValue) {
|
||||
public short getShort(K name, short defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getInt(T name) {
|
||||
public Integer getInt(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(T name, int defaultValue) {
|
||||
public int getInt(K name, int defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLong(T name) {
|
||||
public Long getLong(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(T name, long defaultValue) {
|
||||
public long getLong(K name, long defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getFloat(T name) {
|
||||
public Float getFloat(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(T name, float defaultValue) {
|
||||
public float getFloat(K name, float defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getDouble(T name) {
|
||||
public Double getDouble(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(T name, double defaultValue) {
|
||||
public double getDouble(K name, double defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getTimeMillis(T name) {
|
||||
public Long getTimeMillis(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeMillis(T name, long defaultValue) {
|
||||
public long getTimeMillis(K name, long defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBooleanAndRemove(T name) {
|
||||
public Boolean getBooleanAndRemove(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBooleanAndRemove(T name, boolean defaultValue) {
|
||||
public boolean getBooleanAndRemove(K name, boolean defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte getByteAndRemove(T name) {
|
||||
public Byte getByteAndRemove(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByteAndRemove(T name, byte defaultValue) {
|
||||
public byte getByteAndRemove(K name, byte defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Character getCharAndRemove(T name) {
|
||||
public Character getCharAndRemove(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getCharAndRemove(T name, char defaultValue) {
|
||||
public char getCharAndRemove(K name, char defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short getShortAndRemove(T name) {
|
||||
public Short getShortAndRemove(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShortAndRemove(T name, short defaultValue) {
|
||||
public short getShortAndRemove(K name, short defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getIntAndRemove(T name) {
|
||||
public Integer getIntAndRemove(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntAndRemove(T name, int defaultValue) {
|
||||
public int getIntAndRemove(K name, int defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLongAndRemove(T name) {
|
||||
public Long getLongAndRemove(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLongAndRemove(T name, long defaultValue) {
|
||||
public long getLongAndRemove(K name, long defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getFloatAndRemove(T name) {
|
||||
public Float getFloatAndRemove(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloatAndRemove(T name, float defaultValue) {
|
||||
public float getFloatAndRemove(K name, float defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getDoubleAndRemove(T name) {
|
||||
public Double getDoubleAndRemove(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDoubleAndRemove(T name, double defaultValue) {
|
||||
public double getDoubleAndRemove(K name, double defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getTimeMillisAndRemove(T name) {
|
||||
public Long getTimeMillisAndRemove(K name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeMillisAndRemove(T name, long defaultValue) {
|
||||
public long getTimeMillisAndRemove(K name, long defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(T name) {
|
||||
public boolean contains(K name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(T name, T value) {
|
||||
public boolean contains(K name, V value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsObject(T name, Object value) {
|
||||
public boolean containsObject(K name, Object value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsBoolean(T name, boolean value) {
|
||||
public boolean containsBoolean(K name, boolean value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsByte(T name, byte value) {
|
||||
public boolean containsByte(K name, byte value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsChar(T name, char value) {
|
||||
public boolean containsChar(K name, char value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsShort(T name, short value) {
|
||||
public boolean containsShort(K name, short value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsInt(T name, int value) {
|
||||
public boolean containsInt(K name, int value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsLong(T name, long value) {
|
||||
public boolean containsLong(K name, long value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsFloat(T name, float value) {
|
||||
public boolean containsFloat(K name, float value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsDouble(T name, double value) {
|
||||
public boolean containsDouble(K name, double value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsTimeMillis(T name, long value) {
|
||||
public boolean containsTimeMillis(K name, long value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -302,188 +304,188 @@ public class EmptyHeaders<T> implements Headers<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> names() {
|
||||
public Set<K> names() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> add(T name, T value) {
|
||||
public T add(K name, V value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> add(T name, Iterable<? extends T> values) {
|
||||
public T add(K name, Iterable<? extends V> values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> add(T name, T... values) {
|
||||
public T add(K name, V... values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addObject(T name, Object value) {
|
||||
public T addObject(K name, Object value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addObject(T name, Iterable<?> values) {
|
||||
public T addObject(K name, Iterable<?> values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addObject(T name, Object... values) {
|
||||
public T addObject(K name, Object... values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addBoolean(T name, boolean value) {
|
||||
public T addBoolean(K name, boolean value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addByte(T name, byte value) {
|
||||
public T addByte(K name, byte value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addChar(T name, char value) {
|
||||
public T addChar(K name, char value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addShort(T name, short value) {
|
||||
public T addShort(K name, short value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addInt(T name, int value) {
|
||||
public T addInt(K name, int value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addLong(T name, long value) {
|
||||
public T addLong(K name, long value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addFloat(T name, float value) {
|
||||
public T addFloat(K name, float value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addDouble(T name, double value) {
|
||||
public T addDouble(K name, double value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> addTimeMillis(T name, long value) {
|
||||
public T addTimeMillis(K name, long value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> add(Headers<? extends T> headers) {
|
||||
public T add(Headers<? extends K, ? extends V, ?> headers) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> set(T name, T value) {
|
||||
public T set(K name, V value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> set(T name, Iterable<? extends T> values) {
|
||||
public T set(K name, Iterable<? extends V> values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> set(T name, T... values) {
|
||||
public T set(K name, V... values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setObject(T name, Object value) {
|
||||
public T setObject(K name, Object value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setObject(T name, Iterable<?> values) {
|
||||
public T setObject(K name, Iterable<?> values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setObject(T name, Object... values) {
|
||||
public T setObject(K name, Object... values) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setBoolean(T name, boolean value) {
|
||||
public T setBoolean(K name, boolean value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setByte(T name, byte value) {
|
||||
public T setByte(K name, byte value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setChar(T name, char value) {
|
||||
public T setChar(K name, char value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setShort(T name, short value) {
|
||||
public T setShort(K name, short value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setInt(T name, int value) {
|
||||
public T setInt(K name, int value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setLong(T name, long value) {
|
||||
public T setLong(K name, long value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setFloat(T name, float value) {
|
||||
public T setFloat(K name, float value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setDouble(T name, double value) {
|
||||
public T setDouble(K name, double value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setTimeMillis(T name, long value) {
|
||||
public T setTimeMillis(K name, long value) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> set(Headers<? extends T> headers) {
|
||||
public T set(Headers<? extends K, ? extends V, ?> headers) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> setAll(Headers<? extends T> headers) {
|
||||
public T setAll(Headers<? extends K, ? extends V, ?> headers) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(T name) {
|
||||
public boolean remove(K name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers<T> clear() {
|
||||
return this;
|
||||
public T clear() {
|
||||
return thisT();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<T, T>> iterator() {
|
||||
List<Entry<T, T>> empty = Collections.emptyList();
|
||||
public Iterator<Entry<K, V>> iterator() {
|
||||
List<Entry<K, V>> empty = Collections.emptyList();
|
||||
return empty.iterator();
|
||||
}
|
||||
|
||||
@ -493,17 +495,22 @@ public class EmptyHeaders<T> implements Headers<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
Headers<?> rhs = (Headers<?>) o;
|
||||
Headers<?, ?, ?> rhs = (Headers<?, ?, ?>) o;
|
||||
return isEmpty() && rhs.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 1;
|
||||
return HASH_CODE_SEED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder(getClass().getSimpleName()).append('[').append(']').toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T thisT() {
|
||||
return (T) this;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,15 @@ import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
/**
|
||||
* Common interface for {@link Headers} which represents a mapping of key to value.
|
||||
* Duplicate keys may be allowed by implementations.
|
||||
*
|
||||
* @param <K> the type of the header name.
|
||||
* @param <V> the type of the header value.
|
||||
* @param <T> the type to use for return values when the intention is to return {@code this} object.
|
||||
*/
|
||||
public interface Headers<K, V, T extends Headers<K, V, T>> extends Iterable<Entry<K, V>> {
|
||||
/**
|
||||
* Returns the value of a header with the specified name. If there is more than one value for the specified name,
|
||||
* the first value in insertion order is returned.
|
||||
@ -27,7 +35,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param name the name of the header to retrieve
|
||||
* @return the first header value if the header is found. {@code null} if there's no such header
|
||||
*/
|
||||
T get(T name);
|
||||
V get(K name);
|
||||
|
||||
/**
|
||||
* Returns the value of a header with the specified name. If there is more than one value for the specified name,
|
||||
@ -37,7 +45,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param defaultValue the default value
|
||||
* @return the first header value or {@code defaultValue} if there is no such header
|
||||
*/
|
||||
T get(T name, T defaultValue);
|
||||
V get(K name, V defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the value of a header with the specified name and removes it from this object. If there is more than
|
||||
@ -46,7 +54,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param name the name of the header to retrieve
|
||||
* @return the first header value or {@code null} if there is no such header
|
||||
*/
|
||||
T getAndRemove(T name);
|
||||
V getAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the value of a header with the specified name and removes it from this object. If there is more than
|
||||
@ -56,7 +64,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param defaultValue the default value
|
||||
* @return the first header value or {@code defaultValue} if there is no such header
|
||||
*/
|
||||
T getAndRemove(T name, T defaultValue);
|
||||
V getAndRemove(K name, V defaultValue);
|
||||
|
||||
/**
|
||||
* Returns all values for the header with the specified name. The returned {@link List} can't be modified.
|
||||
@ -64,7 +72,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param name the name of the header to retrieve
|
||||
* @return a {@link List} of header values or an empty {@link List} if no values are found.
|
||||
*/
|
||||
List<T> getAll(T name);
|
||||
List<V> getAll(K name);
|
||||
|
||||
/**
|
||||
* Returns all values for the header with the specified name and removes them from this object.
|
||||
@ -73,7 +81,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param name the name of the header to retrieve
|
||||
* @return a {@link List} of header values or an empty {@link List} if no values are found.
|
||||
*/
|
||||
List<T> getAllAndRemove(T name);
|
||||
List<V> getAllAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code boolean} value of a header with the specified name. If there is more than one value for the
|
||||
@ -83,7 +91,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code boolean} value of the first value in insertion order or {@code null} if there is no such
|
||||
* value or it can't be converted to {@code boolean}.
|
||||
*/
|
||||
Boolean getBoolean(T name);
|
||||
Boolean getBoolean(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code boolean} value of a header with the specified name. If there is more than one value for the
|
||||
@ -94,7 +102,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code boolean} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code boolean}.
|
||||
*/
|
||||
boolean getBoolean(T name, boolean defaultValue);
|
||||
boolean getBoolean(K name, boolean defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code byte} value of a header with the specified name. If there is more than one value for the
|
||||
@ -104,7 +112,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code byte} value of the first value in insertion order or {@code null} if there is no such
|
||||
* value or it can't be converted to {@code byte}.
|
||||
*/
|
||||
Byte getByte(T name);
|
||||
Byte getByte(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code byte} value of a header with the specified name. If there is more than one value for the
|
||||
@ -115,7 +123,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code byte} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code byte}.
|
||||
*/
|
||||
byte getByte(T name, byte defaultValue);
|
||||
byte getByte(K name, byte defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code char} value of a header with the specified name. If there is more than one value for the
|
||||
@ -125,7 +133,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code char} value of the first value in insertion order or {@code null} if there is no such
|
||||
* value or it can't be converted to {@code char}.
|
||||
*/
|
||||
Character getChar(T name);
|
||||
Character getChar(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code char} value of a header with the specified name. If there is more than one value for the
|
||||
@ -136,7 +144,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code char} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code char}.
|
||||
*/
|
||||
char getChar(T name, char defaultValue);
|
||||
char getChar(K name, char defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code short} value of a header with the specified name. If there is more than one value for the
|
||||
@ -146,7 +154,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code short} value of the first value in insertion order or {@code null} if there is no such
|
||||
* value or it can't be converted to {@code short}.
|
||||
*/
|
||||
Short getShort(T name);
|
||||
Short getShort(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code short} value of a header with the specified name. If there is more than one value for the
|
||||
@ -157,7 +165,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code short} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code short}.
|
||||
*/
|
||||
short getShort(T name, short defaultValue);
|
||||
short getShort(K name, short defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code int} value of a header with the specified name. If there is more than one value for the
|
||||
@ -167,7 +175,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code int} value of the first value in insertion order or {@code null} if there is no such
|
||||
* value or it can't be converted to {@code int}.
|
||||
*/
|
||||
Integer getInt(T name);
|
||||
Integer getInt(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code int} value of a header with the specified name. If there is more than one value for the
|
||||
@ -178,7 +186,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code int} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code int}.
|
||||
*/
|
||||
int getInt(T name, int defaultValue);
|
||||
int getInt(K name, int defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code long} value of a header with the specified name. If there is more than one value for the
|
||||
@ -188,7 +196,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code long} value of the first value in insertion order or {@code null} if there is no such
|
||||
* value or it can't be converted to {@code long}.
|
||||
*/
|
||||
Long getLong(T name);
|
||||
Long getLong(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code long} value of a header with the specified name. If there is more than one value for the
|
||||
@ -199,7 +207,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code long} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code long}.
|
||||
*/
|
||||
long getLong(T name, long defaultValue);
|
||||
long getLong(K name, long defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code float} value of a header with the specified name. If there is more than one value for the
|
||||
@ -209,7 +217,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code float} value of the first value in insertion order or {@code null} if there is no such
|
||||
* value or it can't be converted to {@code float}.
|
||||
*/
|
||||
Float getFloat(T name);
|
||||
Float getFloat(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code float} value of a header with the specified name. If there is more than one value for the
|
||||
@ -220,7 +228,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code float} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code float}.
|
||||
*/
|
||||
float getFloat(T name, float defaultValue);
|
||||
float getFloat(K name, float defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code double} value of a header with the specified name. If there is more than one value for the
|
||||
@ -230,7 +238,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code double} value of the first value in insertion order or {@code null} if there is no such
|
||||
* value or it can't be converted to {@code double}.
|
||||
*/
|
||||
Double getDouble(T name);
|
||||
Double getDouble(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code double} value of a header with the specified name. If there is more than one value for the
|
||||
@ -241,7 +249,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code double} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code double}.
|
||||
*/
|
||||
double getDouble(T name, double defaultValue);
|
||||
double getDouble(K name, double defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the value of a header with the specified name in milliseconds. If there is more than one value for the
|
||||
@ -251,7 +259,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the milliseconds value of the first value in insertion order or {@code null} if there is no such
|
||||
* value or it can't be converted to milliseconds.
|
||||
*/
|
||||
Long getTimeMillis(T name);
|
||||
Long getTimeMillis(K name);
|
||||
|
||||
/**
|
||||
* Returns the value of a header with the specified name in milliseconds. If there is more than one value for the
|
||||
@ -262,7 +270,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the milliseconds value of the first value in insertion order or {@code defaultValue} if there is no such
|
||||
* value or it can't be converted to milliseconds.
|
||||
*/
|
||||
long getTimeMillis(T name, long defaultValue);
|
||||
long getTimeMillis(K name, long defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code boolean} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -275,7 +283,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code boolean} value of the first value in insertion order or {@code null} if there is no
|
||||
* such value or it can't be converted to {@code boolean}.
|
||||
*/
|
||||
Boolean getBooleanAndRemove(T name);
|
||||
Boolean getBooleanAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code boolean} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -289,7 +297,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code boolean} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code boolean}.
|
||||
*/
|
||||
boolean getBooleanAndRemove(T name, boolean defaultValue);
|
||||
boolean getBooleanAndRemove(K name, boolean defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code byte} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -302,7 +310,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code byte} value of the first value in insertion order or {@code null} if there is no
|
||||
* such value or it can't be converted to {@code byte}.
|
||||
*/
|
||||
Byte getByteAndRemove(T name);
|
||||
Byte getByteAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code byte} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -316,7 +324,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code byte} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code byte}.
|
||||
*/
|
||||
byte getByteAndRemove(T name, byte defaultValue);
|
||||
byte getByteAndRemove(K name, byte defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code char} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -329,7 +337,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code char} value of the first value in insertion order or {@code null} if there is no
|
||||
* such value or it can't be converted to {@code char}.
|
||||
*/
|
||||
Character getCharAndRemove(T name);
|
||||
Character getCharAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code char} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -343,7 +351,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code char} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code char}.
|
||||
*/
|
||||
char getCharAndRemove(T name, char defaultValue);
|
||||
char getCharAndRemove(K name, char defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code short} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -356,7 +364,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code short} value of the first value in insertion order or {@code null} if there is no
|
||||
* such value or it can't be converted to {@code short}.
|
||||
*/
|
||||
Short getShortAndRemove(T name);
|
||||
Short getShortAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code short} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -370,7 +378,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code short} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code short}.
|
||||
*/
|
||||
short getShortAndRemove(T name, short defaultValue);
|
||||
short getShortAndRemove(K name, short defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code int} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -383,7 +391,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code int} value of the first value in insertion order or {@code null} if there is no
|
||||
* such value or it can't be converted to {@code int}.
|
||||
*/
|
||||
Integer getIntAndRemove(T name);
|
||||
Integer getIntAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code int} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -397,7 +405,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code int} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code int}.
|
||||
*/
|
||||
int getIntAndRemove(T name, int defaultValue);
|
||||
int getIntAndRemove(K name, int defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code long} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -410,7 +418,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code long} value of the first value in insertion order or {@code null} if there is no
|
||||
* such value or it can't be converted to {@code long}.
|
||||
*/
|
||||
Long getLongAndRemove(T name);
|
||||
Long getLongAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code long} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -424,7 +432,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code long} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code long}.
|
||||
*/
|
||||
long getLongAndRemove(T name, long defaultValue);
|
||||
long getLongAndRemove(K name, long defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code float} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -437,7 +445,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code float} value of the first value in insertion order or {@code null} if there is no
|
||||
* such value or it can't be converted to {@code float}.
|
||||
*/
|
||||
Float getFloatAndRemove(T name);
|
||||
Float getFloatAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code float} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -451,7 +459,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code float} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code float}.
|
||||
*/
|
||||
float getFloatAndRemove(T name, float defaultValue);
|
||||
float getFloatAndRemove(K name, float defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the {@code double} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -464,7 +472,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code double} value of the first value in insertion order or {@code null} if there is no
|
||||
* such value or it can't be converted to {@code double}.
|
||||
*/
|
||||
Double getDoubleAndRemove(T name);
|
||||
Double getDoubleAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the {@code double} value of a header with the specified {@code name} and removes the header from this
|
||||
@ -478,7 +486,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the {@code double} value of the first value in insertion order or {@code defaultValue} if there is no
|
||||
* such value or it can't be converted to {@code double}.
|
||||
*/
|
||||
double getDoubleAndRemove(T name, double defaultValue);
|
||||
double getDoubleAndRemove(K name, double defaultValue);
|
||||
|
||||
/**
|
||||
* Returns the value of a header with the specified {@code name} in milliseconds and removes the header from this
|
||||
@ -491,7 +499,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the milliseconds value of the first value in insertion order or {@code null} if there is no such
|
||||
* value or it can't be converted to milliseconds.
|
||||
*/
|
||||
Long getTimeMillisAndRemove(T name);
|
||||
Long getTimeMillisAndRemove(K name);
|
||||
|
||||
/**
|
||||
* Returns the value of a header with the specified {@code name} in milliseconds and removes the header from this
|
||||
@ -505,14 +513,14 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @return the milliseconds value of the first value in insertion order or {@code defaultValue} if there is no such
|
||||
* value or it can't be converted to milliseconds.
|
||||
*/
|
||||
long getTimeMillisAndRemove(T name, long defaultValue);
|
||||
long getTimeMillisAndRemove(K name, long defaultValue);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the {@code name} exists, {@code false} otherwise.
|
||||
*
|
||||
* @param name the header name
|
||||
*/
|
||||
boolean contains(T name);
|
||||
boolean contains(K name);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise.
|
||||
@ -522,7 +530,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param name the header name
|
||||
* @param value the header value of the header to find
|
||||
*/
|
||||
boolean contains(T name, T value);
|
||||
boolean contains(K name, V value);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the name and value exists.
|
||||
@ -531,7 +539,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the header value
|
||||
* @return {@code true} if it contains it {@code false} otherwise
|
||||
*/
|
||||
boolean containsObject(T name, Object value);
|
||||
boolean containsObject(K name, Object value);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the name and value exists.
|
||||
@ -540,7 +548,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the header value
|
||||
* @return {@code true} if it contains it {@code false} otherwise
|
||||
*/
|
||||
boolean containsBoolean(T name, boolean value);
|
||||
boolean containsBoolean(K name, boolean value);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the name and value exists.
|
||||
@ -549,7 +557,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the header value
|
||||
* @return {@code true} if it contains it {@code false} otherwise
|
||||
*/
|
||||
boolean containsByte(T name, byte value);
|
||||
boolean containsByte(K name, byte value);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the name and value exists.
|
||||
@ -558,7 +566,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the header value
|
||||
* @return {@code true} if it contains it {@code false} otherwise
|
||||
*/
|
||||
boolean containsChar(T name, char value);
|
||||
boolean containsChar(K name, char value);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the name and value exists.
|
||||
@ -567,7 +575,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the header value
|
||||
* @return {@code true} if it contains it {@code false} otherwise
|
||||
*/
|
||||
boolean containsShort(T name, short value);
|
||||
boolean containsShort(K name, short value);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the name and value exists.
|
||||
@ -576,7 +584,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the header value
|
||||
* @return {@code true} if it contains it {@code false} otherwise
|
||||
*/
|
||||
boolean containsInt(T name, int value);
|
||||
boolean containsInt(K name, int value);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the name and value exists.
|
||||
@ -585,7 +593,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the header value
|
||||
* @return {@code true} if it contains it {@code false} otherwise
|
||||
*/
|
||||
boolean containsLong(T name, long value);
|
||||
boolean containsLong(K name, long value);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the name and value exists.
|
||||
@ -594,7 +602,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the header value
|
||||
* @return {@code true} if it contains it {@code false} otherwise
|
||||
*/
|
||||
boolean containsFloat(T name, float value);
|
||||
boolean containsFloat(K name, float value);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the name and value exists.
|
||||
@ -603,7 +611,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the header value
|
||||
* @return {@code true} if it contains it {@code false} otherwise
|
||||
*/
|
||||
boolean containsDouble(T name, double value);
|
||||
boolean containsDouble(K name, double value);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a header with the name and value exists.
|
||||
@ -612,7 +620,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the header value
|
||||
* @return {@code true} if it contains it {@code false} otherwise
|
||||
*/
|
||||
boolean containsTimeMillis(T name, long value);
|
||||
boolean containsTimeMillis(K name, long value);
|
||||
|
||||
/**
|
||||
* Returns the number of headers in this object.
|
||||
@ -627,7 +635,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
/**
|
||||
* Returns a {@link Set} of all header names in this object. The returned {@link Set} cannot be modified.
|
||||
*/
|
||||
Set<T> names();
|
||||
Set<K> names();
|
||||
|
||||
/**
|
||||
* Adds a new header with the specified {@code name} and {@code value}.
|
||||
@ -636,7 +644,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> add(T name, T value);
|
||||
T add(K name, V value);
|
||||
|
||||
/**
|
||||
* Adds new headers with the specified {@code name} and {@code values}. This method is semantically equivalent to
|
||||
@ -651,7 +659,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param values the values of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> add(T name, Iterable<? extends T> values);
|
||||
T add(K name, Iterable<? extends V> values);
|
||||
|
||||
/**
|
||||
* Adds new headers with the specified {@code name} and {@code values}. This method is semantically equivalent to
|
||||
@ -666,7 +674,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param values the values of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> add(T name, T... values);
|
||||
T add(K name, V... values);
|
||||
|
||||
/**
|
||||
* Adds a new header. Before the {@code value} is added, it's converted to type {@code T}.
|
||||
@ -675,7 +683,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addObject(T name, Object value);
|
||||
T addObject(K name, Object value);
|
||||
|
||||
/**
|
||||
* Adds a new header with the specified name and values. This method is equivalent to
|
||||
@ -690,7 +698,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param values the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addObject(T name, Iterable<?> values);
|
||||
T addObject(K name, Iterable<?> values);
|
||||
|
||||
/**
|
||||
* Adds a new header with the specified name and values. This method is equivalent to
|
||||
@ -705,7 +713,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param values the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addObject(T name, Object... values);
|
||||
T addObject(K name, Object... values);
|
||||
|
||||
/**
|
||||
* Adds a new header.
|
||||
@ -714,7 +722,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addBoolean(T name, boolean value);
|
||||
T addBoolean(K name, boolean value);
|
||||
|
||||
/**
|
||||
* Adds a new header.
|
||||
@ -723,7 +731,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addByte(T name, byte value);
|
||||
T addByte(K name, byte value);
|
||||
|
||||
/**
|
||||
* Adds a new header.
|
||||
@ -732,7 +740,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addChar(T name, char value);
|
||||
T addChar(K name, char value);
|
||||
|
||||
/**
|
||||
* Adds a new header.
|
||||
@ -741,7 +749,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addShort(T name, short value);
|
||||
T addShort(K name, short value);
|
||||
|
||||
/**
|
||||
* Adds a new header.
|
||||
@ -750,7 +758,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addInt(T name, int value);
|
||||
T addInt(K name, int value);
|
||||
|
||||
/**
|
||||
* Adds a new header.
|
||||
@ -759,7 +767,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addLong(T name, long value);
|
||||
T addLong(K name, long value);
|
||||
|
||||
/**
|
||||
* Adds a new header.
|
||||
@ -768,7 +776,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addFloat(T name, float value);
|
||||
T addFloat(K name, float value);
|
||||
|
||||
/**
|
||||
* Adds a new header.
|
||||
@ -777,7 +785,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addDouble(T name, double value);
|
||||
T addDouble(K name, double value);
|
||||
|
||||
/**
|
||||
* Adds a new header.
|
||||
@ -786,7 +794,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> addTimeMillis(T name, long value);
|
||||
T addTimeMillis(K name, long value);
|
||||
|
||||
/**
|
||||
* Adds all header names and values of {@code headers} to this object.
|
||||
@ -794,7 +802,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @throws IllegalArgumentException if {@code headers == this}.
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> add(Headers<? extends T> headers);
|
||||
T add(Headers<? extends K, ? extends V, ?> headers);
|
||||
|
||||
/**
|
||||
* Sets a header with the specified name and value. Any existing headers with the same name are overwritten.
|
||||
@ -803,7 +811,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> set(T name, T value);
|
||||
T set(K name, V value);
|
||||
|
||||
/**
|
||||
* Sets a new header with the specified name and values. This method is equivalent to
|
||||
@ -818,7 +826,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param values the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> set(T name, Iterable<? extends T> values);
|
||||
T set(K name, Iterable<? extends V> values);
|
||||
|
||||
/**
|
||||
* Sets a header with the specified name and values. Any existing headers with this name are removed. This method
|
||||
@ -835,7 +843,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param values the value of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> set(T name, T... values);
|
||||
T set(K name, V... values);
|
||||
|
||||
/**
|
||||
* Sets a new header. Any existing headers with this name are removed. Before the {@code value} is add, it's
|
||||
@ -847,7 +855,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* {@code null}.
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setObject(T name, Object value);
|
||||
T setObject(K name, Object value);
|
||||
|
||||
/**
|
||||
* Sets a header with the specified name and values. Any existing headers with this name are removed. This method
|
||||
@ -864,7 +872,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param values the values of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setObject(T name, Iterable<?> values);
|
||||
T setObject(K name, Iterable<?> values);
|
||||
|
||||
/**
|
||||
* Sets a header with the specified name and values. Any existing headers with this name are removed. This method
|
||||
@ -881,7 +889,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param values the values of the header
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setObject(T name, Object... values);
|
||||
T setObject(K name, Object... values);
|
||||
|
||||
/**
|
||||
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
|
||||
@ -889,7 +897,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value The value
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setBoolean(T name, boolean value);
|
||||
T setBoolean(K name, boolean value);
|
||||
|
||||
/**
|
||||
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
|
||||
@ -897,7 +905,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value The value
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setByte(T name, byte value);
|
||||
T setByte(K name, byte value);
|
||||
|
||||
/**
|
||||
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
|
||||
@ -905,7 +913,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value The value
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setChar(T name, char value);
|
||||
T setChar(K name, char value);
|
||||
|
||||
/**
|
||||
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
|
||||
@ -913,7 +921,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value The value
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setShort(T name, short value);
|
||||
T setShort(K name, short value);
|
||||
|
||||
/**
|
||||
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
|
||||
@ -921,7 +929,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value The value
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setInt(T name, int value);
|
||||
T setInt(K name, int value);
|
||||
|
||||
/**
|
||||
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
|
||||
@ -929,7 +937,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value The value
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setLong(T name, long value);
|
||||
T setLong(K name, long value);
|
||||
|
||||
/**
|
||||
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
|
||||
@ -937,7 +945,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value The value
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setFloat(T name, float value);
|
||||
T setFloat(K name, float value);
|
||||
|
||||
/**
|
||||
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
|
||||
@ -945,7 +953,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value The value
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setDouble(T name, double value);
|
||||
T setDouble(K name, double value);
|
||||
|
||||
/**
|
||||
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
|
||||
@ -953,14 +961,14 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param value The value
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setTimeMillis(T name, long value);
|
||||
T setTimeMillis(K name, long value);
|
||||
|
||||
/**
|
||||
* Clears the current header entries and copies all header entries of the specified {@code headers}.
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> set(Headers<? extends T> headers);
|
||||
T set(Headers<? extends K, ? extends V, ?> headers);
|
||||
|
||||
/**
|
||||
* Retains all current headers but calls {@link #set(T, T)} for each entry in {@code headers}.
|
||||
@ -968,7 +976,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param headers The headers used to {@link #set(T, T)} values in this instance
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> setAll(Headers<? extends T> headers);
|
||||
T setAll(Headers<? extends K, ? extends V, ?> headers);
|
||||
|
||||
/**
|
||||
* Removes all headers with the specified {@code name}.
|
||||
@ -976,15 +984,15 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
|
||||
* @param name the header name
|
||||
* @return {@code true} if at least one entry has been removed.
|
||||
*/
|
||||
boolean remove(T name);
|
||||
boolean remove(K name);
|
||||
|
||||
/**
|
||||
* Removes all headers. After a call to this method {@link #size()} equals {@code 0}.
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
Headers<T> clear();
|
||||
T clear();
|
||||
|
||||
@Override
|
||||
Iterator<Entry<T, T>> iterator();
|
||||
Iterator<Entry<K, V>> iterator();
|
||||
}
|
||||
|
@ -37,12 +37,12 @@ public final class HeadersUtils {
|
||||
* @param name the name of the header to retrieve
|
||||
* @return a {@link List} of header values or an empty {@link List} if no values are found.
|
||||
*/
|
||||
public static <T> List<String> getAllAsString(Headers<T> headers, T name) {
|
||||
final List<T> allNames = headers.getAll(name);
|
||||
public static <K, V> List<String> getAllAsString(Headers<K, V, ?> headers, K name) {
|
||||
final List<V> allNames = headers.getAll(name);
|
||||
return new AbstractList<String>() {
|
||||
@Override
|
||||
public String get(int index) {
|
||||
T value = allNames.get(index);
|
||||
V value = allNames.get(index);
|
||||
return value != null ? value.toString() : null;
|
||||
}
|
||||
|
||||
@ -59,8 +59,8 @@ public final class HeadersUtils {
|
||||
* @param name the name of the header to retrieve
|
||||
* @return the first header value if the header is found. {@code null} if there's no such entry.
|
||||
*/
|
||||
public static <T> String getAsString(Headers<T> headers, T name) {
|
||||
T orig = headers.get(name);
|
||||
public static <K, V> String getAsString(Headers<K, V, ?> headers, K name) {
|
||||
V orig = headers.get(name);
|
||||
return orig != null ? orig.toString() : null;
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ public final class HeadersUtils {
|
||||
* @param headers the headers to get the names from
|
||||
* @return a {@link Set} of header values or an empty {@link Set} if no values are found.
|
||||
*/
|
||||
public static Set<String> namesAsString(Headers<CharSequence> headers) {
|
||||
public static Set<String> namesAsString(Headers<CharSequence, CharSequence, ?> headers) {
|
||||
return new CharSequenceDelegatingStringSet(headers.names());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2015 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;
|
||||
|
||||
/**
|
||||
* {@link UnsupportedOperationException} will be thrown from all {@link ValueConverter} methods.
|
||||
*/
|
||||
public final class UnsupportedValueConverter<V> implements ValueConverter<V> {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static final UnsupportedValueConverter INSTANCE = new UnsupportedValueConverter();
|
||||
private UnsupportedValueConverter() { }
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <V> UnsupportedValueConverter<V> instance() {
|
||||
return (UnsupportedValueConverter<V>) INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V convertObject(Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V convertBoolean(boolean value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean convertToBoolean(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V convertByte(byte value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte convertToByte(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V convertChar(char value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char convertToChar(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V convertShort(short value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short convertToShort(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V convertInt(int value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertToInt(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V convertLong(long value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long convertToLong(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V convertTimeMillis(long value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long convertToTimeMillis(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V convertFloat(float value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float convertToFloat(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V convertDouble(double value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double convertToDouble(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -14,6 +14,15 @@
|
||||
*/
|
||||
package io.netty.handler.codec;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import static io.netty.util.AsciiString.of;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@ -22,207 +31,203 @@ import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultHeaders}.
|
||||
*/
|
||||
public class DefaultHeadersTest {
|
||||
|
||||
private Headers<ByteString> newInstance() {
|
||||
return new DefaultHeaders<ByteString>(ByteStringValueConverter.INSTANCE);
|
||||
private static final class TestDefaultHeaders extends
|
||||
DefaultHeaders<CharSequence, CharSequence, TestDefaultHeaders> {
|
||||
public TestDefaultHeaders() {
|
||||
super(CharSequenceValueConverter.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
private TestDefaultHeaders newInstance() {
|
||||
return new TestDefaultHeaders();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addShouldIncreaseAndRemoveShouldDecreaseTheSize() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
assertEquals(0, headers.size());
|
||||
headers.add(bs("name1"), bs("value1"), bs("value2"));
|
||||
headers.add(of("name1"), of("value1"), of("value2"));
|
||||
assertEquals(2, headers.size());
|
||||
headers.add(bs("name2"), bs("value3"), bs("value4"));
|
||||
headers.add(of("name2"), of("value3"), of("value4"));
|
||||
assertEquals(4, headers.size());
|
||||
headers.add(bs("name3"), bs("value5"));
|
||||
headers.add(of("name3"), of("value5"));
|
||||
assertEquals(5, headers.size());
|
||||
|
||||
headers.remove(bs("name3"));
|
||||
headers.remove(of("name3"));
|
||||
assertEquals(4, headers.size());
|
||||
headers.remove(bs("name1"));
|
||||
headers.remove(of("name1"));
|
||||
assertEquals(2, headers.size());
|
||||
headers.remove(bs("name2"));
|
||||
headers.remove(of("name2"));
|
||||
assertEquals(0, headers.size());
|
||||
assertTrue(headers.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterClearHeadersShouldBeEmpty() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
headers.add(bs("name1"), bs("value1"));
|
||||
headers.add(bs("name2"), bs("value2"));
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.add(of("name1"), of("value1"));
|
||||
headers.add(of("name2"), of("value2"));
|
||||
assertEquals(2, headers.size());
|
||||
headers.clear();
|
||||
assertEquals(0, headers.size());
|
||||
assertTrue(headers.isEmpty());
|
||||
assertFalse(headers.contains(bs("name1")));
|
||||
assertFalse(headers.contains(bs("name2")));
|
||||
assertFalse(headers.contains(of("name1")));
|
||||
assertFalse(headers.contains(of("name2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removingANameForASecondTimeShouldReturnFalse() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
headers.add(bs("name1"), bs("value1"));
|
||||
headers.add(bs("name2"), bs("value2"));
|
||||
assertTrue(headers.remove(bs("name2")));
|
||||
assertFalse(headers.remove(bs("name2")));
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.add(of("name1"), of("value1"));
|
||||
headers.add(of("name2"), of("value2"));
|
||||
assertTrue(headers.remove(of("name2")));
|
||||
assertFalse(headers.remove(of("name2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleValuesPerNameShouldBeAllowed() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
headers.add(bs("name"), bs("value1"));
|
||||
headers.add(bs("name"), bs("value2"));
|
||||
headers.add(bs("name"), bs("value3"));
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.add(of("name"), of("value1"));
|
||||
headers.add(of("name"), of("value2"));
|
||||
headers.add(of("name"), of("value3"));
|
||||
assertEquals(3, headers.size());
|
||||
|
||||
List<ByteString> values = headers.getAll(bs("name"));
|
||||
List<CharSequence> values = headers.getAll(of("name"));
|
||||
assertEquals(3, values.size());
|
||||
assertTrue(values.containsAll(asList(bs("value1"), bs("value2"), bs("value3"))));
|
||||
assertTrue(values.containsAll(asList(of("value1"), of("value2"), of("value3"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContains() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
headers.addBoolean(bs("boolean"), true);
|
||||
assertTrue(headers.containsBoolean(bs("boolean"), true));
|
||||
assertFalse(headers.containsBoolean(bs("boolean"), false));
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.addBoolean(of("boolean"), true);
|
||||
assertTrue(headers.containsBoolean(of("boolean"), true));
|
||||
assertFalse(headers.containsBoolean(of("boolean"), false));
|
||||
|
||||
headers.addLong(bs("long"), Long.MAX_VALUE);
|
||||
assertTrue(headers.containsLong(bs("long"), Long.MAX_VALUE));
|
||||
assertFalse(headers.containsLong(bs("long"), Long.MIN_VALUE));
|
||||
headers.addLong(of("long"), Long.MAX_VALUE);
|
||||
assertTrue(headers.containsLong(of("long"), Long.MAX_VALUE));
|
||||
assertFalse(headers.containsLong(of("long"), Long.MIN_VALUE));
|
||||
|
||||
headers.addInt(bs("int"), Integer.MIN_VALUE);
|
||||
assertTrue(headers.containsInt(bs("int"), Integer.MIN_VALUE));
|
||||
assertFalse(headers.containsInt(bs("int"), Integer.MAX_VALUE));
|
||||
headers.addInt(of("int"), Integer.MIN_VALUE);
|
||||
assertTrue(headers.containsInt(of("int"), Integer.MIN_VALUE));
|
||||
assertFalse(headers.containsInt(of("int"), Integer.MAX_VALUE));
|
||||
|
||||
headers.addShort(bs("short"), Short.MAX_VALUE);
|
||||
assertTrue(headers.containsShort(bs("short"), Short.MAX_VALUE));
|
||||
assertFalse(headers.containsShort(bs("short"), Short.MIN_VALUE));
|
||||
headers.addShort(of("short"), Short.MAX_VALUE);
|
||||
assertTrue(headers.containsShort(of("short"), Short.MAX_VALUE));
|
||||
assertFalse(headers.containsShort(of("short"), Short.MIN_VALUE));
|
||||
|
||||
headers.addChar(bs("char"), Character.MAX_VALUE);
|
||||
assertTrue(headers.containsChar(bs("char"), Character.MAX_VALUE));
|
||||
assertFalse(headers.containsChar(bs("char"), Character.MIN_VALUE));
|
||||
headers.addChar(of("char"), Character.MAX_VALUE);
|
||||
assertTrue(headers.containsChar(of("char"), Character.MAX_VALUE));
|
||||
assertFalse(headers.containsChar(of("char"), Character.MIN_VALUE));
|
||||
|
||||
headers.addByte(bs("byte"), Byte.MAX_VALUE);
|
||||
assertTrue(headers.containsByte(bs("byte"), Byte.MAX_VALUE));
|
||||
assertFalse(headers.containsLong(bs("byte"), Byte.MIN_VALUE));
|
||||
headers.addByte(of("byte"), Byte.MAX_VALUE);
|
||||
assertTrue(headers.containsByte(of("byte"), Byte.MAX_VALUE));
|
||||
assertFalse(headers.containsLong(of("byte"), Byte.MIN_VALUE));
|
||||
|
||||
headers.addDouble(bs("double"), Double.MAX_VALUE);
|
||||
assertTrue(headers.containsDouble(bs("double"), Double.MAX_VALUE));
|
||||
assertFalse(headers.containsDouble(bs("double"), Double.MIN_VALUE));
|
||||
headers.addDouble(of("double"), Double.MAX_VALUE);
|
||||
assertTrue(headers.containsDouble(of("double"), Double.MAX_VALUE));
|
||||
assertFalse(headers.containsDouble(of("double"), Double.MIN_VALUE));
|
||||
|
||||
headers.addFloat(bs("float"), Float.MAX_VALUE);
|
||||
assertTrue(headers.containsFloat(bs("float"), Float.MAX_VALUE));
|
||||
assertFalse(headers.containsFloat(bs("float"), Float.MIN_VALUE));
|
||||
headers.addFloat(of("float"), Float.MAX_VALUE);
|
||||
assertTrue(headers.containsFloat(of("float"), Float.MAX_VALUE));
|
||||
assertFalse(headers.containsFloat(of("float"), Float.MIN_VALUE));
|
||||
|
||||
long millis = System.currentTimeMillis();
|
||||
headers.addTimeMillis(bs("millis"), millis);
|
||||
assertTrue(headers.containsTimeMillis(bs("millis"), millis));
|
||||
headers.addTimeMillis(of("millis"), millis);
|
||||
assertTrue(headers.containsTimeMillis(of("millis"), millis));
|
||||
// This test doesn't work on midnight, January 1, 1970 UTC
|
||||
assertFalse(headers.containsTimeMillis(bs("millis"), 0));
|
||||
assertFalse(headers.containsTimeMillis(of("millis"), 0));
|
||||
|
||||
headers.addObject(bs("object"), "Hello World");
|
||||
assertTrue(headers.containsObject(bs("object"), "Hello World"));
|
||||
assertFalse(headers.containsObject(bs("object"), ""));
|
||||
headers.addObject(of("object"), "Hello World");
|
||||
assertTrue(headers.containsObject(of("object"), "Hello World"));
|
||||
assertFalse(headers.containsObject(of("object"), ""));
|
||||
|
||||
headers.add(bs("name"), bs("value"));
|
||||
assertTrue(headers.contains(bs("name"), bs("value")));
|
||||
assertFalse(headers.contains(bs("name"), bs("value1")));
|
||||
headers.add(of("name"), of("value"));
|
||||
assertTrue(headers.contains(of("name"), of("value")));
|
||||
assertFalse(headers.contains(of("name"), of("value1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canMixConvertedAndNormalValues() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
headers.add(bs("name"), bs("value"));
|
||||
headers.addInt(bs("name"), 100);
|
||||
headers.addBoolean(bs("name"), false);
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.add(of("name"), of("value"));
|
||||
headers.addInt(of("name"), 100);
|
||||
headers.addBoolean(of("name"), false);
|
||||
|
||||
assertEquals(3, headers.size());
|
||||
assertTrue(headers.contains(bs("name")));
|
||||
assertTrue(headers.contains(bs("name"), bs("value")));
|
||||
assertTrue(headers.containsInt(bs("name"), 100));
|
||||
assertTrue(headers.containsBoolean(bs("name"), false));
|
||||
assertTrue(headers.contains(of("name")));
|
||||
assertTrue(headers.contains(of("name"), of("value")));
|
||||
assertTrue(headers.containsInt(of("name"), 100));
|
||||
assertTrue(headers.containsBoolean(of("name"), false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAndRemove() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
headers.add(bs("name1"), bs("value1"));
|
||||
headers.add(bs("name2"), bs("value2"), bs("value3"));
|
||||
headers.add(bs("name3"), bs("value4"), bs("value5"), bs("value6"));
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.add(of("name1"), of("value1"));
|
||||
headers.add(of("name2"), of("value2"), of("value3"));
|
||||
headers.add(of("name3"), of("value4"), of("value5"), of("value6"));
|
||||
|
||||
assertEquals(bs("value1"), headers.getAndRemove(bs("name1"), bs("defaultvalue")));
|
||||
assertEquals(bs("value2"), headers.getAndRemove(bs("name2")));
|
||||
assertNull(headers.getAndRemove(bs("name2")));
|
||||
assertEquals(asList(bs("value4"), bs("value5"), bs("value6")), headers.getAllAndRemove(bs("name3")));
|
||||
assertEquals(of("value1"), headers.getAndRemove(of("name1"), of("defaultvalue")));
|
||||
assertEquals(of("value2"), headers.getAndRemove(of("name2")));
|
||||
assertNull(headers.getAndRemove(of("name2")));
|
||||
assertEquals(asList(of("value4"), of("value5"), of("value6")), headers.getAllAndRemove(of("name3")));
|
||||
assertEquals(0, headers.size());
|
||||
assertNull(headers.getAndRemove(bs("noname")));
|
||||
assertEquals(bs("defaultvalue"), headers.getAndRemove(bs("noname"), bs("defaultvalue")));
|
||||
assertNull(headers.getAndRemove(of("noname")));
|
||||
assertEquals(of("defaultvalue"), headers.getAndRemove(of("noname"), of("defaultvalue")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNameContainsMultipleValuesGetShouldReturnTheFirst() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
headers.add(bs("name1"), bs("value1"), bs("value2"));
|
||||
assertEquals(bs("value1"), headers.get(bs("name1")));
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.add(of("name1"), of("value1"), of("value2"));
|
||||
assertEquals(of("value1"), headers.get(of("name1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWithDefaultValueWorks() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
headers.add(bs("name1"), bs("value1"));
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.add(of("name1"), of("value1"));
|
||||
|
||||
assertEquals(bs("value1"), headers.get(bs("name1"), bs("defaultvalue")));
|
||||
assertEquals(bs("defaultvalue"), headers.get(bs("noname"), bs("defaultvalue")));
|
||||
assertEquals(of("value1"), headers.get(of("name1"), of("defaultvalue")));
|
||||
assertEquals(of("defaultvalue"), headers.get(of("noname"), of("defaultvalue")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setShouldOverWritePreviousValue() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
headers.set(bs("name"), bs("value1"));
|
||||
headers.set(bs("name"), bs("value2"));
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.set(of("name"), of("value1"));
|
||||
headers.set(of("name"), of("value2"));
|
||||
assertEquals(1, headers.size());
|
||||
assertEquals(1, headers.getAll(bs("name")).size());
|
||||
assertEquals(bs("value2"), headers.getAll(bs("name")).get(0));
|
||||
assertEquals(bs("value2"), headers.get(bs("name")));
|
||||
assertEquals(1, headers.getAll(of("name")).size());
|
||||
assertEquals(of("value2"), headers.getAll(of("name")).get(0));
|
||||
assertEquals(of("value2"), headers.get(of("name")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAllShouldOverwriteSomeAndLeaveOthersUntouched() {
|
||||
Headers<ByteString> h1 = newInstance();
|
||||
TestDefaultHeaders h1 = newInstance();
|
||||
|
||||
h1.add(bs("name1"), bs("value1"));
|
||||
h1.add(bs("name2"), bs("value2"));
|
||||
h1.add(bs("name2"), bs("value3"));
|
||||
h1.add(bs("name3"), bs("value4"));
|
||||
h1.add(of("name1"), of("value1"));
|
||||
h1.add(of("name2"), of("value2"));
|
||||
h1.add(of("name2"), of("value3"));
|
||||
h1.add(of("name3"), of("value4"));
|
||||
|
||||
Headers<ByteString> h2 = newInstance();
|
||||
h2.add(bs("name1"), bs("value5"));
|
||||
h2.add(bs("name2"), bs("value6"));
|
||||
h2.add(bs("name1"), bs("value7"));
|
||||
TestDefaultHeaders h2 = newInstance();
|
||||
h2.add(of("name1"), of("value5"));
|
||||
h2.add(of("name2"), of("value6"));
|
||||
h2.add(of("name1"), of("value7"));
|
||||
|
||||
Headers<ByteString> expected = newInstance();
|
||||
expected.add(bs("name1"), bs("value5"));
|
||||
expected.add(bs("name2"), bs("value6"));
|
||||
expected.add(bs("name1"), bs("value7"));
|
||||
expected.add(bs("name3"), bs("value4"));
|
||||
TestDefaultHeaders expected = newInstance();
|
||||
expected.add(of("name1"), of("value5"));
|
||||
expected.add(of("name2"), of("value6"));
|
||||
expected.add(of("name1"), of("value7"));
|
||||
expected.add(of("name3"), of("value4"));
|
||||
|
||||
h1.setAll(h2);
|
||||
|
||||
@ -231,15 +236,15 @@ public class DefaultHeadersTest {
|
||||
|
||||
@Test
|
||||
public void headersWithSameNamesAndValuesShouldBeEquivalent() {
|
||||
Headers<ByteString> headers1 = newInstance();
|
||||
headers1.add(bs("name1"), bs("value1"));
|
||||
headers1.add(bs("name2"), bs("value2"));
|
||||
headers1.add(bs("name2"), bs("value3"));
|
||||
TestDefaultHeaders headers1 = newInstance();
|
||||
headers1.add(of("name1"), of("value1"));
|
||||
headers1.add(of("name2"), of("value2"));
|
||||
headers1.add(of("name2"), of("value3"));
|
||||
|
||||
Headers<ByteString> headers2 = newInstance();
|
||||
headers2.add(bs("name1"), bs("value1"));
|
||||
headers2.add(bs("name2"), bs("value2"));
|
||||
headers2.add(bs("name2"), bs("value3"));
|
||||
TestDefaultHeaders headers2 = newInstance();
|
||||
headers2.add(of("name1"), of("value1"));
|
||||
headers2.add(of("name2"), of("value2"));
|
||||
headers2.add(of("name2"), of("value3"));
|
||||
|
||||
assertEquals(headers1, headers2);
|
||||
assertEquals(headers2, headers1);
|
||||
@ -252,8 +257,8 @@ public class DefaultHeadersTest {
|
||||
|
||||
@Test
|
||||
public void emptyHeadersShouldBeEqual() {
|
||||
Headers<ByteString> headers1 = newInstance();
|
||||
Headers<ByteString> headers2 = newInstance();
|
||||
TestDefaultHeaders headers1 = newInstance();
|
||||
TestDefaultHeaders headers2 = newInstance();
|
||||
assertNotSame(headers1, headers2);
|
||||
assertEquals(headers1, headers2);
|
||||
assertEquals(headers1.hashCode(), headers2.hashCode());
|
||||
@ -261,29 +266,29 @@ public class DefaultHeadersTest {
|
||||
|
||||
@Test
|
||||
public void headersWithSameNamesButDifferentValuesShouldNotBeEquivalent() {
|
||||
Headers<ByteString> headers1 = newInstance();
|
||||
headers1.add(bs("name1"), bs("value1"));
|
||||
Headers<ByteString> headers2 = newInstance();
|
||||
headers1.add(bs("name1"), bs("value2"));
|
||||
TestDefaultHeaders headers1 = newInstance();
|
||||
headers1.add(of("name1"), of("value1"));
|
||||
TestDefaultHeaders headers2 = newInstance();
|
||||
headers1.add(of("name1"), of("value2"));
|
||||
assertNotEquals(headers1, headers2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsetOfHeadersShouldNotBeEquivalent() {
|
||||
Headers<ByteString> headers1 = newInstance();
|
||||
headers1.add(bs("name1"), bs("value1"));
|
||||
headers1.add(bs("name2"), bs("value2"));
|
||||
Headers<ByteString> headers2 = newInstance();
|
||||
headers1.add(bs("name1"), bs("value1"));
|
||||
TestDefaultHeaders headers1 = newInstance();
|
||||
headers1.add(of("name1"), of("value1"));
|
||||
headers1.add(of("name2"), of("value2"));
|
||||
TestDefaultHeaders headers2 = newInstance();
|
||||
headers1.add(of("name1"), of("value1"));
|
||||
assertNotEquals(headers1, headers2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWithDifferentNamesAndValuesShouldNotBeEquivalent() {
|
||||
Headers<ByteString> h1 = newInstance();
|
||||
h1.set(bs("name1"), bs("value1"));
|
||||
Headers<ByteString> h2 = newInstance();
|
||||
h2.set(bs("name2"), bs("value2"));
|
||||
TestDefaultHeaders h1 = newInstance();
|
||||
h1.set(of("name1"), of("value1"));
|
||||
TestDefaultHeaders h2 = newInstance();
|
||||
h2.set(of("name2"), of("value2"));
|
||||
assertNotEquals(h1, h2);
|
||||
assertNotEquals(h2, h1);
|
||||
assertEquals(h1, h1);
|
||||
@ -292,22 +297,22 @@ public class DefaultHeadersTest {
|
||||
|
||||
@Test(expected = NoSuchElementException.class)
|
||||
public void iterateEmptyHeadersShouldThrow() {
|
||||
Iterator<Map.Entry<ByteString, ByteString>> iterator = newInstance().iterator();
|
||||
Iterator<Map.Entry<CharSequence, CharSequence>> iterator = newInstance().iterator();
|
||||
assertFalse(iterator.hasNext());
|
||||
iterator.next();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void iteratorShouldReturnAllNameValuePairs() {
|
||||
Headers<ByteString> headers1 = newInstance();
|
||||
headers1.add(bs("name1"), bs("value1"), bs("value2"));
|
||||
headers1.add(bs("name2"), bs("value3"));
|
||||
headers1.add(bs("name3"), bs("value4"), bs("value5"), bs("value6"));
|
||||
headers1.add(bs("name1"), bs("value7"), bs("value8"));
|
||||
TestDefaultHeaders headers1 = newInstance();
|
||||
headers1.add(of("name1"), of("value1"), of("value2"));
|
||||
headers1.add(of("name2"), of("value3"));
|
||||
headers1.add(of("name3"), of("value4"), of("value5"), of("value6"));
|
||||
headers1.add(of("name1"), of("value7"), of("value8"));
|
||||
assertEquals(8, headers1.size());
|
||||
|
||||
Headers<ByteString> headers2 = newInstance();
|
||||
for (Entry<ByteString, ByteString> entry : headers1) {
|
||||
TestDefaultHeaders headers2 = newInstance();
|
||||
for (Entry<CharSequence, CharSequence> entry : headers1) {
|
||||
headers2.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
@ -316,45 +321,45 @@ public class DefaultHeadersTest {
|
||||
|
||||
@Test
|
||||
public void iteratorSetValueShouldChangeHeaderValue() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
headers.add(bs("name1"), bs("value1"), bs("value2"), bs("value3"));
|
||||
headers.add(bs("name2"), bs("value4"));
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.add(of("name1"), of("value1"), of("value2"), of("value3"));
|
||||
headers.add(of("name2"), of("value4"));
|
||||
assertEquals(4, headers.size());
|
||||
|
||||
Iterator<Entry<ByteString, ByteString>> iter = headers.iterator();
|
||||
Iterator<Entry<CharSequence, CharSequence>> iter = headers.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entry<ByteString, ByteString> header = iter.next();
|
||||
if (bs("name1").equals(header.getKey()) && bs("value2").equals(header.getValue())) {
|
||||
header.setValue(bs("updatedvalue2"));
|
||||
assertEquals(bs("updatedvalue2"), header.getValue());
|
||||
Entry<CharSequence, CharSequence> header = iter.next();
|
||||
if (of("name1").equals(header.getKey()) && of("value2").equals(header.getValue())) {
|
||||
header.setValue(of("updatedvalue2"));
|
||||
assertEquals(of("updatedvalue2"), header.getValue());
|
||||
}
|
||||
if (bs("name1").equals(header.getKey()) && bs("value3").equals(header.getValue())) {
|
||||
header.setValue(bs("updatedvalue3"));
|
||||
assertEquals(bs("updatedvalue3"), header.getValue());
|
||||
if (of("name1").equals(header.getKey()) && of("value3").equals(header.getValue())) {
|
||||
header.setValue(of("updatedvalue3"));
|
||||
assertEquals(of("updatedvalue3"), header.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(4, headers.size());
|
||||
assertTrue(headers.contains(bs("name1"), bs("updatedvalue2")));
|
||||
assertFalse(headers.contains(bs("name1"), bs("value2")));
|
||||
assertTrue(headers.contains(bs("name1"), bs("updatedvalue3")));
|
||||
assertFalse(headers.contains(bs("name1"), bs("value3")));
|
||||
assertTrue(headers.contains(of("name1"), of("updatedvalue2")));
|
||||
assertFalse(headers.contains(of("name1"), of("value2")));
|
||||
assertTrue(headers.contains(of("name1"), of("updatedvalue3")));
|
||||
assertFalse(headers.contains(of("name1"), of("value3")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAllReturnsEmptyListForUnknownName() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
assertEquals(0, headers.getAll(bs("noname")).size());
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
assertEquals(0, headers.getAll(of("noname")).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setHeadersShouldClearAndOverwrite() {
|
||||
Headers<ByteString> headers1 = newInstance();
|
||||
headers1.add(bs("name"), bs("value"));
|
||||
TestDefaultHeaders headers1 = newInstance();
|
||||
headers1.add(of("name"), of("value"));
|
||||
|
||||
Headers<ByteString> headers2 = newInstance();
|
||||
headers2.add(bs("name"), bs("newvalue"));
|
||||
headers2.add(bs("name1"), bs("value1"));
|
||||
TestDefaultHeaders headers2 = newInstance();
|
||||
headers2.add(of("name"), of("newvalue"));
|
||||
headers2.add(of("name1"), of("value1"));
|
||||
|
||||
headers1.set(headers2);
|
||||
assertEquals(headers1, headers2);
|
||||
@ -362,18 +367,18 @@ public class DefaultHeadersTest {
|
||||
|
||||
@Test
|
||||
public void setAllHeadersShouldOnlyOverwriteHeaders() {
|
||||
Headers<ByteString> headers1 = newInstance();
|
||||
headers1.add(bs("name"), bs("value"));
|
||||
headers1.add(bs("name1"), bs("value1"));
|
||||
TestDefaultHeaders headers1 = newInstance();
|
||||
headers1.add(of("name"), of("value"));
|
||||
headers1.add(of("name1"), of("value1"));
|
||||
|
||||
Headers<ByteString> headers2 = newInstance();
|
||||
headers2.add(bs("name"), bs("newvalue"));
|
||||
headers2.add(bs("name2"), bs("value2"));
|
||||
TestDefaultHeaders headers2 = newInstance();
|
||||
headers2.add(of("name"), of("newvalue"));
|
||||
headers2.add(of("name2"), of("value2"));
|
||||
|
||||
Headers<ByteString> expected = newInstance();
|
||||
expected.add(bs("name"), bs("newvalue"));
|
||||
expected.add(bs("name1"), bs("value1"));
|
||||
expected.add(bs("name2"), bs("value2"));
|
||||
TestDefaultHeaders expected = newInstance();
|
||||
expected.add(of("name"), of("newvalue"));
|
||||
expected.add(of("name1"), of("value1"));
|
||||
expected.add(of("name2"), of("value2"));
|
||||
|
||||
headers1.setAll(headers2);
|
||||
assertEquals(headers1, expected);
|
||||
@ -381,17 +386,13 @@ public class DefaultHeadersTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testAddSelf() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.add(headers);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testSetSelf() {
|
||||
Headers<ByteString> headers = newInstance();
|
||||
TestDefaultHeaders headers = newInstance();
|
||||
headers.set(headers);
|
||||
}
|
||||
|
||||
private ByteString bs(String value) {
|
||||
return new ByteString(value, CharsetUtil.US_ASCII);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,705 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 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;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.util.Arrays;
|
||||
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
/**
|
||||
* The primary use case for this class is to function as an immutable array of bytes. For performance reasons this
|
||||
* class supports sharing memory with external arrays, and also direct access to the underlying memory via
|
||||
* {@link #array()}. Care must be taken when directly accessing the memory as that may invalidate assumptions that
|
||||
* this object is immutable.
|
||||
*/
|
||||
public class ByteString {
|
||||
/**
|
||||
* Allows sub classes to take advantage of {@link ByteString} operations which need to generate new
|
||||
* ByteString objects.
|
||||
*/
|
||||
protected interface ByteStringFactory {
|
||||
ByteString newInstance(byte[] value, int start, int length, boolean copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory which uses the {@link #ByteString(byte[], int, int, boolean)} constructor.
|
||||
*/
|
||||
private static final ByteStringFactory DEFAULT_FACTORY = new ByteStringFactory() {
|
||||
@Override
|
||||
public ByteString newInstance(byte[] value, int start, int length, boolean copy) {
|
||||
return new ByteString(value, start, length, copy);
|
||||
}
|
||||
};
|
||||
|
||||
public static final ByteString EMPTY_STRING = new ByteString(0);
|
||||
protected static final int HASH_CODE_PRIME = 31;
|
||||
|
||||
/**
|
||||
* If this value is modified outside the constructor then call {@link #arrayChanged()}.
|
||||
*/
|
||||
protected final byte[] value;
|
||||
/**
|
||||
* Offset into {@link #value} that all operations should use when acting upon {@link #value}.
|
||||
*/
|
||||
private final int offset;
|
||||
/**
|
||||
* Length in bytes for {@link #value} that we care about. This is independent from {@code value.length}
|
||||
* because we may be looking at a subsection of the array.
|
||||
*/
|
||||
private final int length;
|
||||
/**
|
||||
* The hash code is cached after it is first computed. It can be reset with {@link #arrayChanged()}.
|
||||
*/
|
||||
private int hash;
|
||||
|
||||
/**
|
||||
* Used for classes which extend this class and want to initialize the {@link #value} array by them selves.
|
||||
*/
|
||||
ByteString(int length) {
|
||||
value = new byte[length];
|
||||
offset = 0;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this byte string based upon a byte array. A copy will be made.
|
||||
*/
|
||||
public ByteString(byte[] value) {
|
||||
this(value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this byte string based upon a byte array.
|
||||
* {@code copy} determines if a copy is made or the array is shared.
|
||||
*/
|
||||
public ByteString(byte[] value, boolean copy) {
|
||||
this(value, 0, checkNotNull(value, "value").length, copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link BinaryString} object from a {@code byte[]} array.
|
||||
* @param copy {@code true} then a copy of the memory will be made. {@code false} the underlying memory
|
||||
* will be shared.
|
||||
*/
|
||||
public ByteString(byte[] value, int start, int length, boolean copy) {
|
||||
if (copy) {
|
||||
this.value = Arrays.copyOfRange(value, start, start + length);
|
||||
this.offset = 0;
|
||||
this.length = length;
|
||||
} else {
|
||||
if (start < 0 || start > value.length - length) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" +
|
||||
length + ") <= " + "value.length(" + value.length + ')');
|
||||
}
|
||||
this.value = value;
|
||||
this.offset = start;
|
||||
this.length = length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new object which is equal to {@code value}.
|
||||
* @param value The object to replicate.
|
||||
* @param copy {@code true} mean the underlying storage will be copied.
|
||||
* {@code false} means the underlying storage will be shared.
|
||||
*/
|
||||
public ByteString(ByteString value, boolean copy) {
|
||||
checkNotNull(value, "value");
|
||||
this.length = value.length();
|
||||
this.hash = value.hash;
|
||||
if (copy) {
|
||||
this.value = new byte[length];
|
||||
System.arraycopy(value.array(), value.arrayOffset(), this.value, 0, length);
|
||||
this.offset = 0;
|
||||
} else {
|
||||
this.value = value.array();
|
||||
this.offset = value.offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of the underlying storage from {@link value}.
|
||||
* The copy will start at {@link ByteBuffer#position()} and copy {@link ByteBuffer#remaining()} bytes.
|
||||
*/
|
||||
public ByteString(ByteBuffer value) {
|
||||
this(value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a {@link ByteString} based upon the underlying storage from {@link value}.
|
||||
* There is a potential to share the underlying array storage if {@link ByteBuffer#hasArray()} is {@code true}.
|
||||
* if {@code copy} is {@code true} a copy will be made of the memory.
|
||||
* if {@code copy} is {@code false} the underlying storage will be shared, if possible.
|
||||
*/
|
||||
public ByteString(ByteBuffer value, boolean copy) {
|
||||
this(value, value.position(), checkNotNull(value, "value").remaining(), copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a {@link ByteString} based upon the underlying storage from {@link value}.
|
||||
* There is a potential to share the underlying array storage if {@link ByteBuffer#hasArray()} is {@code true}.
|
||||
* if {@code copy} is {@code true} a copy will be made of the memory.
|
||||
* if {@code copy} is {@code false} the underlying storage will be shared, if possible.
|
||||
*/
|
||||
public ByteString(ByteBuffer value, int start, int length, boolean copy) {
|
||||
if (start < 0 || length > checkNotNull(value, "value").capacity() - start) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
|
||||
+ ") <= " + "value.capacity(" + value.capacity() + ')');
|
||||
}
|
||||
|
||||
if (value.hasArray()) {
|
||||
if (copy) {
|
||||
final int bufferOffset = value.arrayOffset() + start;
|
||||
this.value = Arrays.copyOfRange(value.array(), bufferOffset, bufferOffset + length);
|
||||
offset = 0;
|
||||
this.length = length;
|
||||
} else {
|
||||
this.value = value.array();
|
||||
this.offset = start;
|
||||
this.length = length;
|
||||
}
|
||||
} else {
|
||||
this.value = new byte[length];
|
||||
int oldPos = value.position();
|
||||
value.get(this.value, 0, length);
|
||||
value.position(oldPos);
|
||||
this.offset = 0;
|
||||
this.length = length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
|
||||
*/
|
||||
public ByteString(char[] value, Charset charset) {
|
||||
this(value, charset, 0, checkNotNull(value, "value").length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
|
||||
* The copy will start at index {@code start} and copy {@code length} bytes.
|
||||
*/
|
||||
public ByteString(char[] value, Charset charset, int start, int length) {
|
||||
if (start < 0 || length > checkNotNull(value, "value").length - start) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
|
||||
+ ") <= " + "length(" + length + ')');
|
||||
}
|
||||
|
||||
CharBuffer cbuf = CharBuffer.wrap(value, start, start + length);
|
||||
CharsetEncoder encoder = CharsetUtil.getEncoder(charset);
|
||||
ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length));
|
||||
encoder.encode(cbuf, nativeBuffer, true);
|
||||
final int bufferOffset = nativeBuffer.arrayOffset();
|
||||
this.value = Arrays.copyOfRange(nativeBuffer.array(), bufferOffset, bufferOffset + nativeBuffer.position());
|
||||
this.offset = 0;
|
||||
this.length = this.value.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
|
||||
*/
|
||||
public ByteString(CharSequence value, Charset charset) {
|
||||
this(value, charset, 0, checkNotNull(value, "value").length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
|
||||
* The copy will start at index {@code start} and copy {@code length} bytes.
|
||||
*/
|
||||
public ByteString(CharSequence value, Charset charset, int start, int length) {
|
||||
if (start < 0 || length > checkNotNull(value, "value").length() - start) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
|
||||
+ ") <= " + "length(" + value.length() + ')');
|
||||
}
|
||||
|
||||
CharBuffer cbuf = CharBuffer.wrap(value, start, start + length);
|
||||
CharsetEncoder encoder = CharsetUtil.getEncoder(charset);
|
||||
ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length));
|
||||
encoder.encode(cbuf, nativeBuffer, true);
|
||||
final int offset = nativeBuffer.arrayOffset();
|
||||
this.value = Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position());
|
||||
this.offset = 0;
|
||||
this.length = this.value.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ByteString} assuming ASCII encoding of {@code value}.
|
||||
* @param value value to translate assuming ASCII encoding.
|
||||
*/
|
||||
public static final ByteString fromAscii(CharSequence value) {
|
||||
return new ByteString(value, CharsetUtil.US_ASCII);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the readable bytes of this buffer with the specified {@code processor} in ascending order.
|
||||
*
|
||||
* @return {@code -1} if the processor iterated to or beyond the end of the readable bytes.
|
||||
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
|
||||
*/
|
||||
public final int forEachByte(ByteProcessor visitor) throws Exception {
|
||||
return forEachByte0(0, length(), visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the specified area of this buffer with the specified {@code processor} in ascending order.
|
||||
* (i.e. {@code index}, {@code (index + 1)}, .. {@code (index + length - 1)}).
|
||||
*
|
||||
* @return {@code -1} if the processor iterated to or beyond the end of the specified area.
|
||||
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
|
||||
*/
|
||||
public final int forEachByte(int index, int length, ByteProcessor visitor) throws Exception {
|
||||
if (index < 0 || length > length() - index) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length
|
||||
+ ") <= " + "length(" + length() + ')');
|
||||
}
|
||||
|
||||
return forEachByte0(index, length, visitor);
|
||||
}
|
||||
|
||||
private int forEachByte0(int index, int length, ByteProcessor visitor) throws Exception {
|
||||
final int len = offset + length;
|
||||
for (int i = offset + index; i < len; ++i) {
|
||||
if (!visitor.process(value[i])) {
|
||||
return i - offset;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the readable bytes of this buffer with the specified {@code processor} in descending order.
|
||||
*
|
||||
* @return {@code -1} if the processor iterated to or beyond the beginning of the readable bytes.
|
||||
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
|
||||
*/
|
||||
public final int forEachByteDesc(ByteProcessor visitor) throws Exception {
|
||||
return forEachByteDesc0(0, length(), visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the specified area of this buffer with the specified {@code processor} in descending order.
|
||||
* (i.e. {@code (index + length - 1)}, {@code (index + length - 2)}, ... {@code index}).
|
||||
*
|
||||
* @return {@code -1} if the processor iterated to or beyond the beginning of the specified area.
|
||||
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
|
||||
*/
|
||||
public final int forEachByteDesc(int index, int length, ByteProcessor visitor) throws Exception {
|
||||
if (index < 0 || length > length() - index) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length
|
||||
+ ") <= " + "length(" + length() + ')');
|
||||
}
|
||||
|
||||
return forEachByteDesc0(index, length, visitor);
|
||||
}
|
||||
|
||||
private int forEachByteDesc0(int index, int length, ByteProcessor visitor) throws Exception {
|
||||
final int end = offset + index;
|
||||
for (int i = offset + index + length - 1; i >= end; --i) {
|
||||
if (!visitor.process(value[i])) {
|
||||
return i - offset;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public final byte byteAt(int index) {
|
||||
// We must do a range check here to enforce the access does not go outside our sub region of the array.
|
||||
// We rely on the array access itself to pick up the array out of bounds conditions
|
||||
if (index < 0 || index >= length) {
|
||||
throw new IndexOutOfBoundsException("index: " + index + " must be in the range [0," + length + ")");
|
||||
}
|
||||
return value[index + offset];
|
||||
}
|
||||
|
||||
public final boolean isEmpty() {
|
||||
return length == 0;
|
||||
}
|
||||
|
||||
public final int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* During normal use cases the {@link ByteString} should be immutable, but if the underlying array is shared,
|
||||
* and changes then this needs to be called.
|
||||
*/
|
||||
public void arrayChanged() {
|
||||
hash = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This gives direct access to the underlying storage array.
|
||||
* The {@link #toByteArray()} should be preferred over this method.
|
||||
* If the return value is changed then {@link #arrayChanged()} must be called.
|
||||
* @see #arrayOffset()
|
||||
* @see #isEntireArrayUsed()
|
||||
*/
|
||||
public final byte[] array() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset into {@link #array()} for which data for this ByteString begins.
|
||||
* @see #array()
|
||||
* @see #isEntireArrayUsed()
|
||||
*/
|
||||
public final int arrayOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the storage represented by {@link #array()} is entirely used.
|
||||
* @see #array()
|
||||
*/
|
||||
public final boolean isEntireArrayUsed() {
|
||||
return offset == 0 && length == value.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this string to a byte array.
|
||||
*/
|
||||
public final byte[] toByteArray() {
|
||||
return toByteArray(0, length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a subset of this string to a byte array.
|
||||
* The subset is defined by the range [{@code start}, {@code end}).
|
||||
*/
|
||||
public final byte[] toByteArray(int start, int end) {
|
||||
return Arrays.copyOfRange(value, start + offset, end + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the content of this string to a byte array.
|
||||
*
|
||||
* @param srcIdx the starting offset of characters to copy.
|
||||
* @param dst the destination byte array.
|
||||
* @param dstIdx the starting offset in the destination byte array.
|
||||
* @param length the number of characters to copy.
|
||||
*/
|
||||
public final void copy(int srcIdx, byte[] dst, int dstIdx, int length) {
|
||||
if (srcIdx < 0 || length > length() - srcIdx) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
|
||||
+ length + ") <= srcLen(" + length() + ')');
|
||||
}
|
||||
|
||||
System.arraycopy(value, srcIdx + offset, checkNotNull(dst, "dst"), dstIdx, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = hash;
|
||||
if (h == 0) {
|
||||
final int end = offset + length;
|
||||
for (int i = offset; i < end; ++i) {
|
||||
h = h * HASH_CODE_PRIME + value[i];
|
||||
}
|
||||
|
||||
hash = h;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a range of characters into a new string.
|
||||
* @param start the offset of the first character (inclusive).
|
||||
* @return a new string containing the characters from start to the end of the string.
|
||||
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
|
||||
*/
|
||||
public ByteString subSequence(int start) {
|
||||
return subSequence(start, length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a range of characters into a new string.
|
||||
* @param start the offset of the first character (inclusive).
|
||||
* @param end The index to stop at (exclusive).
|
||||
* @return a new string containing the characters from start to the end of the string.
|
||||
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
|
||||
*/
|
||||
public ByteString subSequence(int start, int end) {
|
||||
return subSequence(start, end, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Either copy or share a subset of underlying sub-sequence of bytes.
|
||||
* @param start the offset of the first character (inclusive).
|
||||
* @param end The index to stop at (exclusive).
|
||||
* @param copy If {@code true} then a copy of the underlying storage will be made.
|
||||
* If {@code false} then the underlying storage will be shared.
|
||||
* @return a new string containing the characters from start to the end of the string.
|
||||
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
|
||||
*/
|
||||
public ByteString subSequence(int start, int end, boolean copy) {
|
||||
return subSequence(start, end, copy, DEFAULT_FACTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Either copy or share a subset of underlying sub-sequence of bytes.
|
||||
* @param start the offset of the first character (inclusive).
|
||||
* @param end The index to stop at (exclusive).
|
||||
* @param copy If {@code true} then a copy of the underlying storage will be made.
|
||||
* If {@code false} then the underlying storage will be shared.
|
||||
* @param factory The factory used to generate a new {@link ByteString} object.
|
||||
* @return a new string containing the characters from start to the end of the string.
|
||||
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
|
||||
*/
|
||||
protected ByteString subSequence(int start, int end, boolean copy, ByteStringFactory factory) {
|
||||
if (start < 0 || start > end || end > length()) {
|
||||
throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length("
|
||||
+ length() + ')');
|
||||
}
|
||||
|
||||
if (start == 0 && end == length()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (end == start) {
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
|
||||
return factory.newInstance(value, start + offset, end - start, copy);
|
||||
}
|
||||
|
||||
public final int parseAsciiInt() {
|
||||
return parseAsciiInt(0, length(), 10);
|
||||
}
|
||||
|
||||
public final int parseAsciiInt(int radix) {
|
||||
return parseAsciiInt(0, length(), radix);
|
||||
}
|
||||
|
||||
public final int parseAsciiInt(int start, int end) {
|
||||
return parseAsciiInt(start, end, 10);
|
||||
}
|
||||
|
||||
public final int parseAsciiInt(int start, int end, int radix) {
|
||||
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
|
||||
if (start == end) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
|
||||
int i = start;
|
||||
boolean negative = byteAt(i) == '-';
|
||||
if (negative && ++i == end) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
|
||||
return parseAsciiInt(i, end, radix, negative);
|
||||
}
|
||||
|
||||
private int parseAsciiInt(int start, int end, int radix, boolean negative) {
|
||||
int max = Integer.MIN_VALUE / radix;
|
||||
int result = 0;
|
||||
int currOffset = start;
|
||||
while (currOffset < end) {
|
||||
int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
|
||||
if (digit == -1) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
if (max > result) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
int next = result * radix - digit;
|
||||
if (next > result) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
result = next;
|
||||
}
|
||||
if (!negative) {
|
||||
result = -result;
|
||||
if (result < 0) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public final long parseAsciiLong() {
|
||||
return parseAsciiLong(0, length(), 10);
|
||||
}
|
||||
|
||||
public final long parseAsciiLong(int radix) {
|
||||
return parseAsciiLong(0, length(), radix);
|
||||
}
|
||||
|
||||
public final long parseAsciiLong(int start, int end) {
|
||||
return parseAsciiLong(start, end, 10);
|
||||
}
|
||||
|
||||
public final long parseAsciiLong(int start, int end, int radix) {
|
||||
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
|
||||
if (start == end) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
|
||||
int i = start;
|
||||
boolean negative = byteAt(i) == '-';
|
||||
if (negative && ++i == end) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
|
||||
return parseAsciiLong(i, end, radix, negative);
|
||||
}
|
||||
|
||||
private long parseAsciiLong(int start, int end, int radix, boolean negative) {
|
||||
long max = Long.MIN_VALUE / radix;
|
||||
long result = 0;
|
||||
int currOffset = start;
|
||||
while (currOffset < end) {
|
||||
int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
|
||||
if (digit == -1) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
if (max > result) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
long next = result * radix - digit;
|
||||
if (next > result) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
result = next;
|
||||
}
|
||||
if (!negative) {
|
||||
result = -result;
|
||||
if (result < 0) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public final char parseChar() {
|
||||
return parseChar(0);
|
||||
}
|
||||
|
||||
public char parseChar(int start) {
|
||||
if (start + 1 >= length()) {
|
||||
throw new IndexOutOfBoundsException("2 bytes required to convert to character. index " +
|
||||
start + " would go out of bounds.");
|
||||
}
|
||||
final int startWithOffset = start + offset;
|
||||
return (char) (((value[startWithOffset] & 0xFF) << 8) | (value[startWithOffset + 1] & 0xFF));
|
||||
}
|
||||
|
||||
public final short parseAsciiShort() {
|
||||
return parseAsciiShort(0, length(), 10);
|
||||
}
|
||||
|
||||
public final short parseAsciiShort(int radix) {
|
||||
return parseAsciiShort(0, length(), radix);
|
||||
}
|
||||
|
||||
public final short parseAsciiShort(int start, int end) {
|
||||
return parseAsciiShort(start, end, 10);
|
||||
}
|
||||
|
||||
public final short parseAsciiShort(int start, int end, int radix) {
|
||||
int intValue = parseAsciiInt(start, end, radix);
|
||||
short result = (short) intValue;
|
||||
if (result != intValue) {
|
||||
throw new NumberFormatException(subSequence(start, end, false).toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public final float parseAsciiFloat() {
|
||||
return parseAsciiFloat(0, length());
|
||||
}
|
||||
|
||||
public final float parseAsciiFloat(int start, int end) {
|
||||
return Float.parseFloat(toString(start, end));
|
||||
}
|
||||
|
||||
public final double parseAsciiDouble() {
|
||||
return parseAsciiDouble(0, length());
|
||||
}
|
||||
|
||||
public final double parseAsciiDouble(int start, int end) {
|
||||
return Double.parseDouble(toString(start, end));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof ByteString)) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ByteString other = (ByteString) obj;
|
||||
return hashCode() == other.hashCode() &&
|
||||
PlatformDependent.equals(array(), arrayOffset(), arrayOffset() + length(),
|
||||
other.array(), other.arrayOffset(), other.arrayOffset() + other.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the entire byte string to a {@link String}.
|
||||
* @see {@link #toString(int, int)}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(0, length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the entire byte string to a {@link String} using the {@code charset} encoding.
|
||||
* @see {@link #toString(Charset, int, int)}
|
||||
*/
|
||||
public final String toString(Charset charset) {
|
||||
return toString(charset, 0, length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the [{@code start}, {@code end}) range of this byte string to a {@link String}.
|
||||
* @see {@link #toString(Charset, int, int)}
|
||||
*/
|
||||
public final String toString(int start, int end) {
|
||||
return toString(CharsetUtil.ISO_8859_1, start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the [{@code start}, {@code end}) range of this byte string to a {@link String}
|
||||
* using the {@code charset} encoding.
|
||||
*/
|
||||
public String toString(Charset charset, int start, int end) {
|
||||
int length = end - start;
|
||||
if (length == 0) {
|
||||
return StringUtil.EMPTY_STRING;
|
||||
}
|
||||
|
||||
if (start < 0 || length > length() - start) {
|
||||
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
|
||||
+ length + ") <= srcLen(" + length() + ')');
|
||||
}
|
||||
|
||||
return new String(value, start + offset, length, charset);
|
||||
}
|
||||
}
|
@ -35,4 +35,16 @@ public final class MathUtil {
|
||||
assert value > Integer.MIN_VALUE && value < 0x40000000;
|
||||
return 1 << (32 - Integer.numberOfLeadingZeros(value - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the requested {@code index} and {@code length} will fit within {@code capacity}.
|
||||
* @param index The starting index.
|
||||
* @param length The length which will be utilized (starting from {@code index}).
|
||||
* @param capacity The capacity that {@code index + length} is allowed to be within.
|
||||
* @return {@code true} if the requested {@code index} and {@code length} will fit within {@code capacity}.
|
||||
* {@code false} if this would result in an index out of bounds exception.
|
||||
*/
|
||||
public static boolean isOutOfBounds(int index, int length, int capacity) {
|
||||
return (index | length | (index + length) | (capacity - (index + length))) < 0;
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ public final class PlatformDependent {
|
||||
private static final int BIT_MODE = bitMode0();
|
||||
|
||||
private static final int ADDRESS_SIZE = addressSize0();
|
||||
private static final boolean NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
|
||||
public static final boolean BIG_ENDIAN_NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
|
||||
|
||||
static {
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
@ -22,17 +22,20 @@ import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static io.netty.util.AsciiString.caseInsensitiveHashCode;
|
||||
import static io.netty.util.AsciiString.contains;
|
||||
import static io.netty.util.AsciiString.containsIgnoreCase;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for the {@link AsciiString} class
|
||||
* Test character encoding and case insensitivity for the {@link AsciiString} class
|
||||
*/
|
||||
public class AsciiStringTest {
|
||||
public class AsciiStringCharacterTest {
|
||||
private static final Random r = new Random();
|
||||
|
||||
@Test
|
||||
public void testGetBytesStringBuilder() {
|
||||
final StringBuilder b = new StringBuilder();
|
||||
@ -44,7 +47,7 @@ public class AsciiStringTest {
|
||||
for (int i = 0; i < charsets.length; ++i) {
|
||||
final Charset charset = charsets[i];
|
||||
byte[] expected = bString.getBytes(charset);
|
||||
byte[] actual = new ByteString(b, charset).toByteArray();
|
||||
byte[] actual = new AsciiString(b, charset).toByteArray();
|
||||
assertArrayEquals("failure for " + charset, expected, actual);
|
||||
}
|
||||
}
|
||||
@ -60,7 +63,7 @@ public class AsciiStringTest {
|
||||
for (int i = 0; i < charsets.length; ++i) {
|
||||
final Charset charset = charsets[i];
|
||||
byte[] expected = bString.getBytes(charset);
|
||||
byte[] actual = new ByteString(bString, charset).toByteArray();
|
||||
byte[] actual = new AsciiString(bString, charset).toByteArray();
|
||||
assertArrayEquals("failure for " + charset, expected, actual);
|
||||
}
|
||||
}
|
||||
@ -156,18 +159,17 @@ public class AsciiStringTest {
|
||||
|
||||
@Test
|
||||
public void testCaseSensitivity() {
|
||||
Random r = new Random();
|
||||
int i = 0;
|
||||
for (; i < 32; i++) {
|
||||
doCaseSensitivity(r, i);
|
||||
doCaseSensitivity(i);
|
||||
}
|
||||
final int min = i;
|
||||
final int max = 4000;
|
||||
final int len = r.nextInt((max - min) + 1) + min;
|
||||
doCaseSensitivity(r, len);
|
||||
doCaseSensitivity(len);
|
||||
}
|
||||
|
||||
private static void doCaseSensitivity(Random r, int len) {
|
||||
private static void doCaseSensitivity(int len) {
|
||||
// Build an upper case and lower case string
|
||||
final int upperA = 'A';
|
||||
final int upperZ = 'Z';
|
||||
@ -183,22 +185,18 @@ public class AsciiStringTest {
|
||||
String lowerCaseString = new String(lowerCaseBytes);
|
||||
AsciiString lowerCaseAscii = new AsciiString(lowerCaseBytes, false);
|
||||
AsciiString upperCaseAscii = new AsciiString(upperCaseString);
|
||||
ByteString lowerCaseByteString = new ByteString(lowerCaseBytes);
|
||||
ByteString upperCaseByteString = new ByteString(upperCaseString, CharsetUtil.US_ASCII);
|
||||
final String errorString = "len: " + len;
|
||||
// Test upper case hash codes are equal
|
||||
final int upperCaseExpected = upperCaseAscii.hashCode();
|
||||
assertEquals(errorString, upperCaseExpected, AsciiString.hashCode(upperCaseBuilder));
|
||||
assertEquals(errorString, upperCaseExpected, AsciiString.hashCode(upperCaseString));
|
||||
assertEquals(errorString, upperCaseExpected, upperCaseAscii.hashCode());
|
||||
assertEquals(errorString, upperCaseExpected, upperCaseByteString.hashCode());
|
||||
|
||||
// Test lower case hash codes are equal
|
||||
final int lowerCaseExpected = lowerCaseAscii.hashCode();
|
||||
assertEquals(errorString, lowerCaseExpected, AsciiString.hashCode(lowerCaseAscii));
|
||||
assertEquals(errorString, lowerCaseExpected, AsciiString.hashCode(lowerCaseString));
|
||||
assertEquals(errorString, lowerCaseExpected, lowerCaseAscii.hashCode());
|
||||
assertEquals(errorString, lowerCaseExpected, lowerCaseByteString.hashCode());
|
||||
|
||||
// Test case insensitive hash codes are equal
|
||||
final int expectedCaseInsensative = lowerCaseAscii.hashCodeCaseInsensitive();
|
||||
@ -210,11 +208,9 @@ public class AsciiStringTest {
|
||||
assertEquals(errorString, expectedCaseInsensative, lowerCaseAscii.hashCodeCaseInsensitive());
|
||||
assertEquals(errorString, expectedCaseInsensative, upperCaseAscii.hashCodeCaseInsensitive());
|
||||
|
||||
// Test that opposite cases are not equal
|
||||
if (len != 0) {
|
||||
assertNotEquals(errorString, lowerCaseAscii.hashCode(), AsciiString.hashCode(upperCaseString));
|
||||
assertNotEquals(errorString, upperCaseAscii.hashCode(), AsciiString.hashCode(lowerCaseString));
|
||||
}
|
||||
// Test that opposite cases are equal
|
||||
assertEquals(errorString, lowerCaseAscii.hashCode(), AsciiString.hashCode(upperCaseString));
|
||||
assertEquals(errorString, upperCaseAscii.hashCode(), AsciiString.hashCode(lowerCaseString));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -228,4 +224,13 @@ public class AsciiStringTest {
|
||||
CharBuffer buffer = CharBuffer.wrap(array, offset, s1.length());
|
||||
assertEquals(caseInsensitiveHashCode(s1), caseInsensitiveHashCode(buffer));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBooleanUtilityMethods() {
|
||||
assertTrue(new AsciiString(new byte[] { 1 }).parseBoolean());
|
||||
assertFalse(AsciiString.EMPTY_STRING.parseBoolean());
|
||||
assertFalse(new AsciiString(new byte[] { 0 }).parseBoolean());
|
||||
assertTrue(new AsciiString(new byte[] { 5 }).parseBoolean());
|
||||
assertTrue(new AsciiString(new byte[] { 2, 0 }).parseBoolean());
|
||||
}
|
||||
}
|
@ -25,16 +25,16 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test for the {@link ByteString} class.
|
||||
* Test the underlying memory methods for the {@link AsciiString} class.
|
||||
*/
|
||||
public class ByteStringTest {
|
||||
public class AsciiStringMemoryTest {
|
||||
private byte[] a;
|
||||
private byte[] b;
|
||||
private int aOffset = 22;
|
||||
private int bOffset = 53;
|
||||
private int length = 100;
|
||||
private ByteString aByteString;
|
||||
private ByteString bByteString;
|
||||
private AsciiString aAsciiString;
|
||||
private AsciiString bAsciiString;
|
||||
private Random r = new Random();
|
||||
|
||||
@Before
|
||||
@ -47,32 +47,32 @@ public class ByteStringTest {
|
||||
bOffset = 53;
|
||||
length = 100;
|
||||
System.arraycopy(a, aOffset, b, bOffset, length);
|
||||
aByteString = new ByteString(a, aOffset, length, false);
|
||||
bByteString = new ByteString(b, bOffset, length, false);
|
||||
aAsciiString = new AsciiString(a, aOffset, length, false);
|
||||
bAsciiString = new AsciiString(b, bOffset, length, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedMemory() {
|
||||
++a[aOffset];
|
||||
ByteString aByteString1 = new ByteString(a, aOffset, length, true);
|
||||
ByteString aByteString2 = new ByteString(a, aOffset, length, false);
|
||||
assertEquals(aByteString, aByteString1);
|
||||
assertEquals(aByteString, aByteString2);
|
||||
AsciiString aAsciiString1 = new AsciiString(a, aOffset, length, true);
|
||||
AsciiString aAsciiString2 = new AsciiString(a, aOffset, length, false);
|
||||
assertEquals(aAsciiString, aAsciiString1);
|
||||
assertEquals(aAsciiString, aAsciiString2);
|
||||
for (int i = aOffset; i < length; ++i) {
|
||||
assertEquals(a[i], aByteString.byteAt(i - aOffset));
|
||||
assertEquals(a[i], aAsciiString.byteAt(i - aOffset));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotSharedMemory() {
|
||||
ByteString aByteString1 = new ByteString(a, aOffset, length, true);
|
||||
AsciiString aAsciiString1 = new AsciiString(a, aOffset, length, true);
|
||||
++a[aOffset];
|
||||
assertNotEquals(aByteString, aByteString1);
|
||||
assertNotEquals(aAsciiString, aAsciiString1);
|
||||
int i = aOffset;
|
||||
assertNotEquals(a[i], aByteString1.byteAt(i - aOffset));
|
||||
assertNotEquals(a[i], aAsciiString1.byteAt(i - aOffset));
|
||||
++i;
|
||||
for (; i < length; ++i) {
|
||||
assertEquals(a[i], aByteString1.byteAt(i - aOffset));
|
||||
assertEquals(a[i], aAsciiString1.byteAt(i - aOffset));
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,69 +80,69 @@ public class ByteStringTest {
|
||||
public void forEachTest() throws Exception {
|
||||
final AtomicReference<Integer> aCount = new AtomicReference<Integer>(0);
|
||||
final AtomicReference<Integer> bCount = new AtomicReference<Integer>(0);
|
||||
aByteString.forEachByte(new ByteProcessor() {
|
||||
aAsciiString.forEachByte(new ByteProcessor() {
|
||||
int i;
|
||||
@Override
|
||||
public boolean process(byte value) throws Exception {
|
||||
assertEquals("failed at index: " + i, value, bByteString.byteAt(i++));
|
||||
assertEquals("failed at index: " + i, value, bAsciiString.byteAt(i++));
|
||||
aCount.set(aCount.get() + 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
bByteString.forEachByte(new ByteProcessor() {
|
||||
bAsciiString.forEachByte(new ByteProcessor() {
|
||||
int i;
|
||||
@Override
|
||||
public boolean process(byte value) throws Exception {
|
||||
assertEquals("failed at index: " + i, value, aByteString.byteAt(i++));
|
||||
assertEquals("failed at index: " + i, value, aAsciiString.byteAt(i++));
|
||||
bCount.set(bCount.get() + 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
assertEquals(aByteString.length(), aCount.get().intValue());
|
||||
assertEquals(bByteString.length(), bCount.get().intValue());
|
||||
assertEquals(aAsciiString.length(), aCount.get().intValue());
|
||||
assertEquals(bAsciiString.length(), bCount.get().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forEachDescTest() throws Exception {
|
||||
final AtomicReference<Integer> aCount = new AtomicReference<Integer>(0);
|
||||
final AtomicReference<Integer> bCount = new AtomicReference<Integer>(0);
|
||||
aByteString.forEachByteDesc(new ByteProcessor() {
|
||||
aAsciiString.forEachByteDesc(new ByteProcessor() {
|
||||
int i = 1;
|
||||
@Override
|
||||
public boolean process(byte value) throws Exception {
|
||||
assertEquals("failed at index: " + i, value, bByteString.byteAt(bByteString.length() - (i++)));
|
||||
assertEquals("failed at index: " + i, value, bAsciiString.byteAt(bAsciiString.length() - (i++)));
|
||||
aCount.set(aCount.get() + 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
bByteString.forEachByteDesc(new ByteProcessor() {
|
||||
bAsciiString.forEachByteDesc(new ByteProcessor() {
|
||||
int i = 1;
|
||||
@Override
|
||||
public boolean process(byte value) throws Exception {
|
||||
assertEquals("failed at index: " + i, value, aByteString.byteAt(aByteString.length() - (i++)));
|
||||
assertEquals("failed at index: " + i, value, aAsciiString.byteAt(aAsciiString.length() - (i++)));
|
||||
bCount.set(bCount.get() + 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
assertEquals(aByteString.length(), aCount.get().intValue());
|
||||
assertEquals(bByteString.length(), bCount.get().intValue());
|
||||
assertEquals(aAsciiString.length(), aCount.get().intValue());
|
||||
assertEquals(bAsciiString.length(), bCount.get().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subSequenceTest() {
|
||||
final int start = 12;
|
||||
final int end = aByteString.length();
|
||||
ByteString aSubSequence = aByteString.subSequence(start, end, false);
|
||||
ByteString bSubSequence = bByteString.subSequence(start, end, true);
|
||||
final int end = aAsciiString.length();
|
||||
AsciiString aSubSequence = aAsciiString.subSequence(start, end, false);
|
||||
AsciiString bSubSequence = bAsciiString.subSequence(start, end, true);
|
||||
assertEquals(aSubSequence, bSubSequence);
|
||||
assertEquals(aSubSequence.hashCode(), bSubSequence.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyTest() {
|
||||
byte[] aCopy = new byte[aByteString.length()];
|
||||
aByteString.copy(0, aCopy, 0, aCopy.length);
|
||||
ByteString aByteStringCopy = new ByteString(aCopy, false);
|
||||
assertEquals(aByteString, aByteStringCopy);
|
||||
byte[] aCopy = new byte[aAsciiString.length()];
|
||||
aAsciiString.copy(0, aCopy, 0, aCopy.length);
|
||||
AsciiString aAsciiStringCopy = new AsciiString(aCopy, false);
|
||||
assertEquals(aAsciiString, aAsciiStringCopy);
|
||||
}
|
||||
}
|
@ -19,8 +19,6 @@ import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||
import io.netty.handler.codec.http2.DefaultHttp2Headers;
|
||||
import io.netty.microbench.util.AbstractMicrobenchmark;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.ByteString;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
@ -53,9 +51,6 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
|
||||
AsciiString[] httpNames;
|
||||
AsciiString[] httpValues;
|
||||
|
||||
ByteString[] http2Names;
|
||||
ByteString[] http2Values;
|
||||
|
||||
DefaultHttpHeaders httpHeaders;
|
||||
DefaultHttp2Headers http2Headers;
|
||||
|
||||
@ -64,8 +59,6 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
|
||||
Map<String, String> headers = ExampleHeaders.EXAMPLES.get(exampleHeader);
|
||||
httpNames = new AsciiString[headers.size()];
|
||||
httpValues = new AsciiString[headers.size()];
|
||||
http2Names = new ByteString[headers.size()];
|
||||
http2Values = new ByteString[headers.size()];
|
||||
httpHeaders = new DefaultHttpHeaders(false);
|
||||
http2Headers = new DefaultHttp2Headers(false);
|
||||
int idx = 0;
|
||||
@ -74,11 +67,9 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
|
||||
String value = header.getValue();
|
||||
httpNames[idx] = new AsciiString(name);
|
||||
httpValues[idx] = new AsciiString(value);
|
||||
http2Names[idx] = new ByteString(name, CharsetUtil.US_ASCII);
|
||||
http2Values[idx] = new ByteString(value, CharsetUtil.US_ASCII);
|
||||
idx++;
|
||||
httpHeaders.add(new AsciiString(name), new AsciiString(value));
|
||||
http2Headers.add(new ByteString(name, CharsetUtil.US_ASCII), new ByteString(value, CharsetUtil.US_ASCII));
|
||||
http2Headers.add(new AsciiString(name), new AsciiString(value));
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +111,7 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
public void http2Remove(Blackhole bh) {
|
||||
for (ByteString name : http2Names) {
|
||||
for (AsciiString name : httpNames) {
|
||||
bh.consume(http2Headers.remove(name));
|
||||
}
|
||||
}
|
||||
@ -128,7 +119,7 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
public void http2Get(Blackhole bh) {
|
||||
for (ByteString name : http2Names) {
|
||||
for (AsciiString name : httpNames) {
|
||||
bh.consume(http2Headers.get(name));
|
||||
}
|
||||
}
|
||||
@ -146,7 +137,7 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
public void http2Iterate(Blackhole bh) {
|
||||
for (Entry<ByteString, ByteString> entry : http2Headers) {
|
||||
for (Entry<CharSequence, CharSequence> entry : http2Headers) {
|
||||
bh.consume(entry);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user