Improve the performance of copying header sets when hashing and name validation are equivalent.

Motivation:
Headers and groups of headers are frequently copied and the current mechanism is slower than it needs to be.

Modifications:
Skip name validation and hash computation when they are not necessary.
Fix emergent bug in CombinedHttpHeaders identified with better testing
Fix memory leak in DefaultHttp2Headers when clearing
Added benchmarks

Result:
Faster header copying and some collateral bug fixes
This commit is contained in:
Louis Ryan 2015-10-30 13:24:46 -07:00 committed by Scott Mitchell
parent 187efca9aa
commit 6e108cb96a
5 changed files with 767 additions and 45 deletions

View File

@ -18,10 +18,12 @@ package io.netty.handler.codec.http;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.ValueConverter;
import io.netty.util.HashingStrategy;
import io.netty.handler.codec.Headers;
import io.netty.util.internal.StringUtil;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
import static io.netty.util.internal.StringUtil.COMMA;
@ -75,6 +77,50 @@ public class CombinedHttpHeaders extends DefaultHttpHeaders {
super(nameHashingStrategy, valueConverter, nameValidator);
}
@Override
public CombinedHttpHeadersImpl add(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
// Override the fast-copy mechanism used by DefaultHeaders
if (headers == this) {
throw new IllegalArgumentException("can't add to itself.");
}
if (headers instanceof CombinedHttpHeadersImpl) {
if (isEmpty()) {
// Can use the fast underlying copy
addImpl(headers);
} else {
// Values are already escaped so don't escape again
for (Map.Entry<? extends CharSequence, ? extends CharSequence> header : headers) {
addEscapedValue(header.getKey(), header.getValue());
}
}
} else {
for (Map.Entry<? extends CharSequence, ? extends CharSequence> header : headers) {
add(header.getKey(), header.getValue());
}
}
return this;
}
@Override
public CombinedHttpHeadersImpl set(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
if (headers == this) {
return this;
}
clear();
return add(headers);
}
@Override
public CombinedHttpHeadersImpl setAll(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
if (headers == this) {
return this;
}
for (CharSequence key : headers.names()) {
remove(key);
}
return add(headers);
}
@Override
public CombinedHttpHeadersImpl add(CharSequence name, CharSequence value) {
return addEscapedValue(name, StringUtil.escapeCsv(value));

View File

@ -15,12 +15,14 @@
*/
package io.netty.handler.codec.http;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.http.HttpHeadersTestUtils.HeaderValue;
import org.junit.Test;
import java.util.Collections;
import static io.netty.util.AsciiString.contentEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class CombinedHttpHeadersTest {
@ -41,6 +43,60 @@ public class CombinedHttpHeadersTest {
assertCsvValues(headers, HeaderValue.FIVE);
}
@Test
public void addCombinedHeadersWhenEmpty() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
final CombinedHttpHeaders otherHeaders = newCombinedHttpHeaders();
otherHeaders.add(HEADER_NAME, "a");
otherHeaders.add(HEADER_NAME, "b");
headers.add(otherHeaders);
assertEquals("a,b", headers.get(HEADER_NAME).toString());
}
@Test
public void addCombinedHeadersWhenNotEmpty() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, "a");
final CombinedHttpHeaders otherHeaders = newCombinedHttpHeaders();
otherHeaders.add(HEADER_NAME, "b");
otherHeaders.add(HEADER_NAME, "c");
headers.add(otherHeaders);
assertEquals("a,b,c", headers.get(HEADER_NAME).toString());
}
@Test
public void setCombinedHeadersWhenNotEmpty() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, "a");
final CombinedHttpHeaders otherHeaders = newCombinedHttpHeaders();
otherHeaders.add(HEADER_NAME, "b");
otherHeaders.add(HEADER_NAME, "c");
headers.set(otherHeaders);
assertEquals("b,c", headers.get(HEADER_NAME).toString());
}
@Test
public void addUncombinedHeaders() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, "a");
final DefaultHttpHeaders otherHeaders = new DefaultHttpHeaders();
otherHeaders.add(HEADER_NAME, "b");
otherHeaders.add(HEADER_NAME, "c");
headers.add(otherHeaders);
assertEquals("a,b,c", headers.get(HEADER_NAME).toString());
}
@Test
public void setUncombinedHeaders() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, "a");
final DefaultHttpHeaders otherHeaders = new DefaultHttpHeaders();
otherHeaders.add(HEADER_NAME, "b");
otherHeaders.add(HEADER_NAME, "c");
headers.set(otherHeaders);
assertEquals("b,c", headers.get(HEADER_NAME).toString());
}
@Test
public void addCharSequencesCsvWithValueContainingComma() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();

