/** * Send a connection-level ping to the peer. {@code ack} indicates this is a reply. The data in * {@code payload1} and {@code payload2} opaque binary, and there are no rules on the content. */ public synchronized void ping(boolean ack, int payload1, int payload2) throws IOException { if (closed) throw new IOException("closed"); int length = 8; byte type = TYPE_PING; byte flags = ack ? FLAG_ACK : FLAG_NONE; int streamId = 0; frameHeader(streamId, length, type, flags); sink.writeInt(payload1); sink.writeInt(payload2); sink.flush(); }
public synchronized void rstStream(int streamId, ErrorCode errorCode) throws IOException { if (closed) throw new IOException("closed"); if (errorCode.httpCode == -1) throw new IllegalArgumentException(); int length = 4; byte type = TYPE_RST_STREAM; byte flags = FLAG_NONE; frameHeader(streamId, length, type, flags); sink.writeInt(errorCode.httpCode); sink.flush(); }
/** * Send a connection-level ping to the peer. {@code ack} indicates this is a reply. The data in * {@code payload1} and {@code payload2} opaque binary, and there are no rules on the content. */ public synchronized void ping(boolean ack, int payload1, int payload2) throws IOException { if (closed) throw new IOException("closed"); int length = 8; byte type = TYPE_PING; byte flags = ack ? FLAG_ACK : FLAG_NONE; int streamId = 0; frameHeader(streamId, length, type, flags); sink.writeInt(payload1); sink.writeInt(payload2); sink.flush(); }
/** * 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(); }
/** * Inform peer that an additional {@code windowSizeIncrement} bytes can be sent on {@code * streamId}, or the connection if {@code streamId} is zero. */ public synchronized void windowUpdate(int streamId, long windowSizeIncrement) throws IOException { if (closed) throw new IOException("closed"); if (windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL) { throw illegalArgument("windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: %s", windowSizeIncrement); } int length = 4; byte type = TYPE_WINDOW_UPDATE; byte flags = FLAG_NONE; frameHeader(streamId, length, type, flags); sink.writeInt((int) windowSizeIncrement); sink.flush(); }
public synchronized void rstStream(int streamId, ErrorCode errorCode) throws IOException { if (closed) throw new IOException("closed"); if (errorCode.httpCode == -1) throw new IllegalArgumentException(); int length = 4; byte type = TYPE_RST_STREAM; byte flags = FLAG_NONE; frameHeader(streamId, length, type, flags); sink.writeInt(errorCode.httpCode); sink.flush(); }
public void frameHeader(int streamId, int length, byte type, byte flags) throws IOException { if (logger.isLoggable(FINE)) logger.fine(frameLog(false, streamId, length, type, flags)); if (length > maxFrameSize) { throw illegalArgument("FRAME_SIZE_ERROR length > %d: %d", maxFrameSize, length); } if ((streamId & 0x80000000) != 0) throw illegalArgument("reserved bit set: %s", streamId); writeMedium(sink, length); sink.writeByte(type & 0xff); sink.writeByte(flags & 0xff); sink.writeInt(streamId & 0x7fffffff); }
/** * 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); }
/** * 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(); }
/** * Inform peer that an additional {@code windowSizeIncrement} bytes can be sent on {@code * streamId}, or the connection if {@code streamId} is zero. */ public synchronized void windowUpdate(int streamId, long windowSizeIncrement) throws IOException { if (closed) throw new IOException("closed"); if (windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL) { throw illegalArgument("windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: %s", windowSizeIncrement); } int length = 4; byte type = TYPE_WINDOW_UPDATE; byte flags = FLAG_NONE; frameHeader(streamId, length, type, flags); sink.writeInt((int) windowSizeIncrement); sink.flush(); }
/** Write okhttp's settings to the peer. */ public synchronized void settings(Settings settings) throws IOException { if (closed) throw new IOException("closed"); int length = settings.size() * 6; byte type = TYPE_SETTINGS; byte flags = FLAG_NONE; int streamId = 0; frameHeader(streamId, length, type, flags); for (int i = 0; i < Settings.COUNT; i++) { if (!settings.isSet(i)) continue; int id = i; if (id == 4) { id = 3; // SETTINGS_MAX_CONCURRENT_STREAMS renumbered. } else if (id == 7) { id = 4; // SETTINGS_INITIAL_WINDOW_SIZE renumbered. } sink.writeShort(id); sink.writeInt(settings.get(i)); } sink.flush(); }
public void frameHeader(int streamId, int length, byte type, byte flags) throws IOException { if (logger.isLoggable(FINE)) logger.fine(frameLog(false, streamId, length, type, flags)); if (length > maxFrameSize) { throw illegalArgument("FRAME_SIZE_ERROR length > %d: %d", maxFrameSize, length); } if ((streamId & 0x80000000) != 0) throw illegalArgument("reserved bit set: %s", streamId); writeMedium(sink, length); sink.writeByte(type & 0xff); sink.writeByte(flags & 0xff); sink.writeInt(streamId & 0x7fffffff); }
/** * 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); }
/** Write okhttp's settings to the peer. */ public synchronized void settings(Settings settings) throws IOException { if (closed) throw new IOException("closed"); int length = settings.size() * 6; byte type = TYPE_SETTINGS; byte flags = FLAG_NONE; int streamId = 0; frameHeader(streamId, length, type, flags); for (int i = 0; i < Settings.COUNT; i++) { if (!settings.isSet(i)) continue; int id = i; if (id == 4) { id = 3; // SETTINGS_MAX_CONCURRENT_STREAMS renumbered. } else if (id == 7) { id = 4; // SETTINGS_INITIAL_WINDOW_SIZE renumbered. } sink.writeShort(id); sink.writeInt(settings.get(i)); } sink.flush(); }
@Test public void writeIntegerDoesNotQuiteFitInSegment() throws Exception { sink.writeUtf8(repeat('a', SEGMENT_SIZE - 3)); sink.writeInt(0xabcdef01); sink.writeInt(0x87654321); sink.flush(); assertEquals(asList(SEGMENT_SIZE - 3, 8), segmentSizes(data)); assertEquals(repeat('a', SEGMENT_SIZE - 3), data.readUtf8(SEGMENT_SIZE - 3)); assertEquals("[hex=abcdef0187654321]", data.toString()); }
@Test public void writeLastIntegerInSegment() throws Exception { sink.writeUtf8(repeat('a', SEGMENT_SIZE - 4)); sink.writeInt(0xabcdef01); sink.writeInt(0x87654321); sink.flush(); assertEquals(asList(SEGMENT_SIZE, 4), segmentSizes(data)); assertEquals(repeat('a', SEGMENT_SIZE - 4), data.readUtf8(SEGMENT_SIZE - 4)); assertEquals("[hex=abcdef0187654321]", data.toString()); }
@Test public void readIntTooShortThrows() throws IOException { sink.writeInt(Integer.MAX_VALUE); sink.emit(); source.readByte(); try { source.readInt(); fail(); } catch (EOFException expected) { } }
@Override public synchronized void ping(boolean ack, int payload1, int payload2) throws IOException { if (closed) throw new IOException("closed"); int length = 8; byte type = TYPE_PING; byte flags = ack ? FLAG_ACK : FLAG_NONE; int streamId = 0; frameHeader(streamId, length, type, flags); sink.writeInt(payload1); sink.writeInt(payload2); sink.flush(); }
private void writeNameValueBlockToBuffer(List<Header> headerBlock) throws IOException { headerBlockOut.writeInt(headerBlock.size()); for (int i = 0, size = headerBlock.size(); i < size; i++) { ByteString name = headerBlock.get(i).name; headerBlockOut.writeInt(name.size()); headerBlockOut.write(name); ByteString value = headerBlock.get(i).value; headerBlockOut.writeInt(value.size()); headerBlockOut.write(value); } headerBlockOut.flush(); }