aboutsummaryrefslogtreecommitdiff
path: root/okhttp-hpacktests
diff options
context:
space:
mode:
Diffstat (limited to 'okhttp-hpacktests')
-rw-r--r--okhttp-hpacktests/README.md19
-rw-r--r--okhttp-hpacktests/pom.xml56
-rw-r--r--okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackDecodeInteropTest.java42
-rw-r--r--okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackDecodeTestBase.java92
-rw-r--r--okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackRoundTripTest.java63
-rw-r--r--okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/Case.java69
-rw-r--r--okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/HpackJsonUtil.java88
-rw-r--r--okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/Story.java66
8 files changed, 495 insertions, 0 deletions
diff --git a/okhttp-hpacktests/README.md b/okhttp-hpacktests/README.md
new file mode 100644
index 0000000..6b85c9a
--- /dev/null
+++ b/okhttp-hpacktests/README.md
@@ -0,0 +1,19 @@
+OkHttp HPACK tests
+==================
+
+These tests use the [hpack-test-case][1] project to validate OkHttp's HPACK
+implementation. The HPACK test cases are in a separate git submodule, so to
+initialize them, you must run:
+
+ git submodule init
+ git submodule update
+
+TODO
+----
+
+ * Add maven goal to avoid manual call to git submodule init.
+ * Make hpack-test-case update itself from git, and run new tests.
+ * Add maven goal to generate stories and a pull request to hpack-test-case
+ to have others validate our output.
+
+[1]: https://github.com/http2jp/hpack-test-case
diff --git a/okhttp-hpacktests/pom.xml b/okhttp-hpacktests/pom.xml
new file mode 100644
index 0000000..70a59f2
--- /dev/null
+++ b/okhttp-hpacktests/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.squareup.okhttp</groupId>
+ <artifactId>parent</artifactId>
+ <version>2.2.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>okhttp-hpacktests</artifactId>
+ <name>OkHttp HPACK Tests</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.squareup.okio</groupId>
+ <artifactId>okio</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.squareup.okhttp</groupId>
+ <artifactId>okhttp</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.squareup.okhttp</groupId>
+ <artifactId>mockwebserver</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <!-- Gson: Java to Json conversion -->
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <!-- Do not deploy this as an artifact to Maven central. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackDecodeInteropTest.java b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackDecodeInteropTest.java
new file mode 100644
index 0000000..30e1a7b
--- /dev/null
+++ b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackDecodeInteropTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 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 com.squareup.okhttp.internal.spdy;
+
+import com.squareup.okhttp.internal.spdy.hpackjson.Story;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static com.squareup.okhttp.internal.spdy.hpackjson.HpackJsonUtil.storiesForCurrentDraft;
+
+@RunWith(Parameterized.class)
+public class HpackDecodeInteropTest extends HpackDecodeTestBase {
+
+ public HpackDecodeInteropTest(Story story) {
+ super(story);
+ }
+
+ @Parameterized.Parameters(name="{0}")
+ public static Collection<Story[]> createStories() throws Exception {
+ return createStories(storiesForCurrentDraft());
+ }
+
+ @Test
+ public void testGoodDecoderInterop() throws Exception {
+ testDecoder();
+ }
+}
diff --git a/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackDecodeTestBase.java b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackDecodeTestBase.java
new file mode 100644
index 0000000..1bd9b00
--- /dev/null
+++ b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackDecodeTestBase.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 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 com.squareup.okhttp.internal.spdy;
+
+import com.squareup.okhttp.internal.spdy.hpackjson.Case;
+import com.squareup.okhttp.internal.spdy.hpackjson.HpackJsonUtil;
+import com.squareup.okhttp.internal.spdy.hpackjson.Story;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import okio.Buffer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests Hpack implementation using https://github.com/http2jp/hpack-test-case/
+ */
+public class HpackDecodeTestBase {
+
+ /**
+ * Reads all stories in the folders provided, asserts if no story found.
+ */
+ protected static Collection<Story[]> createStories(String[] interopTests)
+ throws Exception {
+ List<Story[]> result = new ArrayList<>();
+ for (String interopTestName : interopTests) {
+ List<Story> stories = HpackJsonUtil.readStories(interopTestName);
+ if (stories.isEmpty()) {
+ fail("No stories for: " + interopTestName);
+ }
+ for (Story story : stories) {
+ result.add(new Story[] { story });
+ }
+ }
+ return result;
+ }
+
+ private final Buffer bytesIn = new Buffer();
+ private final HpackDraft10.Reader hpackReader = new HpackDraft10.Reader(4096, bytesIn);
+
+ private final Story story;
+
+ public HpackDecodeTestBase(Story story) {
+ this.story = story;
+ }
+
+ /**
+ * Expects wire to be set for all cases, and compares the decoder's output to
+ * expected headers.
+ */
+ protected void testDecoder() throws Exception {
+ testDecoder(story);
+ }
+
+ protected void testDecoder(Story story) throws Exception {
+ for (Case caze : story.getCases()) {
+ bytesIn.write(caze.getWire());
+ hpackReader.readHeaders();
+ assertSetEquals(String.format("seqno=%d", caze.getSeqno()), caze.getHeaders(),
+ hpackReader.getAndResetHeaderList());
+ }
+ }
+ /**
+ * Checks if {@code expected} and {@code observed} are equal when viewed as a
+ * set and headers are deduped.
+ *
+ * TODO: See if duped headers should be preserved on decode and verify.
+ */
+ private static void assertSetEquals(
+ String message, List<Header> expected, List<Header> observed) {
+ assertEquals(message, new LinkedHashSet<>(expected), new LinkedHashSet<>(observed));
+ }
+
+ protected Story getStory() {
+ return story;
+ }
+}
diff --git a/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackRoundTripTest.java b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackRoundTripTest.java
new file mode 100644
index 0000000..a78dab5
--- /dev/null
+++ b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/HpackRoundTripTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 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 com.squareup.okhttp.internal.spdy;
+
+import com.squareup.okhttp.internal.spdy.hpackjson.Case;
+import com.squareup.okhttp.internal.spdy.hpackjson.Story;
+import okio.Buffer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Collection;
+
+/**
+ * Tests for round-tripping headers through hpack..
+ */
+// TODO: update hpack-test-case with the output of our encoder.
+// This test will hide complementary bugs in the encoder and decoder,
+// We should test that the encoder is producing responses that are
+// d]
+@RunWith(Parameterized.class)
+public class HpackRoundTripTest extends HpackDecodeTestBase {
+
+ private static final String[] RAW_DATA = { "raw-data" };
+
+ @Parameterized.Parameters(name="{0}")
+ public static Collection<Story[]> getStories() throws Exception {
+ return createStories(RAW_DATA);
+ }
+
+ private Buffer bytesOut = new Buffer();
+ private HpackDraft10.Writer hpackWriter = new HpackDraft10.Writer(bytesOut);
+
+ public HpackRoundTripTest(Story story) {
+ super(story);
+ }
+
+ @Test
+ public void testRoundTrip() throws Exception {
+ Story story = getStory().clone();
+ // Mutate cases in base class.
+ for (Case caze : story.getCases()) {
+ hpackWriter.writeHeaders(caze.getHeaders());
+ caze.setWire(bytesOut.readByteString());
+ }
+
+ testDecoder(story);
+ }
+
+}
diff --git a/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/Case.java b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/Case.java
new file mode 100644
index 0000000..d5d2728
--- /dev/null
+++ b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/Case.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 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 com.squareup.okhttp.internal.spdy.hpackjson;
+
+import com.squareup.okhttp.internal.spdy.Header;
+import okio.ByteString;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Representation of an individual case (set of headers and wire format).
+ * There are many cases for a single story. This class is used reflectively
+ * with Gson to parse stories.
+ */
+public class Case implements Cloneable {
+
+ private int seqno;
+ private String wire;
+ private List<Map<String, String>> headers;
+
+ public List<Header> getHeaders() {
+ List<Header> result = new ArrayList<>();
+ for (Map<String, String> inputHeader : headers) {
+ Map.Entry<String, String> entry = inputHeader.entrySet().iterator().next();
+ result.add(new Header(entry.getKey(), entry.getValue()));
+ }
+ return result;
+ }
+
+ public ByteString getWire() {
+ return ByteString.decodeHex(wire);
+ }
+
+ public int getSeqno() {
+ return seqno;
+ }
+
+ public void setWire(ByteString wire) {
+ this.wire = wire.hex();
+ }
+
+ @Override
+ protected Case clone() throws CloneNotSupportedException {
+ Case result = new Case();
+ result.seqno = seqno;
+ result.wire = wire;
+ result.headers = new ArrayList<>();
+ for (Map<String, String> header : headers) {
+ result.headers.add(new LinkedHashMap<String, String>(header));
+ }
+ return result;
+ }
+}
diff --git a/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/HpackJsonUtil.java b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/HpackJsonUtil.java
new file mode 100644
index 0000000..9d721ab
--- /dev/null
+++ b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/HpackJsonUtil.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 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 com.squareup.okhttp.internal.spdy.hpackjson;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utilities for reading HPACK tests.
+ */
+public final class HpackJsonUtil {
+ private static final int CURRENT_DRAFT = 9;
+
+ private static final String STORY_RESOURCE_FORMAT = "/hpack-test-case/%s/story_%02d.json";
+
+ private static final Gson GSON = new GsonBuilder().create();
+
+ private static Story readStory(InputStream jsonResource) throws IOException {
+ return GSON.fromJson(new InputStreamReader(jsonResource, "UTF-8"), Story.class);
+ }
+
+ /** Iterate through the hpack-test-case resources, only picking stories for the current draft. */
+ public static String[] storiesForCurrentDraft() throws URISyntaxException {
+ File testCaseDirectory = new File(HpackJsonUtil.class.getResource("/hpack-test-case").toURI());
+ List<String> storyNames = new ArrayList<String>();
+ for (File path : testCaseDirectory.listFiles()) {
+ if (path.isDirectory() && Arrays.asList(path.list()).contains("story_00.json")) {
+ try {
+ Story firstStory = readStory(new FileInputStream(new File(path, "story_00.json")));
+ if (firstStory.getDraft() == CURRENT_DRAFT) {
+ storyNames.add(path.getName());
+ }
+ } catch (IOException ignored) {
+ // Skip this path.
+ }
+ }
+ }
+ return storyNames.toArray(new String[storyNames.size()]);
+ }
+
+ /**
+ * Reads stories named "story_xx.json" from the folder provided.
+ */
+ public static List<Story> readStories(String testFolderName) throws Exception {
+ List<Story> result = new ArrayList<>();
+ int i = 0;
+ while (true) { // break after last test.
+ String storyResourceName = String.format(STORY_RESOURCE_FORMAT, testFolderName, i);
+ InputStream storyInputStream = HpackJsonUtil.class.getResourceAsStream(storyResourceName);
+ if (storyInputStream == null) {
+ break;
+ }
+ try {
+ Story story = readStory(storyInputStream);
+ story.setFileName(storyResourceName);
+ result.add(story);
+ i++;
+ } finally {
+ storyInputStream.close();
+ }
+ }
+ return result;
+ }
+
+ private HpackJsonUtil() { } // Utilities only.
+} \ No newline at end of file
diff --git a/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/Story.java b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/Story.java
new file mode 100644
index 0000000..5ff2b07
--- /dev/null
+++ b/okhttp-hpacktests/src/test/java/com/squareup/okhttp/internal/spdy/hpackjson/Story.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 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 com.squareup.okhttp.internal.spdy.hpackjson;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Representation of one story, a set of request headers to encode or decode.
+ * This class is used reflectively with Gson to parse stories from files.
+ */
+public class Story implements Cloneable {
+
+ private transient String fileName;
+ private List<Case> cases;
+ private int draft;
+ private String description;
+
+ /**
+ * The filename is only used in the toString representation.
+ */
+ void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public List<Case> getCases() {
+ return cases;
+ }
+
+ /** We only expect stories that match the draft we've implemented to pass. */
+ public int getDraft() {
+ return draft;
+ }
+
+ @Override
+ public Story clone() throws CloneNotSupportedException {
+ Story story = new Story();
+ story.fileName = this.fileName;
+ story.cases = new ArrayList<>();
+ for (Case caze : cases) {
+ story.cases.add(caze.clone());
+ }
+ story.draft = draft;
+ story.description = description;
+ return story;
+ }
+
+ @Override
+ public String toString() {
+ // Used as the test name.
+ return fileName;
+ }
+}