diff --git a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java index 6c50016bc..eef731b0a 100644 --- a/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java +++ b/okhttp-tests/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java @@ -21,6 +21,7 @@ import com.squareup.okhttp.Request; import com.squareup.okhttp.Response; import com.squareup.okhttp.internal.spdy.Header; import java.io.IOException; +import java.util.Arrays; import java.util.List; import org.junit.Test; @@ -145,6 +146,48 @@ public final class HeadersTest { assertEquals("OkHttp", headers.value(0)); } + @Test public void addParsing() { + Headers headers = new Headers.Builder() + .add("foo: bar") + .add(" foo: baz") // Name leading whitespace is trimmed. + .add("foo : bak") // Name trailing whitespace is trimmed. + .add("ping: pong ") // Value whitespace is trimmed. + .add("kit:kat") // Space after colon is not required. + .build(); + assertEquals(Arrays.asList("bar", "baz", "bak"), headers.values("foo")); + assertEquals(Arrays.asList("pong"), headers.values("ping")); + assertEquals(Arrays.asList("kat"), headers.values("kit")); + } + + @Test public void addThrowsOnEmptyName() { + try { + new Headers.Builder().add(": bar"); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + new Headers.Builder().add(" : bar"); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + @Test public void addThrowsOnNoColon() { + try { + new Headers.Builder().add("foo bar"); + fail(); + } catch (IllegalArgumentException expected) { + } + } + + @Test public void addThrowsOnMultiColon() { + try { + new Headers.Builder().add(":status: 200 OK"); + fail(); + } catch (IllegalArgumentException expected) { + } + } + @Test public void ofThrowsOddNumberOfHeaders() { try { Headers.of("User-Agent", "OkHttp", "Content-Length"); diff --git a/okhttp/src/main/java/com/squareup/okhttp/Cache.java b/okhttp/src/main/java/com/squareup/okhttp/Cache.java index 04435ddd7..92a5f0bfd 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/Cache.java +++ b/okhttp/src/main/java/com/squareup/okhttp/Cache.java @@ -485,7 +485,7 @@ public final class Cache { Headers.Builder varyHeadersBuilder = new Headers.Builder(); int varyRequestHeaderLineCount = readInt(source); for (int i = 0; i < varyRequestHeaderLineCount; i++) { - varyHeadersBuilder.addLine(source.readUtf8LineStrict()); + varyHeadersBuilder.addLenient(source.readUtf8LineStrict()); } varyHeaders = varyHeadersBuilder.build(); @@ -496,7 +496,7 @@ public final class Cache { Headers.Builder responseHeadersBuilder = new Headers.Builder(); int responseHeaderLineCount = readInt(source); for (int i = 0; i < responseHeaderLineCount; i++) { - responseHeadersBuilder.addLine(source.readUtf8LineStrict()); + responseHeadersBuilder.addLenient(source.readUtf8LineStrict()); } responseHeaders = responseHeadersBuilder.build(); diff --git a/okhttp/src/main/java/com/squareup/okhttp/Headers.java b/okhttp/src/main/java/com/squareup/okhttp/Headers.java index 152f0cbde..5a5175594 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/Headers.java +++ b/okhttp/src/main/java/com/squareup/okhttp/Headers.java @@ -171,8 +171,11 @@ public final class Headers { public static final class Builder { private final List namesAndValues = new ArrayList<>(20); - /** Add an header line containing a field name, a literal colon, and a value. */ - Builder addLine(String line) { + /** + * Add a header line without any validation. Only appropriate for headers from the remote peer + * or cache. + */ + Builder addLenient(String line) { int index = line.indexOf(":", 1); if (index != -1) { return addLenient(line.substring(0, index), line.substring(index + 1)); @@ -185,6 +188,15 @@ public final class Headers { } } + /** Add an header line containing a field name, a literal colon, and a value. */ + public Builder add(String line) { + int index = line.indexOf(":"); + if (index == -1) { + throw new IllegalArgumentException("Unexpected header: " + line); + } + return add(line.substring(0, index).trim(), line.substring(index + 1)); + } + /** Add a field with the specified value. */ public Builder add(String name, String value) { if (name == null) throw new IllegalArgumentException("name == null"); diff --git a/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java b/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java index d476946da..56b55f9da 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java +++ b/okhttp/src/main/java/com/squareup/okhttp/OkHttpClient.java @@ -87,8 +87,8 @@ public class OkHttpClient implements Cloneable { return pooled.isReadable(); } - @Override public void addLine(Headers.Builder builder, String line) { - builder.addLine(line); + @Override public void addLenient(Headers.Builder builder, String line) { + builder.addLenient(line); } @Override public void setCache(OkHttpClient client, InternalCache internalCache) { diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/Internal.java b/okhttp/src/main/java/com/squareup/okhttp/internal/Internal.java index 3b70b8092..d806b483c 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/Internal.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/Internal.java @@ -52,7 +52,7 @@ public abstract class Internal { public abstract boolean isReadable(Connection pooled); - public abstract void addLine(Headers.Builder builder, String line); + public abstract void addLenient(Headers.Builder builder, String line); public abstract void setCache(OkHttpClient client, InternalCache internalCache); diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpConnection.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpConnection.java index a280c4e34..f78e2b635 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpConnection.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpConnection.java @@ -207,7 +207,7 @@ public final class HttpConnection { public void readHeaders(Headers.Builder builder) throws IOException { // parse the result headers until the first blank line for (String line; (line = source.readUtf8LineStrict()).length() != 0; ) { - Internal.instance.addLine(builder, line); + Internal.instance.addLenient(builder, line); } }