aboutsummaryrefslogtreecommitdiff
path: root/okhttp
diff options
context:
space:
mode:
authorEric Gribkoff <ericgribkoff@google.com>2018-03-27 11:12:44 -0700
committerEric Gribkoff <ericgribkoff@google.com>2018-03-28 14:53:19 -0700
commitecc36ac96d40a8d329eacfe9b20b89b55734a0ba (patch)
tree6b2b83cddc88c4fe4c7153866332480feae1ece4 /okhttp
parent1efb69539930a49107d70318b0defc540f4632fa (diff)
downloadgrpc-grpc-java-ecc36ac96d40a8d329eacfe9b20b89b55734a0ba.tar.gz
okhttp: include tests in third_party/okhttp
Diffstat (limited to 'okhttp')
-rw-r--r--okhttp/BUILD.bazel2
-rw-r--r--okhttp/build.gradle7
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/CipherSuite.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/CipherSuite.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/ConnectionSpec.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/ConnectionSpec.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/DistinguishedNameParser.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/DistinguishedNameParser.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/OkHostnameVerifier.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/OkHostnameVerifier.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/OptionalMethod.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/OptionalMethod.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/Platform.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Platform.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/Protocol.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Protocol.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/TlsVersion.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/TlsVersion.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/Util.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Util.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/ErrorCode.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/ErrorCode.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/FrameReader.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/FrameReader.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/FrameWriter.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/FrameWriter.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Header.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Header.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/HeadersMode.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/HeadersMode.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Hpack.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Hpack.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Http2.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Http2.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Huffman.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Huffman.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Settings.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Settings.java)0
-rw-r--r--okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Variant.java (renamed from okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Variant.java)0
-rw-r--r--okhttp/third_party/okhttp/test/java/io/grpc/okhttp/internal/framed/HpackTest.java738
22 files changed, 745 insertions, 2 deletions
diff --git a/okhttp/BUILD.bazel b/okhttp/BUILD.bazel
index 75b672101..62a7afffc 100644
--- a/okhttp/BUILD.bazel
+++ b/okhttp/BUILD.bazel
@@ -1,7 +1,7 @@
java_library(
name = "okhttp",
srcs = glob([
- "third_party/okhttp/java/**/*.java",
+ "third_party/okhttp/main/java/**/*.java",
"src/main/java/**/*.java",
]),
resources = glob([
diff --git a/okhttp/build.gradle b/okhttp/build.gradle
index 8d61f7332..9328a74ce 100644
--- a/okhttp/build.gradle
+++ b/okhttp/build.gradle
@@ -14,7 +14,12 @@ dependencies {
project.sourceSets {
main {
java {
- srcDir "${projectDir}/third_party/okhttp/java"
+ srcDir "${projectDir}/third_party/okhttp/main/java"
+ }
+ }
+ test {
+ java {
+ srcDir "${projectDir}/third_party/okhttp/test/java"
}
}
}
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/CipherSuite.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/CipherSuite.java
index bc2a9b7f0..bc2a9b7f0 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/CipherSuite.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/CipherSuite.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/ConnectionSpec.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/ConnectionSpec.java
index 457e9c301..457e9c301 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/ConnectionSpec.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/ConnectionSpec.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/DistinguishedNameParser.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/DistinguishedNameParser.java
index 166b09794..166b09794 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/DistinguishedNameParser.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/DistinguishedNameParser.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/OkHostnameVerifier.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/OkHostnameVerifier.java
index b7b1d5367..b7b1d5367 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/OkHostnameVerifier.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/OkHostnameVerifier.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/OptionalMethod.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/OptionalMethod.java
index a93c8384f..a93c8384f 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/OptionalMethod.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/OptionalMethod.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Platform.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/Platform.java
index d6f88ba0e..d6f88ba0e 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Platform.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/Platform.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Protocol.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/Protocol.java
index 7b5a3ae76..7b5a3ae76 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Protocol.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/Protocol.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/TlsVersion.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/TlsVersion.java
index 548f4acbc..548f4acbc 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/TlsVersion.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/TlsVersion.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Util.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/Util.java
index 7501c441d..7501c441d 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/Util.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/Util.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/ErrorCode.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/ErrorCode.java
index 38a545930..38a545930 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/ErrorCode.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/ErrorCode.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/FrameReader.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/FrameReader.java
index 20c50f7cc..20c50f7cc 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/FrameReader.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/FrameReader.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/FrameWriter.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/FrameWriter.java
index 333e06c86..333e06c86 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/FrameWriter.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/FrameWriter.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Header.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Header.java
index 00081d489..00081d489 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Header.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Header.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/HeadersMode.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/HeadersMode.java
index 3826252f4..3826252f4 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/HeadersMode.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/HeadersMode.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Hpack.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Hpack.java
index 5db4f0996..5db4f0996 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Hpack.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Hpack.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Http2.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Http2.java
index 20eed3c3b..20eed3c3b 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Http2.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Http2.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Huffman.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Huffman.java
index 5eb4a88c2..5eb4a88c2 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Huffman.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Huffman.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Settings.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Settings.java
index 0d0ecce99..0d0ecce99 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Settings.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Settings.java
diff --git a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Variant.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Variant.java
index 2b707081b..2b707081b 100644
--- a/okhttp/third_party/okhttp/java/io/grpc/okhttp/internal/framed/Variant.java
+++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Variant.java
diff --git a/okhttp/third_party/okhttp/test/java/io/grpc/okhttp/internal/framed/HpackTest.java b/okhttp/third_party/okhttp/test/java/io/grpc/okhttp/internal/framed/HpackTest.java
new file mode 100644
index 000000000..e512582e9
--- /dev/null
+++ b/okhttp/third_party/okhttp/test/java/io/grpc/okhttp/internal/framed/HpackTest.java
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2013 Square, Inc.
+ *
+ * Licensed 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.grpc.okhttp.internal.framed;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import okio.Buffer;
+import okio.ByteString;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static okio.ByteString.decodeHex;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+@RunWith(JUnit4.class)
+public class HpackTest {
+
+ private static List<Header> headerEntries(String... elements) {
+ List<Header> result = new ArrayList<Header>(elements.length / 2);
+ for (int i = 0; i < elements.length; i += 2) {
+ result.add(new Header(elements[i], elements[i + 1]));
+ }
+ return result;
+ }
+
+ private final Buffer bytesIn = new Buffer();
+ private Hpack.Reader hpackReader;
+ private Buffer bytesOut = new Buffer();
+ private Hpack.Writer hpackWriter;
+
+ @Before public void reset() {
+ hpackReader = newReader(bytesIn);
+ hpackWriter = new Hpack.Writer(bytesOut);
+ }
+
+ /**
+ * Variable-length quantity special cases strings which are longer than 127
+ * bytes. Values such as cookies can be 4KiB, and should be possible to send.
+ *
+ * <p> http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-5.2
+ */
+ @Test public void largeHeaderValue() throws IOException {
+ char[] value = new char[4096];
+ Arrays.fill(value, '!');
+ List<Header> headerBlock = headerEntries("cookie", new String(value));
+
+ hpackWriter.writeHeaders(headerBlock);
+ bytesIn.writeAll(bytesOut);
+ hpackReader.readHeaders();
+
+ assertEquals(0, hpackReader.dynamicTableHeaderCount);
+
+ assertEquals(headerBlock, hpackReader.getAndResetHeaderList());
+ }
+
+ /**
+ * HPACK has a max header table size, which can be smaller than the max header message.
+ * Ensure the larger header content is not lost.
+ */
+ @Test public void tooLargeToHPackIsStillEmitted() throws IOException {
+ bytesIn.writeByte(0x00); // Literal indexed
+ bytesIn.writeByte(0x0a); // Literal name (len = 10)
+ bytesIn.writeUtf8("custom-key");
+
+ bytesIn.writeByte(0x0d); // Literal value (len = 13)
+ bytesIn.writeUtf8("custom-header");
+
+ hpackReader.headerTableSizeSetting(1);
+ hpackReader.readHeaders();
+
+ assertEquals(0, hpackReader.dynamicTableHeaderCount);
+
+ assertEquals(headerEntries("custom-key", "custom-header"), hpackReader.getAndResetHeaderList());
+ }
+
+ /** Oldest entries are evicted to support newer ones. */
+ @Test public void testEviction() throws IOException {
+ bytesIn.writeByte(0x40); // Literal indexed
+ bytesIn.writeByte(0x0a); // Literal name (len = 10)
+ bytesIn.writeUtf8("custom-foo");
+
+ bytesIn.writeByte(0x0d); // Literal value (len = 13)
+ bytesIn.writeUtf8("custom-header");
+
+ bytesIn.writeByte(0x40); // Literal indexed
+ bytesIn.writeByte(0x0a); // Literal name (len = 10)
+ bytesIn.writeUtf8("custom-bar");
+
+ bytesIn.writeByte(0x0d); // Literal value (len = 13)
+ bytesIn.writeUtf8("custom-header");
+
+ bytesIn.writeByte(0x40); // Literal indexed
+ bytesIn.writeByte(0x0a); // Literal name (len = 10)
+ bytesIn.writeUtf8("custom-baz");
+
+ bytesIn.writeByte(0x0d); // Literal value (len = 13)
+ bytesIn.writeUtf8("custom-header");
+
+ // Set to only support 110 bytes (enough for 2 headers).
+ hpackReader.headerTableSizeSetting(110);
+ hpackReader.readHeaders();
+
+ assertEquals(2, hpackReader.dynamicTableHeaderCount);
+
+ Header entry = hpackReader.dynamicTable[headerTableLength() - 1];
+ checkEntry(entry, "custom-bar", "custom-header", 55);
+
+ entry = hpackReader.dynamicTable[headerTableLength() - 2];
+ checkEntry(entry, "custom-baz", "custom-header", 55);
+
+ // Once a header field is decoded and added to the reconstructed header
+ // list, it cannot be removed from it. Hence, foo is here.
+ assertEquals(
+ headerEntries(
+ "custom-foo", "custom-header",
+ "custom-bar", "custom-header",
+ "custom-baz", "custom-header"),
+ hpackReader.getAndResetHeaderList());
+
+ // Simulate receiving a small settings frame, that implies eviction.
+ hpackReader.headerTableSizeSetting(55);
+ assertEquals(1, hpackReader.dynamicTableHeaderCount);
+ }
+
+ /** Header table backing array is initially 8 long, let's ensure it grows. */
+ @Test public void dynamicallyGrowsBeyond64Entries() throws IOException {
+ for (int i = 0; i < 256; i++) {
+ bytesIn.writeByte(0x40); // Literal indexed
+ bytesIn.writeByte(0x0a); // Literal name (len = 10)
+ bytesIn.writeUtf8("custom-foo");
+
+ bytesIn.writeByte(0x0d); // Literal value (len = 13)
+ bytesIn.writeUtf8("custom-header");
+ }
+
+ hpackReader.headerTableSizeSetting(16384); // Lots of headers need more room!
+ hpackReader.readHeaders();
+
+ assertEquals(256, hpackReader.dynamicTableHeaderCount);
+ }
+
+ @Test public void huffmanDecodingSupported() throws IOException {
+ bytesIn.writeByte(0x44); // == Literal indexed ==
+ // Indexed name (idx = 4) -> :path
+ bytesIn.writeByte(0x8c); // Literal value Huffman encoded 12 bytes
+ // decodes to www.example.com which is length 15
+ bytesIn.write(decodeHex("f1e3c2e5f23a6ba0ab90f4ff"));
+
+ hpackReader.readHeaders();
+
+ assertEquals(1, hpackReader.dynamicTableHeaderCount);
+ assertEquals(52, hpackReader.dynamicTableByteCount);
+
+ Header entry = hpackReader.dynamicTable[headerTableLength() - 1];
+ checkEntry(entry, ":path", "www.example.com", 52);
+ }
+
+ /**
+ * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C.2.1
+ */
+ @Test public void readLiteralHeaderFieldWithIndexing() throws IOException {
+ bytesIn.writeByte(0x40); // Literal indexed
+ bytesIn.writeByte(0x0a); // Literal name (len = 10)
+ bytesIn.writeUtf8("custom-key");
+
+ bytesIn.writeByte(0x0d); // Literal value (len = 13)
+ bytesIn.writeUtf8("custom-header");
+
+ hpackReader.readHeaders();
+
+ assertEquals(1, hpackReader.dynamicTableHeaderCount);
+ assertEquals(55, hpackReader.dynamicTableByteCount);
+
+ Header entry = hpackReader.dynamicTable[headerTableLength() - 1];
+ checkEntry(entry, "custom-key", "custom-header", 55);
+
+ assertEquals(headerEntries("custom-key", "custom-header"), hpackReader.getAndResetHeaderList());
+ }
+
+ /**
+ * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C.2.2
+ */
+ @Test public void literalHeaderFieldWithoutIndexingIndexedName() throws IOException {
+ List<Header> headerBlock = headerEntries(":path", "/sample/path");
+
+ bytesIn.writeByte(0x04); // == Literal not indexed ==
+ // Indexed name (idx = 4) -> :path
+ bytesIn.writeByte(0x0c); // Literal value (len = 12)
+ bytesIn.writeUtf8("/sample/path");
+
+ hpackWriter.writeHeaders(headerBlock);
+ assertEquals(bytesIn, bytesOut);
+
+ hpackReader.readHeaders();
+
+ assertEquals(0, hpackReader.dynamicTableHeaderCount);
+
+ assertEquals(headerBlock, hpackReader.getAndResetHeaderList());
+ }
+
+ @Test public void literalHeaderFieldWithoutIndexingNewName() throws IOException {
+ List<Header> headerBlock = headerEntries("custom-key", "custom-header");
+
+ bytesIn.writeByte(0x00); // Not indexed
+ bytesIn.writeByte(0x0a); // Literal name (len = 10)
+ bytesIn.writeUtf8("custom-key");
+
+ bytesIn.writeByte(0x0d); // Literal value (len = 13)
+ bytesIn.writeUtf8("custom-header");
+
+ hpackWriter.writeHeaders(headerBlock);
+ assertEquals(bytesIn, bytesOut);
+
+ hpackReader.readHeaders();
+
+ assertEquals(0, hpackReader.dynamicTableHeaderCount);
+
+ assertEquals(headerBlock, hpackReader.getAndResetHeaderList());
+ }
+
+ @Test public void literalHeaderFieldNeverIndexedIndexedName() throws IOException {
+ bytesIn.writeByte(0x14); // == Literal never indexed ==
+ // Indexed name (idx = 4) -> :path
+ bytesIn.writeByte(0x0c); // Literal value (len = 12)
+ bytesIn.writeUtf8("/sample/path");
+
+ hpackReader.readHeaders();
+
+ assertEquals(0, hpackReader.dynamicTableHeaderCount);
+
+ assertEquals(headerEntries(":path", "/sample/path"), hpackReader.getAndResetHeaderList());
+ }
+
+ @Test public void literalHeaderFieldNeverIndexedNewName() throws IOException {
+ bytesIn.writeByte(0x10); // Never indexed
+ bytesIn.writeByte(0x0a); // Literal name (len = 10)
+ bytesIn.writeUtf8("custom-key");
+
+ bytesIn.writeByte(0x0d); // Literal value (len = 13)
+ bytesIn.writeUtf8("custom-header");
+
+ hpackReader.readHeaders();
+
+ assertEquals(0, hpackReader.dynamicTableHeaderCount);
+
+ assertEquals(headerEntries("custom-key", "custom-header"), hpackReader.getAndResetHeaderList());
+ }
+
+ @Test public void staticHeaderIsNotCopiedIntoTheIndexedTable() throws IOException {
+ bytesIn.writeByte(0x82); // == Indexed - Add ==
+ // idx = 2 -> :method: GET
+
+ hpackReader.readHeaders();
+
+ assertEquals(0, hpackReader.dynamicTableHeaderCount);
+ assertEquals(0, hpackReader.dynamicTableByteCount);
+
+ assertEquals(null, hpackReader.dynamicTable[headerTableLength() - 1]);
+
+ assertEquals(headerEntries(":method", "GET"), hpackReader.getAndResetHeaderList());
+ }
+
+ // Example taken from twitter/hpack DecoderTest.testUnusedIndex
+ @Test public void readIndexedHeaderFieldIndex0() throws IOException {
+ bytesIn.writeByte(0x80); // == Indexed - Add idx = 0
+
+ try {
+ hpackReader.readHeaders();
+ fail();
+ } catch (IOException e) {
+ assertEquals("index == 0", e.getMessage());
+ }
+ }
+
+ // Example taken from twitter/hpack DecoderTest.testIllegalIndex
+ @Test public void readIndexedHeaderFieldTooLargeIndex() throws IOException {
+ bytesIn.writeShort(0xff00); // == Indexed - Add idx = 127
+
+ try {
+ hpackReader.readHeaders();
+ fail();
+ } catch (IOException e) {
+ assertEquals("Header index too large 127", e.getMessage());
+ }
+ }
+
+ // Example taken from twitter/hpack DecoderTest.testInsidiousIndex
+ @Test public void readIndexedHeaderFieldInsidiousIndex() throws IOException {
+ bytesIn.writeByte(0xff); // == Indexed - Add ==
+ bytesIn.write(decodeHex("8080808008")); // idx = -2147483521
+
+ try {
+ hpackReader.readHeaders();
+ fail();
+ } catch (IOException e) {
+ assertEquals("Header index too large -2147483521", e.getMessage());
+ }
+ }
+
+ // Example taken from twitter/hpack DecoderTest.testHeaderTableSizeUpdate
+ @Test public void minMaxHeaderTableSize() throws IOException {
+ bytesIn.writeByte(0x20);
+ hpackReader.readHeaders();
+
+ assertEquals(0, hpackReader.maxDynamicTableByteCount());
+
+ bytesIn.writeByte(0x3f); // encode size 4096
+ bytesIn.writeByte(0xe1);
+ bytesIn.writeByte(0x1f);
+ hpackReader.readHeaders();
+
+ assertEquals(4096, hpackReader.maxDynamicTableByteCount());
+ }
+
+ // Example taken from twitter/hpack DecoderTest.testIllegalHeaderTableSizeUpdate
+ @Test public void cannotSetTableSizeLargerThanSettingsValue() throws IOException {
+ bytesIn.writeByte(0x3f); // encode size 4097
+ bytesIn.writeByte(0xe2);
+ bytesIn.writeByte(0x1f);
+
+ try {
+ hpackReader.readHeaders();
+ fail();
+ } catch (IOException e) {
+ assertEquals("Invalid dynamic table size update 4097", e.getMessage());
+ }
+ }
+
+ // Example taken from twitter/hpack DecoderTest.testInsidiousMaxHeaderSize
+ @Test public void readHeaderTableStateChangeInsidiousMaxHeaderByteCount() throws IOException {
+ bytesIn.writeByte(0x3f);
+ bytesIn.write(decodeHex("e1ffffff07")); // count = -2147483648
+
+ try {
+ hpackReader.readHeaders();
+ fail();
+ } catch (IOException e) {
+ assertEquals("Invalid dynamic table size update -2147483648", e.getMessage());
+ }
+ }
+
+ /**
+ * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C.2.4
+ */
+ @Test public void readIndexedHeaderFieldFromStaticTableWithoutBuffering() throws IOException {
+ bytesIn.writeByte(0x82); // == Indexed - Add ==
+ // idx = 2 -> :method: GET
+
+ hpackReader.headerTableSizeSetting(0); // SETTINGS_HEADER_TABLE_SIZE == 0
+ hpackReader.readHeaders();
+
+ // Not buffered in header table.
+ assertEquals(0, hpackReader.dynamicTableHeaderCount);
+
+ assertEquals(headerEntries(":method", "GET"), hpackReader.getAndResetHeaderList());
+ }
+
+ /**
+ * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C.2
+ */
+ @Test public void readRequestExamplesWithoutHuffman() throws IOException {
+ firstRequestWithoutHuffman();
+ hpackReader.readHeaders();
+ checkReadFirstRequestWithoutHuffman();
+
+ secondRequestWithoutHuffman();
+ hpackReader.readHeaders();
+ checkReadSecondRequestWithoutHuffman();
+
+ thirdRequestWithoutHuffman();
+ hpackReader.readHeaders();
+ checkReadThirdRequestWithoutHuffman();
+ }
+
+ private void firstRequestWithoutHuffman() {
+ bytesIn.writeByte(0x82); // == Indexed - Add ==
+ // idx = 2 -> :method: GET
+ bytesIn.writeByte(0x86); // == Indexed - Add ==
+ // idx = 7 -> :scheme: http
+ bytesIn.writeByte(0x84); // == Indexed - Add ==
+ // idx = 6 -> :path: /
+ bytesIn.writeByte(0x41); // == Literal indexed ==
+ // Indexed name (idx = 4) -> :authority
+ bytesIn.writeByte(0x0f); // Literal value (len = 15)
+ bytesIn.writeUtf8("www.example.com");
+ }
+
+ private void checkReadFirstRequestWithoutHuffman() {
+ assertEquals(1, hpackReader.dynamicTableHeaderCount);
+
+ // [ 1] (s = 57) :authority: www.example.com
+ Header entry = hpackReader.dynamicTable[headerTableLength() - 1];
+ checkEntry(entry, ":authority", "www.example.com", 57);
+
+ // Table size: 57
+ assertEquals(57, hpackReader.dynamicTableByteCount);
+
+ // Decoded header list:
+ assertEquals(headerEntries(
+ ":method", "GET",
+ ":scheme", "http",
+ ":path", "/",
+ ":authority", "www.example.com"), hpackReader.getAndResetHeaderList());
+ }
+
+ private void secondRequestWithoutHuffman() {
+ bytesIn.writeByte(0x82); // == Indexed - Add ==
+ // idx = 2 -> :method: GET
+ bytesIn.writeByte(0x86); // == Indexed - Add ==
+ // idx = 7 -> :scheme: http
+ bytesIn.writeByte(0x84); // == Indexed - Add ==
+ // idx = 6 -> :path: /
+ bytesIn.writeByte(0xbe); // == Indexed - Add ==
+ // Indexed name (idx = 62) -> :authority: www.example.com
+ bytesIn.writeByte(0x58); // == Literal indexed ==
+ // Indexed name (idx = 24) -> cache-control
+ bytesIn.writeByte(0x08); // Literal value (len = 8)
+ bytesIn.writeUtf8("no-cache");
+ }
+
+ private void checkReadSecondRequestWithoutHuffman() {
+ assertEquals(2, hpackReader.dynamicTableHeaderCount);
+
+ // [ 1] (s = 53) cache-control: no-cache
+ Header entry = hpackReader.dynamicTable[headerTableLength() - 2];
+ checkEntry(entry, "cache-control", "no-cache", 53);
+
+ // [ 2] (s = 57) :authority: www.example.com
+ entry = hpackReader.dynamicTable[headerTableLength() - 1];
+ checkEntry(entry, ":authority", "www.example.com", 57);
+
+ // Table size: 110
+ assertEquals(110, hpackReader.dynamicTableByteCount);
+
+ // Decoded header list:
+ assertEquals(headerEntries(
+ ":method", "GET",
+ ":scheme", "http",
+ ":path", "/",
+ ":authority", "www.example.com",
+ "cache-control", "no-cache"), hpackReader.getAndResetHeaderList());
+ }
+
+ private void thirdRequestWithoutHuffman() {
+ bytesIn.writeByte(0x82); // == Indexed - Add ==
+ // idx = 2 -> :method: GET
+ bytesIn.writeByte(0x87); // == Indexed - Add ==
+ // idx = 7 -> :scheme: http
+ bytesIn.writeByte(0x85); // == Indexed - Add ==
+ // idx = 5 -> :path: /index.html
+ bytesIn.writeByte(0xbf); // == Indexed - Add ==
+ // Indexed name (idx = 63) -> :authority: www.example.com
+ bytesIn.writeByte(0x40); // Literal indexed
+ bytesIn.writeByte(0x0a); // Literal name (len = 10)
+ bytesIn.writeUtf8("custom-key");
+ bytesIn.writeByte(0x0c); // Literal value (len = 12)
+ bytesIn.writeUtf8("custom-value");
+ }
+
+ private void checkReadThirdRequestWithoutHuffman() {
+ assertEquals(3, hpackReader.dynamicTableHeaderCount);
+
+ // [ 1] (s = 54) custom-key: custom-value
+ Header entry = hpackReader.dynamicTable[headerTableLength() - 3];
+ checkEntry(entry, "custom-key", "custom-value", 54);
+
+ // [ 2] (s = 53) cache-control: no-cache
+ entry = hpackReader.dynamicTable[headerTableLength() - 2];
+ checkEntry(entry, "cache-control", "no-cache", 53);
+
+ // [ 3] (s = 57) :authority: www.example.com
+ entry = hpackReader.dynamicTable[headerTableLength() - 1];
+ checkEntry(entry, ":authority", "www.example.com", 57);
+
+ // Table size: 164
+ assertEquals(164, hpackReader.dynamicTableByteCount);
+
+ // Decoded header list:
+ assertEquals(headerEntries(
+ ":method", "GET",
+ ":scheme", "https",
+ ":path", "/index.html",
+ ":authority", "www.example.com",
+ "custom-key", "custom-value"), hpackReader.getAndResetHeaderList());
+ }
+
+ /**
+ * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#appendix-C.4
+ */
+ @Test public void readRequestExamplesWithHuffman() throws IOException {
+ firstRequestWithHuffman();
+ hpackReader.readHeaders();
+ checkReadFirstRequestWithHuffman();
+
+ secondRequestWithHuffman();
+ hpackReader.readHeaders();
+ checkReadSecondRequestWithHuffman();
+
+ thirdRequestWithHuffman();
+ hpackReader.readHeaders();
+ checkReadThirdRequestWithHuffman();
+ }
+
+ private void firstRequestWithHuffman() {
+ bytesIn.writeByte(0x82); // == Indexed - Add ==
+ // idx = 2 -> :method: GET
+ bytesIn.writeByte(0x86); // == Indexed - Add ==
+ // idx = 6 -> :scheme: http
+ bytesIn.writeByte(0x84); // == Indexed - Add ==
+ // idx = 4 -> :path: /
+ bytesIn.writeByte(0x41); // == Literal indexed ==
+ // Indexed name (idx = 1) -> :authority
+ bytesIn.writeByte(0x8c); // Literal value Huffman encoded 12 bytes
+ // decodes to www.example.com which is length 15
+ bytesIn.write(decodeHex("f1e3c2e5f23a6ba0ab90f4ff"));
+ }
+
+ private void checkReadFirstRequestWithHuffman() {
+ assertEquals(1, hpackReader.dynamicTableHeaderCount);
+
+ // [ 1] (s = 57) :authority: www.example.com
+ Header entry = hpackReader.dynamicTable[headerTableLength() - 1];
+ checkEntry(entry, ":authority", "www.example.com", 57);
+
+ // Table size: 57
+ assertEquals(57, hpackReader.dynamicTableByteCount);
+
+ // Decoded header list:
+ assertEquals(headerEntries(
+ ":method", "GET",
+ ":scheme", "http",
+ ":path", "/",
+ ":authority", "www.example.com"), hpackReader.getAndResetHeaderList());
+ }
+
+ private void secondRequestWithHuffman() {
+ bytesIn.writeByte(0x82); // == Indexed - Add ==
+ // idx = 2 -> :method: GET
+ bytesIn.writeByte(0x86); // == Indexed - Add ==
+ // idx = 6 -> :scheme: http
+ bytesIn.writeByte(0x84); // == Indexed - Add ==
+ // idx = 4 -> :path: /
+ bytesIn.writeByte(0xbe); // == Indexed - Add ==
+ // idx = 62 -> :authority: www.example.com
+ bytesIn.writeByte(0x58); // == Literal indexed ==
+ // Indexed name (idx = 24) -> cache-control
+ bytesIn.writeByte(0x86); // Literal value Huffman encoded 6 bytes
+ // decodes to no-cache which is length 8
+ bytesIn.write(decodeHex("a8eb10649cbf"));
+ }
+
+ private void checkReadSecondRequestWithHuffman() {
+ assertEquals(2, hpackReader.dynamicTableHeaderCount);
+
+ // [ 1] (s = 53) cache-control: no-cache
+ Header entry = hpackReader.dynamicTable[headerTableLength() - 2];
+ checkEntry(entry, "cache-control", "no-cache", 53);
+
+ // [ 2] (s = 57) :authority: www.example.com
+ entry = hpackReader.dynamicTable[headerTableLength() - 1];
+ checkEntry(entry, ":authority", "www.example.com", 57);
+
+ // Table size: 110
+ assertEquals(110, hpackReader.dynamicTableByteCount);
+
+ // Decoded header list:
+ assertEquals(headerEntries(
+ ":method", "GET",
+ ":scheme", "http",
+ ":path", "/",
+ ":authority", "www.example.com",
+ "cache-control", "no-cache"), hpackReader.getAndResetHeaderList());
+ }
+
+ private void thirdRequestWithHuffman() {
+ bytesIn.writeByte(0x82); // == Indexed - Add ==
+ // idx = 2 -> :method: GET
+ bytesIn.writeByte(0x87); // == Indexed - Add ==
+ // idx = 7 -> :scheme: https
+ bytesIn.writeByte(0x85); // == Indexed - Add ==
+ // idx = 5 -> :path: /index.html
+ bytesIn.writeByte(0xbf); // == Indexed - Add ==
+ // idx = 63 -> :authority: www.example.com
+ bytesIn.writeByte(0x40); // Literal indexed
+ bytesIn.writeByte(0x88); // Literal name Huffman encoded 8 bytes
+ // decodes to custom-key which is length 10
+ bytesIn.write(decodeHex("25a849e95ba97d7f"));
+ bytesIn.writeByte(0x89); // Literal value Huffman encoded 9 bytes
+ // decodes to custom-value which is length 12
+ bytesIn.write(decodeHex("25a849e95bb8e8b4bf"));
+ }
+
+ private void checkReadThirdRequestWithHuffman() {
+ assertEquals(3, hpackReader.dynamicTableHeaderCount);
+
+ // [ 1] (s = 54) custom-key: custom-value
+ Header entry = hpackReader.dynamicTable[headerTableLength() - 3];
+ checkEntry(entry, "custom-key", "custom-value", 54);
+
+ // [ 2] (s = 53) cache-control: no-cache
+ entry = hpackReader.dynamicTable[headerTableLength() - 2];
+ checkEntry(entry, "cache-control", "no-cache", 53);
+
+ // [ 3] (s = 57) :authority: www.example.com
+ entry = hpackReader.dynamicTable[headerTableLength() - 1];
+ checkEntry(entry, ":authority", "www.example.com", 57);
+
+ // Table size: 164
+ assertEquals(164, hpackReader.dynamicTableByteCount);
+
+ // Decoded header list:
+ assertEquals(headerEntries(
+ ":method", "GET",
+ ":scheme", "https",
+ ":path", "/index.html",
+ ":authority", "www.example.com",
+ "custom-key", "custom-value"), hpackReader.getAndResetHeaderList());
+ }
+
+ @Test public void readSingleByteInt() throws IOException {
+ assertEquals(10, newReader(byteStream()).readInt(10, 31));
+ assertEquals(10, newReader(byteStream()).readInt(0xe0 | 10, 31));
+ }
+
+ @Test public void readMultibyteInt() throws IOException {
+ assertEquals(1337, newReader(byteStream(154, 10)).readInt(31, 31));
+ }
+
+ @Test public void writeSingleByteInt() throws IOException {
+ hpackWriter.writeInt(10, 31, 0);
+ assertBytes(10);
+ hpackWriter.writeInt(10, 31, 0xe0);
+ assertBytes(0xe0 | 10);
+ }
+
+ @Test public void writeMultibyteInt() throws IOException {
+ hpackWriter.writeInt(1337, 31, 0);
+ assertBytes(31, 154, 10);
+ hpackWriter.writeInt(1337, 31, 0xe0);
+ assertBytes(0xe0 | 31, 154, 10);
+ }
+
+ @Test public void max31BitValue() throws IOException {
+ hpackWriter.writeInt(0x7fffffff, 31, 0);
+ assertBytes(31, 224, 255, 255, 255, 7);
+ assertEquals(0x7fffffff,
+ newReader(byteStream(224, 255, 255, 255, 7)).readInt(31, 31));
+ }
+
+ @Test public void prefixMask() throws IOException {
+ hpackWriter.writeInt(31, 31, 0);
+ assertBytes(31, 0);
+ assertEquals(31, newReader(byteStream(0)).readInt(31, 31));
+ }
+
+ @Test public void prefixMaskMinusOne() throws IOException {
+ hpackWriter.writeInt(30, 31, 0);
+ assertBytes(30);
+ assertEquals(31, newReader(byteStream(0)).readInt(31, 31));
+ }
+
+ @Test public void zero() throws IOException {
+ hpackWriter.writeInt(0, 31, 0);
+ assertBytes(0);
+ assertEquals(0, newReader(byteStream()).readInt(0, 31));
+ }
+
+ @Test public void lowercaseHeaderNameBeforeEmit() throws IOException {
+ hpackWriter.writeHeaders(Arrays.asList(new Header("FoO", "BaR")));
+ assertBytes(0, 3, 'f', 'o', 'o', 3, 'B', 'a', 'R');
+ }
+
+ @Test public void mixedCaseHeaderNameIsMalformed() throws IOException {
+ try {
+ newReader(byteStream(0, 3, 'F', 'o', 'o', 3, 'B', 'a', 'R')).readHeaders();
+ fail();
+ } catch (IOException e) {
+ assertEquals("PROTOCOL_ERROR response malformed: mixed case name: Foo", e.getMessage());
+ }
+ }
+
+ @Test public void emptyHeaderName() throws IOException {
+ hpackWriter.writeByteString(ByteString.encodeUtf8(""));
+ assertBytes(0);
+ assertEquals(ByteString.EMPTY, newReader(byteStream(0)).readByteString());
+ }
+
+ private Hpack.Reader newReader(Buffer source) {
+ return new Hpack.Reader(4096, source);
+ }
+
+ private Buffer byteStream(int... bytes) {
+ return new Buffer().write(intArrayToByteArray(bytes));
+ }
+
+ private void checkEntry(Header entry, String name, String value, int size) {
+ assertEquals(name, entry.name.utf8());
+ assertEquals(value, entry.value.utf8());
+ assertEquals(size, entry.hpackSize);
+ }
+
+ private void assertBytes(int... bytes) throws IOException {
+ ByteString expected = intArrayToByteArray(bytes);
+ ByteString actual = bytesOut.readByteString();
+ assertEquals(expected, actual);
+ }
+
+ private ByteString intArrayToByteArray(int[] bytes) {
+ byte[] data = new byte[bytes.length];
+ for (int i = 0; i < bytes.length; i++) {
+ data[i] = (byte) bytes[i];
+ }
+ return ByteString.of(data);
+ }
+
+ private int headerTableLength() {
+ return hpackReader.dynamicTable.length;
+ }
+}