From 6e108cb96a39725d7cebb7a3e8963347499b4512 Mon Sep 17 00:00:00 2001 From: Louis Ryan Date: Fri, 30 Oct 2015 13:24:46 -0700 Subject: [PATCH] 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 --- .../codec/http/CombinedHttpHeaders.java | 46 ++ .../codec/http/CombinedHttpHeadersTest.java | 56 ++ .../netty/handler/codec/DefaultHeaders.java | 66 +- .../handler/codec/DefaultHeadersTest.java | 53 ++ .../microbench/headers/HeadersBenchmark.java | 591 +++++++++++++++++- 5 files changed, 767 insertions(+), 45 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java index 6b95464035..2c63917607 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java @@ -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 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 header : headers) { + addEscapedValue(header.getKey(), header.getValue()); + } + } + } else { + for (Map.Entry header : headers) { + add(header.getKey(), header.getValue()); + } + } + return this; + } + + @Override + public CombinedHttpHeadersImpl set(Headers headers) { + if (headers == this) { + return this; + } + clear(); + return add(headers); + } + + @Override + public CombinedHttpHeadersImpl setAll(Headers 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)); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/CombinedHttpHeadersTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/CombinedHttpHeadersTest.java index ed01ea4c84..d786d30fe3 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/CombinedHttpHeadersTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/CombinedHttpHeadersTest.java @@ -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(); diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java index cc9b6a2db3..1c4a642b1f 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java @@ -316,7 +316,6 @@ public class DefaultHeaders> 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> 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> implements Headers @Override public T add(Headers headers) { - checkNotNull(headers, "headers"); if (headers == this) { throw new IllegalArgumentException("can't add to itself."); } + addImpl(headers); + return thisT(); + } + + protected void addImpl(Headers headers) { if (headers instanceof DefaultHeaders) { @SuppressWarnings("unchecked") - DefaultHeaders defaultHeaders = (DefaultHeaders) headers; - HeaderEntry e = defaultHeaders.head.after; - while (e != defaultHeaders.head) { - add(e.key, e.value); - e = e.after; + final DefaultHeaders defaultHeaders = + (DefaultHeaders) headers; + HeaderEntry 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 header : headers) { add(header.getKey(), header.getValue()); } } - return thisT(); } @Override @@ -459,7 +471,6 @@ public class DefaultHeaders> 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> 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> implements Headers @Override public T set(Headers headers) { - checkNotNull(headers, "headers"); - if (headers == this) { - return thisT(); - } - clear(); - if (headers instanceof DefaultHeaders) { - @SuppressWarnings("unchecked") - DefaultHeaders defaultHeaders = (DefaultHeaders) headers; - HeaderEntry 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 headers) { - checkNotNull(headers, "headers"); - if (headers == this) { - return thisT(); - } - for (K key : headers.names()) { - remove(key); - } - for (Entry entry : headers) { - add(entry.getKey(), entry.getValue()); + if (headers != this) { + for (K key : headers.names()) { + remove(key); + } + addImpl(headers); } return thisT(); } diff --git a/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java b/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java index ceee0b80be..54836b7d11 100644 --- a/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java +++ b/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java @@ -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(); diff --git a/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java b/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java index 7a0d0713e9..985f424198 100644 --- a/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java +++ b/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java @@ -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 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 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 { + private final Headers delegate; + private SlowHeaders(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 getAll(CharSequence name) { + return delegate.getAll(name); + } + + @Override + public List 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 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 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 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 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 headers) { + delegate.set(headers); + return this; + } + + @Override + public SlowHeaders setAll(Headers 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> iterator() { + return delegate.iterator(); + } + } }