private void readContinuationHeader() throws IOException { int previousStreamId = streamId; length = left = readMedium(source); byte type = (byte) (source.readByte() & 0xff); flags = (byte) (source.readByte() & 0xff); if (logger.isLoggable(FINE)) logger.fine(formatHeader(true, streamId, length, type, flags)); streamId = (source.readInt() & 0x7fffffff); if (type != TYPE_CONTINUATION) throw ioException("%s != TYPE_CONTINUATION", type); if (streamId != previousStreamId) throw ioException("TYPE_CONTINUATION streamId changed"); }
private void readHeader() throws IOException { if (closed) throw new IOException("closed"); int b0 = source.readByte() & 0xff; opcode = b0 & B0_MASK_OPCODE; isFinalFrame = (b0 & B0_FLAG_FIN) != 0; isControlFrame = (b0 & OPCODE_FLAG_CONTROL) != 0; // Control frames must be final frames (cannot contain continuations). if (isControlFrame && !isFinalFrame) { throw new ProtocolException("Control frames must be final."); } boolean reservedFlag1 = (b0 & B0_FLAG_RSV1) != 0; boolean reservedFlag2 = (b0 & B0_FLAG_RSV2) != 0; boolean reservedFlag3 = (b0 & B0_FLAG_RSV3) != 0; if (reservedFlag1 || reservedFlag2 || reservedFlag3) { // Reserved flags are for extensions which we currently do not support. throw new ProtocolException("Reserved flags are unsupported."); } int b1 = source.readByte() & 0xff; isMasked = (b1 & B1_FLAG_MASK) != 0; if (isMasked == isClient) { // Masked payloads must be read on the server. Unmasked payloads must be read on the client. throw new ProtocolException("Client-sent frames must be masked. Server sent must not."); } // Get frame length, optionally reading from follow-up bytes if indicated by special values. frameLength = b1 & B1_MASK_LENGTH; if (frameLength == PAYLOAD_SHORT) { frameLength = source.readShort() & 0xffffL; // Value is unsigned. } else if (frameLength == PAYLOAD_LONG) { frameLength = source.readLong(); if (frameLength < 0) { throw new ProtocolException( "Frame length 0x" + Long.toHexString(frameLength) + " > 0x7FFFFFFFFFFFFFFF"); } } frameBytesRead = 0; if (isControlFrame && frameLength > PAYLOAD_BYTE_MAX) { throw new ProtocolException("Control frame must be less than " + PAYLOAD_BYTE_MAX + "B."); } if (isMasked) { // Read the masking key as bytes so that they can be used directly for unmasking. source.readFully(maskKey); } }
private void readPriority(Handler handler, int streamId) throws IOException { int w1 = source.readInt(); boolean exclusive = (w1 & 0x80000000) != 0; int streamDependency = (w1 & 0x7fffffff); int weight = (source.readByte() & 0xff) + 1; handler.priority(streamId, streamDependency, weight, exclusive); }
private void readPushPromise(Handler handler, int length, byte flags, int streamId) throws IOException { if (streamId == 0) { throw ioException("PROTOCOL_ERROR: TYPE_PUSH_PROMISE streamId == 0"); } short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0; int promisedStreamId = source.readInt() & 0x7fffffff; length -= 4; // account for above read. length = lengthWithoutPadding(length, flags, padding); List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId); handler.pushPromise(streamId, promisedStreamId, headerBlock); }
private void readData(Handler handler, int length, byte flags, int streamId) throws IOException { // TODO: checkState open or half-closed (local) or raise STREAM_CLOSED boolean inFinished = (flags & FLAG_END_STREAM) != 0; boolean gzipped = (flags & FLAG_COMPRESSED) != 0; if (gzipped) { throw ioException("PROTOCOL_ERROR: FLAG_COMPRESSED without SETTINGS_COMPRESS_DATA"); } short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0; length = lengthWithoutPadding(length, flags, padding); handler.data(inFinished, streamId, source, length); source.skip(padding); }
private void readHeaders(Handler handler, int length, byte flags, int streamId) throws IOException { if (streamId == 0) throw ioException("PROTOCOL_ERROR: TYPE_HEADERS streamId == 0"); boolean endStream = (flags & FLAG_END_STREAM) != 0; short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0; if ((flags & FLAG_PRIORITY) != 0) { readPriority(handler, streamId); length -= 5; // account for above read. } length = lengthWithoutPadding(length, flags, padding); List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId); handler.headers(false, endStream, streamId, -1, headerBlock, HeadersMode.HTTP_20_HEADERS); }
private static int readMedium(BufferedSource source) throws IOException { return (source.readByte() & 0xff) << 16 | (source.readByte() & 0xff) << 8 | (source.readByte() & 0xff); }
@Override public boolean nextFrame(Handler handler) throws IOException { try { source.require(9); // Frame header size } catch (IOException e) { return false; // This might be a normal socket close. } /* 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Length (24) | * +---------------+---------------+---------------+ * | Type (8) | Flags (8) | * +-+-+-----------+---------------+-------------------------------+ * |R| Stream Identifier (31) | * +=+=============================================================+ * | Frame Payload (0...) ... * +---------------------------------------------------------------+ */ int length = readMedium(source); if (length < 0 || length > INITIAL_MAX_FRAME_SIZE) { throw ioException("FRAME_SIZE_ERROR: %s", length); } byte type = (byte) (source.readByte() & 0xff); byte flags = (byte) (source.readByte() & 0xff); int streamId = (source.readInt() & 0x7fffffff); // Ignore reserved bit. if (logger.isLoggable(FINE)) logger.fine(formatHeader(true, streamId, length, type, flags)); switch (type) { case TYPE_DATA: readData(handler, length, flags, streamId); break; case TYPE_HEADERS: readHeaders(handler, length, flags, streamId); break; case TYPE_PRIORITY: readPriority(handler, length, flags, streamId); break; case TYPE_RST_STREAM: readRstStream(handler, length, flags, streamId); break; case TYPE_SETTINGS: readSettings(handler, length, flags, streamId); break; case TYPE_PUSH_PROMISE: readPushPromise(handler, length, flags, streamId); break; case TYPE_PING: readPing(handler, length, flags, streamId); break; case TYPE_GOAWAY: readGoAway(handler, length, flags, streamId); break; case TYPE_WINDOW_UPDATE: readWindowUpdate(handler, length, flags, streamId); break; default: // Implementations MUST discard frames that have unknown or unsupported types. source.skip(length); } return true; }