@Override public void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { try { decode(ctx, in, out); } finally { headerBlockDecoder.end(); } }
/** Creates a new instance with the specified parameters. */ public SpdyFrameDecoder(SpdyVersion version, int maxChunkSize, int maxHeaderSize) { this(version, maxChunkSize, SpdyHeaderBlockDecoder.newInstance(version, maxHeaderSize)); }
@Override protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception { switch (state) { case READ_COMMON_HEADER: state = readCommonHeader(buffer); if (state == State.FRAME_ERROR) { if (version != spdyVersion) { fireProtocolException(ctx, "Unsupported version: " + version); } else { fireInvalidFrameException(ctx); } } // FrameDecoders must consume data when producing frames // All length 0 frames must be generated now if (length == 0) { if (state == State.READ_DATA_FRAME) { SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamId); spdyDataFrame.setLast((flags & SPDY_DATA_FLAG_FIN) != 0); state = State.READ_COMMON_HEADER; out.add(spdyDataFrame); return; } // There are no length 0 control frames state = State.READ_COMMON_HEADER; } return; case READ_CONTROL_FRAME: try { Object frame = readControlFrame(buffer); if (frame != null) { state = State.READ_COMMON_HEADER; out.add(frame); } return; } catch (IllegalArgumentException e) { state = State.FRAME_ERROR; fireInvalidFrameException(ctx); } return; case READ_SETTINGS_FRAME: if (spdySettingsFrame == null) { // Validate frame length against number of entries if (buffer.readableBytes() < 4) { return; } int numEntries = getUnsignedInt(buffer, buffer.readerIndex()); buffer.skipBytes(4); length -= 4; // Each ID/Value entry is 8 bytes if ((length & 0x07) != 0 || length >> 3 != numEntries) { state = State.FRAME_ERROR; fireInvalidFrameException(ctx); return; } spdySettingsFrame = new DefaultSpdySettingsFrame(); boolean clear = (flags & SPDY_SETTINGS_CLEAR) != 0; spdySettingsFrame.setClearPreviouslyPersistedSettings(clear); } int readableEntries = Math.min(buffer.readableBytes() >> 3, length >> 3); for (int i = 0; i < readableEntries; i++) { byte ID_flags = buffer.getByte(buffer.readerIndex()); int ID = getUnsignedMedium(buffer, buffer.readerIndex() + 1); int value = getSignedInt(buffer, buffer.readerIndex() + 4); buffer.skipBytes(8); if (!spdySettingsFrame.isSet(ID)) { boolean persistVal = (ID_flags & SPDY_SETTINGS_PERSIST_VALUE) != 0; boolean persisted = (ID_flags & SPDY_SETTINGS_PERSISTED) != 0; spdySettingsFrame.setValue(ID, value, persistVal, persisted); } } length -= 8 * readableEntries; if (length == 0) { state = State.READ_COMMON_HEADER; Object frame = spdySettingsFrame; spdySettingsFrame = null; out.add(frame); return; } return; case READ_HEADER_BLOCK_FRAME: try { spdyHeadersFrame = readHeaderBlockFrame(buffer); if (spdyHeadersFrame != null) { if (length == 0) { state = State.READ_COMMON_HEADER; Object frame = spdyHeadersFrame; spdyHeadersFrame = null; out.add(frame); return; } state = State.READ_HEADER_BLOCK; } return; } catch (IllegalArgumentException e) { state = State.FRAME_ERROR; fireInvalidFrameException(ctx); return; } case READ_HEADER_BLOCK: int compressedBytes = Math.min(buffer.readableBytes(), length); ByteBuf compressed = buffer.slice(buffer.readerIndex(), compressedBytes); try { headerBlockDecoder.decode(compressed, spdyHeadersFrame); } catch (Exception e) { state = State.FRAME_ERROR; spdyHeadersFrame = null; ctx.fireExceptionCaught(e); return; } int readBytes = compressedBytes - compressed.readableBytes(); buffer.skipBytes(readBytes); length -= readBytes; if (spdyHeadersFrame != null && (spdyHeadersFrame.isInvalid() || spdyHeadersFrame.isTruncated())) { Object frame = spdyHeadersFrame; spdyHeadersFrame = null; if (length == 0) { headerBlockDecoder.reset(); state = State.READ_COMMON_HEADER; } out.add(frame); return; } if (length == 0) { Object frame = spdyHeadersFrame; spdyHeadersFrame = null; headerBlockDecoder.reset(); state = State.READ_COMMON_HEADER; if (frame != null) { out.add(frame); } } return; case READ_DATA_FRAME: if (streamId == 0) { state = State.FRAME_ERROR; fireProtocolException(ctx, "Received invalid data frame"); return; } // Generate data frames that do not exceed maxChunkSize int dataLength = Math.min(maxChunkSize, length); // Wait until entire frame is readable if (buffer.readableBytes() < dataLength) { return; } ByteBuf data = ctx.alloc().buffer(dataLength); data.writeBytes(buffer, dataLength); SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamId, data); length -= dataLength; if (length == 0) { spdyDataFrame.setLast((flags & SPDY_DATA_FLAG_FIN) != 0); state = State.READ_COMMON_HEADER; } out.add(spdyDataFrame); return; case DISCARD_FRAME: int numBytes = Math.min(buffer.readableBytes(), length); buffer.skipBytes(numBytes); length -= numBytes; if (length == 0) { state = State.READ_COMMON_HEADER; } return; case FRAME_ERROR: buffer.skipBytes(buffer.readableBytes()); return; default: throw new Error("Shouldn't reach here."); } }