diff --git a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java index 3fdaf676e..aed4b1038 100644 --- a/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java +++ b/mockwebserver/src/main/java/com/squareup/okhttp/mockwebserver/MockWebServer.java @@ -344,7 +344,6 @@ public final class MockWebServer { .handler(spdySocketHandler).build(); openSpdyConnections.put(spdyConnection, Boolean.TRUE); openClientSockets.remove(socket); - spdyConnection.readConnectionHeader(); return; } diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameReader.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameReader.java index 9d4c233c6..0c086c4d6 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameReader.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/FrameReader.java @@ -16,6 +16,7 @@ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.bytes.ByteString; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; @@ -71,11 +72,12 @@ public interface FrameReader extends Closeable { * on a new connection if they are idempotent. * * @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 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 diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/HpackDraft05.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/HpackDraft05.java index 0bd8b0677..1f0ea21ff 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/HpackDraft05.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/HpackDraft05.java @@ -1,10 +1,12 @@ package com.squareup.okhttp.internal.spdy; 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.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.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; @@ -13,8 +15,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import static com.squareup.okhttp.internal.Util.asciiLowerCase; - /** * Read and write HPACK v05. * @@ -99,8 +99,9 @@ final class HpackDraft05 { static final class Reader { private final Huffman.Codec huffmanCodec; - private final InputStream in; private final List
emittedHeaders = new ArrayList
(); + private final Source source; + private final OkBuffer buffer = new OkBuffer(); private int maxHeaderTableByteCount; // Visible for testing. @@ -123,10 +124,10 @@ final class HpackDraft05 { long referencedStaticHeaders = 0L; 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.maxHeaderTableByteCount = maxHeaderTableByteCount; - this.in = in; + this.source = source; } int maxHeaderTableByteCount() { @@ -181,8 +182,9 @@ final class HpackDraft05 { * set of emitted headers. */ void readHeaders() throws IOException { - while (in.available() > 0) { - int b = in.read() & 0xff; + while (buffer.byteCount() > 0 + || source.read(buffer, 2048, Deadline.NONE) != -1) { + int b = buffer.readByte() & 0xff; if (b == 0x80) { // 10000000 clearReferenceSet(); } else if ((b & 0x80) == 0x80) { // 1NNNNNNN @@ -333,7 +335,8 @@ final class HpackDraft05 { } 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 { @@ -365,16 +368,25 @@ final class HpackDraft05 { ByteString readByteString(boolean asciiLowercase) throws IOException { int firstByte = readByte(); int length = readInt(firstByte, PREFIX_8_BITS); + + boolean huffmanDecode = false; if ((length & 0x80) == 0x80) { // 1NNNNNNN length &= ~0x80; - byte[] buff = new byte[length]; - Util.readFully(in, buff); - buff = huffmanCodec.decode(buff); // TODO: streaming Huffman! - if (asciiLowercase) asciiLowerCase(buff); - return ByteString.of(buff); + huffmanDecode = true; } - 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; } } diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Http20Draft09.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Http20Draft09.java index 0bc07a87b..365f4a733 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Http20Draft09.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Http20Draft09.java @@ -16,15 +16,16 @@ package com.squareup.okhttp.internal.spdy; 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.DataInputStream; import java.io.DataOutputStream; -import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Arrays; import java.util.List; /** @@ -37,8 +38,8 @@ public final class Http20Draft09 implements Variant { return Protocol.HTTP_2; } - private static final byte[] CONNECTION_HEADER = - "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(Util.UTF_8); + private static final ByteString CONNECTION_HEADER + = ByteString.encodeUtf8("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"); static final byte TYPE_DATA = 0x0; static final byte TYPE_HEADERS = 0x1; @@ -59,7 +60,7 @@ public final class Http20Draft09 implements Variant { static final byte FLAG_PRIORITY = 0x8; @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) { @@ -67,39 +68,38 @@ public final class Http20Draft09 implements Variant { } static final class Reader implements FrameReader { - private final DataInputStream in; - private final ContinuationInputStream continuation; + private final OkBuffer buffer = new OkBuffer(); + private final Source source; + private final ContinuationSource continuation; private final boolean client; // Visible for testing. final HpackDraft05.Reader hpackReader; - Reader(InputStream in, int headerTableSize, boolean client) { - this.in = new DataInputStream(in); + Reader(Source source, int headerTableSize, boolean client) { + this.source = source; this.client = client; - this.continuation = new ContinuationInputStream(this.in); + this.continuation = new ContinuationSource(source, buffer); this.hpackReader = new HpackDraft05.Reader(client, headerTableSize, continuation); } @Override public void readConnectionHeader() throws IOException { if (client) return; // Nothing to read; servers don't send connection headers! - byte[] connectionHeader = new byte[CONNECTION_HEADER.length]; - Util.readFully(in, connectionHeader); - if (!Arrays.equals(connectionHeader, CONNECTION_HEADER)) { - throw ioException("Expected a connection header but was %s", - Arrays.toString(connectionHeader)); + OkBuffers.require(source, buffer, CONNECTION_HEADER.size(), Deadline.NONE); + ByteString connectionHeader = buffer.readByteString(CONNECTION_HEADER.size()); + if (!CONNECTION_HEADER.equals(connectionHeader)) { + throw ioException("Expected a connection header but was %s", connectionHeader.utf8()); } } @Override public boolean nextFrame(Handler handler) throws IOException { - int w1; try { - w1 = in.readInt(); + OkBuffers.require(source, buffer, 8, Deadline.NONE); } catch (IOException e) { return false; // This might be a normal socket close. } - - int w2 = in.readInt(); + int w1 = buffer.readInt(); + int w2 = buffer.readInt(); // boolean r = (w1 & 0xc0000000) != 0; // Reserved: Ignore first 2 bits. short length = (short) ((w1 & 0x3fff0000) >> 16); // 14-bit unsigned == max 16383 @@ -147,7 +147,7 @@ public final class Http20Draft09 implements Variant { default: // Implementations MUST ignore frames of unsupported or unrecognized types. - Util.skipByReading(in, length); + OkBuffers.skip(source, buffer, length, Deadline.NONE); } return true; } @@ -160,7 +160,8 @@ public final class Http20Draft09 implements Variant { int priority = -1; 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. } @@ -187,14 +188,15 @@ public final class Http20Draft09 implements Variant { throws IOException { boolean inFinished = (flags & FLAG_END_STREAM) != 0; // 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) throws IOException { if (length != 4) throw ioException("TYPE_PRIORITY length: %d != 4", length); 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. int priority = (w1 & 0x7fffffff); handler.priority(streamId, priority); @@ -204,7 +206,8 @@ public final class Http20Draft09 implements Variant { throws IOException { if (length != 4) throw ioException("TYPE_RST_STREAM length: %d != 4", length); 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); if (errorCode == null) { 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); Settings settings = new Settings(); + OkBuffers.require(source, buffer, length, Deadline.NONE); for (int i = 0; i < length; i += 8) { - int w1 = in.readInt(); - int value = in.readInt(); + int w1 = buffer.readInt(); + int value = buffer.readInt(); // int r = (w1 & 0xff000000) >>> 24; // Reserved. int id = w1 & 0xffffff; settings.set(id, 0, value); @@ -241,7 +245,8 @@ public final class Http20Draft09 implements Variant { if (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. List
headerBlock = readHeaderBlock(length, flags, streamId); handler.pushPromise(streamId, promisedStreamId, headerBlock); @@ -251,8 +256,9 @@ public final class Http20Draft09 implements Variant { throws IOException { if (length != 8) throw ioException("TYPE_PING length != 8: %s", length); if (streamId != 0) throw ioException("TYPE_PING streamId != 0"); - int payload1 = in.readInt(); - int payload2 = in.readInt(); + OkBuffers.require(source, buffer, 8, Deadline.NONE); + int payload1 = buffer.readInt(); + int payload2 = buffer.readInt(); boolean ack = (flags & FLAG_ACK) != 0; handler.ping(ack, payload1, payload2); } @@ -261,17 +267,18 @@ public final class Http20Draft09 implements Variant { throws IOException { if (length < 8) throw ioException("TYPE_GOAWAY length < 8: %s", length); if (streamId != 0) throw ioException("TYPE_GOAWAY streamId != 0"); - int lastStreamId = in.readInt(); - int errorCodeInt = in.readInt(); + OkBuffers.require(source, buffer, 8, Deadline.NONE); + int lastStreamId = buffer.readInt(); + int errorCodeInt = buffer.readInt(); int opaqueDataLength = length - 8; ErrorCode errorCode = ErrorCode.fromHttp2(errorCodeInt); if (errorCode == null) { 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. - debugData = new byte[opaqueDataLength]; - Util.readFully(in, debugData); + OkBuffers.require(source, buffer, opaqueDataLength, Deadline.NONE); + debugData = buffer.readByteString(opaqueDataLength); } 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) throws IOException { 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); handler.windowUpdate(streamId, increment); } @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 { if (!client) return; // Nothing to write; servers don't send connection headers! - out.write(CONNECTION_HEADER); + out.write(CONNECTION_HEADER.toByteArray()); 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 - * continuation frames as they are needed by {@link HpackDraft05.Reader#readHeaders()}. + * Decompression of the header block occurs above the framing layer. This + * class lazily reads continuation frames as they are needed by {@link + * HpackDraft05.Reader#readHeaders()}. */ - static final class ContinuationInputStream extends InputStream { - private final DataInputStream in; + static final class ContinuationSource implements Source { + private final Source source; + private final OkBuffer buffer; int length; byte flags; @@ -489,65 +499,36 @@ public final class Http20Draft09 implements Variant { int left; - ContinuationInputStream(DataInputStream in) { - this.in = in; + public ContinuationSource(Source source, OkBuffer buffer) { + this.source = source; + this.buffer = buffer; } - @Override public int read() throws IOException { - if (left == 0) { - if (endHeaders()) { - throw eofReading(1); - } else { - readContinuationHeader(); - } + @Override public long read(OkBuffer sink, long byteCount, Deadline deadline) + throws IOException { + while (left == 0) { + if ((flags & FLAG_END_HEADERS) != 0) return -1; + readContinuationHeader(deadline); + // TODO: test case for empty continuation header? } - left--; - return in.read(); + + long toRead = Math.min(byteCount, left); + long read = buffer.byteCount() > 0 + ? buffer.read(sink, toRead, deadline) + : source.read(sink, toRead, deadline); + if (read == -1) return -1; + left -= read; + return read; } - @Override public int available() throws IOException { - if (left == 0) { - if (endHeaders()) { - return 0; - } else { - readContinuationHeader(); - } - } - return left; + @Override public void close(Deadline deadline) throws IOException { } - @Override public int read(byte[] dst, int offset, int byteCount) 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 { - int read = length - left; - throw new EOFException( - String.format("EOF reading %s more bytes; read %s/%s of frame.", byteCount, read, - length)); - } - - private void readContinuationHeader() throws IOException { + private void readContinuationHeader(Deadline deadline) throws IOException { + OkBuffers.require(source, buffer, 8, deadline); int previousStreamId = streamId; - int w1 = in.readInt(); - int w2 = in.readInt(); + int w1 = buffer.readInt(); + int w2 = buffer.readInt(); length = left = (short) ((w1 & 0x3fff0000) >> 16); byte type = (byte) ((w1 & 0xff00) >> 8); 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 (streamId != previousStreamId) throw ioException("TYPE_CONTINUATION streamId changed"); } - - private boolean endHeaders() { - return (flags & FLAG_END_HEADERS) != 0; - } } } diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Huffman.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Huffman.java index a7768d0ae..d59f528d7 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Huffman.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Huffman.java @@ -15,6 +15,7 @@ */ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.bytes.ByteString; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -84,6 +85,10 @@ class Huffman { return (int) ((len + 7) >> 3); } + ByteString decode(ByteString buf) throws IOException { + return ByteString.of(decode(buf.toByteArray())); + } + byte[] decode(byte[] buf) throws IOException { // FIXME ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Spdy3.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Spdy3.java index 6376d3c97..d9c6ee1b1 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Spdy3.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/Spdy3.java @@ -31,8 +31,6 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.ProtocolException; import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.zip.Deflater; /** @@ -184,8 +182,6 @@ final class Spdy3 implements Variant { return true; 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); return true; } @@ -271,7 +267,7 @@ final class Spdy3 implements Variant { if (errorCode == null) { 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 { diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyConnection.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyConnection.java index daec4ce2a..2eb7a49c2 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyConnection.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/spdy/SpdyConnection.java @@ -18,6 +18,7 @@ package com.squareup.okhttp.internal.spdy; import com.squareup.okhttp.Protocol; import com.squareup.okhttp.internal.NamedRunnable; import com.squareup.okhttp.internal.Util; +import com.squareup.okhttp.internal.bytes.ByteString; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; @@ -444,14 +445,6 @@ public final class SpdyConnection implements Closeable { 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 { private String hostName; private InputStream in; @@ -515,6 +508,9 @@ public final class SpdyConnection implements Closeable { ErrorCode connectionErrorCode = ErrorCode.INTERNAL_ERROR; ErrorCode streamErrorCode = ErrorCode.INTERNAL_ERROR; try { + if (!client) { + frameReader.readConnectionHeader(); + } while (frameReader.nextFrame(this)) { } connectionErrorCode = ErrorCode.NO_ERROR; @@ -665,8 +661,8 @@ public final class SpdyConnection implements Closeable { } } - @Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) { - if (debugData.length > 0) { // TODO: log the debugData + @Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) { + if (debugData.size() > 0) { // TODO: log the debugData } synchronized (SpdyConnection.this) { shutdown = true; diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/BaseTestHandler.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/BaseTestHandler.java index 6dfc1e9a4..67384e406 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/BaseTestHandler.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/BaseTestHandler.java @@ -15,6 +15,7 @@ */ package com.squareup.okhttp.internal.spdy; +import com.squareup.okhttp.internal.bytes.ByteString; import java.io.IOException; import java.io.InputStream; import java.util.List; @@ -49,13 +50,11 @@ class BaseTestHandler implements FrameReader.Handler { fail(); } - @Override - public void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) { + @Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) { fail(); } - @Override - public void windowUpdate(int streamId, long windowSizeIncrement) { + @Override public void windowUpdate(int streamId, long windowSizeIncrement) { fail(); } diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/ContinuationInputStreamTest.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/ContinuationInputStreamTest.java deleted file mode 100644 index 67b53223d..000000000 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/ContinuationInputStreamTest.java +++ /dev/null @@ -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(); - } -} diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/HpackDraft05Test.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/HpackDraft05Test.java index 5f011835f..45301e711 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/HpackDraft05Test.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/HpackDraft05Test.java @@ -16,6 +16,7 @@ package com.squareup.okhttp.internal.spdy; import com.squareup.okhttp.internal.bytes.ByteString; +import com.squareup.okhttp.internal.bytes.OkBuffers; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; 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.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; public class HpackDraft05Test { @@ -795,12 +795,12 @@ public class HpackDraft05Test { @Test public void emptyHeaderName() throws IOException { hpackWriter.writeByteString(ByteString.encodeUtf8("")); assertBytes(0); - assertSame(ByteString.EMPTY, newReader(byteStream(0)).readByteString(true)); - assertSame(ByteString.EMPTY, newReader(byteStream(0)).readByteString(false)); + assertEquals(ByteString.EMPTY, newReader(byteStream(0)).readByteString(true)); + assertEquals(ByteString.EMPTY, newReader(byteStream(0)).readByteString(false)); } 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) { diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Http20Draft09Test.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Http20Draft09Test.java index 802373413..71ba37800 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Http20Draft09Test.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Http20Draft09Test.java @@ -16,7 +16,8 @@ package com.squareup.okhttp.internal.spdy; 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.DataOutputStream; import java.io.IOException; @@ -434,10 +435,11 @@ public class Http20Draft09Test { FrameReader fr = newReader(out); 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(expectedError, errorCode); - assertEquals(0, debugData.length); + assertEquals(0, debugData.size()); } }); } @@ -447,34 +449,36 @@ public class Http20Draft09Test { DataOutputStream dataOut = new DataOutputStream(out); final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR; - final byte[] expectedData = new byte[8]; - Arrays.fill(expectedData, (byte) '*'); + final ByteString expectedData = ByteString.encodeUtf8("abcdefgh"); // 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(0); // no flags. dataOut.writeInt(0); // connection-scope dataOut.writeInt(0); // never read any stream! dataOut.writeInt(expectedError.httpCode); - dataOut.write(expectedData); + dataOut.write(expectedData.toByteArray()); // 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); 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(expectedError, errorCode); - assertArrayEquals(expectedData, debugData); + assertEquals(expectedData, debugData); } }); } 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 { diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/MockSpdyPeer.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/MockSpdyPeer.java index f890c001e..bc51ea404 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/MockSpdyPeer.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/MockSpdyPeer.java @@ -17,6 +17,7 @@ package com.squareup.okhttp.internal.spdy; import com.squareup.okhttp.internal.Util; +import com.squareup.okhttp.internal.bytes.ByteString; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; @@ -256,13 +257,12 @@ public final class MockSpdyPeer implements Closeable { this.payload2 = payload2; } - @Override - public void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) { + @Override public void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) { if (this.type != -1) throw new IllegalStateException(); this.type = Spdy3.TYPE_GOAWAY; this.streamId = lastGoodStreamId; this.errorCode = errorCode; - this.data = debugData; + this.data = debugData.toByteArray(); } @Override public void windowUpdate(int streamId, long windowSizeIncrement) { diff --git a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Spdy3Test.java b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Spdy3Test.java index 4968bd8e4..e488617e8 100644 --- a/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Spdy3Test.java +++ b/okhttp-protocols/src/test/java/com/squareup/okhttp/internal/spdy/Spdy3Test.java @@ -79,10 +79,11 @@ public class Spdy3Test { FrameReader fr = newReader(out); 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(expectedError, errorCode); - assertEquals(0, debugData.length); + assertEquals(0, debugData.size()); } }); }