diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/internal/spdy/SpdyServer.java b/mockwebserver/src/main/java/com/squareup/okhttp/internal/spdy/SpdyServer.java index 7e7dbb9f4..5a1b0a7ec 100644 --- a/mockwebserver/src/main/java/com/squareup/okhttp/internal/spdy/SpdyServer.java +++ b/mockwebserver/src/main/java/com/squareup/okhttp/internal/spdy/SpdyServer.java @@ -16,6 +16,7 @@ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import com.squareup.okhttp.internal.SslContextBuilder; import java.io.File; import java.io.FileInputStream; @@ -32,6 +33,8 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import org.eclipse.jetty.npn.NextProtoNego; +import static com.squareup.okhttp.internal.Util.asByteStringList; + /** A basic SPDY server that serves the contents of a local directory. */ public final class SpdyServer implements IncomingStreamHandler { private final File baseDirectory; @@ -78,12 +81,12 @@ public final class SpdyServer implements IncomingStreamHandler { } @Override public void receive(final SpdyStream stream) throws IOException { - List requestHeaders = stream.getRequestHeaders(); + List requestHeaders = stream.getRequestHeaders(); String path = null; for (int i = 0; i < requestHeaders.size(); i += 2) { - String s = requestHeaders.get(i); - if (":path".equals(s)) { - path = requestHeaders.get(i + 1); + ByteString s = requestHeaders.get(i); + if (s.utf8Equals(":path")) { + path = requestHeaders.get(i + 1).utf8(); break; } } @@ -105,8 +108,8 @@ public final class SpdyServer implements IncomingStreamHandler { } private void send404(SpdyStream stream, String path) throws IOException { - List responseHeaders = - Arrays.asList(":status", "404", ":version", "HTTP/1.1", "content-type", "text/plain"); + List responseHeaders = + asByteStringList(":status", "404", ":version", "HTTP/1.1", "content-type", "text/plain"); stream.reply(responseHeaders, true); OutputStream out = stream.getOutputStream(); String text = "Not found: " + path; @@ -115,8 +118,8 @@ public final class SpdyServer implements IncomingStreamHandler { } private void serveDirectory(SpdyStream stream, String[] files) throws IOException { - List responseHeaders = - Arrays.asList(":status", "200", ":version", "HTTP/1.1", "content-type", + List responseHeaders = + asByteStringList(":status", "200", ":version", "HTTP/1.1", "content-type", "text/html; charset=UTF-8"); stream.reply(responseHeaders, true); OutputStream out = stream.getOutputStream(); @@ -130,9 +133,8 @@ public final class SpdyServer implements IncomingStreamHandler { private void serveFile(SpdyStream stream, File file) throws IOException { InputStream in = new FileInputStream(file); byte[] buffer = new byte[8192]; - stream.reply( - Arrays.asList(":status", "200", ":version", "HTTP/1.1", "content-type", contentType(file)), - true); + stream.reply(asByteStringList(":status", "200", ":version", "HTTP/1.1", "content-type", + contentType(file)), true); OutputStream out = stream.getOutputStream(); int count; while ((count = in.read(buffer)) != -1) { diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java index 1079fd41f..9d5ffb182 100644 --- a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java +++ b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java @@ -17,6 +17,7 @@ package com.squareup.okhttp.mockwebserver; +import com.squareup.okhttp.internal.ByteString; import com.squareup.okhttp.internal.Platform; import com.squareup.okhttp.internal.Util; import com.squareup.okhttp.internal.spdy.IncomingStreamHandler; @@ -658,14 +659,14 @@ public final class MockWebServer { } private RecordedRequest readRequest(SpdyStream stream) throws IOException { - List spdyHeaders = stream.getRequestHeaders(); + List spdyHeaders = stream.getRequestHeaders(); List httpHeaders = new ArrayList(); String method = "<:method omitted>"; String path = "<:path omitted>"; String version = "<:version omitted>"; for (int i = 0, size = spdyHeaders.size(); i < size; i += 2) { - String name = spdyHeaders.get(i); - String value = spdyHeaders.get(i + 1); + String name = spdyHeaders.get(i).utf8(); + String value = spdyHeaders.get(i + 1).utf8(); if (":method".equals(name)) { method = value; } else if (":path".equals(name)) { @@ -695,16 +696,17 @@ public final class MockWebServer { if (response.getSocketPolicy() == SocketPolicy.NO_RESPONSE) { return; } - List spdyHeaders = new ArrayList(); + List spdyHeaders = new ArrayList(); String[] statusParts = response.getStatus().split(" ", 2); if (statusParts.length != 2) { throw new AssertionError("Unexpected status: " + response.getStatus()); } - spdyHeaders.add(":status"); - spdyHeaders.add(statusParts[1]); + // TODO: constants for well-known header names. + spdyHeaders.add(ByteString.encodeUtf8(":status")); + spdyHeaders.add(ByteString.encodeUtf8(statusParts[1])); // TODO: no ":version" header for HTTP/2.0, only SPDY. - spdyHeaders.add(":version"); - spdyHeaders.add(statusParts[0]); + spdyHeaders.add(ByteString.encodeUtf8(":version")); + spdyHeaders.add(ByteString.encodeUtf8(statusParts[0])); List headers = response.getHeaders(); for (int i = 0, size = headers.size(); i < size; i++) { String header = headers.get(i); @@ -712,8 +714,8 @@ public final class MockWebServer { if (headerParts.length != 2) { throw new AssertionError("Unexpected header: " + header); } - spdyHeaders.add(headerParts[0].toLowerCase(Locale.US).trim()); - spdyHeaders.add(headerParts[1].trim()); + spdyHeaders.add(ByteString.encodeUtf8(headerParts[0].toLowerCase(Locale.US).trim())); + spdyHeaders.add(ByteString.encodeUtf8(headerParts[1].trim())); } byte[] body = response.getBody(); stream.reply(spdyHeaders, body.length > 0); diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/ByteString.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/ByteString.java index 6e3d3fea1..6cbdfb611 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/ByteString.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/ByteString.java @@ -66,7 +66,9 @@ public final class ByteString { * or {@link #EMPTY} if {@code s} is zero length. */ public static ByteString encodeUtf8(String s) { - return new ByteString(s.getBytes(Util.UTF_8)); + ByteString byteString = new ByteString(s.getBytes(Util.UTF_8)); + byteString.utf8 = s; + return byteString; } /** Constructs a new {@code String} by decoding the bytes as UTF-8. */ diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/Util.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/Util.java index f2adbf58f..b160bb6e7 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/Util.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/Util.java @@ -392,4 +392,12 @@ public final class Util { } }; } + + public static List asByteStringList(String... strings) { + List out = new ArrayList(strings.length); + for (String string : strings) { + out.add(ByteString.encodeUtf8(string)); + } + return out; + } } diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameReader.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameReader.java index 1371262e6..046580cc8 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameReader.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameReader.java @@ -16,6 +16,7 @@ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; @@ -33,17 +34,17 @@ public interface FrameReader extends Closeable { * if necessary. Frames that trigger this are SPDY SYN_STREAM, HEADERS, and * SYN_REPLY, and HTTP/2.0 HEADERS and PUSH_PROMISE. * - * @param inFinished true if the sender will not send further frames. * @param outFinished true if the receiver should not send further frames. + * @param inFinished true if the sender will not send further frames. * @param streamId the stream owning these headers. * @param associatedStreamId the stream that triggered the sender to create - * this stream. +* this stream. * @param priority or -1 for no priority. For SPDY, priorities range from 0 - * (highest) thru 7 (lowest). For HTTP/2.0, priorities range from 0 - * (highest) thru 2**31-1 (lowest). +* (highest) thru 7 (lowest). For HTTP/2.0, priorities range from 0 + * @param nameValueBlock */ void headers(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId, - int priority, List nameValueBlock, HeadersMode headersMode); + int priority, List nameValueBlock, HeadersMode headersMode); void rstStream(int streamId, ErrorCode errorCode); void settings(boolean clearPrevious, Settings settings); void noop(); diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameWriter.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameWriter.java index 354f43d1d..77bbdf8c9 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameWriter.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameWriter.java @@ -16,6 +16,7 @@ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import java.io.Closeable; import java.io.IOException; import java.util.List; @@ -28,9 +29,10 @@ public interface FrameWriter extends Closeable { /** SPDY/3 only. */ void flush() throws IOException; void synStream(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId, - int priority, int slot, List nameValueBlock) throws IOException; - void synReply(boolean outFinished, int streamId, List nameValueBlock) throws IOException; - void headers(int streamId, List nameValueBlock) throws IOException; + int priority, int slot, List nameValueBlock) throws IOException; + void synReply(boolean outFinished, int streamId, List nameValueBlock) + throws IOException; + void headers(int streamId, List nameValueBlock) throws IOException; void rstStream(int streamId, ErrorCode errorCode) throws IOException; void data(boolean outFinished, int streamId, byte[] data) throws IOException; void data(boolean outFinished, int streamId, byte[] data, int offset, int byteCount) diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/HpackDraft05.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/HpackDraft05.java index d4f1e1ef3..30ab507b3 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/HpackDraft05.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/HpackDraft05.java @@ -1,5 +1,6 @@ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import java.io.DataInputStream; import java.io.IOException; import java.io.OutputStream; @@ -16,20 +17,23 @@ final class HpackDraft05 { // Visible for testing. static class HeaderEntry implements Cloneable { - final String name; - final String value; + final ByteString name; + final ByteString value; final int size; boolean referenced = true; - HeaderEntry(String name, String value) { + HeaderEntry(ByteString name, ByteString value) { this.name = name; this.value = value; - // TODO: This needs to be the size in bytes, not the length in chars. - this.size = 32 + name.length() + value.length(); + this.size = 32 + name.size() + value.size(); + } + + public HeaderEntry(String name, String value) { + this(ByteString.encodeUtf8(name), ByteString.encodeUtf8(value)); } /** Adds name and value, if this entry is referenced. */ - void addTo(List out) { + void addTo(List out) { if (!referenced) return; out.add(name); out.add(value); @@ -118,7 +122,7 @@ final class HpackDraft05 { // http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#section-4.1.2 static class Reader { private final DataInputStream in; - private final List emittedHeaders = new ArrayList(); + private final List emittedHeaders = new ArrayList(); private long bytesLeft = 0; // Visible for testing. @@ -187,8 +191,8 @@ final class HpackDraft05 { * Returns all headers emitted since they were last cleared, then clears the * emitted headers. */ - public List getAndReset() { - List result = new ArrayList(emittedHeaders); + public List getAndReset() { + List result = new ArrayList(emittedHeaders); emittedHeaders.clear(); return result; } @@ -213,34 +217,34 @@ final class HpackDraft05 { private void readLiteralHeaderWithoutIndexingIndexedName(int index) throws IOException { - String name = getName(index); - String value = readString(); + ByteString name = getName(index); + ByteString value = readString(); emittedHeaders.add(name); emittedHeaders.add(value); } private void readLiteralHeaderWithoutIndexingNewName() throws IOException { - String name = readString(); - String value = readString(); + ByteString name = readString(); + ByteString value = readString(); emittedHeaders.add(name); emittedHeaders.add(value); } private void readLiteralHeaderWithIncrementalIndexingIndexedName(int nameIndex) throws IOException { - String name = getName(nameIndex); - String value = readString(); + ByteString name = getName(nameIndex); + ByteString value = readString(); insertIntoHeaderTable(-1, new HeaderEntry(name, value)); } private void readLiteralHeaderWithIncrementalIndexingNewName() throws IOException { - String name = readString(); - String value = readString(); + ByteString name = readString(); + ByteString value = readString(); insertIntoHeaderTable(-1, new HeaderEntry(name, value)); } - private String getName(int index) { + private ByteString getName(int index) { if (isStaticHeader(index)) { return STATIC_HEADER_TABLE.get(index - headerTable.size()).name; } else { @@ -318,13 +322,13 @@ final class HpackDraft05 { * Reads a UTF-8 encoded string. Since ASCII is a subset of UTF-8, this method * may be used to read strings that are known to be ASCII-only. */ - public String readString() throws IOException { + public ByteString readString() throws IOException { int firstByte = readByte(); int length = readInt(firstByte, PREFIX_8_BITS); byte[] encoded = new byte[length]; bytesLeft -= length; in.readFully(encoded); - return new String(encoded, "UTF-8"); + return ByteString.of(encoded); } } @@ -335,12 +339,12 @@ final class HpackDraft05 { this.out = out; } - public void writeHeaders(List nameValueBlock) throws IOException { + public void writeHeaders(List nameValueBlock) throws IOException { // TODO: implement a compression strategy. for (int i = 0, size = nameValueBlock.size(); i < size; i += 2) { out.write(0x40); // Literal Header without Indexing - New Name. - writeString(nameValueBlock.get(i)); - writeString(nameValueBlock.get(i + 1)); + writeByteString(nameValueBlock.get(i)); + writeByteString(nameValueBlock.get(i + 1)); } } @@ -364,14 +368,9 @@ final class HpackDraft05 { out.write(value); } - /** - * Writes a UTF-8 encoded string. Since ASCII is a subset of UTF-8, this - * method can be used to write strings that are known to be ASCII-only. - */ - public void writeString(String headerName) throws IOException { - byte[] bytes = headerName.getBytes("UTF-8"); - writeInt(bytes.length, PREFIX_8_BITS, 0); - out.write(bytes); + public void writeByteString(ByteString data) throws IOException { + writeInt(data.size(), PREFIX_8_BITS, 0); + data.write(out); } } } diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Http20Draft09.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Http20Draft09.java index a3884cb32..8ec7e8162 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Http20Draft09.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Http20Draft09.java @@ -15,6 +15,7 @@ */ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import com.squareup.okhttp.internal.Util; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -163,7 +164,7 @@ public final class Http20Draft09 implements Variant { if ((flags & FLAG_END_HEADERS) != 0) { hpackReader.emitReferenceSet(); - List nameValueBlock = hpackReader.getAndReset(); + List nameValueBlock = hpackReader.getAndReset(); // TODO: Concat multi-value headers with 0x0, except COOKIE, which uses 0x3B, 0x20. // http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-8.1.3 int priority = -1; // TODO: priority @@ -306,25 +307,26 @@ public final class Http20Draft09 implements Variant { out.write(CONNECTION_HEADER); } - @Override public synchronized void synStream(boolean outFinished, boolean inFinished, - int streamId, int associatedStreamId, int priority, int slot, List nameValueBlock) + @Override + public synchronized void synStream(boolean outFinished, boolean inFinished, int streamId, + int associatedStreamId, int priority, int slot, List nameValueBlock) throws IOException { if (inFinished) throw new UnsupportedOperationException(); headers(outFinished, streamId, priority, nameValueBlock); } @Override public synchronized void synReply(boolean outFinished, int streamId, - List nameValueBlock) throws IOException { + List nameValueBlock) throws IOException { headers(outFinished, streamId, -1, nameValueBlock); } - @Override public synchronized void headers(int streamId, List nameValueBlock) + @Override public synchronized void headers(int streamId, List nameValueBlock) throws IOException { headers(false, streamId, -1, nameValueBlock); } private void headers(boolean outFinished, int streamId, int priority, - List nameValueBlock) throws IOException { + List nameValueBlock) throws IOException { hpackBuffer.reset(); hpackWriter.writeHeaders(nameValueBlock); int type = TYPE_HEADERS; diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/NameValueBlockReader.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/NameValueBlockReader.java index b95d01381..85af6ed40 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/NameValueBlockReader.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/NameValueBlockReader.java @@ -1,5 +1,6 @@ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import com.squareup.okhttp.internal.Util; import java.io.Closeable; import java.io.DataInputStream; @@ -71,7 +72,7 @@ class NameValueBlockReader implements Closeable { } } - public List readNameValueBlock(int length) throws IOException { + public List readNameValueBlock(int length) throws IOException { this.compressedLimit += length; try { int numberOfPairs = nameValueBlockIn.readInt(); @@ -81,11 +82,11 @@ class NameValueBlockReader implements Closeable { if (numberOfPairs > 1024) { throw new IOException("numberOfPairs > 1024: " + numberOfPairs); } - List entries = new ArrayList(numberOfPairs * 2); + List entries = new ArrayList(numberOfPairs * 2); for (int i = 0; i < numberOfPairs; i++) { - String name = readString(); - String values = readString(); - if (name.length() == 0) throw new IOException("name.length == 0"); + ByteString name = readString(); + ByteString values = readString(); + if (name.size() == 0) throw new IOException("name.size == 0"); entries.add(name); entries.add(values); } @@ -110,11 +111,11 @@ class NameValueBlockReader implements Closeable { } } - private String readString() throws DataFormatException, IOException { + private ByteString readString() throws DataFormatException, IOException { int length = nameValueBlockIn.readInt(); byte[] bytes = new byte[length]; Util.readFully(nameValueBlockIn, bytes); - return new String(bytes, 0, length, "UTF-8"); + return ByteString.of(bytes); } @Override public void close() throws IOException { diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Spdy3.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Spdy3.java index aa51756be..595ea7b78 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Spdy3.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Spdy3.java @@ -15,6 +15,7 @@ */ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import com.squareup.okhttp.internal.Platform; import com.squareup.okhttp.internal.Util; import java.io.ByteArrayOutputStream; @@ -201,7 +202,7 @@ final class Spdy3 implements Variant { int associatedStreamId = w2 & 0x7fffffff; int priority = (s3 & 0xe000) >>> 13; int slot = s3 & 0xff; - List nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 10); + List nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 10); boolean inFinished = (flags & FLAG_FIN) != 0; boolean outFinished = (flags & FLAG_UNIDIRECTIONAL) != 0; @@ -212,7 +213,7 @@ final class Spdy3 implements Variant { private void readSynReply(Handler handler, int flags, int length) throws IOException { int w1 = in.readInt(); int streamId = w1 & 0x7fffffff; - List nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 4); + List nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 4); boolean inFinished = (flags & FLAG_FIN) != 0; handler.headers(false, inFinished, streamId, -1, -1, nameValueBlock, HeadersMode.SPDY_REPLY); } @@ -231,7 +232,7 @@ final class Spdy3 implements Variant { private void readHeaders(Handler handler, int flags, int length) throws IOException { int w1 = in.readInt(); int streamId = w1 & 0x7fffffff; - List nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 4); + List nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 4); handler.headers(false, false, streamId, -1, -1, nameValueBlock, HeadersMode.SPDY_HEADERS); } @@ -314,8 +315,9 @@ final class Spdy3 implements Variant { out.flush(); } - @Override public synchronized void synStream(boolean outFinished, boolean inFinished, - int streamId, int associatedStreamId, int priority, int slot, List nameValueBlock) + @Override + public synchronized void synStream(boolean outFinished, boolean inFinished, int streamId, + int associatedStreamId, int priority, int slot, List nameValueBlock) throws IOException { writeNameValueBlockToBuffer(nameValueBlock); int length = 10 + nameValueBlockBuffer.size(); @@ -332,8 +334,8 @@ final class Spdy3 implements Variant { out.flush(); } - @Override public synchronized void synReply( - boolean outFinished, int streamId, List nameValueBlock) throws IOException { + @Override public synchronized void synReply(boolean outFinished, int streamId, + List nameValueBlock) throws IOException { writeNameValueBlockToBuffer(nameValueBlock); int type = TYPE_SYN_REPLY; int flags = (outFinished ? FLAG_FIN : 0); @@ -346,7 +348,7 @@ final class Spdy3 implements Variant { out.flush(); } - @Override public synchronized void headers(int streamId, List nameValueBlock) + @Override public synchronized void headers(int streamId, List nameValueBlock) throws IOException { writeNameValueBlockToBuffer(nameValueBlock); int flags = 0; @@ -386,14 +388,14 @@ final class Spdy3 implements Variant { out.write(data, offset, byteCount); } - private void writeNameValueBlockToBuffer(List nameValueBlock) throws IOException { + private void writeNameValueBlockToBuffer(List nameValueBlock) throws IOException { nameValueBlockBuffer.reset(); int numberOfPairs = nameValueBlock.size() / 2; nameValueBlockOut.writeInt(numberOfPairs); for (int i = 0, size = nameValueBlock.size(); i < size; i++) { - String s = nameValueBlock.get(i); - nameValueBlockOut.writeInt(s.length()); - nameValueBlockOut.write(s.getBytes("UTF-8")); + ByteString s = nameValueBlock.get(i); + nameValueBlockOut.writeInt(s.size()); + s.write(nameValueBlockOut); } nameValueBlockOut.flush(); } diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyConnection.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyConnection.java index 15986a35d..8b09f09b6 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyConnection.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyConnection.java @@ -16,6 +16,7 @@ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import com.squareup.okhttp.internal.NamedRunnable; import com.squareup.okhttp.internal.Util; import java.io.Closeable; @@ -157,7 +158,7 @@ public final class SpdyConnection implements Closeable { * @param in true to create an input stream that the remote peer can use to * send data to us. Corresponds to {@code FLAG_UNIDIRECTIONAL}. */ - public SpdyStream newStream(List requestHeaders, boolean out, boolean in) + public SpdyStream newStream(List requestHeaders, boolean out, boolean in) throws IOException { boolean outFinished = !out; boolean inFinished = !in; @@ -189,7 +190,7 @@ public final class SpdyConnection implements Closeable { return stream; } - void writeSynReply(int streamId, boolean outFinished, List alternating) + void writeSynReply(int streamId, boolean outFinished, List alternating) throws IOException { frameWriter.synReply(outFinished, streamId, alternating); } @@ -472,7 +473,7 @@ public final class SpdyConnection implements Closeable { } @Override public void headers(boolean outFinished, boolean inFinished, int streamId, - int associatedStreamId, int priority, List nameValueBlock, + int associatedStreamId, int priority, List nameValueBlock, HeadersMode headersMode) { SpdyStream stream; synchronized (SpdyConnection.this) { diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyStream.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyStream.java index cacb4a5a7..baf7e6dc1 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyStream.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyStream.java @@ -16,6 +16,7 @@ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import com.squareup.okhttp.internal.Util; import java.io.IOException; import java.io.InputStream; @@ -48,10 +49,10 @@ public final class SpdyStream { private int writeWindowSize; /** Headers sent by the stream initiator. Immutable and non null. */ - private final List requestHeaders; + private final List requestHeaders; /** Headers sent in the stream reply. Null if reply is either not sent or not sent yet. */ - private List responseHeaders; + private List responseHeaders; private final SpdyDataInputStream in; private final SpdyDataOutputStream out; @@ -64,7 +65,7 @@ public final class SpdyStream { private ErrorCode errorCode = null; SpdyStream(int id, SpdyConnection connection, boolean outFinished, boolean inFinished, - int priority, List requestHeaders, Settings settings) { + int priority, List requestHeaders, Settings settings) { if (connection == null) throw new NullPointerException("connection == null"); if (requestHeaders == null) throw new NullPointerException("requestHeaders == null"); this.id = id; @@ -109,7 +110,7 @@ public final class SpdyStream { return connection; } - public List getRequestHeaders() { + public List getRequestHeaders() { return requestHeaders; } @@ -117,7 +118,7 @@ public final class SpdyStream { * Returns the stream's response headers, blocking if necessary if they * have not been received yet. */ - public synchronized List getResponseHeaders() throws IOException { + public synchronized List getResponseHeaders() throws IOException { long remaining = 0; long start = 0; if (readTimeoutMillis != 0) { @@ -161,7 +162,7 @@ public final class SpdyStream { * @param out true to create an output stream that we can use to send data * to the remote peer. Corresponds to {@code FLAG_FIN}. */ - public void reply(List responseHeaders, boolean out) throws IOException { + public void reply(List responseHeaders, boolean out) throws IOException { assert (!Thread.holdsLock(SpdyStream.this)); boolean outFinished = false; synchronized (this) { @@ -254,7 +255,7 @@ public final class SpdyStream { return true; } - void receiveHeaders(List headers, HeadersMode headersMode) { + void receiveHeaders(List headers, HeadersMode headersMode) { assert (!Thread.holdsLock(SpdyStream.this)); ErrorCode errorCode = null; boolean open = true; @@ -271,7 +272,7 @@ public final class SpdyStream { if (headersMode.failIfHeadersPresent()) { errorCode = ErrorCode.STREAM_IN_USE; } else { - List newHeaders = new ArrayList(); + List newHeaders = new ArrayList(); newHeaders.addAll(responseHeaders); newHeaders.addAll(headers); this.responseHeaders = newHeaders; diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/BaseTestHandler.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/BaseTestHandler.java index 95e644167..9933b09a8 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/BaseTestHandler.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/BaseTestHandler.java @@ -15,6 +15,7 @@ */ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import java.io.IOException; import java.io.InputStream; import java.util.List; @@ -29,7 +30,7 @@ class BaseTestHandler implements FrameReader.Handler { @Override public void headers(boolean outFinished, boolean inFinished, int streamId, int associatedStreamId, - int priority, List nameValueBlock, HeadersMode headersMode) { + int priority, List nameValueBlock, HeadersMode headersMode) { fail(); } diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/ByteArrayPoolTest.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/ByteArrayPoolTest.java index f87ffdbbd..23df68407 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/ByteArrayPoolTest.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/ByteArrayPoolTest.java @@ -1,75 +1,75 @@ - /* - * Copyright (C) 2012 The Android Open Source Project - * - * 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. - */ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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; +package com.squareup.okhttp.internal.spdy; - import org.junit.Test; +import org.junit.Test; - import static org.junit.Assert.assertTrue; - import static org.junit.Assert.assertNotSame; - import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; - public class ByteArrayPoolTest { - @Test public void testReusesBuffer() { - ByteArrayPool pool = new ByteArrayPool(32); +public class ByteArrayPoolTest { + @Test public void testReusesBuffer() { + ByteArrayPool pool = new ByteArrayPool(32); - byte[] buf1 = pool.getBuf(16); - byte[] buf2 = pool.getBuf(16); + byte[] buf1 = pool.getBuf(16); + byte[] buf2 = pool.getBuf(16); - pool.returnBuf(buf1); - pool.returnBuf(buf2); + pool.returnBuf(buf1); + pool.returnBuf(buf2); - byte[] buf3 = pool.getBuf(16); - byte[] buf4 = pool.getBuf(16); - assertTrue(buf3 == buf1 || buf3 == buf2); - assertTrue(buf4 == buf1 || buf4 == buf2); - assertTrue(buf3 != buf4); - } - - @Test public void testObeysSizeLimit() { - ByteArrayPool pool = new ByteArrayPool(32); - - byte[] buf1 = pool.getBuf(16); - byte[] buf2 = pool.getBuf(16); - byte[] buf3 = pool.getBuf(16); - - pool.returnBuf(buf1); - pool.returnBuf(buf2); - pool.returnBuf(buf3); - - byte[] buf4 = pool.getBuf(16); - byte[] buf5 = pool.getBuf(16); - byte[] buf6 = pool.getBuf(16); - - assertTrue(buf4 == buf2 || buf4 == buf3); - assertTrue(buf5 == buf2 || buf5 == buf3); - assertTrue(buf4 != buf5); - assertTrue(buf6 != buf1 && buf6 != buf2 && buf6 != buf3); - } - - @Test public void testReturnsBufferWithRightSize() { - ByteArrayPool pool = new ByteArrayPool(32); - - byte[] buf1 = pool.getBuf(16); - pool.returnBuf(buf1); - - byte[] buf2 = pool.getBuf(17); - assertNotSame(buf2, buf1); - - byte[] buf3 = pool.getBuf(15); - assertSame(buf3, buf1); - } + byte[] buf3 = pool.getBuf(16); + byte[] buf4 = pool.getBuf(16); + assertTrue(buf3 == buf1 || buf3 == buf2); + assertTrue(buf4 == buf1 || buf4 == buf2); + assertTrue(buf3 != buf4); } + + @Test public void testObeysSizeLimit() { + ByteArrayPool pool = new ByteArrayPool(32); + + byte[] buf1 = pool.getBuf(16); + byte[] buf2 = pool.getBuf(16); + byte[] buf3 = pool.getBuf(16); + + pool.returnBuf(buf1); + pool.returnBuf(buf2); + pool.returnBuf(buf3); + + byte[] buf4 = pool.getBuf(16); + byte[] buf5 = pool.getBuf(16); + byte[] buf6 = pool.getBuf(16); + + assertTrue(buf4 == buf2 || buf4 == buf3); + assertTrue(buf5 == buf2 || buf5 == buf3); + assertTrue(buf4 != buf5); + assertTrue(buf6 != buf1 && buf6 != buf2 && buf6 != buf3); + } + + @Test public void testReturnsBufferWithRightSize() { + ByteArrayPool pool = new ByteArrayPool(32); + + byte[] buf1 = pool.getBuf(16); + pool.returnBuf(buf1); + + byte[] buf2 = pool.getBuf(17); + assertNotSame(buf2, buf1); + + byte[] buf3 = pool.getBuf(15); + assertSame(buf3, buf1); + } +} diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/HpackDraft05Test.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/HpackDraft05Test.java index 877fe20a4..f600423c1 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/HpackDraft05Test.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/HpackDraft05Test.java @@ -15,6 +15,7 @@ */ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -25,6 +26,7 @@ import java.util.List; import org.junit.Before; import org.junit.Test; +import static com.squareup.okhttp.internal.Util.asByteStringList; import static org.junit.Assert.assertEquals; public class HpackDraft05Test { @@ -43,7 +45,7 @@ public class HpackDraft05Test { @Test public void tooLargeToHPackIsStillEmitted() throws IOException { char[] tooLarge = new char[4096]; Arrays.fill(tooLarge, 'a'); - final List sentHeaders = Arrays.asList("foo", new String(tooLarge)); + final List sentHeaders = asByteStringList("foo", new String(tooLarge)); ByteArrayOutputStream out = literalHeaders(sentHeaders); bytesIn.set(out.toByteArray()); @@ -78,7 +80,7 @@ public class HpackDraft05Test { HpackDraft05.HeaderEntry entry = hpackReader.headerTable.get(0); checkEntry(entry, "custom-key", "custom-header", 55, true); - assertEquals(Arrays.asList("custom-key", "custom-header"), hpackReader.getAndReset()); + assertEquals(asByteStringList("custom-key", "custom-header"), hpackReader.getAndReset()); } /** @@ -98,7 +100,7 @@ public class HpackDraft05Test { assertEquals(0, hpackReader.headerTable.size()); - assertEquals(Arrays.asList(":path", "/sample/path"), hpackReader.getAndReset()); + assertEquals(asByteStringList(":path", "/sample/path"), hpackReader.getAndReset()); } /** @@ -120,7 +122,7 @@ public class HpackDraft05Test { HpackDraft05.HeaderEntry entry = hpackReader.headerTable.get(0); checkEntry(entry, ":method", "GET", 42, true); - assertEquals(Arrays.asList(":method", "GET"), hpackReader.getAndReset()); + assertEquals(asByteStringList(":method", "GET"), hpackReader.getAndReset()); } /** @@ -140,7 +142,7 @@ public class HpackDraft05Test { // Not buffered in header table. assertEquals(0, hpackReader.headerTable.size()); - assertEquals(Arrays.asList(":method", "GET"), hpackReader.getAndReset()); + assertEquals(asByteStringList(":method", "GET"), hpackReader.getAndReset()); } /** @@ -206,7 +208,7 @@ public class HpackDraft05Test { assertEquals(180, hpackReader.headerTableSize); // Decoded header set: - assertEquals(Arrays.asList( // + assertEquals(asByteStringList( // ":method", "GET", // ":scheme", "http", // ":path", "/", // @@ -251,7 +253,7 @@ public class HpackDraft05Test { assertEquals(233, hpackReader.headerTableSize); // Decoded header set: - assertEquals(Arrays.asList( // + assertEquals(asByteStringList( // ":method", "GET", // ":scheme", "http", // ":path", "/", // @@ -320,7 +322,7 @@ public class HpackDraft05Test { // Decoded header set: // TODO: order is not correct per docs, but then again, the spec doesn't require ordering. - assertEquals(Arrays.asList( // + assertEquals(asByteStringList( // ":method", "GET", // ":authority", "www.example.com", // ":scheme", "https", // @@ -380,25 +382,25 @@ public class HpackDraft05Test { } @Test public void headerName() throws IOException { - hpackWriter.writeString("foo"); + hpackWriter.writeByteString(ByteString.encodeUtf8("foo")); assertBytes(3, 'f', 'o', 'o'); - assertEquals("foo", new HpackDraft05.Reader(byteStream(3, 'f', 'o', 'o')).readString()); + assertEquals("foo", new HpackDraft05.Reader(byteStream(3, 'f', 'o', 'o')).readString().utf8()); } @Test public void emptyHeaderName() throws IOException { - hpackWriter.writeString(""); + hpackWriter.writeByteString(ByteString.encodeUtf8("")); assertBytes(0); - assertEquals("", new HpackDraft05.Reader(byteStream(0)).readString()); + assertEquals("", new HpackDraft05.Reader(byteStream(0)).readString().utf8()); } @Test public void headersRoundTrip() throws IOException { - List sentHeaders = Arrays.asList("name", "value"); + List sentHeaders = asByteStringList("name", "value"); hpackWriter.writeHeaders(sentHeaders); ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesOut.toByteArray()); HpackDraft05.Reader reader = new HpackDraft05.Reader(new DataInputStream(bytesIn)); reader.readHeaders(bytesOut.size()); reader.emitReferenceSet(); - List receivedHeaders = reader.getAndReset(); + List receivedHeaders = reader.getAndReset(); assertEquals(sentHeaders, receivedHeaders); } @@ -407,7 +409,7 @@ public class HpackDraft05Test { return new DataInputStream(new ByteArrayInputStream(data)); } - private ByteArrayOutputStream literalHeaders(List sentHeaders) throws IOException { + private ByteArrayOutputStream literalHeaders(List sentHeaders) throws IOException { ByteArrayOutputStream headerBytes = new ByteArrayOutputStream(); new HpackDraft05.Writer(new DataOutputStream(headerBytes)).writeHeaders(sentHeaders); return headerBytes; @@ -415,8 +417,8 @@ public class HpackDraft05Test { private void checkEntry(HpackDraft05.HeaderEntry entry, String name, String value, int size, boolean referenced) { - assertEquals(name, entry.name); - assertEquals(value, entry.value); + assertEquals(name, entry.name.utf8()); + assertEquals(value, entry.value.utf8()); assertEquals(size, entry.size); assertEquals(referenced, entry.referenced); } diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Http20Draft09Test.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Http20Draft09Test.java index 31efb4596..6f58032aa 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Http20Draft09Test.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Http20Draft09Test.java @@ -15,24 +15,24 @@ */ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.util.Arrays; import java.util.List; import org.junit.Test; +import static com.squareup.okhttp.internal.Util.asByteStringList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; public class Http20Draft09Test { static final int expectedStreamId = 15; @Test public void onlyOneLiteralHeadersFrame() throws IOException { - final List sentHeaders = Arrays.asList("name", "value"); + final List sentHeaders = asByteStringList("name", "value"); ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dataOut = new DataOutputStream(out); @@ -54,7 +54,7 @@ public class Http20Draft09Test { @Override public void headers(boolean outFinished, boolean inFinished, int streamId, - int associatedStreamId, int priority, List nameValueBlock, + int associatedStreamId, int priority, List nameValueBlock, HeadersMode headersMode) { assertFalse(outFinished); assertTrue(inFinished); @@ -74,7 +74,7 @@ public class Http20Draft09Test { // Write the first headers frame. { - byte[] headerBytes = literalHeaders(Arrays.asList("foo", "bar")); + byte[] headerBytes = literalHeaders(asByteStringList("foo", "bar")); dataOut.writeShort(headerBytes.length); dataOut.write(Http20Draft09.TYPE_HEADERS); dataOut.write(0); // no flags @@ -84,7 +84,7 @@ public class Http20Draft09Test { // Write the continuation frame, specifying no more frames are expected. { - byte[] headerBytes = literalHeaders(Arrays.asList("baz", "qux")); + byte[] headerBytes = literalHeaders(asByteStringList("baz", "qux")); dataOut.writeShort(headerBytes.length); dataOut.write(Http20Draft09.TYPE_CONTINUATION); dataOut.write(Http20Draft09.FLAG_END_HEADERS | Http20Draft09.FLAG_END_STREAM); @@ -99,14 +99,14 @@ public class Http20Draft09Test { @Override public void headers(boolean outFinished, boolean inFinished, int streamId, - int associatedStreamId, int priority, List nameValueBlock, + int associatedStreamId, int priority, List nameValueBlock, HeadersMode headersMode) { assertFalse(outFinished); assertFalse(inFinished); assertEquals(expectedStreamId, streamId); assertEquals(-1, associatedStreamId); assertEquals(-1, priority); - assertEquals(Arrays.asList("foo", "bar", "baz", "qux"), nameValueBlock); + assertEquals(asByteStringList("foo", "bar", "baz", "qux"), nameValueBlock); assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode); } }); @@ -133,7 +133,7 @@ public class Http20Draft09Test { }); } - private byte[] literalHeaders(List sentHeaders) throws IOException { + private byte[] literalHeaders(List sentHeaders) throws IOException { ByteArrayOutputStream headerBytes = new ByteArrayOutputStream(); new HpackDraft05.Writer(new DataOutputStream(headerBytes)).writeHeaders(sentHeaders); return headerBytes.toByteArray(); diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/MockSpdyPeer.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/MockSpdyPeer.java index 99ddc6dc5..5828fe65c 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/MockSpdyPeer.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/MockSpdyPeer.java @@ -16,6 +16,7 @@ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.ByteString; import com.squareup.okhttp.internal.Util; import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -185,7 +186,7 @@ public final class MockSpdyPeer implements Closeable { public int priority; public ErrorCode errorCode; public int deltaWindowSize; - public List nameValueBlock; + public List nameValueBlock; public byte[] data; public Settings settings; public HeadersMode headersMode; @@ -203,7 +204,7 @@ public final class MockSpdyPeer implements Closeable { } @Override public void headers(boolean outFinished, boolean inFinished, int streamId, - int associatedStreamId, int priority, List nameValueBlock, + int associatedStreamId, int priority, List nameValueBlock, HeadersMode headersMode) { if (this.type != -1) throw new IllegalStateException(); this.type = Spdy3.TYPE_HEADERS; diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/SpdyConnectionTest.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/SpdyConnectionTest.java index b0f23f44c..6490bc863 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/SpdyConnectionTest.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/SpdyConnectionTest.java @@ -30,6 +30,7 @@ import org.junit.After; import org.junit.Test; import static com.squareup.okhttp.internal.Util.UTF_8; +import static com.squareup.okhttp.internal.Util.asByteStringList; import static com.squareup.okhttp.internal.spdy.ErrorCode.CANCEL; import static com.squareup.okhttp.internal.spdy.ErrorCode.FLOW_CONTROL_ERROR; import static com.squareup.okhttp.internal.spdy.ErrorCode.INTERNAL_ERROR; @@ -66,15 +67,16 @@ public final class SpdyConnectionTest { @Test public void clientCreatesStreamAndServerReplies() throws Exception { // write the mocking script peer.acceptFrame(); // SYN_STREAM - peer.sendFrame().synReply(false, 1, Arrays.asList("a", "android")); + peer.sendFrame() + .synReply(false, 1, asByteStringList("a", "android")); peer.sendFrame().data(true, 1, "robot".getBytes("UTF-8")); peer.acceptFrame(); // DATA peer.play(); // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true); - assertEquals(Arrays.asList("a", "android"), stream.getResponseHeaders()); + SpdyStream stream = connection.newStream(asByteStringList("b", "banana"), true, true); + assertEquals(asByteStringList("a", "android"), stream.getResponseHeaders()); assertStreamData("robot", stream.getInputStream()); writeAndClose(stream, "c3po"); assertEquals(0, connection.openStreamCount()); @@ -87,20 +89,20 @@ public final class SpdyConnectionTest { assertFalse(synStream.outFinished); assertEquals(1, synStream.streamId); assertEquals(0, synStream.associatedStreamId); - assertEquals(Arrays.asList("b", "banana"), synStream.nameValueBlock); + assertEquals(asByteStringList("b", "banana"), synStream.nameValueBlock); MockSpdyPeer.InFrame requestData = peer.takeFrame(); assertTrue(Arrays.equals("c3po".getBytes("UTF-8"), requestData.data)); } @Test public void headersOnlyStreamIsClosedAfterReplyHeaders() throws Exception { peer.acceptFrame(); // SYN_STREAM - peer.sendFrame().synReply(false, 1, Arrays.asList("b", "banana")); + peer.sendFrame().synReply(false, 1, asByteStringList("b", "banana")); peer.play(); SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("a", "android"), false, false); + SpdyStream stream = connection.newStream(asByteStringList("a", "android"), false, false); assertEquals(1, connection.openStreamCount()); - assertEquals(Arrays.asList("b", "banana"), stream.getResponseHeaders()); + assertEquals(asByteStringList("b", "banana"), stream.getResponseHeaders()); assertEquals(0, connection.openStreamCount()); } @@ -108,13 +110,13 @@ public final class SpdyConnectionTest { // write the mocking script peer.acceptFrame(); // SYN_STREAM peer.acceptFrame(); // PING - peer.sendFrame().synReply(true, 1, Arrays.asList("a", "android")); + peer.sendFrame().synReply(true, 1, asByteStringList("a", "android")); peer.sendFrame().ping(true, 1, 0); peer.play(); // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - connection.newStream(Arrays.asList("b", "banana"), false, true); + connection.newStream(asByteStringList("b", "banana"), false, true); assertEquals(1, connection.openStreamCount()); connection.ping().roundTripTime(); // Ensure that the SYN_REPLY has been received. assertEquals(0, connection.openStreamCount()); @@ -129,7 +131,7 @@ public final class SpdyConnectionTest { @Test public void serverCreatesStreamAndClientReplies() throws Exception { // write the mocking script - peer.sendFrame().synStream(false, false, 2, 0, 5, 129, Arrays.asList("a", "android")); + peer.sendFrame().synStream(false, false, 2, 0, 5, 129, asByteStringList("a", "android")); peer.acceptFrame(); // SYN_REPLY peer.play(); @@ -138,10 +140,10 @@ public final class SpdyConnectionTest { IncomingStreamHandler handler = new IncomingStreamHandler() { @Override public void receive(SpdyStream stream) throws IOException { receiveCount.incrementAndGet(); - assertEquals(Arrays.asList("a", "android"), stream.getRequestHeaders()); + assertEquals(asByteStringList("a", "android"), stream.getRequestHeaders()); assertEquals(null, stream.getErrorCode()); assertEquals(5, stream.getPriority()); - stream.reply(Arrays.asList("b", "banana"), true); + stream.reply(asByteStringList("b", "banana"), true); } }; new SpdyConnection.Builder(true, peer.openSocket()).handler(handler).build(); @@ -152,13 +154,13 @@ public final class SpdyConnectionTest { assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode); assertFalse(reply.inFinished); assertEquals(2, reply.streamId); - assertEquals(Arrays.asList("b", "banana"), reply.nameValueBlock); + assertEquals(asByteStringList("b", "banana"), reply.nameValueBlock); assertEquals(1, receiveCount.get()); } @Test public void replyWithNoData() throws Exception { // write the mocking script - peer.sendFrame().synStream(false, false, 2, 0, 0, 0, Arrays.asList("a", "android")); + peer.sendFrame().synStream(false, false, 2, 0, 0, 0, asByteStringList("a", "android")); peer.acceptFrame(); // SYN_REPLY peer.play(); @@ -166,7 +168,7 @@ public final class SpdyConnectionTest { final AtomicInteger receiveCount = new AtomicInteger(); IncomingStreamHandler handler = new IncomingStreamHandler() { @Override public void receive(SpdyStream stream) throws IOException { - stream.reply(Arrays.asList("b", "banana"), false); + stream.reply(asByteStringList("b", "banana"), false); receiveCount.incrementAndGet(); } }; @@ -177,7 +179,7 @@ public final class SpdyConnectionTest { assertEquals(TYPE_HEADERS, reply.type); assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode); assertTrue(reply.inFinished); - assertEquals(Arrays.asList("b", "banana"), reply.nameValueBlock); + assertEquals(asByteStringList("b", "banana"), reply.nameValueBlock); assertEquals(1, receiveCount.get()); } @@ -327,7 +329,7 @@ public final class SpdyConnectionTest { @Test public void bogusReplyFrameDoesNotDisruptConnection() throws Exception { // write the mocking script - peer.sendFrame().synReply(false, 42, Arrays.asList("a", "android")); + peer.sendFrame().synReply(false, 42, asByteStringList("a", "android")); peer.acceptFrame(); // RST_STREAM peer.sendFrame().ping(false, 2, 0); peer.acceptFrame(); // PING @@ -348,7 +350,7 @@ public final class SpdyConnectionTest { @Test public void clientClosesClientOutputStream() throws Exception { // write the mocking script peer.acceptFrame(); // SYN_STREAM - peer.sendFrame().synReply(false, 1, Arrays.asList("b", "banana")); + peer.sendFrame().synReply(false, 1, asByteStringList("b", "banana")); peer.acceptFrame(); // TYPE_DATA peer.acceptFrame(); // TYPE_DATA with FLAG_FIN peer.acceptFrame(); // PING @@ -359,7 +361,7 @@ public final class SpdyConnectionTest { SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()) .handler(REJECT_INCOMING_STREAMS) .build(); - SpdyStream stream = connection.newStream(Arrays.asList("a", "android"), true, false); + SpdyStream stream = connection.newStream(asByteStringList("a", "android"), true, false); OutputStream out = stream.getOutputStream(); out.write("square".getBytes(UTF_8)); out.flush(); @@ -405,7 +407,7 @@ public final class SpdyConnectionTest { SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()) .handler(REJECT_INCOMING_STREAMS) .build(); - SpdyStream stream = connection.newStream(Arrays.asList("a", "android"), true, true); + SpdyStream stream = connection.newStream(asByteStringList("a", "android"), true, true); OutputStream out = stream.getOutputStream(); connection.ping().roundTripTime(); // Ensure that the RST_CANCEL has been received. try { @@ -447,7 +449,7 @@ public final class SpdyConnectionTest { SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()) .handler(REJECT_INCOMING_STREAMS) .build(); - SpdyStream stream = connection.newStream(Arrays.asList("a", "android"), false, true); + SpdyStream stream = connection.newStream(asByteStringList("a", "android"), false, true); InputStream in = stream.getInputStream(); OutputStream out = stream.getOutputStream(); in.close(); @@ -492,7 +494,7 @@ public final class SpdyConnectionTest { SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()) .handler(REJECT_INCOMING_STREAMS) .build(); - SpdyStream stream = connection.newStream(Arrays.asList("a", "android"), true, true); + SpdyStream stream = connection.newStream(asByteStringList("a", "android"), true, true); InputStream in = stream.getInputStream(); OutputStream out = stream.getOutputStream(); in.close(); @@ -528,7 +530,7 @@ public final class SpdyConnectionTest { @Test public void serverClosesClientInputStream() throws Exception { // write the mocking script peer.acceptFrame(); // SYN_STREAM - peer.sendFrame().synReply(false, 1, Arrays.asList("b", "banana")); + peer.sendFrame().synReply(false, 1, asByteStringList("b", "banana")); peer.sendFrame().data(true, 1, "square".getBytes(UTF_8)); peer.play(); @@ -536,7 +538,7 @@ public final class SpdyConnectionTest { SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()) .handler(REJECT_INCOMING_STREAMS) .build(); - SpdyStream stream = connection.newStream(Arrays.asList("a", "android"), false, true); + SpdyStream stream = connection.newStream(asByteStringList("a", "android"), false, true); InputStream in = stream.getInputStream(); assertStreamData("square", in); assertEquals(0, connection.openStreamCount()); @@ -552,17 +554,17 @@ public final class SpdyConnectionTest { @Test public void remoteDoubleSynReply() throws Exception { // write the mocking script peer.acceptFrame(); // SYN_STREAM - peer.sendFrame().synReply(false, 1, Arrays.asList("a", "android")); + peer.sendFrame().synReply(false, 1, asByteStringList("a", "android")); peer.acceptFrame(); // PING - peer.sendFrame().synReply(false, 1, Arrays.asList("b", "banana")); + peer.sendFrame().synReply(false, 1, asByteStringList("b", "banana")); peer.sendFrame().ping(true, 1, 0); peer.acceptFrame(); // RST_STREAM peer.play(); // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("c", "cola"), true, true); - assertEquals(Arrays.asList("a", "android"), stream.getResponseHeaders()); + SpdyStream stream = connection.newStream(asByteStringList("c", "cola"), true, true); + assertEquals(asByteStringList("a", "android"), stream.getResponseHeaders()); connection.ping().roundTripTime(); // Ensure that the 2nd SYN REPLY has been received. try { stream.getInputStream().read(); @@ -585,9 +587,9 @@ public final class SpdyConnectionTest { @Test public void remoteDoubleSynStream() throws Exception { // write the mocking script - peer.sendFrame().synStream(false, false, 2, 0, 0, 0, Arrays.asList("a", "android")); + peer.sendFrame().synStream(false, false, 2, 0, 0, 0, asByteStringList("a", "android")); peer.acceptFrame(); // SYN_REPLY - peer.sendFrame().synStream(false, false, 2, 0, 0, 0, Arrays.asList("b", "banana")); + peer.sendFrame().synStream(false, false, 2, 0, 0, 0, asByteStringList("b", "banana")); peer.acceptFrame(); // RST_STREAM peer.play(); @@ -596,9 +598,9 @@ public final class SpdyConnectionTest { IncomingStreamHandler handler = new IncomingStreamHandler() { @Override public void receive(SpdyStream stream) throws IOException { receiveCount.incrementAndGet(); - assertEquals(Arrays.asList("a", "android"), stream.getRequestHeaders()); + assertEquals(asByteStringList("a", "android"), stream.getRequestHeaders()); assertEquals(null, stream.getErrorCode()); - stream.reply(Arrays.asList("c", "cola"), true); + stream.reply(asByteStringList("c", "cola"), true); } }; new SpdyConnection.Builder(true, peer.openSocket()).handler(handler).build(); @@ -617,7 +619,7 @@ public final class SpdyConnectionTest { @Test public void remoteSendsDataAfterInFinished() throws Exception { // write the mocking script peer.acceptFrame(); // SYN_STREAM - peer.sendFrame().synReply(false, 1, Arrays.asList("a", "android")); + peer.sendFrame().synReply(false, 1, asByteStringList("a", "android")); peer.sendFrame().data(true, 1, "robot".getBytes("UTF-8")); peer.sendFrame().data(true, 1, "c3po".getBytes("UTF-8")); // Ignored. peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded. @@ -626,8 +628,8 @@ public final class SpdyConnectionTest { // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true); - assertEquals(Arrays.asList("a", "android"), stream.getResponseHeaders()); + SpdyStream stream = connection.newStream(asByteStringList("b", "banana"), true, true); + assertEquals(asByteStringList("a", "android"), stream.getResponseHeaders()); assertStreamData("robot", stream.getInputStream()); // verify the peer received what was expected @@ -642,7 +644,7 @@ public final class SpdyConnectionTest { @Test public void remoteSendsTooMuchData() throws Exception { // write the mocking script peer.acceptFrame(); // SYN_STREAM - peer.sendFrame().synReply(false, 1, Arrays.asList("b", "banana")); + peer.sendFrame().synReply(false, 1, asByteStringList("b", "banana")); peer.sendFrame().data(false, 1, new byte[64 * 1024 + 1]); peer.acceptFrame(); // RST_STREAM peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded. @@ -651,8 +653,8 @@ public final class SpdyConnectionTest { // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("a", "android"), true, true); - assertEquals(Arrays.asList("b", "banana"), stream.getResponseHeaders()); + SpdyStream stream = connection.newStream(asByteStringList("a", "android"), true, true); + assertEquals(asByteStringList("b", "banana"), stream.getResponseHeaders()); // verify the peer received what was expected MockSpdyPeer.InFrame synStream = peer.takeFrame(); @@ -677,7 +679,7 @@ public final class SpdyConnectionTest { // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("a", "android"), true, true); + SpdyStream stream = connection.newStream(asByteStringList("a", "android"), true, true); try { stream.getResponseHeaders(); fail(); @@ -707,8 +709,8 @@ public final class SpdyConnectionTest { // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream1 = connection.newStream(Arrays.asList("a", "android"), true, true); - SpdyStream stream2 = connection.newStream(Arrays.asList("b", "banana"), true, true); + SpdyStream stream1 = connection.newStream(asByteStringList("a", "android"), true, true); + SpdyStream stream2 = connection.newStream(asByteStringList("b", "banana"), true, true); connection.ping().roundTripTime(); // Ensure that the GO_AWAY has been received. stream1.getOutputStream().write("abc".getBytes(UTF_8)); try { @@ -720,7 +722,7 @@ public final class SpdyConnectionTest { stream1.getOutputStream().write("def".getBytes(UTF_8)); stream1.getOutputStream().close(); try { - connection.newStream(Arrays.asList("c", "cola"), true, true); + connection.newStream(asByteStringList("c", "cola"), true, true); fail(); } catch (IOException expected) { assertEquals("shutdown", expected.getMessage()); @@ -745,13 +747,13 @@ public final class SpdyConnectionTest { peer.acceptFrame(); // SYN_STREAM 1 peer.acceptFrame(); // GOAWAY peer.acceptFrame(); // PING - peer.sendFrame().synStream(false, false, 2, 0, 0, 0, Arrays.asList("b", "b")); // Should be ignored! + peer.sendFrame().synStream(false, false, 2, 0, 0, 0, asByteStringList("b", "b")); // Should be ignored! peer.sendFrame().ping(true, 1, 0); peer.play(); // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - connection.newStream(Arrays.asList("a", "android"), true, true); + connection.newStream(asByteStringList("a", "android"), true, true); Ping ping = connection.ping(); connection.shutdown(PROTOCOL_ERROR); assertEquals(1, connection.openStreamCount()); @@ -798,12 +800,12 @@ public final class SpdyConnectionTest { // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("a", "android"), true, true); + SpdyStream stream = connection.newStream(asByteStringList("a", "android"), true, true); assertEquals(1, connection.openStreamCount()); connection.close(); assertEquals(0, connection.openStreamCount()); try { - connection.newStream(Arrays.asList("b", "banana"), true, true); + connection.newStream(asByteStringList("b", "banana"), true, true); fail(); } catch (IOException expected) { assertEquals("shutdown", expected.getMessage()); @@ -848,14 +850,14 @@ public final class SpdyConnectionTest { @Test public void readTimeoutExpires() throws Exception { // write the mocking script peer.acceptFrame(); // SYN_STREAM - peer.sendFrame().synReply(false, 1, Arrays.asList("a", "android")); + peer.sendFrame().synReply(false, 1, asByteStringList("a", "android")); peer.acceptFrame(); // PING peer.sendFrame().ping(true, 1, 0); peer.play(); // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true); + SpdyStream stream = connection.newStream(asByteStringList("b", "banana"), true, true); stream.setReadTimeout(1000); InputStream in = stream.getInputStream(); long startNanos = System.nanoTime(); @@ -878,16 +880,16 @@ public final class SpdyConnectionTest { // write the mocking script peer.acceptFrame(); // SYN_STREAM peer.acceptFrame(); // PING - peer.sendFrame().synReply(false, 1, Arrays.asList("a", "android")); - peer.sendFrame().headers(1, Arrays.asList("c", "c3po")); + peer.sendFrame().synReply(false, 1, asByteStringList("a", "android")); + peer.sendFrame().headers(1, asByteStringList("c", "c3po")); peer.sendFrame().ping(true, 1, 0); peer.play(); // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true); + SpdyStream stream = connection.newStream(asByteStringList("b", "banana"), true, true); connection.ping().roundTripTime(); // Ensure that the HEADERS has been received. - assertEquals(Arrays.asList("a", "android", "c", "c3po"), stream.getResponseHeaders()); + assertEquals(asByteStringList("a", "android", "c", "c3po"), stream.getResponseHeaders()); // verify the peer received what was expected MockSpdyPeer.InFrame synStream = peer.takeFrame(); @@ -901,14 +903,14 @@ public final class SpdyConnectionTest { // write the mocking script peer.acceptFrame(); // SYN_STREAM peer.acceptFrame(); // PING - peer.sendFrame().headers(1, Arrays.asList("c", "c3po")); + peer.sendFrame().headers(1, asByteStringList("c", "c3po")); peer.acceptFrame(); // RST_STREAM peer.sendFrame().ping(true, 1, 0); peer.play(); // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true); + SpdyStream stream = connection.newStream(asByteStringList("b", "banana"), true, true); connection.ping().roundTripTime(); // Ensure that the HEADERS has been received. try { stream.getResponseHeaders(); @@ -931,7 +933,7 @@ public final class SpdyConnectionTest { @Test public void readSendsWindowUpdate() throws Exception { // Write the mocking script. peer.acceptFrame(); // SYN_STREAM - peer.sendFrame().synReply(false, 1, Arrays.asList("a", "android")); + peer.sendFrame().synReply(false, 1, asByteStringList("a", "android")); for (int i = 0; i < 3; i++) { peer.sendFrame().data(false, 1, new byte[WINDOW_UPDATE_THRESHOLD]); peer.acceptFrame(); // WINDOW UPDATE @@ -941,8 +943,8 @@ public final class SpdyConnectionTest { // Play it back. SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true); - assertEquals(Arrays.asList("a", "android"), stream.getResponseHeaders()); + SpdyStream stream = connection.newStream(asByteStringList("b", "banana"), true, true); + assertEquals(asByteStringList("a", "android"), stream.getResponseHeaders()); InputStream in = stream.getInputStream(); int total = 0; byte[] buffer = new byte[1024]; @@ -974,7 +976,7 @@ public final class SpdyConnectionTest { // Play it back. SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true); + SpdyStream stream = connection.newStream(asByteStringList("b", "banana"), true, true); OutputStream out = stream.getOutputStream(); out.write(new byte[Settings.DEFAULT_INITIAL_WINDOW_SIZE]); interruptAfterDelay(500); @@ -995,14 +997,14 @@ public final class SpdyConnectionTest { @Test public void testTruncatedDataFrame() throws Exception { // write the mocking script peer.acceptFrame(); // SYN_STREAM - peer.sendFrame().synReply(false, 1, Arrays.asList("a", "android")); + peer.sendFrame().synReply(false, 1, asByteStringList("a", "android")); peer.sendTruncatedFrame(8 + 100).data(false, 1, new byte[1024]); peer.play(); // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true); - assertEquals(Arrays.asList("a", "android"), stream.getResponseHeaders()); + SpdyStream stream = connection.newStream(asByteStringList("b", "banana"), true, true); + assertEquals(asByteStringList("a", "android"), stream.getResponseHeaders()); InputStream in = stream.getInputStream(); try { Util.readFully(in, new byte[101]); @@ -1033,9 +1035,9 @@ public final class SpdyConnectionTest { // play it back SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build(); - SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true); - assertEquals("a", stream.getResponseHeaders().get(0)); - assertEquals(60, stream.getResponseHeaders().get(1).length()); + SpdyStream stream = connection.newStream(asByteStringList("b", "banana"), true, true); + assertEquals("a", stream.getResponseHeaders().get(0).utf8()); + assertEquals(60, stream.getResponseHeaders().get(1).size()); assertStreamData("robot", stream.getInputStream()); } diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java index 19dba82b3..2962b1ccb 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java @@ -19,6 +19,7 @@ package com.squareup.okhttp.internal.http; import com.squareup.okhttp.Headers; import com.squareup.okhttp.Request; import com.squareup.okhttp.Response; +import com.squareup.okhttp.internal.ByteString; import com.squareup.okhttp.internal.spdy.ErrorCode; import com.squareup.okhttp.internal.spdy.SpdyConnection; import com.squareup.okhttp.internal.spdy.SpdyStream; @@ -79,27 +80,29 @@ public final class SpdyTransport implements Transport { * Names are all lower case. No names are repeated. If any name has multiple * values, they are concatenated using "\0" as a delimiter. */ - public static List writeNameValueBlock(Request request, String protocol, String version) { + public static List writeNameValueBlock(Request request, String protocol, + String version) { Headers headers = request.headers(); - List result = new ArrayList(headers.size() + 10); - result.add(":method"); - result.add(request.method()); - result.add(":path"); - result.add(RequestLine.requestPath(request.url())); - result.add(":version"); - result.add(version); + // TODO: make the known header names constants. + List result = new ArrayList(headers.size() + 10); + result.add(ByteString.encodeUtf8(":method")); + result.add(ByteString.encodeUtf8(request.method())); + result.add(ByteString.encodeUtf8(":path")); + result.add(ByteString.encodeUtf8(RequestLine.requestPath(request.url()))); + result.add(ByteString.encodeUtf8(":version")); + result.add(ByteString.encodeUtf8(version)); if (protocol.equals("spdy/3")) { - result.add(":host"); + result.add(ByteString.encodeUtf8(":host")); } else if (protocol.equals("HTTP-draft-09/2.0")) { - result.add(":authority"); + result.add(ByteString.encodeUtf8(":authority")); } else { throw new AssertionError(); } - result.add(HttpEngine.hostHeader(request.url())); - result.add(":scheme"); - result.add(request.url().getProtocol()); + result.add(ByteString.encodeUtf8(HttpEngine.hostHeader(request.url()))); + result.add(ByteString.encodeUtf8(":scheme")); + result.add(ByteString.encodeUtf8(request.url().getProtocol())); - Set names = new LinkedHashSet(); + Set names = new LinkedHashSet(); for (int i = 0; i < headers.size(); i++) { String name = headers.name(i).toLowerCase(Locale.US); String value = headers.value(i); @@ -118,16 +121,16 @@ public final class SpdyTransport implements Transport { } // If we haven't seen this name before, add the pair to the end of the list... - if (names.add(name)) { - result.add(name); - result.add(value); + if (names.add(ByteString.encodeUtf8(name))) { + result.add(ByteString.encodeUtf8(name)); + result.add(ByteString.encodeUtf8(value)); continue; } // ...otherwise concatenate the existing values and this value. for (int j = 0; j < result.size(); j += 2) { - if (name.equals(result.get(j))) { - result.set(j + 1, result.get(j + 1) + "\0" + value); + if (result.get(j).utf8Equals(name)) { + result.set(j + 1, ByteString.encodeUtf8(result.get(j + 1).utf8() + "\0" + value)); break; } } @@ -136,8 +139,8 @@ public final class SpdyTransport implements Transport { } /** Returns headers for a name value block containing a SPDY response. */ - public static Response.Builder readNameValueBlock(List nameValueBlock, String protocol) - throws IOException { + public static Response.Builder readNameValueBlock(List nameValueBlock, + String protocol) throws IOException { if (nameValueBlock.size() % 2 != 0) { throw new IllegalArgumentException("Unexpected name value block: " + nameValueBlock); } @@ -147,8 +150,8 @@ public final class SpdyTransport implements Transport { Headers.Builder headersBuilder = new Headers.Builder(); headersBuilder.set(OkHeaders.SELECTED_TRANSPORT, protocol); for (int i = 0; i < nameValueBlock.size(); i += 2) { - String name = nameValueBlock.get(i); - String values = nameValueBlock.get(i + 1); + String name = nameValueBlock.get(i).utf8(); + String values = nameValueBlock.get(i + 1).utf8(); for (int start = 0; start < values.length(); ) { int end = values.indexOf('\0', start); if (end == -1) { diff --git a/okhttp/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java b/okhttp/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java index 172d9369d..2387b3bde 100644 --- a/okhttp/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java +++ b/okhttp/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java @@ -18,20 +18,22 @@ package com.squareup.okhttp.internal.http; import com.squareup.okhttp.Headers; import com.squareup.okhttp.Request; import com.squareup.okhttp.Response; +import com.squareup.okhttp.internal.ByteString; import java.io.IOException; import java.util.Arrays; import java.util.List; import org.junit.Test; +import static com.squareup.okhttp.internal.Util.asByteStringList; import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertEquals; public final class HeadersTest { @Test public void parseNameValueBlock() throws IOException { - List nameValueBlock = Arrays.asList( - "cache-control", "no-cache, no-store", - "set-cookie", "Cookie1\u0000Cookie2", - ":status", "200 OK", + List nameValueBlock = asByteStringList( // + "cache-control", "no-cache, no-store", // + "set-cookie", "Cookie1\u0000Cookie2", // + ":status", "200 OK", // ":version", "HTTP/1.1"); Request request = new Request.Builder().url("http://square.com/").build(); Response response = @@ -55,9 +57,9 @@ public final class HeadersTest { } @Test public void readNameValueBlockDropsForbiddenHeadersSpdy3() throws IOException { - List nameValueBlock = Arrays.asList( - ":status", "200 OK", - ":version", "HTTP/1.1", + List nameValueBlock = asByteStringList( // + ":status", "200 OK", // + ":version", "HTTP/1.1", // "connection", "close"); Request request = new Request.Builder().url("http://square.com/").build(); Response response = @@ -69,9 +71,9 @@ public final class HeadersTest { } @Test public void readNameValueBlockDropsForbiddenHeadersHttp2() throws IOException { - List nameValueBlock = Arrays.asList( - ":status", "200 OK", - ":version", "HTTP/1.1", + List nameValueBlock = asByteStringList( // + ":status", "200 OK", // + ":version", "HTTP/1.1", // "connection", "close"); Request request = new Request.Builder().url("http://square.com/").build(); Response response = @@ -90,15 +92,15 @@ public final class HeadersTest { .addHeader("set-cookie", "Cookie2") .header(":status", "200 OK") .build(); - List nameValueBlock = SpdyTransport.writeNameValueBlock(request, "spdy/3", "HTTP/1.1"); - List expected = Arrays.asList( - ":method", "GET", - ":path", "/", - ":version", "HTTP/1.1", - ":host", "square.com", - ":scheme", "http", - "cache-control", "no-cache, no-store", - "set-cookie", "Cookie1\u0000Cookie2", + List nameValueBlock = SpdyTransport.writeNameValueBlock(request, "spdy/3", "HTTP/1.1"); + List expected = asByteStringList( // + ":method", "GET", // + ":path", "/", // + ":version", "HTTP/1.1", // + ":host", "square.com", // + ":scheme", "http", // + "cache-control", "no-cache, no-store", // + "set-cookie", "Cookie1\u0000Cookie2", // ":status", "200 OK"); assertEquals(expected, nameValueBlock); } @@ -109,11 +111,11 @@ public final class HeadersTest { .header("Connection", "close") .header("Transfer-Encoding", "chunked") .build(); - List expected = Arrays.asList( - ":method", "GET", - ":path", "/", - ":version", "HTTP/1.1", - ":host", "square.com", + List expected = asByteStringList( // + ":method", "GET", // + ":path", "/", // + ":version", "HTTP/1.1", // + ":host", "square.com", // ":scheme", "http"); assertEquals(expected, SpdyTransport.writeNameValueBlock(request, "spdy/3", "HTTP/1.1")); } @@ -124,11 +126,11 @@ public final class HeadersTest { .header("Connection", "upgrade") .header("Upgrade", "websocket") .build(); - List expected = Arrays.asList( - ":method", "GET", - ":path", "/", - ":version", "HTTP/1.1", - ":authority", "square.com", + List expected = asByteStringList( // + ":method", "GET", // + ":path", "/", // + ":version", "HTTP/1.1", // + ":authority", "square.com", // ":scheme", "http"); assertEquals(expected, SpdyTransport.writeNameValueBlock(request, "HTTP-draft-09/2.0", "HTTP/1.1"));