@Override public void writeTo(BufferedSink sink) throws IOException { sink.write(content, offset, byteCount); } };
@Override public void write(Buffer source, long byteCount) throws IOException { if (closed) throw new IllegalStateException("closed"); if (byteCount == 0) return; sink.writeHexadecimalUnsignedLong(byteCount); sink.writeUtf8("\r\n"); sink.write(source, byteCount); sink.writeUtf8("\r\n"); }
/** * Tell the peer to stop creating streams and that we last processed {@code lastGoodStreamId}, or * zero if no streams were processed. * * @param lastGoodStreamId the last stream ID processed, or zero if no streams were processed. * @param errorCode reason for closing the connection. * @param debugData only valid for HTTP/2; opaque debug data to send. */ public synchronized void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) throws IOException { if (closed) throw new IOException("closed"); if (errorCode.httpCode == -1) throw illegalArgument("errorCode.httpCode == -1"); int length = 8 + debugData.length; byte type = TYPE_GOAWAY; byte flags = FLAG_NONE; int streamId = 0; frameHeader(streamId, length, type, flags); sink.writeInt(lastGoodStreamId); sink.writeInt(errorCode.httpCode); if (debugData.length > 0) { sink.write(debugData); } sink.flush(); }
@Test public void readIntSplitAcrossMultipleSegments() throws Exception { sink.writeUtf8(repeat('a', SEGMENT_SIZE - 3)); sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01 }); sink.emit(); source.skip(SEGMENT_SIZE - 3); assertEquals(0xabcdef01, source.readInt()); assertTrue(source.exhausted()); }
@Override public void writeTo(BufferedSink sink) throws IOException { sink.write(content); } };
/** * HTTP/2 only. Send a push promise header block. * * <p>A push promise contains all the headers that pertain to a server-initiated request, and a * {@code promisedStreamId} to which response frames will be delivered. Push promise frames are * sent as a part of the response to {@code streamId}. The {@code promisedStreamId} has a priority * of one greater than {@code streamId}. * * @param streamId client-initiated stream ID. Must be an odd number. * @param promisedStreamId server-initiated stream ID. Must be an even number. * @param requestHeaders minimally includes {@code :method}, {@code :scheme}, {@code :authority}, * and {@code :path}. */ public synchronized void pushPromise(int streamId, int promisedStreamId, List<Header> requestHeaders) throws IOException { if (closed) throw new IOException("closed"); hpackWriter.writeHeaders(requestHeaders); long byteCount = hpackBuffer.size(); int length = (int) Math.min(maxFrameSize - 4, byteCount); byte type = TYPE_PUSH_PROMISE; byte flags = byteCount == length ? FLAG_END_HEADERS : 0; frameHeader(streamId, length + 4, type, flags); sink.writeInt(promisedStreamId & 0x7fffffff); sink.write(hpackBuffer, length); if (byteCount > length) writeContinuationFrames(streamId, byteCount - length); }
void dataFrame(int streamId, byte flags, Buffer buffer, int byteCount) throws IOException { byte type = TYPE_DATA; frameHeader(streamId, byteCount, type, flags); if (byteCount > 0) { sink.write(buffer, byteCount); } }
private void writeContinuationFrames(int streamId, long byteCount) throws IOException { while (byteCount > 0) { int length = (int) Math.min(maxFrameSize, byteCount); byteCount -= length; frameHeader(streamId, length, TYPE_CONTINUATION, byteCount == 0 ? FLAG_END_HEADERS : 0); sink.write(hpackBuffer, length); } }
@Override public void write(byte[] source, int offset, int byteCount) throws IOException { if (closed) throw new IOException("closed"); // Not IllegalStateException! if (expectedContentLength != -1L && bytesReceived + byteCount > expectedContentLength) { throw new ProtocolException("expected " + expectedContentLength + " bytes but received " + bytesReceived + byteCount); } bytesReceived += byteCount; try { sink.write(source, offset, byteCount); } catch (InterruptedIOException e) { throw new SocketTimeoutException(e.getMessage()); } }
public synchronized void connectionPreface() throws IOException { if (closed) throw new IOException("closed"); if (!client) return; // Nothing to write; servers don't send connection headers! if (logger.isLoggable(FINE)) { logger.fine(format(">> CONNECTION %s", CONNECTION_PREFACE.hex())); } sink.write(CONNECTION_PREFACE.toByteArray()); sink.flush(); }
@Override public void writeTo(BufferedSink sink) throws IOException { Buffer buffer = new Buffer(); while (pipe.source().read(buffer, 8192) != -1L) { sink.write(buffer, buffer.size()); } } }
@Override public void write(Buffer source, long byteCount) throws IOException { if (closed) throw new IllegalStateException("closed"); checkOffsetAndCount(source.size(), 0, byteCount); if (byteCount > bytesRemaining) { throw new ProtocolException("expected " + bytesRemaining + " bytes but received " + byteCount); } sink.write(source, byteCount); bytesRemaining -= byteCount; }
public synchronized void headers( boolean outFinished, int streamId, List<Header> headerBlock) throws IOException { if (closed) throw new IOException("closed"); hpackWriter.writeHeaders(headerBlock); long byteCount = hpackBuffer.size(); int length = (int) Math.min(maxFrameSize, byteCount); byte type = TYPE_HEADERS; byte flags = byteCount == length ? FLAG_END_HEADERS : 0; if (outFinished) flags |= FLAG_END_STREAM; frameHeader(streamId, length, type, flags); sink.write(hpackBuffer, length); if (byteCount > length) writeContinuationFrames(streamId, byteCount - length); } }
@Test public void writeSourceWithZeroIsNoOp() throws IOException { // This test ensures that a zero byte count never calls through to read the source. It may be // tied to something like a socket which will potentially block trying to read a segment when // ultimately we don't want any data. Source source = new ForwardingSource(new Buffer()) { @Override public long read(Buffer sink, long byteCount) throws IOException { throw new AssertionError(); } }; sink.write(source, 0); assertEquals(0, data.size()); }
@Test public void writeNioBuffer() throws Exception { String expected = "abcdefg"; ByteBuffer nioByteBuffer = ByteBuffer.allocate(1024); nioByteBuffer.put("abcdefg".getBytes(UTF_8)); nioByteBuffer.flip(); int byteCount = sink.write(nioByteBuffer); assertEquals(expected.length(), byteCount); assertEquals(expected.length(), nioByteBuffer.position()); assertEquals(expected.length(), nioByteBuffer.limit()); sink.flush(); assertEquals(expected, data.readUtf8()); }
@Test public void writeSourceReadsFully() throws Exception { Source source = new ForwardingSource(new Buffer()) { @Override public long read(Buffer sink, long byteCount) throws IOException { sink.writeUtf8("abcd"); return 4; } }; sink.write(source, 8); sink.flush(); assertEquals("abcdabcd", data.readUtf8()); }
@Test public void writeSourcePropagatesEof() throws IOException { Source source = new Buffer().writeUtf8("abcd"); try { sink.write(source, 8); fail(); } catch (EOFException expected) { } // Ensure that whatever was available was correctly written. sink.flush(); assertEquals("abcd", data.readUtf8()); }
@Test public void readFullyByteArray() throws IOException { Buffer data = new Buffer(); data.writeUtf8("Hello").writeUtf8(repeat('e', SEGMENT_SIZE)); byte[] expected = data.clone().readByteArray(); sink.write(data, data.size()); sink.emit(); byte[] sink = new byte[SEGMENT_SIZE + 5]; source.readFully(sink); assertByteArraysEquals(expected, sink); }
@Test public void readShort() throws Exception { sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01 }); sink.emit(); assertEquals((short) 0xabcd, source.readShort()); assertEquals((short) 0xef01, source.readShort()); assertTrue(source.exhausted()); }
@Test public void readInt() throws Exception { sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01, (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21 }); sink.emit(); assertEquals(0xabcdef01, source.readInt()); assertEquals(0x87654321, source.readInt()); assertTrue(source.exhausted()); }