mirror of
https://github.com/square/okhttp.git
synced 2026-01-22 15:42:00 +03:00
Deadlines are per-stream, not per-operation.
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.squareup.okhttp.internal;
|
||||
|
||||
import com.squareup.okhttp.internal.bytes.Deadline;
|
||||
import com.squareup.okhttp.internal.bytes.OkBuffer;
|
||||
import com.squareup.okhttp.internal.bytes.Sink;
|
||||
import com.squareup.okhttp.internal.bytes.Source;
|
||||
@@ -115,7 +114,7 @@ public final class Util {
|
||||
public static void closeQuietly(Source source) {
|
||||
if (source != null) {
|
||||
try {
|
||||
source.close(Deadline.NONE);
|
||||
source.close();
|
||||
} catch (RuntimeException rethrown) {
|
||||
throw rethrown;
|
||||
} catch (Exception ignored) {
|
||||
@@ -130,7 +129,7 @@ public final class Util {
|
||||
public static void closeQuietly(Sink sink) {
|
||||
if (sink != null) {
|
||||
try {
|
||||
sink.close(Deadline.NONE);
|
||||
sink.close();
|
||||
} catch (RuntimeException rethrown) {
|
||||
throw rethrown;
|
||||
} catch (Exception ignored) {
|
||||
@@ -277,7 +276,7 @@ public final class Util {
|
||||
long startNanos = System.nanoTime();
|
||||
OkBuffer skipBuffer = new OkBuffer();
|
||||
while (NANOSECONDS.toMillis(System.nanoTime() - startNanos) < timeoutMillis) {
|
||||
long read = in.read(skipBuffer, 2048, Deadline.NONE);
|
||||
long read = in.read(skipBuffer, 2048);
|
||||
if (read == -1) return true; // Successfully exhausted the stream.
|
||||
skipBuffer.clear();
|
||||
}
|
||||
|
||||
@@ -36,53 +36,53 @@ public final class BufferedSink implements Sink {
|
||||
this(sink, new OkBuffer());
|
||||
}
|
||||
|
||||
@Override public void write(OkBuffer source, long byteCount, Deadline deadline)
|
||||
@Override public void write(OkBuffer source, long byteCount)
|
||||
throws IOException {
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
buffer.write(source, byteCount, deadline);
|
||||
emitCompleteSegments(deadline);
|
||||
buffer.write(source, byteCount);
|
||||
emitCompleteSegments();
|
||||
}
|
||||
|
||||
public void write(ByteString byteString, Deadline deadline) throws IOException {
|
||||
public void write(ByteString byteString) throws IOException {
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
buffer.write(byteString);
|
||||
emitCompleteSegments(deadline);
|
||||
emitCompleteSegments();
|
||||
}
|
||||
|
||||
public void writeUtf8(String string, Deadline deadline) throws IOException {
|
||||
public void writeUtf8(String string) throws IOException {
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
buffer.writeUtf8(string);
|
||||
emitCompleteSegments(deadline);
|
||||
emitCompleteSegments();
|
||||
}
|
||||
|
||||
public void write(byte[] data, int offset, int byteCount, Deadline deadline) throws IOException {
|
||||
public void write(byte[] data, int offset, int byteCount) throws IOException {
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
buffer.write(data, offset, byteCount);
|
||||
emitCompleteSegments(deadline);
|
||||
emitCompleteSegments();
|
||||
}
|
||||
|
||||
public void writeByte(int b, Deadline deadline) throws IOException {
|
||||
public void writeByte(int b) throws IOException {
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
buffer.writeByte(b);
|
||||
emitCompleteSegments(deadline);
|
||||
emitCompleteSegments();
|
||||
}
|
||||
|
||||
public void writeShort(int s, Deadline deadline) throws IOException {
|
||||
public void writeShort(int s) throws IOException {
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
buffer.writeShort(s);
|
||||
emitCompleteSegments(deadline);
|
||||
emitCompleteSegments();
|
||||
}
|
||||
|
||||
public void writeInt(int i, Deadline deadline) throws IOException {
|
||||
public void writeInt(int i) throws IOException {
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
buffer.writeInt(i);
|
||||
emitCompleteSegments(deadline);
|
||||
emitCompleteSegments();
|
||||
}
|
||||
|
||||
void emitCompleteSegments(Deadline deadline) throws IOException {
|
||||
void emitCompleteSegments() throws IOException {
|
||||
long byteCount = buffer.completeSegmentByteCount();
|
||||
if (byteCount == 0) return;
|
||||
sink.write(buffer, byteCount, deadline);
|
||||
sink.write(buffer, byteCount);
|
||||
}
|
||||
|
||||
/** Returns an output stream that writes to this sink. */
|
||||
@@ -91,21 +91,21 @@ public final class BufferedSink implements Sink {
|
||||
@Override public void write(int b) throws IOException {
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
buffer.writeByte((byte) b);
|
||||
emitCompleteSegments(Deadline.NONE);
|
||||
emitCompleteSegments();
|
||||
}
|
||||
|
||||
@Override public void write(byte[] data, int offset, int byteCount) throws IOException {
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
buffer.write(data, offset, byteCount);
|
||||
emitCompleteSegments(Deadline.NONE);
|
||||
emitCompleteSegments();
|
||||
}
|
||||
|
||||
@Override public void flush() throws IOException {
|
||||
BufferedSink.this.flush(Deadline.NONE);
|
||||
BufferedSink.this.flush();
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
BufferedSink.this.close(Deadline.NONE);
|
||||
BufferedSink.this.close();
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
@@ -114,20 +114,25 @@ public final class BufferedSink implements Sink {
|
||||
};
|
||||
}
|
||||
|
||||
@Override public void flush(Deadline deadline) throws IOException {
|
||||
@Override public void flush() throws IOException {
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
if (buffer.byteCount > 0) {
|
||||
sink.write(buffer, buffer.byteCount, deadline);
|
||||
sink.write(buffer, buffer.byteCount);
|
||||
}
|
||||
sink.flush(deadline);
|
||||
sink.flush();
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
flush(deadline);
|
||||
sink.close(deadline);
|
||||
@Override public void close() throws IOException {
|
||||
flush();
|
||||
sink.close();
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override public Sink deadline(Deadline deadline) {
|
||||
sink.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "BufferedSink(" + sink + ")";
|
||||
}
|
||||
|
||||
@@ -39,18 +39,18 @@ public final class BufferedSource implements Source {
|
||||
this(source, new OkBuffer());
|
||||
}
|
||||
|
||||
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline)
|
||||
@Override public long read(OkBuffer sink, long byteCount)
|
||||
throws IOException {
|
||||
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
|
||||
if (buffer.byteCount == 0) {
|
||||
long read = source.read(buffer, Segment.SIZE, deadline);
|
||||
long read = source.read(buffer, Segment.SIZE);
|
||||
if (read == -1) return -1;
|
||||
}
|
||||
|
||||
long toRead = Math.min(byteCount, buffer.byteCount);
|
||||
return buffer.read(sink, toRead, deadline);
|
||||
return buffer.read(sink, toRead);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,8 +58,8 @@ public final class BufferedSource implements Source {
|
||||
* will block until there are bytes to read or the source is definitely
|
||||
* exhausted.
|
||||
*/
|
||||
public boolean exhausted(Deadline deadline) throws IOException {
|
||||
return buffer.byteCount() == 0 && source.read(buffer, Segment.SIZE, deadline) == -1;
|
||||
public boolean exhausted() throws IOException {
|
||||
return buffer.byteCount() == 0 && source.read(buffer, Segment.SIZE) == -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,39 +67,39 @@ public final class BufferedSource implements Source {
|
||||
* an {@link EOFException} if the source is exhausted before the required
|
||||
* bytes can be read.
|
||||
*/
|
||||
void require(long byteCount, Deadline deadline) throws IOException {
|
||||
void require(long byteCount) throws IOException {
|
||||
while (buffer.byteCount < byteCount) {
|
||||
if (source.read(buffer, Segment.SIZE, deadline) == -1) throw new EOFException();
|
||||
if (source.read(buffer, Segment.SIZE) == -1) throw new EOFException();
|
||||
}
|
||||
}
|
||||
|
||||
public byte readByte() throws IOException {
|
||||
require(1, Deadline.NONE);
|
||||
require(1);
|
||||
return buffer.readByte();
|
||||
}
|
||||
|
||||
public ByteString readByteString(int byteCount) throws IOException {
|
||||
require(byteCount, Deadline.NONE);
|
||||
require(byteCount);
|
||||
return buffer.readByteString(byteCount);
|
||||
}
|
||||
|
||||
public short readShort() throws IOException {
|
||||
require(2, Deadline.NONE);
|
||||
require(2);
|
||||
return buffer.readShort();
|
||||
}
|
||||
|
||||
public int readShortLe() throws IOException {
|
||||
require(2, Deadline.NONE);
|
||||
require(2);
|
||||
return buffer.readShortLe();
|
||||
}
|
||||
|
||||
public int readInt() throws IOException {
|
||||
require(4, Deadline.NONE);
|
||||
require(4);
|
||||
return buffer.readInt();
|
||||
}
|
||||
|
||||
public int readIntLe() throws IOException {
|
||||
require(4, Deadline.NONE);
|
||||
require(4);
|
||||
return buffer.readIntLe();
|
||||
}
|
||||
|
||||
@@ -108,9 +108,9 @@ public final class BufferedSource implements Source {
|
||||
* buffer} as a buffer. Throws an {@link EOFException} if the source is
|
||||
* exhausted before the requested bytes can be skipped.
|
||||
*/
|
||||
public void skip(long byteCount, Deadline deadline) throws IOException {
|
||||
public void skip(long byteCount) throws IOException {
|
||||
while (byteCount > 0) {
|
||||
if (buffer.byteCount == 0 && source.read(buffer, Segment.SIZE, deadline) == -1) {
|
||||
if (buffer.byteCount == 0 && source.read(buffer, Segment.SIZE) == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
long toSkip = Math.min(byteCount, buffer.byteCount());
|
||||
@@ -123,12 +123,12 @@ public final class BufferedSource implements Source {
|
||||
* Returns the index of {@code b} in the buffer, refilling it if necessary
|
||||
* until it is found. This reads an unbounded number of bytes into the buffer.
|
||||
*/
|
||||
public long seek(byte b, Deadline deadline) throws IOException {
|
||||
public long seek(byte b) throws IOException {
|
||||
long start = 0;
|
||||
long index;
|
||||
while ((index = buffer.indexOf(b, start)) == -1) {
|
||||
start = buffer.byteCount;
|
||||
if (source.read(buffer, Segment.SIZE, deadline) == -1) throw new EOFException();
|
||||
if (source.read(buffer, Segment.SIZE) == -1) throw new EOFException();
|
||||
}
|
||||
return index;
|
||||
}
|
||||
@@ -138,7 +138,7 @@ public final class BufferedSource implements Source {
|
||||
return new InputStream() {
|
||||
@Override public int read() throws IOException {
|
||||
if (buffer.byteCount == 0) {
|
||||
long count = source.read(buffer, Segment.SIZE, Deadline.NONE);
|
||||
long count = source.read(buffer, Segment.SIZE);
|
||||
if (count == -1) return -1;
|
||||
}
|
||||
return buffer.readByte() & 0xff;
|
||||
@@ -148,7 +148,7 @@ public final class BufferedSource implements Source {
|
||||
checkOffsetAndCount(data.length, offset, byteCount);
|
||||
|
||||
if (buffer.byteCount == 0) {
|
||||
long count = source.read(buffer, Segment.SIZE, Deadline.NONE);
|
||||
long count = source.read(buffer, Segment.SIZE);
|
||||
if (count == -1) return -1;
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ public final class BufferedSource implements Source {
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
BufferedSource.this.close(Deadline.NONE);
|
||||
BufferedSource.this.close();
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
@@ -181,10 +181,15 @@ public final class BufferedSource implements Source {
|
||||
};
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
source.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
source.close(deadline);
|
||||
source.close();
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class DeflaterSink implements Sink {
|
||||
this.deflater = deflater;
|
||||
}
|
||||
|
||||
@Override public void write(OkBuffer source, long byteCount, Deadline deadline)
|
||||
@Override public void write(OkBuffer source, long byteCount)
|
||||
throws IOException {
|
||||
checkOffsetAndCount(source.byteCount, 0, byteCount);
|
||||
while (byteCount > 0) {
|
||||
@@ -54,7 +54,7 @@ public final class DeflaterSink implements Sink {
|
||||
deflater.setInput(head.data, head.pos, toDeflate);
|
||||
|
||||
// Deflate those bytes into sink.
|
||||
deflate(deadline, false);
|
||||
deflate(false);
|
||||
|
||||
// Mark those bytes as read.
|
||||
source.byteCount -= toDeflate;
|
||||
@@ -69,7 +69,7 @@ public final class DeflaterSink implements Sink {
|
||||
}
|
||||
|
||||
@IgnoreJRERequirement
|
||||
private void deflate(Deadline deadline, boolean syncFlush) throws IOException {
|
||||
private void deflate(boolean syncFlush) throws IOException {
|
||||
while (true) {
|
||||
Segment s = sink.buffer.writableSegment(1);
|
||||
|
||||
@@ -84,18 +84,27 @@ public final class DeflaterSink implements Sink {
|
||||
if (deflated == 0) return;
|
||||
s.limit += deflated;
|
||||
sink.buffer.byteCount += deflated;
|
||||
sink.emitCompleteSegments(deadline);
|
||||
sink.emitCompleteSegments();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void flush(Deadline deadline) throws IOException {
|
||||
deflate(deadline, true);
|
||||
sink.flush(deadline);
|
||||
@Override public void flush() throws IOException {
|
||||
deflate(true);
|
||||
sink.flush();
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public void close() throws IOException {
|
||||
deflater.finish();
|
||||
deflate(deadline, false);
|
||||
sink.close(deadline);
|
||||
deflate(false);
|
||||
sink.close();
|
||||
}
|
||||
|
||||
@Override public Sink deadline(Deadline deadline) {
|
||||
sink.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "DeflaterSink(" + sink + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,20 +58,20 @@ public final class GzipSource implements Source {
|
||||
this.inflaterSource = new InflaterSource(this.source, inflater);
|
||||
}
|
||||
|
||||
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline) throws IOException {
|
||||
@Override public long read(OkBuffer sink, long byteCount) throws IOException {
|
||||
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
|
||||
if (byteCount == 0) return 0;
|
||||
|
||||
// If we haven't consumed the header, we must consume it before anything else.
|
||||
if (section == SECTION_HEADER) {
|
||||
consumeHeader(deadline);
|
||||
consumeHeader();
|
||||
section = SECTION_BODY;
|
||||
}
|
||||
|
||||
// Attempt to read at least a byte of the body. If we do, we're done.
|
||||
if (section == SECTION_BODY) {
|
||||
long offset = sink.byteCount;
|
||||
long result = inflaterSource.read(sink, byteCount, deadline);
|
||||
long result = inflaterSource.read(sink, byteCount);
|
||||
if (result != -1) {
|
||||
updateCrc(sink, offset, result);
|
||||
return result;
|
||||
@@ -83,40 +83,40 @@ public final class GzipSource implements Source {
|
||||
// trailer before returning a -1 exhausted result; that way if you read to
|
||||
// the end of a GzipSource you guarantee that the CRC has been checked.
|
||||
if (section == SECTION_TRAILER) {
|
||||
consumeTrailer(deadline);
|
||||
consumeTrailer();
|
||||
section = SECTION_DONE;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void consumeHeader(Deadline deadline) throws IOException {
|
||||
private void consumeHeader() throws IOException {
|
||||
// Read the 10-byte header. We peek at the flags byte first so we know if we
|
||||
// need to CRC the entire header. Then we read the magic ID1ID2 sequence.
|
||||
// We can skip everything else in the first 10 bytes.
|
||||
// +---+---+---+---+---+---+---+---+---+---+
|
||||
// |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->)
|
||||
// +---+---+---+---+---+---+---+---+---+---+
|
||||
source.require(10, deadline);
|
||||
source.require(10);
|
||||
byte flags = source.buffer.getByte(3);
|
||||
boolean fhcrc = ((flags >> FHCRC) & 1) == 1;
|
||||
if (fhcrc) updateCrc(source.buffer, 0, 10);
|
||||
|
||||
short id1id2 = source.readShort();
|
||||
checkEqual("ID1ID2", (short) 0x1f8b, id1id2);
|
||||
source.skip(8, deadline);
|
||||
source.skip(8);
|
||||
|
||||
// Skip optional extra fields.
|
||||
// +---+---+=================================+
|
||||
// | XLEN |...XLEN bytes of "extra field"...| (more-->)
|
||||
// +---+---+=================================+
|
||||
if (((flags >> FEXTRA) & 1) == 1) {
|
||||
source.require(2, deadline);
|
||||
source.require(2);
|
||||
if (fhcrc) updateCrc(source.buffer, 0, 2);
|
||||
int xlen = source.buffer.readShortLe() & 0xffff;
|
||||
source.require(xlen, deadline);
|
||||
source.require(xlen);
|
||||
if (fhcrc) updateCrc(source.buffer, 0, xlen);
|
||||
source.skip(xlen, deadline);
|
||||
source.skip(xlen);
|
||||
}
|
||||
|
||||
// Skip an optional 0-terminated name.
|
||||
@@ -124,7 +124,7 @@ public final class GzipSource implements Source {
|
||||
// |...original file name, zero-terminated...| (more-->)
|
||||
// +=========================================+
|
||||
if (((flags >> FNAME) & 1) == 1) {
|
||||
long index = source.seek((byte) 0, deadline);
|
||||
long index = source.seek((byte) 0);
|
||||
if (fhcrc) updateCrc(source.buffer, 0, index + 1);
|
||||
source.buffer.skip(index + 1);
|
||||
}
|
||||
@@ -134,9 +134,9 @@ public final class GzipSource implements Source {
|
||||
// |...file comment, zero-terminated...| (more-->)
|
||||
// +===================================+
|
||||
if (((flags >> FCOMMENT) & 1) == 1) {
|
||||
long index = source.seek((byte) 0, deadline);
|
||||
long index = source.seek((byte) 0);
|
||||
if (fhcrc) updateCrc(source.buffer, 0, index + 1);
|
||||
source.skip(index + 1, deadline);
|
||||
source.skip(index + 1);
|
||||
}
|
||||
|
||||
// Confirm the optional header CRC.
|
||||
@@ -149,7 +149,7 @@ public final class GzipSource implements Source {
|
||||
}
|
||||
}
|
||||
|
||||
private void consumeTrailer(Deadline deadline) throws IOException {
|
||||
private void consumeTrailer() throws IOException {
|
||||
// Read the eight-byte trailer. Confirm the body's CRC and size.
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
// | CRC32 | ISIZE |
|
||||
@@ -158,8 +158,13 @@ public final class GzipSource implements Source {
|
||||
checkEqual("ISIZE", source.readIntLe(), inflater.getTotalOut());
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
inflaterSource.close(deadline);
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
source.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
inflaterSource.close();
|
||||
}
|
||||
|
||||
/** Updates the CRC with the given bytes. */
|
||||
|
||||
@@ -53,13 +53,13 @@ public final class InflaterSource implements Source {
|
||||
}
|
||||
|
||||
@Override public long read(
|
||||
OkBuffer sink, long byteCount, Deadline deadline) throws IOException {
|
||||
OkBuffer sink, long byteCount) throws IOException {
|
||||
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
if (byteCount == 0) return 0;
|
||||
|
||||
while (true) {
|
||||
boolean sourceExhausted = refill(deadline);
|
||||
boolean sourceExhausted = refill();
|
||||
|
||||
// Decompress the inflater's compressed data into the sink.
|
||||
try {
|
||||
@@ -86,14 +86,14 @@ public final class InflaterSource implements Source {
|
||||
* it needs input). Returns true if the inflater required input but the source
|
||||
* was exhausted.
|
||||
*/
|
||||
public boolean refill(Deadline deadline) throws IOException {
|
||||
public boolean refill() throws IOException {
|
||||
if (!inflater.needsInput()) return false;
|
||||
|
||||
releaseInflatedBytes();
|
||||
if (inflater.getRemaining() != 0) throw new IllegalStateException("?"); // TODO: possible?
|
||||
|
||||
// If there are compressed bytes in the source, assign them to the inflater.
|
||||
if (source.exhausted(deadline)) return true;
|
||||
if (source.exhausted()) return true;
|
||||
|
||||
// Assign buffer bytes to the inflater.
|
||||
Segment head = source.buffer.head;
|
||||
@@ -110,10 +110,15 @@ public final class InflaterSource implements Source {
|
||||
source.buffer.skip(toRelease);
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
source.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
if (closed) return;
|
||||
inflater.end();
|
||||
closed = true;
|
||||
source.close(deadline);
|
||||
source.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ public final class OkBuffer implements Source, Sink, Cloneable {
|
||||
return tail;
|
||||
}
|
||||
|
||||
@Override public void write(OkBuffer source, long byteCount, Deadline deadline) {
|
||||
@Override public void write(OkBuffer source, long byteCount) {
|
||||
// Move bytes from the head of the source buffer to the tail of this buffer
|
||||
// while balancing two conflicting goals: don't waste CPU and don't waste
|
||||
// memory.
|
||||
@@ -425,13 +425,18 @@ public final class OkBuffer implements Source, Sink, Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline) throws IOException {
|
||||
@Override public long read(OkBuffer sink, long byteCount) throws IOException {
|
||||
if (this.byteCount == 0) return -1L;
|
||||
if (byteCount > this.byteCount) byteCount = this.byteCount;
|
||||
sink.write(this, byteCount, deadline);
|
||||
sink.write(this, byteCount);
|
||||
return byteCount;
|
||||
}
|
||||
|
||||
@Override public OkBuffer deadline(Deadline deadline) {
|
||||
// All operations are in memory so this class doesn't need to honor deadlines.
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of {@code b} in this, or -1 if this buffer does not
|
||||
* contain {@code b}.
|
||||
@@ -465,10 +470,10 @@ public final class OkBuffer implements Source, Sink, Cloneable {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
@Override public void flush(Deadline deadline) {
|
||||
@Override public void flush() {
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) {
|
||||
@Override public void close() {
|
||||
}
|
||||
|
||||
/** For testing. This returns the sizes of the segments in this buffer. */
|
||||
|
||||
@@ -50,7 +50,9 @@ public final class OkBuffers {
|
||||
/** Returns a sink that writes to {@code out}. */
|
||||
public static Sink sink(final OutputStream out) {
|
||||
return new Sink() {
|
||||
@Override public void write(OkBuffer source, long byteCount, Deadline deadline)
|
||||
private Deadline deadline = Deadline.NONE;
|
||||
|
||||
@Override public void write(OkBuffer source, long byteCount)
|
||||
throws IOException {
|
||||
checkOffsetAndCount(source.byteCount, 0, byteCount);
|
||||
while (byteCount > 0) {
|
||||
@@ -70,14 +72,20 @@ public final class OkBuffers {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void flush(Deadline deadline) throws IOException {
|
||||
@Override public void flush() throws IOException {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public void close() throws IOException {
|
||||
out.close();
|
||||
}
|
||||
|
||||
@Override public Sink deadline(Deadline deadline) {
|
||||
if (deadline == null) throw new IllegalArgumentException("deadline == null");
|
||||
this.deadline = deadline;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "sink(" + out + ")";
|
||||
}
|
||||
@@ -87,8 +95,9 @@ public final class OkBuffers {
|
||||
/** Returns a source that reads from {@code in}. */
|
||||
public static Source source(final InputStream in) {
|
||||
return new Source() {
|
||||
@Override public long read(
|
||||
OkBuffer sink, long byteCount, Deadline deadline) throws IOException {
|
||||
private Deadline deadline = Deadline.NONE;
|
||||
|
||||
@Override public long read(OkBuffer sink, long byteCount) throws IOException {
|
||||
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
|
||||
deadline.throwIfReached();
|
||||
Segment tail = sink.writableSegment(1);
|
||||
@@ -100,10 +109,16 @@ public final class OkBuffers {
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public void close() throws IOException {
|
||||
in.close();
|
||||
}
|
||||
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
if (deadline == null) throw new IllegalArgumentException("deadline == null");
|
||||
this.deadline = deadline;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "source(" + in + ")";
|
||||
}
|
||||
|
||||
@@ -15,22 +15,29 @@
|
||||
*/
|
||||
package com.squareup.okhttp.internal.bytes;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An alternative to OutputStream.
|
||||
*/
|
||||
public interface Sink {
|
||||
public interface Sink extends Closeable {
|
||||
/** Removes {@code byteCount} bytes from {@code source} and appends them to this. */
|
||||
void write(OkBuffer source, long byteCount, Deadline deadline) throws IOException;
|
||||
void write(OkBuffer source, long byteCount) throws IOException;
|
||||
|
||||
/** Pushes all buffered bytes to their final destination. */
|
||||
void flush(Deadline deadline) throws IOException;
|
||||
void flush() throws IOException;
|
||||
|
||||
/**
|
||||
* Pushes all buffered bytes to their final destination and releases the
|
||||
* resources held by this sink. It is an error to write a closed sink. It is
|
||||
* safe to close a sink more than once.
|
||||
*/
|
||||
void close(Deadline deadline) throws IOException;
|
||||
@Override void close() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the deadline for all operations on this sink.
|
||||
* @return this sink.
|
||||
*/
|
||||
Sink deadline(Deadline deadline);
|
||||
}
|
||||
|
||||
@@ -15,22 +15,29 @@
|
||||
*/
|
||||
package com.squareup.okhttp.internal.bytes;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An alternative to InputStream.
|
||||
*/
|
||||
public interface Source {
|
||||
public interface Source extends Closeable {
|
||||
/**
|
||||
* Removes at least 1, and up to {@code byteCount} bytes from this and appends
|
||||
* them to {@code sink}. Returns the number of bytes read, or -1 if this
|
||||
* source is exhausted.
|
||||
*/
|
||||
long read(OkBuffer sink, long byteCount, Deadline deadline) throws IOException;
|
||||
long read(OkBuffer sink, long byteCount) throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the deadline for all operations on this source.
|
||||
* @return this source.
|
||||
*/
|
||||
Source deadline(Deadline deadline);
|
||||
|
||||
/**
|
||||
* Closes this source and releases the resources held by this source. It is an
|
||||
* error to read a closed source. It is safe to close a source more than once.
|
||||
*/
|
||||
void close(Deadline deadline) throws IOException;
|
||||
@Override void close() throws IOException;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.squareup.okhttp.internal.spdy;
|
||||
import com.squareup.okhttp.internal.BitArray;
|
||||
import com.squareup.okhttp.internal.bytes.BufferedSource;
|
||||
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||
import com.squareup.okhttp.internal.bytes.Deadline;
|
||||
import com.squareup.okhttp.internal.bytes.Source;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -180,7 +179,7 @@ final class HpackDraft05 {
|
||||
* set of emitted headers.
|
||||
*/
|
||||
void readHeaders() throws IOException {
|
||||
while (!source.exhausted(Deadline.NONE)) {
|
||||
while (!source.exhausted()) {
|
||||
int b = source.readByte() & 0xff;
|
||||
if (b == 0x80) { // 10000000
|
||||
clearReferenceSet();
|
||||
|
||||
@@ -145,7 +145,7 @@ public final class Http20Draft09 implements Variant {
|
||||
|
||||
default:
|
||||
// Implementations MUST ignore frames of unsupported or unrecognized types.
|
||||
source.skip(length, Deadline.NONE);
|
||||
source.skip(length);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -282,7 +282,7 @@ public final class Http20Draft09 implements Variant {
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
source.close(Deadline.NONE);
|
||||
source.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +491,7 @@ public final class Http20Draft09 implements Variant {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline)
|
||||
@Override public long read(OkBuffer sink, long byteCount)
|
||||
throws IOException {
|
||||
while (left == 0) {
|
||||
if ((flags & FLAG_END_HEADERS) != 0) return -1;
|
||||
@@ -499,13 +499,18 @@ public final class Http20Draft09 implements Variant {
|
||||
// TODO: test case for empty continuation header?
|
||||
}
|
||||
|
||||
long read = source.read(sink, Math.min(byteCount, left), deadline);
|
||||
long read = source.read(sink, Math.min(byteCount, left));
|
||||
if (read == -1) return -1;
|
||||
left -= read;
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
source.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
}
|
||||
|
||||
private void readContinuationHeader() throws IOException {
|
||||
|
||||
@@ -36,17 +36,22 @@ class NameValueBlockReader {
|
||||
// block. We cut the inflater off at its source because we can't predict the
|
||||
// ratio of compressed bytes to uncompressed bytes.
|
||||
Source throttleSource = new Source() {
|
||||
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline)
|
||||
@Override public long read(OkBuffer sink, long byteCount)
|
||||
throws IOException {
|
||||
if (compressedLimit == 0) return -1; // Out of data for the current block.
|
||||
long read = source.read(sink, Math.min(byteCount, compressedLimit), deadline);
|
||||
long read = source.read(sink, Math.min(byteCount, compressedLimit));
|
||||
if (read == -1) return -1;
|
||||
compressedLimit -= read;
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
source.close(deadline);
|
||||
@Override public void close() throws IOException {
|
||||
source.close();
|
||||
}
|
||||
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
source.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,12 +101,12 @@ class NameValueBlockReader {
|
||||
// deflate compression is that sometimes there are bytes remaining in the
|
||||
// stream after we've consumed all of the content.
|
||||
if (compressedLimit > 0) {
|
||||
inflaterSource.refill(Deadline.NONE);
|
||||
inflaterSource.refill();
|
||||
if (compressedLimit != 0) throw new IOException("compressedLimit > 0: " + compressedLimit);
|
||||
}
|
||||
}
|
||||
|
||||
public void close(Deadline deadline) throws IOException {
|
||||
source.close(deadline);
|
||||
public void close() throws IOException {
|
||||
source.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.squareup.okhttp.internal.Platform;
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
import com.squareup.okhttp.internal.bytes.BufferedSource;
|
||||
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||
import com.squareup.okhttp.internal.bytes.Deadline;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -179,7 +178,7 @@ final class Spdy3 implements Variant {
|
||||
return true;
|
||||
|
||||
default:
|
||||
source.skip(length, Deadline.NONE);
|
||||
source.skip(length);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@@ -282,7 +281,7 @@ final class Spdy3 implements Variant {
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
headerBlockReader.close(Deadline.NONE);
|
||||
headerBlockReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.squareup.okhttp.internal.NamedRunnable;
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
import com.squareup.okhttp.internal.bytes.BufferedSource;
|
||||
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||
import com.squareup.okhttp.internal.bytes.Deadline;
|
||||
import com.squareup.okhttp.internal.bytes.OkBuffers;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
@@ -531,7 +530,7 @@ public final class SpdyConnection implements Closeable {
|
||||
SpdyStream dataStream = getStream(streamId);
|
||||
if (dataStream == null) {
|
||||
writeSynResetLater(streamId, ErrorCode.INVALID_STREAM);
|
||||
source.skip(length, Deadline.NONE);
|
||||
source.skip(length);
|
||||
return;
|
||||
}
|
||||
dataStream.receiveData(source, length);
|
||||
|
||||
@@ -355,7 +355,7 @@ public final class SpdyStream {
|
||||
this.maxByteCount = maxByteCount;
|
||||
}
|
||||
|
||||
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline)
|
||||
@Override public long read(OkBuffer sink, long byteCount)
|
||||
throws IOException {
|
||||
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
|
||||
|
||||
@@ -366,7 +366,7 @@ public final class SpdyStream {
|
||||
if (readBuffer.byteCount() == 0) return -1; // This source is exhausted.
|
||||
|
||||
// Move bytes from the read buffer into the caller's buffer.
|
||||
read = readBuffer.read(sink, Math.min(byteCount, readBuffer.byteCount()), deadline);
|
||||
read = readBuffer.read(sink, Math.min(byteCount, readBuffer.byteCount()));
|
||||
|
||||
// Flow control: notify the peer that we're ready for more data!
|
||||
unacknowledgedBytesRead += read;
|
||||
@@ -430,26 +430,26 @@ public final class SpdyStream {
|
||||
|
||||
// If the peer sends more data than we can handle, discard it and close the connection.
|
||||
if (flowControlError) {
|
||||
in.skip(byteCount, Deadline.NONE);
|
||||
in.skip(byteCount);
|
||||
closeLater(ErrorCode.FLOW_CONTROL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Discard data received after the stream is finished. It's probably a benign race.
|
||||
if (finished) {
|
||||
in.skip(byteCount, Deadline.NONE);
|
||||
in.skip(byteCount);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill the receive buffer without holding any locks.
|
||||
long read = in.read(receiveBuffer, byteCount, Deadline.NONE);
|
||||
long read = in.read(receiveBuffer, byteCount);
|
||||
if (read == -1) throw new EOFException();
|
||||
byteCount -= read;
|
||||
|
||||
// Move the received data to the read buffer to the reader can read it.
|
||||
synchronized (SpdyStream.this) {
|
||||
boolean wasEmpty = readBuffer.byteCount() == 0;
|
||||
readBuffer.write(receiveBuffer, receiveBuffer.byteCount(), Deadline.NONE);
|
||||
readBuffer.write(receiveBuffer, receiveBuffer.byteCount());
|
||||
if (wasEmpty) {
|
||||
SpdyStream.this.notifyAll();
|
||||
}
|
||||
@@ -457,7 +457,12 @@ public final class SpdyStream {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
// TODO: honor deadlines.
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
synchronized (SpdyStream.this) {
|
||||
closed = true;
|
||||
readBuffer.clear();
|
||||
|
||||
@@ -24,29 +24,29 @@ public final class BufferedSinkTest {
|
||||
@Test public void bytesEmittedToSinkWithFlush() throws Exception {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
BufferedSink bufferedSink = new BufferedSink(sink);
|
||||
bufferedSink.writeUtf8("abc", Deadline.NONE);
|
||||
bufferedSink.flush(Deadline.NONE);
|
||||
bufferedSink.writeUtf8("abc");
|
||||
bufferedSink.flush();
|
||||
assertEquals(3, sink.byteCount());
|
||||
}
|
||||
|
||||
@Test public void bytesNotEmittedToSinkWithoutFlush() throws Exception {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
BufferedSink bufferedSink = new BufferedSink(sink);
|
||||
bufferedSink.writeUtf8("abc", Deadline.NONE);
|
||||
bufferedSink.writeUtf8("abc");
|
||||
assertEquals(0, sink.byteCount());
|
||||
}
|
||||
|
||||
@Test public void completeSegmentsEmitted() throws Exception {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
BufferedSink bufferedSink = new BufferedSink(sink);
|
||||
bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 3), Deadline.NONE);
|
||||
bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 3));
|
||||
assertEquals(Segment.SIZE * 3, sink.byteCount());
|
||||
}
|
||||
|
||||
@Test public void incompleteSegmentsNotEmitted() throws Exception {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
BufferedSink bufferedSink = new BufferedSink(sink);
|
||||
bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 3 - 1), Deadline.NONE);
|
||||
bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 3 - 1));
|
||||
assertEquals(Segment.SIZE * 2, sink.byteCount());
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ public final class DeflaterSinkTest {
|
||||
data.writeUtf8(original);
|
||||
OkBuffer sink = new OkBuffer();
|
||||
DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
|
||||
deflaterSink.write(data, data.byteCount(), Deadline.NONE);
|
||||
deflaterSink.close(Deadline.NONE);
|
||||
deflaterSink.write(data, data.byteCount());
|
||||
deflaterSink.close();
|
||||
OkBuffer inflated = inflate(sink);
|
||||
assertEquals(original, inflated.readUtf8((int) inflated.byteCount()));
|
||||
}
|
||||
@@ -45,8 +45,8 @@ public final class DeflaterSinkTest {
|
||||
data.writeUtf8(original);
|
||||
OkBuffer sink = new OkBuffer();
|
||||
DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
|
||||
deflaterSink.write(data, data.byteCount(), Deadline.NONE);
|
||||
deflaterSink.flush(Deadline.NONE);
|
||||
deflaterSink.write(data, data.byteCount());
|
||||
deflaterSink.flush();
|
||||
OkBuffer inflated = inflate(sink);
|
||||
assertEquals(original, inflated.readUtf8((int) inflated.byteCount()));
|
||||
}
|
||||
@@ -57,8 +57,8 @@ public final class DeflaterSinkTest {
|
||||
data.writeUtf8(original);
|
||||
OkBuffer sink = new OkBuffer();
|
||||
DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
|
||||
deflaterSink.write(data, data.byteCount(), Deadline.NONE);
|
||||
deflaterSink.close(Deadline.NONE);
|
||||
deflaterSink.write(data, data.byteCount());
|
||||
deflaterSink.close();
|
||||
OkBuffer inflated = inflate(sink);
|
||||
assertEquals(original, inflated.readUtf8((int) inflated.byteCount()));
|
||||
}
|
||||
@@ -69,8 +69,8 @@ public final class DeflaterSinkTest {
|
||||
data.write(original);
|
||||
OkBuffer sink = new OkBuffer();
|
||||
DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater());
|
||||
deflaterSink.write(data, data.byteCount(), Deadline.NONE);
|
||||
deflaterSink.close(Deadline.NONE);
|
||||
deflaterSink.write(data, data.byteCount());
|
||||
deflaterSink.close();
|
||||
OkBuffer inflated = inflate(sink);
|
||||
assertEquals(original, inflated.readByteString((int) inflated.byteCount()));
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ public class GzipSourceTest {
|
||||
private OkBuffer gunzip(OkBuffer gzipped) throws IOException {
|
||||
OkBuffer result = new OkBuffer();
|
||||
GzipSource source = new GzipSource(gzipped);
|
||||
while (source.read(result, Integer.MAX_VALUE, Deadline.NONE) != -1) {
|
||||
while (source.read(result, Integer.MAX_VALUE) != -1) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ public final class InflaterSourceTest {
|
||||
private OkBuffer deflate(OkBuffer buffer) throws IOException {
|
||||
OkBuffer result = new OkBuffer();
|
||||
Sink sink = OkBuffers.sink(new DeflaterOutputStream(new BufferedSink(result).outputStream()));
|
||||
sink.write(buffer, buffer.byteCount(), Deadline.NONE);
|
||||
sink.close(Deadline.NONE);
|
||||
sink.write(buffer, buffer.byteCount());
|
||||
sink.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public final class InflaterSourceTest {
|
||||
private OkBuffer inflate(OkBuffer deflated) throws IOException {
|
||||
OkBuffer result = new OkBuffer();
|
||||
InflaterSource source = new InflaterSource(deflated, new Inflater());
|
||||
while (source.read(result, Integer.MAX_VALUE, Deadline.NONE) != -1) {
|
||||
while (source.read(result, Integer.MAX_VALUE) != -1) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public final class OkBufferTest {
|
||||
for (String s : contents) {
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(s);
|
||||
buffer.write(source, source.byteCount(), Deadline.NONE);
|
||||
buffer.write(source, source.byteCount());
|
||||
expected.append(s);
|
||||
}
|
||||
List<Integer> segmentSizes = buffer.segmentSizes();
|
||||
@@ -169,7 +169,7 @@ public final class OkBufferTest {
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('a', Segment.SIZE * 2));
|
||||
sink.write(source, writeSize, Deadline.NONE);
|
||||
sink.write(source, writeSize);
|
||||
|
||||
assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes());
|
||||
assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes());
|
||||
@@ -184,7 +184,7 @@ public final class OkBufferTest {
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('a', Segment.SIZE * 2));
|
||||
sink.write(source, writeSize, Deadline.NONE);
|
||||
sink.write(source, writeSize);
|
||||
|
||||
assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes());
|
||||
assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes());
|
||||
@@ -196,7 +196,7 @@ public final class OkBufferTest {
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('a', Segment.SIZE * 2));
|
||||
sink.write(source, 20, Deadline.NONE);
|
||||
sink.write(source, 20);
|
||||
|
||||
assertEquals(asList(30), sink.segmentSizes());
|
||||
assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes());
|
||||
@@ -211,7 +211,7 @@ public final class OkBufferTest {
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('a', Segment.SIZE * 2));
|
||||
sink.write(source, 20, Deadline.NONE);
|
||||
sink.write(source, 20);
|
||||
|
||||
assertEquals(asList(30), sink.segmentSizes());
|
||||
assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes());
|
||||
@@ -225,7 +225,7 @@ public final class OkBufferTest {
|
||||
|
||||
OkBuffer source = new OkBuffer();
|
||||
|
||||
assertEquals(-1, source.read(sink, 10, Deadline.NONE));
|
||||
assertEquals(-1, source.read(sink, 10));
|
||||
assertEquals(10, sink.byteCount());
|
||||
assertEquals(0, source.byteCount());
|
||||
}
|
||||
@@ -238,7 +238,7 @@ public final class OkBufferTest {
|
||||
|
||||
// Either 0 or -1 is reasonable here. For consistency with Android's
|
||||
// ByteArrayInputStream we return 0.
|
||||
assertEquals(-1, source.read(sink, 0, Deadline.NONE));
|
||||
assertEquals(-1, source.read(sink, 0));
|
||||
assertEquals(10, sink.byteCount());
|
||||
assertEquals(0, source.byteCount());
|
||||
}
|
||||
@@ -250,7 +250,7 @@ public final class OkBufferTest {
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('b', 15));
|
||||
|
||||
assertEquals(10, source.read(sink, 10, Deadline.NONE));
|
||||
assertEquals(10, source.read(sink, 10));
|
||||
assertEquals(20, sink.byteCount());
|
||||
assertEquals(5, source.byteCount());
|
||||
assertEquals(repeat('a', 10) + repeat('b', 10), sink.readUtf8(20));
|
||||
@@ -263,7 +263,7 @@ public final class OkBufferTest {
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8(repeat('b', 20));
|
||||
|
||||
assertEquals(20, source.read(sink, 25, Deadline.NONE));
|
||||
assertEquals(20, source.read(sink, 25));
|
||||
assertEquals(30, sink.byteCount());
|
||||
assertEquals(0, source.byteCount());
|
||||
assertEquals(repeat('a', 10) + repeat('b', 20), sink.readUtf8(30));
|
||||
@@ -336,9 +336,9 @@ public final class OkBufferTest {
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Sink sink = OkBuffers.sink(out);
|
||||
sink.write(data, 3, Deadline.NONE);
|
||||
sink.write(data, 3);
|
||||
assertEquals("abb", out.toString("UTF-8"));
|
||||
sink.write(data, data.byteCount(), Deadline.NONE);
|
||||
sink.write(data, data.byteCount());
|
||||
assertEquals("a" + repeat('b', 9998) + "c", out.toString("UTF-8"));
|
||||
}
|
||||
|
||||
@@ -365,9 +365,9 @@ public final class OkBufferTest {
|
||||
@Test public void bufferedSinkEmitsTailWhenItIsComplete() throws IOException {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
BufferedSink bufferedSink = new BufferedSink(sink);
|
||||
bufferedSink.writeUtf8(repeat('a', Segment.SIZE - 1), Deadline.NONE);
|
||||
bufferedSink.writeUtf8(repeat('a', Segment.SIZE - 1));
|
||||
assertEquals(0, sink.byteCount());
|
||||
bufferedSink.writeByte(0, Deadline.NONE);
|
||||
bufferedSink.writeByte(0);
|
||||
assertEquals(Segment.SIZE, sink.byteCount());
|
||||
assertEquals(0, bufferedSink.buffer.byteCount());
|
||||
}
|
||||
@@ -375,14 +375,14 @@ public final class OkBufferTest {
|
||||
@Test public void bufferedSinkEmitZero() throws IOException {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
BufferedSink bufferedSink = new BufferedSink(sink);
|
||||
bufferedSink.writeUtf8("", Deadline.NONE);
|
||||
bufferedSink.writeUtf8("");
|
||||
assertEquals(0, sink.byteCount());
|
||||
}
|
||||
|
||||
@Test public void bufferedSinkEmitMultipleSegments() throws IOException {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
BufferedSink bufferedSink = new BufferedSink(sink);
|
||||
bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 4 - 1), Deadline.NONE);
|
||||
bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 4 - 1));
|
||||
assertEquals(Segment.SIZE * 3, sink.byteCount());
|
||||
assertEquals(Segment.SIZE - 1, bufferedSink.buffer.byteCount());
|
||||
}
|
||||
@@ -390,9 +390,9 @@ public final class OkBufferTest {
|
||||
@Test public void bufferedSinkFlush() throws IOException {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
BufferedSink bufferedSink = new BufferedSink(sink);
|
||||
bufferedSink.writeByte('a', Deadline.NONE);
|
||||
bufferedSink.writeByte('a');
|
||||
assertEquals(0, sink.byteCount());
|
||||
bufferedSink.flush(Deadline.NONE);
|
||||
bufferedSink.flush();
|
||||
assertEquals(0, bufferedSink.buffer.byteCount());
|
||||
assertEquals(1, sink.byteCount());
|
||||
}
|
||||
@@ -406,25 +406,25 @@ public final class OkBufferTest {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
|
||||
// Source: b...bc. Sink: abb.
|
||||
assertEquals(3, source.read(sink, 3, Deadline.NONE));
|
||||
assertEquals(3, source.read(sink, 3));
|
||||
assertEquals("abb", sink.readUtf8(3));
|
||||
|
||||
// Source: b...bc. Sink: b...b.
|
||||
assertEquals(Segment.SIZE, source.read(sink, 20000, Deadline.NONE));
|
||||
assertEquals(Segment.SIZE, source.read(sink, 20000));
|
||||
assertEquals(repeat('b', Segment.SIZE), sink.readUtf8((int) sink.byteCount()));
|
||||
|
||||
// Source: b...bc. Sink: b...bc.
|
||||
assertEquals(Segment.SIZE - 1, source.read(sink, 20000, Deadline.NONE));
|
||||
assertEquals(Segment.SIZE - 1, source.read(sink, 20000));
|
||||
assertEquals(repeat('b', Segment.SIZE - 2) + "c", sink.readUtf8((int) sink.byteCount()));
|
||||
|
||||
// Source and sink are empty.
|
||||
assertEquals(-1, source.read(sink, 1, Deadline.NONE));
|
||||
assertEquals(-1, source.read(sink, 1));
|
||||
}
|
||||
|
||||
@Test public void sourceFromInputStreamBounds() throws Exception {
|
||||
Source source = OkBuffers.source(new ByteArrayInputStream(new byte[100]));
|
||||
try {
|
||||
source.read(new OkBuffer(), -1, Deadline.NONE);
|
||||
source.read(new OkBuffer(), -1);
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
@@ -615,7 +615,7 @@ public final class OkBufferTest {
|
||||
OkBuffer sink = new OkBuffer();
|
||||
OkBuffer source = new OkBuffer();
|
||||
source.writeUtf8("abcd");
|
||||
sink.write(source, 2, Deadline.NONE);
|
||||
sink.write(source, 2);
|
||||
assertEquals("ab", sink.readUtf8(2));
|
||||
}
|
||||
|
||||
@@ -626,7 +626,7 @@ public final class OkBufferTest {
|
||||
BufferedSource bufferedSource = new BufferedSource(source);
|
||||
bufferedSource.buffer.writeUtf8("aa");
|
||||
|
||||
bufferedSource.require(2, Deadline.NONE);
|
||||
bufferedSource.require(2);
|
||||
assertEquals(2, bufferedSource.buffer.byteCount());
|
||||
assertEquals(2, source.byteCount());
|
||||
}
|
||||
@@ -638,7 +638,7 @@ public final class OkBufferTest {
|
||||
BufferedSource bufferedSource = new BufferedSource(source);
|
||||
bufferedSource.buffer.writeUtf8("a");
|
||||
|
||||
bufferedSource.require(2, Deadline.NONE);
|
||||
bufferedSource.require(2);
|
||||
assertEquals("ab", bufferedSource.buffer.readUtf8(2));
|
||||
}
|
||||
|
||||
@@ -649,7 +649,7 @@ public final class OkBufferTest {
|
||||
BufferedSource bufferedSource = new BufferedSource(source);
|
||||
|
||||
try {
|
||||
bufferedSource.require(2, Deadline.NONE);
|
||||
bufferedSource.require(2);
|
||||
fail();
|
||||
} catch (EOFException expected) {
|
||||
}
|
||||
@@ -662,7 +662,7 @@ public final class OkBufferTest {
|
||||
|
||||
BufferedSource bufferedSource = new BufferedSource(source);
|
||||
|
||||
bufferedSource.require(2, Deadline.NONE);
|
||||
bufferedSource.require(2);
|
||||
assertEquals(Segment.SIZE, source.byteCount());
|
||||
assertEquals(Segment.SIZE, bufferedSource.buffer.byteCount());
|
||||
}
|
||||
@@ -673,7 +673,7 @@ public final class OkBufferTest {
|
||||
|
||||
BufferedSource bufferedSource = new BufferedSource(source);
|
||||
try {
|
||||
bufferedSource.skip(2, Deadline.NONE);
|
||||
bufferedSource.skip(2);
|
||||
fail();
|
||||
} catch (EOFException expected) {
|
||||
}
|
||||
@@ -684,7 +684,7 @@ public final class OkBufferTest {
|
||||
source.writeUtf8(repeat('a', Segment.SIZE));
|
||||
source.writeUtf8(repeat('b', Segment.SIZE));
|
||||
BufferedSource bufferedSource = new BufferedSource(source);
|
||||
bufferedSource.skip(2, Deadline.NONE);
|
||||
bufferedSource.skip(2);
|
||||
assertEquals(Segment.SIZE, source.byteCount());
|
||||
assertEquals(Segment.SIZE - 2, bufferedSource.buffer.byteCount());
|
||||
}
|
||||
@@ -696,7 +696,7 @@ public final class OkBufferTest {
|
||||
BufferedSource bufferedSource = new BufferedSource(source);
|
||||
bufferedSource.buffer.writeUtf8("aa");
|
||||
|
||||
bufferedSource.skip(2, Deadline.NONE);
|
||||
bufferedSource.skip(2);
|
||||
assertEquals(0, bufferedSource.buffer.byteCount());
|
||||
assertEquals(2, source.byteCount());
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.squareup.okhttp.internal.spdy;
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
import com.squareup.okhttp.internal.bytes.BufferedSource;
|
||||
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.Source;
|
||||
import java.io.IOException;
|
||||
@@ -33,7 +32,6 @@ import org.junit.Test;
|
||||
import static com.squareup.okhttp.internal.Util.UTF_8;
|
||||
import static com.squareup.okhttp.internal.Util.headerEntries;
|
||||
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;
|
||||
import static com.squareup.okhttp.internal.spdy.ErrorCode.INVALID_STREAM;
|
||||
import static com.squareup.okhttp.internal.spdy.ErrorCode.PROTOCOL_ERROR;
|
||||
@@ -551,9 +549,9 @@ public final class SpdyConnectionTest {
|
||||
SpdyStream stream = connection.newStream(headerEntries("a", "android"), false, true);
|
||||
Source in = stream.getSource();
|
||||
OutputStream out = stream.getOutputStream();
|
||||
in.close(Deadline.NONE);
|
||||
in.close();
|
||||
try {
|
||||
in.read(new OkBuffer(), 1, Deadline.NONE);
|
||||
in.read(new OkBuffer(), 1);
|
||||
fail();
|
||||
} catch (IOException expected) {
|
||||
assertEquals("stream closed", expected.getMessage());
|
||||
@@ -594,9 +592,9 @@ public final class SpdyConnectionTest {
|
||||
SpdyStream stream = connection.newStream(headerEntries("a", "android"), true, true);
|
||||
Source source = stream.getSource();
|
||||
OutputStream out = stream.getOutputStream();
|
||||
source.close(Deadline.NONE);
|
||||
source.close();
|
||||
try {
|
||||
source.read(new OkBuffer(), 1, Deadline.NONE);
|
||||
source.read(new OkBuffer(), 1);
|
||||
fail();
|
||||
} catch (IOException expected) {
|
||||
assertEquals("stream closed", expected.getMessage());
|
||||
@@ -662,7 +660,7 @@ public final class SpdyConnectionTest {
|
||||
assertEquals(headerEntries("a", "android"), stream.getResponseHeaders());
|
||||
connection.ping().roundTripTime(); // Ensure that the 2nd SYN REPLY has been received.
|
||||
try {
|
||||
stream.getSource().read(new OkBuffer(), 1, Deadline.NONE);
|
||||
stream.getSource().read(new OkBuffer(), 1);
|
||||
fail();
|
||||
} catch (IOException expected) {
|
||||
assertEquals("stream was reset: STREAM_IN_USE", expected.getMessage());
|
||||
@@ -918,7 +916,7 @@ public final class SpdyConnectionTest {
|
||||
assertEquals("stream was reset: CANCEL", expected.getMessage());
|
||||
}
|
||||
try {
|
||||
stream.getSource().read(new OkBuffer(), 1, Deadline.NONE);
|
||||
stream.getSource().read(new OkBuffer(), 1);
|
||||
fail();
|
||||
} catch (IOException expected) {
|
||||
assertEquals("stream was reset: CANCEL", expected.getMessage());
|
||||
@@ -963,7 +961,7 @@ public final class SpdyConnectionTest {
|
||||
Source source = stream.getSource();
|
||||
long startNanos = System.nanoTime();
|
||||
try {
|
||||
source.read(new OkBuffer(), 1, Deadline.NONE);
|
||||
source.read(new OkBuffer(), 1);
|
||||
fail();
|
||||
} catch (IOException expected) {
|
||||
}
|
||||
@@ -1067,10 +1065,10 @@ public final class SpdyConnectionTest {
|
||||
assertEquals(headerEntries("a", "android"), stream.getResponseHeaders());
|
||||
Source in = stream.getSource();
|
||||
OkBuffer buffer = new OkBuffer();
|
||||
while (in.read(buffer, 1024, Deadline.NONE) != -1) {
|
||||
while (in.read(buffer, 1024) != -1) {
|
||||
if (buffer.byteCount() == 3 * windowUpdateThreshold) break;
|
||||
}
|
||||
assertEquals(-1, in.read(buffer, 1, Deadline.NONE));
|
||||
assertEquals(-1, in.read(buffer, 1));
|
||||
|
||||
// Verify the peer received what was expected.
|
||||
assertEquals(21, peer.frameCount());
|
||||
@@ -1110,7 +1108,7 @@ public final class SpdyConnectionTest {
|
||||
// Play it back.
|
||||
SpdyConnection connection = connection(peer, variant);
|
||||
SpdyStream client = connection.newStream(headerEntries("b", "banana"), false, true);
|
||||
assertEquals(-1, client.getSource().read(new OkBuffer(), 1, Deadline.NONE));
|
||||
assertEquals(-1, client.getSource().read(new OkBuffer(), 1));
|
||||
|
||||
// Verify the peer received what was expected.
|
||||
MockSpdyPeer.InFrame synStream = peer.takeFrame();
|
||||
@@ -1462,7 +1460,7 @@ public final class SpdyConnectionTest {
|
||||
|
||||
private void assertStreamData(String expected, Source source) throws IOException {
|
||||
OkBuffer buffer = new OkBuffer();
|
||||
while (source.read(buffer, Long.MAX_VALUE, Deadline.NONE) != -1) {
|
||||
while (source.read(buffer, Long.MAX_VALUE) != -1) {
|
||||
}
|
||||
String actual = buffer.readUtf8((int) buffer.byteCount());
|
||||
assertEquals(expected, actual);
|
||||
|
||||
@@ -19,7 +19,6 @@ package com.squareup.okhttp;
|
||||
import com.squareup.okhttp.internal.Platform;
|
||||
import com.squareup.okhttp.internal.bytes.BufferedSource;
|
||||
import com.squareup.okhttp.internal.bytes.ByteString;
|
||||
import com.squareup.okhttp.internal.bytes.Deadline;
|
||||
import com.squareup.okhttp.internal.bytes.OkBuffers;
|
||||
import com.squareup.okhttp.internal.http.HttpAuthenticator;
|
||||
import com.squareup.okhttp.internal.http.HttpConnection;
|
||||
@@ -215,7 +214,7 @@ public final class Connection implements Closeable {
|
||||
int readTimeout = socket.getSoTimeout();
|
||||
try {
|
||||
socket.setSoTimeout(1);
|
||||
if (source.source.read(source.buffer, 1, Deadline.NONE) == -1) {
|
||||
if (source.source.read(source.buffer, 1) == -1) {
|
||||
return false; // Stream is exhausted; socket is closed.
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -174,7 +174,7 @@ public final class HttpConnection {
|
||||
}
|
||||
|
||||
private String readLine() throws IOException {
|
||||
long newline = source.seek((byte) '\n', Deadline.NONE);
|
||||
long newline = source.seek((byte) '\n');
|
||||
|
||||
if (newline > 0 && source.buffer.getByte(newline - 1) == '\r') {
|
||||
// Read everything until '\r\n', then skip the '\r\n'.
|
||||
@@ -483,13 +483,13 @@ public final class HttpConnection {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline)
|
||||
@Override public long read(OkBuffer sink, long byteCount)
|
||||
throws IOException {
|
||||
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
if (bytesRemaining == 0) return -1;
|
||||
|
||||
long read = source.read(sink, Math.min(bytesRemaining, byteCount), deadline);
|
||||
long read = source.read(sink, Math.min(bytesRemaining, byteCount));
|
||||
if (read == -1) {
|
||||
unexpectedEndOfInput(); // the server didn't supply the promised content length
|
||||
throw new ProtocolException("unexpected end of stream");
|
||||
@@ -503,7 +503,12 @@ public final class HttpConnection {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
source.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
if (closed) return;
|
||||
|
||||
if (bytesRemaining != 0 && !discard(this, DISCARD_STREAM_TIMEOUT_MILLIS)) {
|
||||
@@ -527,7 +532,7 @@ public final class HttpConnection {
|
||||
}
|
||||
|
||||
@Override public long read(
|
||||
OkBuffer sink, long byteCount, Deadline deadline) throws IOException {
|
||||
OkBuffer sink, long byteCount) throws IOException {
|
||||
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
if (!hasMoreChunks) return -1;
|
||||
@@ -537,7 +542,7 @@ public final class HttpConnection {
|
||||
if (!hasMoreChunks) return -1;
|
||||
}
|
||||
|
||||
long read = source.read(sink, Math.min(byteCount, bytesRemainingInChunk), deadline);
|
||||
long read = source.read(sink, Math.min(byteCount, bytesRemainingInChunk));
|
||||
if (read == -1) {
|
||||
unexpectedEndOfInput(); // the server didn't supply the promised chunk length
|
||||
throw new IOException("unexpected end of stream");
|
||||
@@ -571,7 +576,12 @@ public final class HttpConnection {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
source.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
if (closed) return;
|
||||
if (hasMoreChunks && !discard(this, DISCARD_STREAM_TIMEOUT_MILLIS)) {
|
||||
unexpectedEndOfInput();
|
||||
@@ -588,13 +598,13 @@ public final class HttpConnection {
|
||||
super(cacheRequest);
|
||||
}
|
||||
|
||||
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline)
|
||||
@Override public long read(OkBuffer sink, long byteCount)
|
||||
throws IOException {
|
||||
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
if (inputExhausted) return -1;
|
||||
|
||||
long read = source.read(sink, byteCount, deadline);
|
||||
long read = source.read(sink, byteCount);
|
||||
if (read == -1) {
|
||||
inputExhausted = true;
|
||||
endOfInput();
|
||||
@@ -604,7 +614,12 @@ public final class HttpConnection {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
source.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
if (closed) return;
|
||||
// TODO: discard unknown length streams for best caching?
|
||||
if (!inputExhausted) {
|
||||
|
||||
@@ -257,13 +257,13 @@ public final class SpdyTransport implements Transport {
|
||||
this.cacheRequest = cacheRequest;
|
||||
}
|
||||
|
||||
@Override public long read(OkBuffer sink, long byteCount, Deadline deadline)
|
||||
@Override public long read(OkBuffer sink, long byteCount)
|
||||
throws IOException {
|
||||
if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
|
||||
if (closed) throw new IllegalStateException("closed");
|
||||
if (inputExhausted) return -1;
|
||||
|
||||
long read = source.read(sink, byteCount, deadline);
|
||||
long read = source.read(sink, byteCount);
|
||||
if (read == -1) {
|
||||
inputExhausted = true;
|
||||
if (cacheRequest != null) {
|
||||
@@ -279,7 +279,12 @@ public final class SpdyTransport implements Transport {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override public void close(Deadline deadline) throws IOException {
|
||||
@Override public Source deadline(Deadline deadline) {
|
||||
source.deadline(deadline);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
if (closed) return;
|
||||
|
||||
if (!inputExhausted && cacheBody != null) {
|
||||
|
||||
Reference in New Issue
Block a user