private long beforeLengthDelimitedScalar() throws IOException { if (state != STATE_LENGTH_DELIMITED) { throw new ProtocolException("Expected LENGTH_DELIMITED but was " + state); } long byteCount = limit - pos; source.require(byteCount); // Throws EOFException if insufficient bytes are available. state = STATE_TAG; // We've completed a length-delimited scalar. Pop the limit. pos = limit; limit = pushedLimit; pushedLimit = -1; return byteCount; }
/** * Eagerly reads {@code byteCount} bytes from the source before launching a background task to * process the data. This avoids corrupting the stream. */ void pushDataLater(final int streamId, final BufferedSource source, final int byteCount, final boolean inFinished) throws IOException { final Buffer buffer = new Buffer(); source.require(byteCount); // Eagerly read the frame before firing client thread. source.read(buffer, byteCount); if (buffer.size() != byteCount) throw new IOException(buffer.size() + " != " + byteCount); pushExecutorExecute(new NamedRunnable("OkHttp %s Push Data[%s]", connectionName, streamId) { @Override public void execute() { try { boolean cancel = pushObserver.onData(streamId, buffer, byteCount, inFinished); if (cancel) writer.rstStream(streamId, ErrorCode.CANCEL); if (cancel || inFinished) { synchronized (Http2Connection.this) { currentPushRequests.remove(streamId); } } } catch (IOException ignored) { } } }); }
/** * Reads a {@code bytes} field value from the stream. The length is read from the * stream prior to the actual data. */ public ByteString readBytes() throws IOException { long byteCount = beforeLengthDelimitedScalar(); source.require(byteCount); // Throws EOFException if insufficient bytes are available. return source.readByteString(byteCount); }
/** Reads a {@code string} field value from the stream. */ public String readString() throws IOException { long byteCount = beforeLengthDelimitedScalar(); source.require(byteCount); // Throws EOFException if insufficient bytes are available. return source.readUtf8(byteCount); }
/** Copy-constructor makes a deep copy for peeking. */ JsonUtf8Reader(JsonUtf8Reader copyFrom) { super(copyFrom); BufferedSource sourcePeek = copyFrom.source.peek(); this.source = sourcePeek; this.buffer = sourcePeek.getBuffer(); this.peeked = copyFrom.peeked; this.peekedLong = copyFrom.peekedLong; this.peekedNumberLength = copyFrom.peekedNumberLength; this.peekedString = copyFrom.peekedString; // Make sure our buffer has as many bytes as the source's buffer. This is necessary because // JsonUtf8Reader assumes any data it has peeked (like the peekedNumberLength) are buffered. try { sourcePeek.require(copyFrom.buffer.size()); } catch (IOException e) { throw new AssertionError(); } }
/** Reads a 64-bit little-endian integer from the stream. */ public long readFixed64() throws IOException { if (state != STATE_FIXED64 && state != STATE_LENGTH_DELIMITED) { throw new ProtocolException("Expected FIXED64 or LENGTH_DELIMITED but was " + state); } source.require(8); // Throws EOFException if insufficient bytes are available. pos += 8; long result = source.readLongLe(); afterPackableScalar(STATE_FIXED64); return result; }
/** Reads a 32-bit little-endian integer from the stream. */ public int readFixed32() throws IOException { if (state != STATE_FIXED32 && state != STATE_LENGTH_DELIMITED) { throw new ProtocolException("Expected FIXED32 or LENGTH_DELIMITED but was " + state); } source.require(4); // Throws EOFException if insufficient bytes are available. pos += 4; int result = source.readIntLe(); afterPackableScalar(STATE_FIXED32); return result; }
/** * Eagerly reads {@code byteCount} bytes from the source before launching a background task to * process the data. This avoids corrupting the stream. */ void pushDataLater(final int streamId, final BufferedSource source, final int byteCount, final boolean inFinished) throws IOException { final Buffer buffer = new Buffer(); source.require(byteCount); // Eagerly read the frame before firing client thread. source.read(buffer, byteCount); if (buffer.size() != byteCount) throw new IOException(buffer.size() + " != " + byteCount); pushExecutorExecute(new NamedRunnable("OkHttp %s Push Data[%s]", connectionName, streamId) { @Override public void execute() { try { boolean cancel = pushObserver.onData(streamId, buffer, byteCount, inFinished); if (cancel) writer.rstStream(streamId, ErrorCode.CANCEL); if (cancel || inFinished) { synchronized (Http2Connection.this) { currentPushRequests.remove(streamId); } } } catch (IOException ignored) { } } }); }
@Test public void inputStreamCloses() throws Exception { BufferedSource source = Okio.buffer((Source) new Buffer()); InputStream in = source.inputStream(); in.close(); try { source.require(1); fail(); } catch (IllegalStateException e) { assertEquals("closed", e.getMessage()); } }
@Test public void requireInsufficientData() throws Exception { Buffer source = new Buffer(); source.writeUtf8("a"); BufferedSource bufferedSource = Okio.buffer((Source) source); try { bufferedSource.require(2); fail(); } catch (EOFException expected) { } }
/** Reads a raw varint up to 64 bits in length from the stream. */ public long readVarint64() throws IOException { if (state != STATE_VARINT && state != STATE_LENGTH_DELIMITED) { throw new ProtocolException("Expected VARINT or LENGTH_DELIMITED but was " + state); } int shift = 0; long result = 0; while (shift < 64) { source.require(1); // Throws EOFException if insufficient bytes are available. pos++; byte b = source.readByte(); result |= (long) (b & 0x7F) << shift; if ((b & 0x80) == 0) { afterPackableScalar(STATE_VARINT); return result; } shift += 7; } throw new ProtocolException("WireInput encountered a malformed varint"); }
@Test public void readWithoutTimeout() throws Exception { Socket socket = socket(ONE_MB, 0); BufferedSource source = Okio.buffer(Okio.source(socket)); source.timeout().timeout(5000, TimeUnit.MILLISECONDS); source.require(ONE_MB); socket.close(); }
@Test public void markAndLimitSmallerThanUserBuffer() throws Exception { SourceMarker marker = new SourceMarker(new Buffer().writeUtf8("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); BufferedSource source = marker.source(); // Load 5 bytes into the user buffer, then mark 0..3 and confirm that resetting from 4 fails. source.require(5); long pos0 = marker.mark(3); assertThat(source.readUtf8(3)).isEqualTo("ABC"); marker.reset(pos0); assertThat(source.readUtf8(4)).isEqualTo("ABCD"); try { marker.reset(pos0); fail(); } catch (IOException expected) { assertThat(expected).hasMessage("cannot reset to 0: out of range"); } }
@Test public void readWithTimeout() throws Exception { Socket socket = socket(0, 0); BufferedSource source = Okio.buffer(Okio.source(socket)); source.timeout().timeout(250, TimeUnit.MILLISECONDS); try { source.require(ONE_MB); fail(); } catch (SocketTimeoutException expected) { } socket.close(); }
@Test public void requireIncludesBufferBytes() throws Exception { Buffer source = new Buffer(); source.writeUtf8("b"); BufferedSource bufferedSource = Okio.buffer((Source) source); bufferedSource.getBuffer().writeUtf8("a"); bufferedSource.require(2); assertEquals("ab", bufferedSource.getBuffer().readUtf8(2)); }
@Test public void requireTracksBufferFirst() throws Exception { Buffer source = new Buffer(); source.writeUtf8("bb"); BufferedSource bufferedSource = Okio.buffer((Source) source); bufferedSource.getBuffer().writeUtf8("aa"); bufferedSource.require(2); assertEquals(2, bufferedSource.getBuffer().size()); assertEquals(2, source.size()); }
public boolean nextFrame(boolean requireSettings, Handler handler) throws IOException { try { source.require(9); // Frame header size } catch (IOException e) { return false; // This might be a normal socket close.
@Test public void requireReadsOneSegmentAtATime() throws Exception { Buffer source = new Buffer(); source.writeUtf8(repeat('a', SEGMENT_SIZE)); source.writeUtf8(repeat('b', SEGMENT_SIZE)); BufferedSource bufferedSource = Okio.buffer((Source) source); bufferedSource.require(2); assertEquals(SEGMENT_SIZE, source.size()); assertEquals(SEGMENT_SIZE, bufferedSource.getBuffer().size()); }
@Test public void factorySegmentSizes() throws Exception { sink.writeUtf8("abc"); sink.emit(); source.require(3); if (factory.isOneByteAtATime()) { assertEquals(Arrays.asList(1, 1, 1), TestUtil.segmentSizes(source.getBuffer())); } else { assertEquals(Collections.singletonList(3), TestUtil.segmentSizes(source.getBuffer())); } } }