View File

@ -316,7 +316,6 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
@Override
public T addObject(K name, Iterable<?> values) {
checkNotNull(values, "values");
for (Object value : values) {
addObject(name, value);
}
@ -325,7 +324,6 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
@Override
public T addObject(K name, Object... values) {
checkNotNull(values, "values");
for (int i = 0; i < values.length; i++) {
addObject(name, values[i]);
}
@ -379,25 +377,39 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
@Override
public T add(Headers<? extends K, ? extends V, ?> headers) {
checkNotNull(headers, "headers");
if (headers == this) {
throw new IllegalArgumentException("can't add to itself.");
}
addImpl(headers);
return thisT();
}
protected void addImpl(Headers<? extends K, ? extends V, ?> headers) {
if (headers instanceof DefaultHeaders) {
@SuppressWarnings("unchecked")
DefaultHeaders<K, V, T> defaultHeaders = (DefaultHeaders<K, V, T>) headers;
HeaderEntry<K, V> e = defaultHeaders.head.after;
while (e != defaultHeaders.head) {
add(e.key, e.value);
e = e.after;
final DefaultHeaders<? extends K, ? extends V, T> defaultHeaders =
(DefaultHeaders<? extends K, ? extends V, T>) headers;
HeaderEntry<? extends K, ? extends V> e = defaultHeaders.head.after;
if (defaultHeaders.hashingStrategy == hashingStrategy &&
defaultHeaders.nameValidator == nameValidator) {
// Fastest copy
while (e != defaultHeaders.head) {
add0(e.hash, index(e.hash), e.key, e.value);
e = e.after;
}
} else {
// Fast copy
while (e != defaultHeaders.head) {
add(e.key, e.value);
e = e.after;
}
}
return thisT();
} else {
// Slow copy
for (Entry<? extends K, ? extends V> header : headers) {
add(header.getKey(), header.getValue());
}
}
return thisT();
}
@Override
@ -459,7 +471,6 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
@Override
public T setObject(K name, Iterable<?> values) {
nameValidator.validateName(name);
checkNotNull(values, "values");
int h = hashingStrategy.hashCode(name);
int i = index(h);
@ -478,7 +489,6 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
@Override
public T setObject(K name, Object... values) {
nameValidator.validateName(name);
checkNotNull(values, "values");
int h = hashingStrategy.hashCode(name);
int i = index(h);
@ -541,36 +551,20 @@ public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers
@Override
public T set(Headers<? extends K, ? extends V, ?> headers) {
checkNotNull(headers, "headers");
if (headers == this) {
return thisT();
}
clear();
if (headers instanceof DefaultHeaders) {
@SuppressWarnings("unchecked")
DefaultHeaders<K, V, T> defaultHeaders = (DefaultHeaders<K, V, T>) headers;
HeaderEntry<K, V> e = defaultHeaders.head.after;
while (e != defaultHeaders.head) {
add(e.key, e.value);
e = e.after;
}
} else {
add(headers);
if (headers != this) {
clear();
addImpl(headers);
}
return thisT();
}
@Override
public T setAll(Headers<? extends K, ? extends V, ?> headers) {
checkNotNull(headers, "headers");
if (headers == this) {
return thisT();
}
for (K key : headers.names()) {
remove(key);
}
for (Entry<? extends K, ? extends V> entry : headers) {
add(entry.getKey(), entry.getValue());
if (headers != this) {
for (K key : headers.names()) {
remove(key);
}
addImpl(headers);
}
return thisT();
}

View File

@ -152,6 +152,59 @@ public class DefaultHeadersTest {
assertFalse(headers.contains(of("name"), of("value1")));
}
@Test
public void testCopy() throws Exception {
TestDefaultHeaders headers = newInstance();
headers.addBoolean(of("boolean"), true);
headers.addLong(of("long"), Long.MAX_VALUE);
headers.addInt(of("int"), Integer.MIN_VALUE);
headers.addShort(of("short"), Short.MAX_VALUE);
headers.addChar(of("char"), Character.MAX_VALUE);
headers.addByte(of("byte"), Byte.MAX_VALUE);
headers.addDouble(of("double"), Double.MAX_VALUE);
headers.addFloat(of("float"), Float.MAX_VALUE);
long millis = System.currentTimeMillis();
headers.addTimeMillis(of("millis"), millis);
headers.addObject(of("object"), "Hello World");
headers.add(of("name"), of("value"));
headers = newInstance().add(headers);
assertTrue(headers.containsBoolean(of("boolean"), true));
assertFalse(headers.containsBoolean(of("boolean"), false));
assertTrue(headers.containsLong(of("long"), Long.MAX_VALUE));
assertFalse(headers.containsLong(of("long"), Long.MIN_VALUE));
assertTrue(headers.containsInt(of("int"), Integer.MIN_VALUE));
assertFalse(headers.containsInt(of("int"), Integer.MAX_VALUE));
assertTrue(headers.containsShort(of("short"), Short.MAX_VALUE));
assertFalse(headers.containsShort(of("short"), Short.MIN_VALUE));
assertTrue(headers.containsChar(of("char"), Character.MAX_VALUE));
assertFalse(headers.containsChar(of("char"), Character.MIN_VALUE));
assertTrue(headers.containsByte(of("byte"), Byte.MAX_VALUE));
assertFalse(headers.containsLong(of("byte"), Byte.MIN_VALUE));
assertTrue(headers.containsDouble(of("double"), Double.MAX_VALUE));
assertFalse(headers.containsDouble(of("double"), Double.MIN_VALUE));
assertTrue(headers.containsFloat(of("float"), Float.MAX_VALUE));
assertFalse(headers.containsFloat(of("float"), Float.MIN_VALUE));
assertTrue(headers.containsTimeMillis(of("millis"), millis));
// This test doesn't work on midnight, January 1, 1970 UTC
assertFalse(headers.containsTimeMillis(of("millis"), 0));
assertTrue(headers.containsObject(of("object"), "Hello World"));
assertFalse(headers.containsObject(of("object"), ""));
assertTrue(headers.contains(of("name"), of("value")));
assertFalse(headers.contains(of("name"), of("value1")));
}
@Test
public void canMixConvertedAndNormalValues() {
TestDefaultHeaders headers = newInstance();

View File

@ -15,12 +15,14 @@
*/
package io.netty.microbench.headers;
import io.netty.handler.codec.Headers;
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 org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
@ -34,43 +36,78 @@ import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Threads(1)
@State(Scope.Benchmark)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@Fork(1)
@Warmup(iterations = 10)
@Measurement(iterations = 10)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class HeadersBenchmark extends AbstractMicrobenchmark {
private static String toHttpName(String name) {
return (name.startsWith(":")) ? name.substring(1) : name;
}
private static String toHttp2Name(String name) {
name = name.toLowerCase();
return (name.equals("host")) ? "xhost" : name;
}
@Param
ExampleHeaders.HeaderExample exampleHeader;
AsciiString[] httpNames;
AsciiString[] http2Names;
AsciiString[] httpValues;
DefaultHttpHeaders httpHeaders;
DefaultHttp2Headers http2Headers;
DefaultHttpHeaders emptyHttpHeaders;
DefaultHttp2Headers emptyHttp2Headers;
DefaultHttpHeaders emptyHttpHeadersNoValidate;
DefaultHttp2Headers emptyHttp2HeadersNoValidate;
SlowHeaders slowHttp2Headers;
@Setup(Level.Trial)
public void setup() {
Map<String, String> headers = ExampleHeaders.EXAMPLES.get(exampleHeader);
httpNames = new AsciiString[headers.size()];
http2Names = new AsciiString[headers.size()];
httpValues = new AsciiString[headers.size()];
httpHeaders = new DefaultHttpHeaders(false);
http2Headers = new DefaultHttp2Headers(false);
int idx = 0;
for (Map.Entry<String, String> header : headers.entrySet()) {
String name = header.getKey();
String httpName = toHttpName(name);
String http2Name = toHttp2Name(name);
String value = header.getValue();
httpNames[idx] = new AsciiString(name);
httpNames[idx] = new AsciiString(httpName);
http2Names[idx] = new AsciiString(http2Name);
httpValues[idx] = new AsciiString(value);
httpHeaders.add(httpNames[idx], httpValues[idx]);
http2Headers.add(http2Names[idx], httpValues[idx]);
idx++;
httpHeaders.add(new AsciiString(name), new AsciiString(value));
http2Headers.add(new AsciiString(name), new AsciiString(value));
}
slowHttp2Headers = new SlowHeaders(http2Headers);
emptyHttpHeaders = new DefaultHttpHeaders(true);
emptyHttp2Headers = new DefaultHttp2Headers(true);
emptyHttpHeadersNoValidate = new DefaultHttpHeaders(false);
emptyHttp2HeadersNoValidate = new DefaultHttp2Headers(false);
}
@Setup(Level.Invocation)
public void setupEmptyHeaders() {
emptyHttpHeaders.clear();
emptyHttp2Headers .clear();
emptyHttpHeadersNoValidate.clear();
emptyHttp2HeadersNoValidate.clear();
}
@Benchmark
@ -111,7 +148,7 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void http2Remove(Blackhole bh) {
for (AsciiString name : httpNames) {
for (AsciiString name : http2Names) {
bh.consume(http2Headers.remove(name));
}
}
@ -119,7 +156,7 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void http2Get(Blackhole bh) {
for (AsciiString name : httpNames) {
for (AsciiString name : http2Names) {
bh.consume(http2Headers.get(name));
}
}
@ -128,8 +165,8 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
@BenchmarkMode(Mode.AverageTime)
public DefaultHttp2Headers http2Put() {
DefaultHttp2Headers headers = new DefaultHttp2Headers(false);
for (int i = 0; i < httpNames.length; i++) {
headers.add(httpNames[i], httpValues[i]);
for (int i = 0; i < http2Names.length; i++) {
headers.add(http2Names[i], httpValues[i]);
}
return headers;
}
@ -141,4 +178,540 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
bh.consume(entry);
}
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void httpAddAllFastest(Blackhole bh) {
bh.consume(emptyHttpHeadersNoValidate.add(httpHeaders));
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void httpAddAllFast(Blackhole bh) {
bh.consume(emptyHttpHeaders.add(httpHeaders));
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void http2AddAllFastest(Blackhole bh) {
bh.consume(emptyHttp2HeadersNoValidate.add(http2Headers));
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void http2AddAllFast(Blackhole bh) {
bh.consume(emptyHttp2Headers.add(http2Headers));
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void http2AddAllSlow(Blackhole bh) {
bh.consume(emptyHttp2Headers.add(slowHttp2Headers));
}
private static final class SlowHeaders implements Headers<CharSequence, CharSequence, SlowHeaders> {
private final Headers<CharSequence, CharSequence, ? extends Headers> delegate;
private SlowHeaders(Headers<CharSequence, CharSequence, ? extends Headers> delegate) {
this.delegate = delegate;
}
@Override
public CharSequence get(CharSequence name) {
return delegate.get(name);
}
@Override
public CharSequence get(CharSequence name, CharSequence defaultValue) {
return delegate.get(name, defaultValue);
}
@Override
public CharSequence getAndRemove(CharSequence name) {
return delegate.getAndRemove(name);
}
@Override
public CharSequence getAndRemove(CharSequence name, CharSequence defaultValue) {
return delegate.getAndRemove(name, defaultValue);
}
@Override
public List<CharSequence> getAll(CharSequence name) {
return delegate.getAll(name);
}
@Override
public List<CharSequence> getAllAndRemove(CharSequence name) {
return delegate.getAllAndRemove(name);
}
@Override
public Boolean getBoolean(CharSequence name) {
return delegate.getBoolean(name);
}
@Override
public boolean getBoolean(CharSequence name, boolean defaultValue) {
return delegate.getBoolean(name, defaultValue);
}
@Override
public Byte getByte(CharSequence name) {
return delegate.getByte(name);
}
@Override
public byte getByte(CharSequence name, byte defaultValue) {
return delegate.getByte(name, defaultValue);
}
@Override
public Character getChar(CharSequence name) {
return delegate.getChar(name);
}
@Override
public char getChar(CharSequence name, char defaultValue) {
return delegate.getChar(name, defaultValue);
}
@Override
public Short getShort(CharSequence name) {
return delegate.getShort(name);
}
@Override
public short getShort(CharSequence name, short defaultValue) {
return delegate.getShort(name, defaultValue);
}
@Override
public Integer getInt(CharSequence name) {
return delegate.getInt(name);
}
@Override
public int getInt(CharSequence name, int defaultValue) {
return delegate.getInt(name, defaultValue);
}
@Override
public Long getLong(CharSequence name) {
return delegate.getLong(name);
}
@Override
public long getLong(CharSequence name, long defaultValue) {
return delegate.getLong(name, defaultValue);
}
@Override
public Float getFloat(CharSequence name) {
return delegate.getFloat(name);
}
@Override
public float getFloat(CharSequence name, float defaultValue) {
return delegate.getFloat(name, defaultValue);
}
@Override
public Double getDouble(CharSequence name) {
return delegate.getDouble(name);
}
@Override
public double getDouble(CharSequence name, double defaultValue) {
return delegate.getDouble(name, defaultValue);
}
@Override
public Long getTimeMillis(CharSequence name) {
return delegate.getTimeMillis(name);
}
@Override
public long getTimeMillis(CharSequence name, long defaultValue) {
return delegate.getTimeMillis(name, defaultValue);
}
@Override
public Boolean getBooleanAndRemove(CharSequence name) {
return delegate.getBooleanAndRemove(name);
}
@Override
public boolean getBooleanAndRemove(CharSequence name, boolean defaultValue) {
return delegate.getBooleanAndRemove(name, defaultValue);
}
@Override
public Byte getByteAndRemove(CharSequence name) {
return delegate.getByteAndRemove(name);
}
@Override
public byte getByteAndRemove(CharSequence name, byte defaultValue) {
return delegate.getByteAndRemove(name, defaultValue);
}
@Override
public Character getCharAndRemove(CharSequence name) {
return delegate.getCharAndRemove(name);
}
@Override
public char getCharAndRemove(CharSequence name, char defaultValue) {
return delegate.getCharAndRemove(name, defaultValue);
}
@Override
public Short getShortAndRemove(CharSequence name) {
return delegate.getShortAndRemove(name);
}
@Override
public short getShortAndRemove(CharSequence name, short defaultValue) {
return delegate.getShortAndRemove(name, defaultValue);
}
@Override
public Integer getIntAndRemove(CharSequence name) {
return delegate.getIntAndRemove(name);
}
@Override
public int getIntAndRemove(CharSequence name, int defaultValue) {
return delegate.getIntAndRemove(name, defaultValue);
}
@Override
public Long getLongAndRemove(CharSequence name) {
return delegate.getLongAndRemove(name);
}
@Override
public long getLongAndRemove(CharSequence name, long defaultValue) {
return delegate.getLongAndRemove(name, defaultValue);
}
@Override
public Float getFloatAndRemove(CharSequence name) {
return delegate.getFloatAndRemove(name);
}
@Override
public float getFloatAndRemove(CharSequence name, float defaultValue) {
return delegate.getFloatAndRemove(name, defaultValue);
}
@Override
public Double getDoubleAndRemove(CharSequence name) {
return delegate.getDoubleAndRemove(name);
}
@Override
public double getDoubleAndRemove(CharSequence name, double defaultValue) {
return delegate.getDoubleAndRemove(name, defaultValue);
}
@Override
public Long getTimeMillisAndRemove(CharSequence name) {
return delegate.getTimeMillisAndRemove(name);
}
@Override
public long getTimeMillisAndRemove(CharSequence name, long defaultValue) {
return delegate.getTimeMillisAndRemove(name, defaultValue);
}
@Override
public boolean contains(CharSequence name) {
return delegate.contains(name);
}
@Override
public boolean contains(CharSequence name, CharSequence value) {
return delegate.contains(name, value);
}
@Override
public boolean containsObject(CharSequence name, Object value) {
return delegate.containsObject(name, value);
}
@Override
public boolean containsBoolean(CharSequence name, boolean value) {
return delegate.containsBoolean(name, value);
}
@Override
public boolean containsByte(CharSequence name, byte value) {
return delegate.containsByte(name, value);
}
@Override
public boolean containsChar(CharSequence name, char value) {
return delegate.containsChar(name, value);
}
@Override
public boolean containsShort(CharSequence name, short value) {
return delegate.containsShort(name, value);
}
@Override
public boolean containsInt(CharSequence name, int value) {
return delegate.containsInt(name, value);
}
@Override
public boolean containsLong(CharSequence name, long value) {
return delegate.containsLong(name, value);
}
@Override
public boolean containsFloat(CharSequence name, float value) {
return delegate.containsFloat(name, value);
}
@Override
public boolean containsDouble(CharSequence name, double value) {
return delegate.containsDouble(name, value);
}
@Override
public boolean containsTimeMillis(CharSequence name, long value) {
return delegate.containsTimeMillis(name, value);
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public Set<CharSequence> names() {
return delegate.names();
}
@Override
public SlowHeaders add(CharSequence name, CharSequence value) {
delegate.add(name, value);
return this;
}
@Override
public SlowHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
delegate.add(name, values);
return this;
}
@Override
public SlowHeaders add(CharSequence name, CharSequence... values) {
delegate.add(name, values);
return this;
}
@Override
public SlowHeaders addObject(CharSequence name, Object value) {
delegate.addObject(name, value);
return this;
}
@Override
public SlowHeaders addObject(CharSequence name, Iterable<?> values) {
delegate.addObject(name, values);
return this;
}
@Override
public SlowHeaders addObject(CharSequence name, Object... values) {
delegate.addObject(name, values);
return this;
}
@Override
public SlowHeaders addBoolean(CharSequence name, boolean value) {
delegate.addBoolean(name, value);
return this;
}
@Override
public SlowHeaders addByte(CharSequence name, byte value) {
delegate.addByte(name, value);
return this;
}
@Override
public SlowHeaders addChar(CharSequence name, char value) {
delegate.addChar(name, value);
return this;
}
@Override
public SlowHeaders addShort(CharSequence name, short value) {
delegate.addShort(name, value);
return this;
}
@Override
public SlowHeaders addInt(CharSequence name, int value) {
delegate.addInt(name, value);
return this;
}
@Override
public SlowHeaders addLong(CharSequence name, long value) {
delegate.addLong(name, value);
return this;
}
@Override
public SlowHeaders addFloat(CharSequence name, float value) {
delegate.addFloat(name, value);
return this;
}
@Override
public SlowHeaders addDouble(CharSequence name, double value) {
delegate.addDouble(name, value);
return this;
}
@Override
public SlowHeaders addTimeMillis(CharSequence name, long value) {
delegate.addTimeMillis(name, value);
return this;
}
@Override
public SlowHeaders add(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
delegate.add(headers);
return this;
}
@Override
public SlowHeaders set(CharSequence name, CharSequence value) {
delegate.set(name, value);
return this;
}
@Override
public SlowHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
delegate.set(name, values);
return this;
}
@Override
public SlowHeaders set(CharSequence name, CharSequence... values) {
delegate.set(name, values);
return this;
}
@Override
public SlowHeaders setObject(CharSequence name, Object value) {
delegate.setObject(name, value);
return this;
}
@Override
public SlowHeaders setObject(CharSequence name, Iterable<?> values) {
delegate.setObject(name, values);
return this;
}
@Override
public SlowHeaders setObject(CharSequence name, Object... values) {
delegate.setObject(name, values);
return this;
}
@Override
public SlowHeaders setBoolean(CharSequence name, boolean value) {
delegate.setBoolean(name, value);
return this;
}
@Override
public SlowHeaders setByte(CharSequence name, byte value) {
delegate.setByte(name, value);
return this;
}
@Override
public SlowHeaders setChar(CharSequence name, char value) {
delegate.setChar(name, value);
return this;
}
@Override
public SlowHeaders setShort(CharSequence name, short value) {
delegate.setShort(name, value);
return this;
}
@Override
public SlowHeaders setInt(CharSequence name, int value) {
delegate.setInt(name, value);
return this;
}
@Override
public SlowHeaders setLong(CharSequence name, long value) {
delegate.setLong(name, value);
return this;
}
@Override
public SlowHeaders setFloat(CharSequence name, float value) {
delegate.setFloat(name, value);
return this;
}
@Override
public SlowHeaders setDouble(CharSequence name, double value) {
delegate.setDouble(name, value);
return this;
}
@Override
public SlowHeaders setTimeMillis(CharSequence name, long value) {
delegate.setTimeMillis(name, value);
return this;
}
@Override
public SlowHeaders set(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
delegate.set(headers);
return this;
}
@Override
public SlowHeaders setAll(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
delegate.setAll(headers);
return this;
}
@Override
public boolean remove(CharSequence name) {
return delegate.remove(name);
}
@Override
public SlowHeaders clear() {
delegate.clear();
return this;
}
@Override
public Iterator<Entry<CharSequence, CharSequence>> iterator() {
return delegate.iterator();
}
}
}