mirror of
https://github.com/square/okhttp.git
synced 2025-12-03 18:31:17 +03:00
Use OkBuffer in http/2 source stream
This commit is contained in:
@@ -344,7 +344,6 @@ public final class MockWebServer {
|
|||||||
.handler(spdySocketHandler).build();
|
.handler(spdySocketHandler).build();
|
||||||
openSpdyConnections.put(spdyConnection, Boolean.TRUE);
|
openSpdyConnections.put(spdyConnection, Boolean.TRUE);
|
||||||
openClientSockets.remove(socket);
|
openClientSockets.remove(socket);
|
||||||
spdyConnection.readConnectionHeader();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
|
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -71,11 +72,12 @@ public interface FrameReader extends Closeable {
|
|||||||
* on a new connection if they are idempotent.
|
* on a new connection if they are idempotent.
|
||||||
*
|
*
|
||||||
* @param lastGoodStreamId the last stream ID the peer processed before
|
* @param lastGoodStreamId the last stream ID the peer processed before
|
||||||
* sending this message. If {@lastGoodStreamId} is zero, the peer processed no frames.
|
* sending this message. If {@code lastGoodStreamId} is zero, the peer
|
||||||
|
* processed no frames.
|
||||||
* @param errorCode reason for closing the connection.
|
* @param errorCode reason for closing the connection.
|
||||||
* @param debugData only valid for http/2; opaque debug data to send.
|
* @param debugData only valid for http/2; opaque debug data to send.
|
||||||
*/
|
*/
|
||||||
void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData);
|
void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies that an additional {@code windowSizeIncrement} bytes can be
|
* Notifies that an additional {@code windowSizeIncrement} bytes can be
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
import com.squareup.okhttp.internal.BitArray;
|
import com.squareup.okhttp.internal.BitArray;
|
||||||
import com.squareup.okhttp.internal.Util;
|
|
||||||
import com.squareup.okhttp.internal.bytes.ByteString;
|
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||||
|
import com.squareup.okhttp.internal.bytes.Deadline;
|
||||||
|
import com.squareup.okhttp.internal.bytes.OkBuffer;
|
||||||
|
import com.squareup.okhttp.internal.bytes.OkBuffers;
|
||||||
|
import com.squareup.okhttp.internal.bytes.Source;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -13,8 +15,6 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.squareup.okhttp.internal.Util.asciiLowerCase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read and write HPACK v05.
|
* Read and write HPACK v05.
|
||||||
*
|
*
|
||||||
@@ -99,8 +99,9 @@ final class HpackDraft05 {
|
|||||||
static final class Reader {
|
static final class Reader {
|
||||||
private final Huffman.Codec huffmanCodec;
|
private final Huffman.Codec huffmanCodec;
|
||||||
|
|
||||||
private final InputStream in;
|
|
||||||
private final List<Header> emittedHeaders = new ArrayList<Header>();
|
private final List<Header> emittedHeaders = new ArrayList<Header>();
|
||||||
|
private final Source source;
|
||||||
|
private final OkBuffer buffer = new OkBuffer();
|
||||||
private int maxHeaderTableByteCount;
|
private int maxHeaderTableByteCount;
|
||||||
|
|
||||||
// Visible for testing.
|
// Visible for testing.
|
||||||
@@ -123,10 +124,10 @@ final class HpackDraft05 {
|
|||||||
long referencedStaticHeaders = 0L;
|
long referencedStaticHeaders = 0L;
|
||||||
int headerTableByteCount = 0;
|
int headerTableByteCount = 0;
|
||||||
|
|
||||||
Reader(boolean client, int maxHeaderTableByteCount, InputStream in) {
|
Reader(boolean client, int maxHeaderTableByteCount, Source source) {
|
||||||
this.huffmanCodec = client ? Huffman.Codec.RESPONSE : Huffman.Codec.REQUEST;
|
this.huffmanCodec = client ? Huffman.Codec.RESPONSE : Huffman.Codec.REQUEST;
|
||||||
this.maxHeaderTableByteCount = maxHeaderTableByteCount;
|
this.maxHeaderTableByteCount = maxHeaderTableByteCount;
|
||||||
this.in = in;
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxHeaderTableByteCount() {
|
int maxHeaderTableByteCount() {
|
||||||
@@ -181,8 +182,9 @@ final class HpackDraft05 {
|
|||||||
* set of emitted headers.
|
* set of emitted headers.
|
||||||
*/
|
*/
|
||||||
void readHeaders() throws IOException {
|
void readHeaders() throws IOException {
|
||||||
while (in.available() > 0) {
|
while (buffer.byteCount() > 0
|
||||||
int b = in.read() & 0xff;
|
|| source.read(buffer, 2048, Deadline.NONE) != -1) {
|
||||||
|
int b = buffer.readByte() & 0xff;
|
||||||
if (b == 0x80) { // 10000000
|
if (b == 0x80) { // 10000000
|
||||||
clearReferenceSet();
|
clearReferenceSet();
|
||||||
} else if ((b & 0x80) == 0x80) { // 1NNNNNNN
|
} else if ((b & 0x80) == 0x80) { // 1NNNNNNN
|
||||||
@@ -333,7 +335,8 @@ final class HpackDraft05 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int readByte() throws IOException {
|
private int readByte() throws IOException {
|
||||||
return in.read() & 0xff;
|
OkBuffers.require(source, buffer, 1, Deadline.NONE);
|
||||||
|
return buffer.readByte() & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
int readInt(int firstByte, int prefixMask) throws IOException {
|
int readInt(int firstByte, int prefixMask) throws IOException {
|
||||||
@@ -365,16 +368,25 @@ final class HpackDraft05 {
|
|||||||
ByteString readByteString(boolean asciiLowercase) throws IOException {
|
ByteString readByteString(boolean asciiLowercase) throws IOException {
|
||||||
int firstByte = readByte();
|
int firstByte = readByte();
|
||||||
int length = readInt(firstByte, PREFIX_8_BITS);
|
int length = readInt(firstByte, PREFIX_8_BITS);
|
||||||
|
|
||||||
|
boolean huffmanDecode = false;
|
||||||
if ((length & 0x80) == 0x80) { // 1NNNNNNN
|
if ((length & 0x80) == 0x80) { // 1NNNNNNN
|
||||||
length &= ~0x80;
|
length &= ~0x80;
|
||||||
byte[] buff = new byte[length];
|
huffmanDecode = true;
|
||||||
Util.readFully(in, buff);
|
|
||||||
buff = huffmanCodec.decode(buff); // TODO: streaming Huffman!
|
|
||||||
if (asciiLowercase) asciiLowerCase(buff);
|
|
||||||
return ByteString.of(buff);
|
|
||||||
}
|
}
|
||||||
return length == 0 ? ByteString.EMPTY
|
|
||||||
: asciiLowercase ? ByteString.readLowerCase(in, length) : ByteString.read(in, length);
|
OkBuffers.require(source, buffer, length, Deadline.NONE);
|
||||||
|
ByteString byteString = buffer.readByteString(length);
|
||||||
|
|
||||||
|
if (huffmanDecode) {
|
||||||
|
byteString = huffmanCodec.decode(byteString); // TODO: streaming Huffman!
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asciiLowercase) {
|
||||||
|
byteString = byteString.toAsciiLowercase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,15 +16,16 @@
|
|||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
import com.squareup.okhttp.Protocol;
|
import com.squareup.okhttp.Protocol;
|
||||||
import com.squareup.okhttp.internal.Util;
|
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||||
|
import com.squareup.okhttp.internal.bytes.Deadline;
|
||||||
|
import com.squareup.okhttp.internal.bytes.OkBuffer;
|
||||||
|
import com.squareup.okhttp.internal.bytes.OkBuffers;
|
||||||
|
import com.squareup.okhttp.internal.bytes.Source;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,8 +38,8 @@ public final class Http20Draft09 implements Variant {
|
|||||||
return Protocol.HTTP_2;
|
return Protocol.HTTP_2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final byte[] CONNECTION_HEADER =
|
private static final ByteString CONNECTION_HEADER
|
||||||
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(Util.UTF_8);
|
= ByteString.encodeUtf8("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
|
||||||
|
|
||||||
static final byte TYPE_DATA = 0x0;
|
static final byte TYPE_DATA = 0x0;
|
||||||
static final byte TYPE_HEADERS = 0x1;
|
static final byte TYPE_HEADERS = 0x1;
|
||||||
@@ -59,7 +60,7 @@ public final class Http20Draft09 implements Variant {
|
|||||||
static final byte FLAG_PRIORITY = 0x8;
|
static final byte FLAG_PRIORITY = 0x8;
|
||||||
|
|
||||||
@Override public FrameReader newReader(InputStream in, boolean client) {
|
@Override public FrameReader newReader(InputStream in, boolean client) {
|
||||||
return new Reader(in, 4096, client);
|
return new Reader(OkBuffers.source(in), 4096, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public FrameWriter newWriter(OutputStream out, boolean client) {
|
@Override public FrameWriter newWriter(OutputStream out, boolean client) {
|
||||||
@@ -67,39 +68,38 @@ public final class Http20Draft09 implements Variant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final class Reader implements FrameReader {
|
static final class Reader implements FrameReader {
|
||||||
private final DataInputStream in;
|
private final OkBuffer buffer = new OkBuffer();
|
||||||
private final ContinuationInputStream continuation;
|
private final Source source;
|
||||||
|
private final ContinuationSource continuation;
|
||||||
private final boolean client;
|
private final boolean client;
|
||||||
|
|
||||||
// Visible for testing.
|
// Visible for testing.
|
||||||
final HpackDraft05.Reader hpackReader;
|
final HpackDraft05.Reader hpackReader;
|
||||||
|
|
||||||
Reader(InputStream in, int headerTableSize, boolean client) {
|
Reader(Source source, int headerTableSize, boolean client) {
|
||||||
this.in = new DataInputStream(in);
|
this.source = source;
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.continuation = new ContinuationInputStream(this.in);
|
this.continuation = new ContinuationSource(source, buffer);
|
||||||
this.hpackReader = new HpackDraft05.Reader(client, headerTableSize, continuation);
|
this.hpackReader = new HpackDraft05.Reader(client, headerTableSize, continuation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void readConnectionHeader() throws IOException {
|
@Override public void readConnectionHeader() throws IOException {
|
||||||
if (client) return; // Nothing to read; servers don't send connection headers!
|
if (client) return; // Nothing to read; servers don't send connection headers!
|
||||||
byte[] connectionHeader = new byte[CONNECTION_HEADER.length];
|
OkBuffers.require(source, buffer, CONNECTION_HEADER.size(), Deadline.NONE);
|
||||||
Util.readFully(in, connectionHeader);
|
ByteString connectionHeader = buffer.readByteString(CONNECTION_HEADER.size());
|
||||||
if (!Arrays.equals(connectionHeader, CONNECTION_HEADER)) {
|
if (!CONNECTION_HEADER.equals(connectionHeader)) {
|
||||||
throw ioException("Expected a connection header but was %s",
|
throw ioException("Expected a connection header but was %s", connectionHeader.utf8());
|
||||||
Arrays.toString(connectionHeader));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean nextFrame(Handler handler) throws IOException {
|
@Override public boolean nextFrame(Handler handler) throws IOException {
|
||||||
int w1;
|
|
||||||
try {
|
try {
|
||||||
w1 = in.readInt();
|
OkBuffers.require(source, buffer, 8, Deadline.NONE);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return false; // This might be a normal socket close.
|
return false; // This might be a normal socket close.
|
||||||
}
|
}
|
||||||
|
int w1 = buffer.readInt();
|
||||||
int w2 = in.readInt();
|
int w2 = buffer.readInt();
|
||||||
|
|
||||||
// boolean r = (w1 & 0xc0000000) != 0; // Reserved: Ignore first 2 bits.
|
// boolean r = (w1 & 0xc0000000) != 0; // Reserved: Ignore first 2 bits.
|
||||||
short length = (short) ((w1 & 0x3fff0000) >> 16); // 14-bit unsigned == max 16383
|
short length = (short) ((w1 & 0x3fff0000) >> 16); // 14-bit unsigned == max 16383
|
||||||
@@ -147,7 +147,7 @@ public final class Http20Draft09 implements Variant {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
// Implementations MUST ignore frames of unsupported or unrecognized types.
|
// Implementations MUST ignore frames of unsupported or unrecognized types.
|
||||||
Util.skipByReading(in, length);
|
OkBuffers.skip(source, buffer, length, Deadline.NONE);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,8 @@ public final class Http20Draft09 implements Variant {
|
|||||||
|
|
||||||
int priority = -1;
|
int priority = -1;
|
||||||
if ((flags & FLAG_PRIORITY) != 0) {
|
if ((flags & FLAG_PRIORITY) != 0) {
|
||||||
priority = in.readInt() & 0x7fffffff;
|
OkBuffers.require(source, buffer, 4, Deadline.NONE);
|
||||||
|
priority = buffer.readInt() & 0x7fffffff;
|
||||||
length -= 4; // account for above read.
|
length -= 4; // account for above read.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,14 +188,15 @@ public final class Http20Draft09 implements Variant {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
boolean inFinished = (flags & FLAG_END_STREAM) != 0;
|
boolean inFinished = (flags & FLAG_END_STREAM) != 0;
|
||||||
// TODO: checkState open or half-closed (local) or raise STREAM_CLOSED
|
// TODO: checkState open or half-closed (local) or raise STREAM_CLOSED
|
||||||
handler.data(inFinished, streamId, in, length);
|
handler.data(inFinished, streamId, OkBuffers.inputStream(source, buffer), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readPriority(Handler handler, short length, byte flags, int streamId)
|
private void readPriority(Handler handler, short length, byte flags, int streamId)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (length != 4) throw ioException("TYPE_PRIORITY length: %d != 4", length);
|
if (length != 4) throw ioException("TYPE_PRIORITY length: %d != 4", length);
|
||||||
if (streamId == 0) throw ioException("TYPE_PRIORITY streamId == 0");
|
if (streamId == 0) throw ioException("TYPE_PRIORITY streamId == 0");
|
||||||
int w1 = in.readInt();
|
OkBuffers.require(source, buffer, 4, Deadline.NONE);
|
||||||
|
int w1 = buffer.readInt();
|
||||||
// boolean r = (w1 & 0x80000000) != 0; // Reserved.
|
// boolean r = (w1 & 0x80000000) != 0; // Reserved.
|
||||||
int priority = (w1 & 0x7fffffff);
|
int priority = (w1 & 0x7fffffff);
|
||||||
handler.priority(streamId, priority);
|
handler.priority(streamId, priority);
|
||||||
@@ -204,7 +206,8 @@ public final class Http20Draft09 implements Variant {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
if (length != 4) throw ioException("TYPE_RST_STREAM length: %d != 4", length);
|
if (length != 4) throw ioException("TYPE_RST_STREAM length: %d != 4", length);
|
||||||
if (streamId == 0) throw ioException("TYPE_RST_STREAM streamId == 0");
|
if (streamId == 0) throw ioException("TYPE_RST_STREAM streamId == 0");
|
||||||
int errorCodeInt = in.readInt();
|
OkBuffers.require(source, buffer, 4, Deadline.NONE);
|
||||||
|
int errorCodeInt = buffer.readInt();
|
||||||
ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt);
|
ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt);
|
||||||
if (errorCode == null) {
|
if (errorCode == null) {
|
||||||
throw ioException("TYPE_RST_STREAM unexpected error code: %d", errorCodeInt);
|
throw ioException("TYPE_RST_STREAM unexpected error code: %d", errorCodeInt);
|
||||||
@@ -223,9 +226,10 @@ public final class Http20Draft09 implements Variant {
|
|||||||
|
|
||||||
if (length % 8 != 0) throw ioException("TYPE_SETTINGS length %% 8 != 0: %s", length);
|
if (length % 8 != 0) throw ioException("TYPE_SETTINGS length %% 8 != 0: %s", length);
|
||||||
Settings settings = new Settings();
|
Settings settings = new Settings();
|
||||||
|
OkBuffers.require(source, buffer, length, Deadline.NONE);
|
||||||
for (int i = 0; i < length; i += 8) {
|
for (int i = 0; i < length; i += 8) {
|
||||||
int w1 = in.readInt();
|
int w1 = buffer.readInt();
|
||||||
int value = in.readInt();
|
int value = buffer.readInt();
|
||||||
// int r = (w1 & 0xff000000) >>> 24; // Reserved.
|
// int r = (w1 & 0xff000000) >>> 24; // Reserved.
|
||||||
int id = w1 & 0xffffff;
|
int id = w1 & 0xffffff;
|
||||||
settings.set(id, 0, value);
|
settings.set(id, 0, value);
|
||||||
@@ -241,7 +245,8 @@ public final class Http20Draft09 implements Variant {
|
|||||||
if (streamId == 0) {
|
if (streamId == 0) {
|
||||||
throw ioException("PROTOCOL_ERROR: TYPE_PUSH_PROMISE streamId == 0");
|
throw ioException("PROTOCOL_ERROR: TYPE_PUSH_PROMISE streamId == 0");
|
||||||
}
|
}
|
||||||
int promisedStreamId = in.readInt() & 0x7fffffff;
|
OkBuffers.require(source, buffer, 4, Deadline.NONE);
|
||||||
|
int promisedStreamId = buffer.readInt() & 0x7fffffff;
|
||||||
length -= 4; // account for above read.
|
length -= 4; // account for above read.
|
||||||
List<Header> headerBlock = readHeaderBlock(length, flags, streamId);
|
List<Header> headerBlock = readHeaderBlock(length, flags, streamId);
|
||||||
handler.pushPromise(streamId, promisedStreamId, headerBlock);
|
handler.pushPromise(streamId, promisedStreamId, headerBlock);
|
||||||
@@ -251,8 +256,9 @@ public final class Http20Draft09 implements Variant {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
if (length != 8) throw ioException("TYPE_PING length != 8: %s", length);
|
if (length != 8) throw ioException("TYPE_PING length != 8: %s", length);
|
||||||
if (streamId != 0) throw ioException("TYPE_PING streamId != 0");
|
if (streamId != 0) throw ioException("TYPE_PING streamId != 0");
|
||||||
int payload1 = in.readInt();
|
OkBuffers.require(source, buffer, 8, Deadline.NONE);
|
||||||
int payload2 = in.readInt();
|
int payload1 = buffer.readInt();
|
||||||
|
int payload2 = buffer.readInt();
|
||||||
boolean ack = (flags & FLAG_ACK) != 0;
|
boolean ack = (flags & FLAG_ACK) != 0;
|
||||||
handler.ping(ack, payload1, payload2);
|
handler.ping(ack, payload1, payload2);
|
||||||
}
|
}
|
||||||
@@ -261,17 +267,18 @@ public final class Http20Draft09 implements Variant {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
if (length < 8) throw ioException("TYPE_GOAWAY length < 8: %s", length);
|
if (length < 8) throw ioException("TYPE_GOAWAY length < 8: %s", length);
|
||||||
if (streamId != 0) throw ioException("TYPE_GOAWAY streamId != 0");
|
if (streamId != 0) throw ioException("TYPE_GOAWAY streamId != 0");
|
||||||
int lastStreamId = in.readInt();
|
OkBuffers.require(source, buffer, 8, Deadline.NONE);
|
||||||
int errorCodeInt = in.readInt();
|
int lastStreamId = buffer.readInt();
|
||||||
|
int errorCodeInt = buffer.readInt();
|
||||||
int opaqueDataLength = length - 8;
|
int opaqueDataLength = length - 8;
|
||||||
ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt);
|
ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt);
|
||||||
if (errorCode == null) {
|
if (errorCode == null) {
|
||||||
throw ioException("TYPE_GOAWAY unexpected error code: %d", errorCodeInt);
|
throw ioException("TYPE_GOAWAY unexpected error code: %d", errorCodeInt);
|
||||||
}
|
}
|
||||||
byte[] debugData = Util.EMPTY_BYTE_ARRAY;
|
ByteString debugData = ByteString.EMPTY;
|
||||||
if (opaqueDataLength > 0) { // Must read debug data in order to not corrupt the connection.
|
if (opaqueDataLength > 0) { // Must read debug data in order to not corrupt the connection.
|
||||||
debugData = new byte[opaqueDataLength];
|
OkBuffers.require(source, buffer, opaqueDataLength, Deadline.NONE);
|
||||||
Util.readFully(in, debugData);
|
debugData = buffer.readByteString(opaqueDataLength);
|
||||||
}
|
}
|
||||||
handler.goAway(lastStreamId, errorCode, debugData);
|
handler.goAway(lastStreamId, errorCode, debugData);
|
||||||
}
|
}
|
||||||
@@ -279,13 +286,14 @@ public final class Http20Draft09 implements Variant {
|
|||||||
private void readWindowUpdate(Handler handler, short length, byte flags, int streamId)
|
private void readWindowUpdate(Handler handler, short length, byte flags, int streamId)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (length != 4) throw ioException("TYPE_WINDOW_UPDATE length !=4: %s", length);
|
if (length != 4) throw ioException("TYPE_WINDOW_UPDATE length !=4: %s", length);
|
||||||
long increment = (in.readInt() & 0x7fffffff);
|
OkBuffers.require(source, buffer, 4, Deadline.NONE);
|
||||||
|
long increment = (buffer.readInt() & 0x7fffffff);
|
||||||
if (increment == 0) throw ioException("windowSizeIncrement was 0", increment);
|
if (increment == 0) throw ioException("windowSizeIncrement was 0", increment);
|
||||||
handler.windowUpdate(streamId, increment);
|
handler.windowUpdate(streamId, increment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void close() throws IOException {
|
@Override public void close() throws IOException {
|
||||||
in.close();
|
source.close(Deadline.NONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +324,7 @@ public final class Http20Draft09 implements Variant {
|
|||||||
|
|
||||||
@Override public synchronized void connectionHeader() throws IOException {
|
@Override public synchronized void connectionHeader() throws IOException {
|
||||||
if (!client) return; // Nothing to write; servers don't send connection headers!
|
if (!client) return; // Nothing to write; servers don't send connection headers!
|
||||||
out.write(CONNECTION_HEADER);
|
out.write(CONNECTION_HEADER.toByteArray());
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,11 +485,13 @@ public final class Http20Draft09 implements Variant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decompression of the header block occurs above the framing layer. This class lazily reads
|
* Decompression of the header block occurs above the framing layer. This
|
||||||
* continuation frames as they are needed by {@link HpackDraft05.Reader#readHeaders()}.
|
* class lazily reads continuation frames as they are needed by {@link
|
||||||
|
* HpackDraft05.Reader#readHeaders()}.
|
||||||
*/
|
*/
|
||||||
static final class ContinuationInputStream extends InputStream {
|
static final class ContinuationSource implements Source {
|
||||||
private final DataInputStream in;
|
private final Source source;
|
||||||
|
private final OkBuffer buffer;
|
||||||
|
|
||||||
int length;
|
int length;
|
||||||
byte flags;
|
byte flags;
|
||||||
@@ -489,65 +499,36 @@ public final class Http20Draft09 implements Variant {
|
|||||||
|
|
||||||
int left;
|
int left;
|
||||||
|
|
||||||
ContinuationInputStream(DataInputStream in) {
|
public ContinuationSource(Source source, OkBuffer buffer) {
|
||||||
this.in = in;
|
this.source = source;
|
||||||
|
this.buffer = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public int read() throws IOException {
|
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline)
|
||||||
if (left == 0) {
|
throws IOException {
|
||||||
if (endHeaders()) {
|
while (left == 0) {
|
||||||
throw eofReading(1);
|
if ((flags & FLAG_END_HEADERS) != 0) return -1;
|
||||||
} else {
|
readContinuationHeader(deadline);
|
||||||
readContinuationHeader();
|
// TODO: test case for empty continuation header?
|
||||||
}
|
|
||||||
}
|
|
||||||
left--;
|
|
||||||
return in.read();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public int available() throws IOException {
|
long toRead = Math.min(byteCount, left);
|
||||||
if (left == 0) {
|
long read = buffer.byteCount() > 0
|
||||||
if (endHeaders()) {
|
? buffer.read(sink, toRead, deadline)
|
||||||
return 0;
|
: source.read(sink, toRead, deadline);
|
||||||
} else {
|
if (read == -1) return -1;
|
||||||
readContinuationHeader();
|
left -= read;
|
||||||
}
|
return read;
|
||||||
}
|
|
||||||
return left;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public int read(byte[] dst, int offset, int byteCount) throws IOException {
|
@Override public void close(Deadline deadline) throws IOException {
|
||||||
if (byteCount > left) {
|
|
||||||
if (endHeaders()) {
|
|
||||||
throw eofReading(byteCount);
|
|
||||||
} else {
|
|
||||||
int beforeContinuation = left;
|
|
||||||
Util.readFully(in, dst, offset, beforeContinuation);
|
|
||||||
readContinuationHeader();
|
|
||||||
int afterContinuation = byteCount - beforeContinuation;
|
|
||||||
offset += beforeContinuation;
|
|
||||||
Util.readFully(in, dst, offset, afterContinuation);
|
|
||||||
left -= afterContinuation;
|
|
||||||
return byteCount;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Util.readFully(in, dst, offset, byteCount);
|
|
||||||
left -= byteCount;
|
|
||||||
return byteCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private EOFException eofReading(int byteCount) throws EOFException {
|
private void readContinuationHeader(Deadline deadline) throws IOException {
|
||||||
int read = length - left;
|
OkBuffers.require(source, buffer, 8, deadline);
|
||||||
throw new EOFException(
|
|
||||||
String.format("EOF reading %s more bytes; read %s/%s of frame.", byteCount, read,
|
|
||||||
length));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readContinuationHeader() throws IOException {
|
|
||||||
int previousStreamId = streamId;
|
int previousStreamId = streamId;
|
||||||
int w1 = in.readInt();
|
int w1 = buffer.readInt();
|
||||||
int w2 = in.readInt();
|
int w2 = buffer.readInt();
|
||||||
length = left = (short) ((w1 & 0x3fff0000) >> 16);
|
length = left = (short) ((w1 & 0x3fff0000) >> 16);
|
||||||
byte type = (byte) ((w1 & 0xff00) >> 8);
|
byte type = (byte) ((w1 & 0xff00) >> 8);
|
||||||
flags = (byte) (w1 & 0xff);
|
flags = (byte) (w1 & 0xff);
|
||||||
@@ -555,9 +536,5 @@ public final class Http20Draft09 implements Variant {
|
|||||||
if (type != TYPE_CONTINUATION) throw ioException("%s != TYPE_CONTINUATION", type);
|
if (type != TYPE_CONTINUATION) throw ioException("%s != TYPE_CONTINUATION", type);
|
||||||
if (streamId != previousStreamId) throw ioException("TYPE_CONTINUATION streamId changed");
|
if (streamId != previousStreamId) throw ioException("TYPE_CONTINUATION streamId changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean endHeaders() {
|
|
||||||
return (flags & FLAG_END_HEADERS) != 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
|
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@@ -84,6 +85,10 @@ class Huffman {
|
|||||||
return (int) ((len + 7) >> 3);
|
return (int) ((len + 7) >> 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ByteString decode(ByteString buf) throws IOException {
|
||||||
|
return ByteString.of(decode(buf.toByteArray()));
|
||||||
|
}
|
||||||
|
|
||||||
byte[] decode(byte[] buf) throws IOException {
|
byte[] decode(byte[] buf) throws IOException {
|
||||||
// FIXME
|
// FIXME
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ import java.io.OutputStream;
|
|||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.ProtocolException;
|
import java.net.ProtocolException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.zip.Deflater;
|
import java.util.zip.Deflater;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,8 +182,6 @@ final class Spdy3 implements Variant {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Logger logger = Logger.getLogger("com.squareup.okhttp.internal.spdy.Spdy3");
|
|
||||||
logger.log(Level.INFO, "Ignoring unknown frame type " + type);
|
|
||||||
OkBuffers.skip(source, buffer, length, Deadline.NONE);
|
OkBuffers.skip(source, buffer, length, Deadline.NONE);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -271,7 +267,7 @@ final class Spdy3 implements Variant {
|
|||||||
if (errorCode == null) {
|
if (errorCode == null) {
|
||||||
throw ioException("TYPE_GOAWAY unexpected error code: %d", errorCodeInt);
|
throw ioException("TYPE_GOAWAY unexpected error code: %d", errorCodeInt);
|
||||||
}
|
}
|
||||||
handler.goAway(lastGoodStreamId, errorCode, Util.EMPTY_BYTE_ARRAY);
|
handler.goAway(lastGoodStreamId, errorCode, ByteString.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readSettings(Handler handler, int flags, int length) throws IOException {
|
private void readSettings(Handler handler, int flags, int length) throws IOException {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.squareup.okhttp.internal.spdy;
|
|||||||
import com.squareup.okhttp.Protocol;
|
import com.squareup.okhttp.Protocol;
|
||||||
import com.squareup.okhttp.internal.NamedRunnable;
|
import com.squareup.okhttp.internal.NamedRunnable;
|
||||||
import com.squareup.okhttp.internal.Util;
|
import com.squareup.okhttp.internal.Util;
|
||||||
|
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -444,14 +445,6 @@ public final class SpdyConnection implements Closeable {
|
|||||||
frameWriter.settings(okHttpSettings);
|
frameWriter.settings(okHttpSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a connection header if the current variant requires it. This should
|
|
||||||
* be called after {@link Builder#build} for all new connections.
|
|
||||||
*/
|
|
||||||
public void readConnectionHeader() throws IOException {
|
|
||||||
frameReader.readConnectionHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private String hostName;
|
private String hostName;
|
||||||
private InputStream in;
|
private InputStream in;
|
||||||
@@ -515,6 +508,9 @@ public final class SpdyConnection implements Closeable {
|
|||||||
ErrorCode connectionErrorCode = ErrorCode.INTERNAL_ERROR;
|
ErrorCode connectionErrorCode = ErrorCode.INTERNAL_ERROR;
|
||||||
ErrorCode streamErrorCode = ErrorCode.INTERNAL_ERROR;
|
ErrorCode streamErrorCode = ErrorCode.INTERNAL_ERROR;
|
||||||
try {
|
try {
|
||||||
|
if (!client) {
|
||||||
|
frameReader.readConnectionHeader();
|
||||||
|
}
|
||||||
while (frameReader.nextFrame(this)) {
|
while (frameReader.nextFrame(this)) {
|
||||||
}
|
}
|
||||||
connectionErrorCode = ErrorCode.NO_ERROR;
|
connectionErrorCode = ErrorCode.NO_ERROR;
|
||||||
@@ -665,8 +661,8 @@ public final class SpdyConnection implements Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) {
|
@Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
|
||||||
if (debugData.length > 0) { // TODO: log the debugData
|
if (debugData.size() > 0) { // TODO: log the debugData
|
||||||
}
|
}
|
||||||
synchronized (SpdyConnection.this) {
|
synchronized (SpdyConnection.this) {
|
||||||
shutdown = true;
|
shutdown = true;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
|
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -49,13 +50,11 @@ class BaseTestHandler implements FrameReader.Handler {
|
|||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
|
||||||
public void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) {
|
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override public void windowUpdate(int streamId, long windowSizeIncrement) {
|
||||||
public void windowUpdate(int streamId, long windowSizeIncrement) {
|
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,165 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 Square, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.squareup.okhttp.internal.spdy;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static com.squareup.okhttp.internal.spdy.HpackDraft05Test.MutableByteArrayInputStream;
|
|
||||||
import static com.squareup.okhttp.internal.spdy.Http20Draft09.ContinuationInputStream;
|
|
||||||
import static com.squareup.okhttp.internal.spdy.Http20Draft09.FLAG_END_STREAM;
|
|
||||||
import static com.squareup.okhttp.internal.spdy.Http20Draft09.TYPE_DATA;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
public final class ContinuationInputStreamTest {
|
|
||||||
private final MutableByteArrayInputStream bytesIn = new MutableByteArrayInputStream();
|
|
||||||
private final ContinuationInputStream continuation =
|
|
||||||
new ContinuationInputStream(new DataInputStream(bytesIn));
|
|
||||||
|
|
||||||
@Test public void readCantOverrunHeaderPayload() throws IOException {
|
|
||||||
bytesIn.set(onlyHeadersPayloadFollowedByData());
|
|
||||||
|
|
||||||
continuation.length = continuation.left = 3;
|
|
||||||
continuation.flags = Http20Draft09.FLAG_END_HEADERS;
|
|
||||||
continuation.streamId = 12345;
|
|
||||||
|
|
||||||
assertEquals(1, continuation.read());
|
|
||||||
assertEquals(2, continuation.read());
|
|
||||||
assertEquals(3, continuation.read());
|
|
||||||
|
|
||||||
try {
|
|
||||||
continuation.read();
|
|
||||||
fail();
|
|
||||||
} catch (EOFException expected) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void readCantOverrunHeaderContinuationPayload() throws IOException {
|
|
||||||
bytesIn.set(headersPayloadWithContinuationFollowedByData());
|
|
||||||
|
|
||||||
continuation.length = continuation.left = 2;
|
|
||||||
continuation.flags = Http20Draft09.FLAG_NONE;
|
|
||||||
continuation.streamId = 12345;
|
|
||||||
|
|
||||||
assertEquals(1, continuation.read());
|
|
||||||
assertEquals(2, continuation.read());
|
|
||||||
assertEquals(3, continuation.read());
|
|
||||||
assertEquals(0, continuation.available());
|
|
||||||
|
|
||||||
try {
|
|
||||||
continuation.read();
|
|
||||||
fail();
|
|
||||||
} catch (EOFException expected) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void availableWithContinuation() throws IOException {
|
|
||||||
bytesIn.set(headersPayloadWithContinuationFollowedByData());
|
|
||||||
|
|
||||||
continuation.length = continuation.left = 2;
|
|
||||||
continuation.flags = Http20Draft09.FLAG_NONE;
|
|
||||||
continuation.streamId = 12345;
|
|
||||||
|
|
||||||
assertEquals(1, continuation.read());
|
|
||||||
assertEquals(2, continuation.read()); // exhaust frame one
|
|
||||||
|
|
||||||
assertEquals(0, continuation.left);
|
|
||||||
assertEquals(1, continuation.available()); // lazy reads next
|
|
||||||
|
|
||||||
assertEquals(1, continuation.length);
|
|
||||||
assertEquals(1, continuation.left);
|
|
||||||
assertEquals(3, continuation.read());
|
|
||||||
|
|
||||||
assertEquals(0, continuation.available());
|
|
||||||
assertEquals(0, continuation.left);
|
|
||||||
|
|
||||||
try {
|
|
||||||
continuation.read();
|
|
||||||
fail();
|
|
||||||
} catch (EOFException expected) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void readArrayCantOverrunHeaderPayload() throws IOException {
|
|
||||||
bytesIn.set(onlyHeadersPayloadFollowedByData());
|
|
||||||
|
|
||||||
continuation.length = continuation.left = 3;
|
|
||||||
continuation.flags = Http20Draft09.FLAG_END_HEADERS;
|
|
||||||
continuation.streamId = 12345;
|
|
||||||
|
|
||||||
byte[] buff = new byte[3];
|
|
||||||
assertEquals(3, continuation.read(buff));
|
|
||||||
assertTrue(Arrays.equals(buff, new byte[] {1, 2, 3}));
|
|
||||||
|
|
||||||
try {
|
|
||||||
continuation.read(buff);
|
|
||||||
fail();
|
|
||||||
} catch (EOFException expected) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void readArrayCantOverrunHeaderContinuationPayload() throws IOException {
|
|
||||||
bytesIn.set(headersPayloadWithContinuationFollowedByData());
|
|
||||||
|
|
||||||
continuation.length = continuation.left = 2;
|
|
||||||
continuation.flags = Http20Draft09.FLAG_NONE;
|
|
||||||
continuation.streamId = 12345;
|
|
||||||
|
|
||||||
byte[] buff = new byte[3];
|
|
||||||
assertEquals(3, continuation.read(buff));
|
|
||||||
assertTrue(Arrays.equals(buff, new byte[] {1, 2, 3}));
|
|
||||||
|
|
||||||
try {
|
|
||||||
continuation.read(buff);
|
|
||||||
fail();
|
|
||||||
} catch (EOFException expected) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static byte[] onlyHeadersPayloadFollowedByData() throws IOException {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
DataOutputStream dataOut = new DataOutputStream(out);
|
|
||||||
dataOut.write(new byte[] {1, 2, 3});
|
|
||||||
dataOut.writeShort(0);
|
|
||||||
dataOut.write(TYPE_DATA);
|
|
||||||
dataOut.write(FLAG_END_STREAM);
|
|
||||||
dataOut.writeInt(0xFFFFFFFF);
|
|
||||||
return out.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
static byte[] headersPayloadWithContinuationFollowedByData() throws IOException {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
DataOutputStream dataOut = new DataOutputStream(out);
|
|
||||||
dataOut.write(new byte[] {1, 2});
|
|
||||||
dataOut.writeShort(1);
|
|
||||||
dataOut.write(Http20Draft09.TYPE_CONTINUATION);
|
|
||||||
dataOut.write(Http20Draft09.FLAG_END_HEADERS);
|
|
||||||
dataOut.writeInt(12345);
|
|
||||||
dataOut.write(3);
|
|
||||||
dataOut.writeShort(0);
|
|
||||||
dataOut.write(TYPE_DATA);
|
|
||||||
dataOut.write(FLAG_END_STREAM);
|
|
||||||
dataOut.writeInt(0xFFFFFFFF);
|
|
||||||
return out.toByteArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
import com.squareup.okhttp.internal.bytes.ByteString;
|
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||||
|
import com.squareup.okhttp.internal.bytes.OkBuffers;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
@@ -30,7 +31,6 @@ import static com.squareup.okhttp.internal.Util.headerEntries;
|
|||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertSame;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class HpackDraft05Test {
|
public class HpackDraft05Test {
|
||||||
@@ -795,12 +795,12 @@ public class HpackDraft05Test {
|
|||||||
@Test public void emptyHeaderName() throws IOException {
|
@Test public void emptyHeaderName() throws IOException {
|
||||||
hpackWriter.writeByteString(ByteString.encodeUtf8(""));
|
hpackWriter.writeByteString(ByteString.encodeUtf8(""));
|
||||||
assertBytes(0);
|
assertBytes(0);
|
||||||
assertSame(ByteString.EMPTY, newReader(byteStream(0)).readByteString(true));
|
assertEquals(ByteString.EMPTY, newReader(byteStream(0)).readByteString(true));
|
||||||
assertSame(ByteString.EMPTY, newReader(byteStream(0)).readByteString(false));
|
assertEquals(ByteString.EMPTY, newReader(byteStream(0)).readByteString(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private HpackDraft05.Reader newReader(InputStream input) {
|
private HpackDraft05.Reader newReader(InputStream input) {
|
||||||
return new HpackDraft05.Reader(false, 4096, input);
|
return new HpackDraft05.Reader(false, 4096, OkBuffers.source(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream byteStream(int... bytes) {
|
private InputStream byteStream(int... bytes) {
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
import com.squareup.okhttp.internal.Util;
|
import com.squareup.okhttp.internal.Util;
|
||||||
import java.io.ByteArrayInputStream;
|
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||||
|
import com.squareup.okhttp.internal.bytes.OkBuffer;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -434,10 +435,11 @@ public class Http20Draft09Test {
|
|||||||
FrameReader fr = newReader(out);
|
FrameReader fr = newReader(out);
|
||||||
|
|
||||||
fr.nextFrame(new BaseTestHandler() { // Consume the go away frame.
|
fr.nextFrame(new BaseTestHandler() { // Consume the go away frame.
|
||||||
@Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) {
|
@Override public void goAway(
|
||||||
|
int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
|
||||||
assertEquals(expectedStreamId, lastGoodStreamId);
|
assertEquals(expectedStreamId, lastGoodStreamId);
|
||||||
assertEquals(expectedError, errorCode);
|
assertEquals(expectedError, errorCode);
|
||||||
assertEquals(0, debugData.length);
|
assertEquals(0, debugData.size());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -447,34 +449,36 @@ public class Http20Draft09Test {
|
|||||||
DataOutputStream dataOut = new DataOutputStream(out);
|
DataOutputStream dataOut = new DataOutputStream(out);
|
||||||
|
|
||||||
final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;
|
final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;
|
||||||
final byte[] expectedData = new byte[8];
|
final ByteString expectedData = ByteString.encodeUtf8("abcdefgh");
|
||||||
Arrays.fill(expectedData, (byte) '*');
|
|
||||||
|
|
||||||
// Compose the expected GOAWAY frame without debug data.
|
// Compose the expected GOAWAY frame without debug data.
|
||||||
dataOut.writeShort(8 + expectedData.length);
|
dataOut.writeShort(8 + expectedData.size());
|
||||||
dataOut.write(Http20Draft09.TYPE_GOAWAY);
|
dataOut.write(Http20Draft09.TYPE_GOAWAY);
|
||||||
dataOut.write(0); // no flags.
|
dataOut.write(0); // no flags.
|
||||||
dataOut.writeInt(0); // connection-scope
|
dataOut.writeInt(0); // connection-scope
|
||||||
dataOut.writeInt(0); // never read any stream!
|
dataOut.writeInt(0); // never read any stream!
|
||||||
dataOut.writeInt(expectedError.httpCode);
|
dataOut.writeInt(expectedError.httpCode);
|
||||||
dataOut.write(expectedData);
|
dataOut.write(expectedData.toByteArray());
|
||||||
|
|
||||||
// Check writer sends the same bytes.
|
// Check writer sends the same bytes.
|
||||||
assertArrayEquals(out.toByteArray(), sendGoAway(0, expectedError, expectedData));
|
assertArrayEquals(out.toByteArray(), sendGoAway(0, expectedError, expectedData.toByteArray()));
|
||||||
|
|
||||||
FrameReader fr = newReader(out);
|
FrameReader fr = newReader(out);
|
||||||
|
|
||||||
fr.nextFrame(new BaseTestHandler() { // Consume the go away frame.
|
fr.nextFrame(new BaseTestHandler() { // Consume the go away frame.
|
||||||
@Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) {
|
@Override public void goAway(
|
||||||
|
int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
|
||||||
assertEquals(0, lastGoodStreamId);
|
assertEquals(0, lastGoodStreamId);
|
||||||
assertEquals(expectedError, errorCode);
|
assertEquals(expectedError, errorCode);
|
||||||
assertArrayEquals(expectedData, debugData);
|
assertEquals(expectedData, debugData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Http20Draft09.Reader newReader(ByteArrayOutputStream out) {
|
private Http20Draft09.Reader newReader(ByteArrayOutputStream out) {
|
||||||
return new Http20Draft09.Reader(new ByteArrayInputStream(out.toByteArray()), 4096, false);
|
OkBuffer buffer = new OkBuffer();
|
||||||
|
buffer.write(ByteString.of(out.toByteArray()));
|
||||||
|
return new Http20Draft09.Reader(buffer, 4096, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void frameSizeError() throws IOException {
|
@Test public void frameSizeError() throws IOException {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.squareup.okhttp.internal.spdy;
|
package com.squareup.okhttp.internal.spdy;
|
||||||
|
|
||||||
import com.squareup.okhttp.internal.Util;
|
import com.squareup.okhttp.internal.Util;
|
||||||
|
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -256,13 +257,12 @@ public final class MockSpdyPeer implements Closeable {
|
|||||||
this.payload2 = payload2;
|
this.payload2 = payload2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
|
||||||
public void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) {
|
|
||||||
if (this.type != -1) throw new IllegalStateException();
|
if (this.type != -1) throw new IllegalStateException();
|
||||||
this.type = Spdy3.TYPE_GOAWAY;
|
this.type = Spdy3.TYPE_GOAWAY;
|
||||||
this.streamId = lastGoodStreamId;
|
this.streamId = lastGoodStreamId;
|
||||||
this.errorCode = errorCode;
|
this.errorCode = errorCode;
|
||||||
this.data = debugData;
|
this.data = debugData.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void windowUpdate(int streamId, long windowSizeIncrement) {
|
@Override public void windowUpdate(int streamId, long windowSizeIncrement) {
|
||||||
|
|||||||
@@ -79,10 +79,11 @@ public class Spdy3Test {
|
|||||||
FrameReader fr = newReader(out);
|
FrameReader fr = newReader(out);
|
||||||
|
|
||||||
fr.nextFrame(new BaseTestHandler() { // Consume the goAway frame.
|
fr.nextFrame(new BaseTestHandler() { // Consume the goAway frame.
|
||||||
@Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) {
|
@Override public void goAway(
|
||||||
|
int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
|
||||||
assertEquals(expectedStreamId, lastGoodStreamId);
|
assertEquals(expectedStreamId, lastGoodStreamId);
|
||||||
assertEquals(expectedError, errorCode);
|
assertEquals(expectedError, errorCode);
|
||||||
assertEquals(0, debugData.length);
|
assertEquals(0, debugData.size());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user