mirror of
https://github.com/square/okhttp.git
synced 2026-01-24 04:02:07 +03:00
Merge pull request #413 from adriancole/spdy-bytestring
Refactor SPDY code to use ByteString for headers.
This commit is contained in:
@@ -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<String> requestHeaders = stream.getRequestHeaders();
|
||||
List<ByteString> 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<String> responseHeaders =
|
||||
Arrays.asList(":status", "404", ":version", "HTTP/1.1", "content-type", "text/plain");
|
||||
List<ByteString> 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<String> responseHeaders =
|
||||
Arrays.asList(":status", "200", ":version", "HTTP/1.1", "content-type",
|
||||
List<ByteString> 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) {
|
||||
|
||||
@@ -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<String> spdyHeaders = stream.getRequestHeaders();
|
||||
List<ByteString> spdyHeaders = stream.getRequestHeaders();
|
||||
List<String> httpHeaders = new ArrayList<String>();
|
||||
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<String> spdyHeaders = new ArrayList<String>();
|
||||
List<ByteString> spdyHeaders = new ArrayList<ByteString>();
|
||||
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<String> 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);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -392,4 +392,12 @@ public final class Util {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static List<ByteString> asByteStringList(String... strings) {
|
||||
List<ByteString> out = new ArrayList<ByteString>(strings.length);
|
||||
for (String string : strings) {
|
||||
out.add(ByteString.encodeUtf8(string));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> nameValueBlock, HeadersMode headersMode);
|
||||
int priority, List<ByteString> nameValueBlock, HeadersMode headersMode);
|
||||
void rstStream(int streamId, ErrorCode errorCode);
|
||||
void settings(boolean clearPrevious, Settings settings);
|
||||
void noop();
|
||||
|
||||
@@ -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<String> nameValueBlock) throws IOException;
|
||||
void synReply(boolean outFinished, int streamId, List<String> nameValueBlock) throws IOException;
|
||||
void headers(int streamId, List<String> nameValueBlock) throws IOException;
|
||||
int priority, int slot, List<ByteString> nameValueBlock) throws IOException;
|
||||
void synReply(boolean outFinished, int streamId, List<ByteString> nameValueBlock)
|
||||
throws IOException;
|
||||
void headers(int streamId, List<ByteString> 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)
|
||||
|
||||
@@ -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<String> out) {
|
||||
void addTo(List<ByteString> 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<String> emittedHeaders = new ArrayList<String>();
|
||||
private final List<ByteString> emittedHeaders = new ArrayList<ByteString>();
|
||||
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<String> getAndReset() {
|
||||
List<String> result = new ArrayList<String>(emittedHeaders);
|
||||
public List<ByteString> getAndReset() {
|
||||
List<ByteString> result = new ArrayList<ByteString>(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<String> nameValueBlock) throws IOException {
|
||||
public void writeHeaders(List<ByteString> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> nameValueBlock = hpackReader.getAndReset();
|
||||
List<ByteString> 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<String> nameValueBlock)
|
||||
@Override
|
||||
public synchronized void synStream(boolean outFinished, boolean inFinished, int streamId,
|
||||
int associatedStreamId, int priority, int slot, List<ByteString> nameValueBlock)
|
||||
throws IOException {
|
||||
if (inFinished) throw new UnsupportedOperationException();
|
||||
headers(outFinished, streamId, priority, nameValueBlock);
|
||||
}
|
||||
|
||||
@Override public synchronized void synReply(boolean outFinished, int streamId,
|
||||
List<String> nameValueBlock) throws IOException {
|
||||
List<ByteString> nameValueBlock) throws IOException {
|
||||
headers(outFinished, streamId, -1, nameValueBlock);
|
||||
}
|
||||
|
||||
@Override public synchronized void headers(int streamId, List<String> nameValueBlock)
|
||||
@Override public synchronized void headers(int streamId, List<ByteString> nameValueBlock)
|
||||
throws IOException {
|
||||
headers(false, streamId, -1, nameValueBlock);
|
||||
}
|
||||
|
||||
private void headers(boolean outFinished, int streamId, int priority,
|
||||
List<String> nameValueBlock) throws IOException {
|
||||
List<ByteString> nameValueBlock) throws IOException {
|
||||
hpackBuffer.reset();
|
||||
hpackWriter.writeHeaders(nameValueBlock);
|
||||
int type = TYPE_HEADERS;
|
||||
|
||||
@@ -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<String> readNameValueBlock(int length) throws IOException {
|
||||
public List<ByteString> 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<String> entries = new ArrayList<String>(numberOfPairs * 2);
|
||||
List<ByteString> entries = new ArrayList<ByteString>(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 {
|
||||
|
||||
@@ -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<String> nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 10);
|
||||
List<ByteString> 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<String> nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 4);
|
||||
List<ByteString> 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<String> nameValueBlock = nameValueBlockReader.readNameValueBlock(length - 4);
|
||||
List<ByteString> 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<String> nameValueBlock)
|
||||
@Override
|
||||
public synchronized void synStream(boolean outFinished, boolean inFinished, int streamId,
|
||||
int associatedStreamId, int priority, int slot, List<ByteString> 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<String> nameValueBlock) throws IOException {
|
||||
@Override public synchronized void synReply(boolean outFinished, int streamId,
|
||||
List<ByteString> 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<String> nameValueBlock)
|
||||
@Override public synchronized void headers(int streamId, List<ByteString> 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<String> nameValueBlock) throws IOException {
|
||||
private void writeNameValueBlockToBuffer(List<ByteString> 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();
|
||||
}
|
||||
|
||||
@@ -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<String> requestHeaders, boolean out, boolean in)
|
||||
public SpdyStream newStream(List<ByteString> 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<String> alternating)
|
||||
void writeSynReply(int streamId, boolean outFinished, List<ByteString> 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<String> nameValueBlock,
|
||||
int associatedStreamId, int priority, List<ByteString> nameValueBlock,
|
||||
HeadersMode headersMode) {
|
||||
SpdyStream stream;
|
||||
synchronized (SpdyConnection.this) {
|
||||
|
||||
@@ -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<String> requestHeaders;
|
||||
private final List<ByteString> requestHeaders;
|
||||
|
||||
/** Headers sent in the stream reply. Null if reply is either not sent or not sent yet. */
|
||||
private List<String> responseHeaders;
|
||||
private List<ByteString> 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<String> requestHeaders, Settings settings) {
|
||||
int priority, List<ByteString> 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<String> getRequestHeaders() {
|
||||
public List<ByteString> 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<String> getResponseHeaders() throws IOException {
|
||||
public synchronized List<ByteString> 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<String> responseHeaders, boolean out) throws IOException {
|
||||
public void reply(List<ByteString> 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<String> headers, HeadersMode headersMode) {
|
||||
void receiveHeaders(List<ByteString> 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<String> newHeaders = new ArrayList<String>();
|
||||
List<ByteString> newHeaders = new ArrayList<ByteString>();
|
||||
newHeaders.addAll(responseHeaders);
|
||||
newHeaders.addAll(headers);
|
||||
this.responseHeaders = newHeaders;
|
||||
|
||||
@@ -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<String> nameValueBlock, HeadersMode headersMode) {
|
||||
int priority, List<ByteString> nameValueBlock, HeadersMode headersMode) {
|
||||
fail();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> sentHeaders = Arrays.asList("foo", new String(tooLarge));
|
||||
final List<ByteString> 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<String> sentHeaders = Arrays.asList("name", "value");
|
||||
List<ByteString> 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<String> receivedHeaders = reader.getAndReset();
|
||||
List<ByteString> receivedHeaders = reader.getAndReset();
|
||||
assertEquals(sentHeaders, receivedHeaders);
|
||||
}
|
||||
|
||||
@@ -407,7 +409,7 @@ public class HpackDraft05Test {
|
||||
return new DataInputStream(new ByteArrayInputStream(data));
|
||||
}
|
||||
|
||||
private ByteArrayOutputStream literalHeaders(List<String> sentHeaders) throws IOException {
|
||||
private ByteArrayOutputStream literalHeaders(List<ByteString> 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);
|
||||
}
|
||||
|
||||
@@ -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<String> sentHeaders = Arrays.asList("name", "value");
|
||||
final List<ByteString> 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<String> nameValueBlock,
|
||||
int associatedStreamId, int priority, List<ByteString> 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<String> nameValueBlock,
|
||||
int associatedStreamId, int priority, List<ByteString> 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<String> sentHeaders) throws IOException {
|
||||
private byte[] literalHeaders(List<ByteString> sentHeaders) throws IOException {
|
||||
ByteArrayOutputStream headerBytes = new ByteArrayOutputStream();
|
||||
new HpackDraft05.Writer(new DataOutputStream(headerBytes)).writeHeaders(sentHeaders);
|
||||
return headerBytes.toByteArray();
|
||||
|
||||
@@ -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<String> nameValueBlock;
|
||||
public List<ByteString> 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<String> nameValueBlock,
|
||||
int associatedStreamId, int priority, List<ByteString> nameValueBlock,
|
||||
HeadersMode headersMode) {
|
||||
if (this.type != -1) throw new IllegalStateException();
|
||||
this.type = Spdy3.TYPE_HEADERS;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String> writeNameValueBlock(Request request, String protocol, String version) {
|
||||
public static List<ByteString> writeNameValueBlock(Request request, String protocol,
|
||||
String version) {
|
||||
Headers headers = request.headers();
|
||||
List<String> result = new ArrayList<String>(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<ByteString> result = new ArrayList<ByteString>(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<String> names = new LinkedHashSet<String>();
|
||||
Set<ByteString> names = new LinkedHashSet<ByteString>();
|
||||
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<String> nameValueBlock, String protocol)
|
||||
throws IOException {
|
||||
public static Response.Builder readNameValueBlock(List<ByteString> 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) {
|
||||
|
||||
@@ -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<String> nameValueBlock = Arrays.asList(
|
||||
"cache-control", "no-cache, no-store",
|
||||
"set-cookie", "Cookie1\u0000Cookie2",
|
||||
":status", "200 OK",
|
||||
List<ByteString> 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<String> nameValueBlock = Arrays.asList(
|
||||
":status", "200 OK",
|
||||
":version", "HTTP/1.1",
|
||||
List<ByteString> 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<String> nameValueBlock = Arrays.asList(
|
||||
":status", "200 OK",
|
||||
":version", "HTTP/1.1",
|
||||
List<ByteString> 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<String> nameValueBlock = SpdyTransport.writeNameValueBlock(request, "spdy/3", "HTTP/1.1");
|
||||
List<String> 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<ByteString> nameValueBlock = SpdyTransport.writeNameValueBlock(request, "spdy/3", "HTTP/1.1");
|
||||
List<ByteString> 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<String> expected = Arrays.asList(
|
||||
":method", "GET",
|
||||
":path", "/",
|
||||
":version", "HTTP/1.1",
|
||||
":host", "square.com",
|
||||
List<ByteString> 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<String> expected = Arrays.asList(
|
||||
":method", "GET",
|
||||
":path", "/",
|
||||
":version", "HTTP/1.1",
|
||||
":authority", "square.com",
|
||||
List<ByteString> 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"));
|
||||
|
||||
Reference in New Issue
Block a user