public void decode()

in src/main/java/com/twitter/whiskey/net/SpdyFrameDecoder.java [105:388]


    public void decode(ByteBuffer buffer) {
        boolean last;
        int statusCode;

        while (true) {
            switch(state) {
                case READ_COMMON_HEADER:
                    if (buffer.remaining() < SPDY_HEADER_SIZE) {
                        return;
                    }

                    int frameOffset  = buffer.position();
                    int flagsOffset  = frameOffset + SPDY_HEADER_FLAGS_OFFSET;
                    int lengthOffset = frameOffset + SPDY_HEADER_LENGTH_OFFSET;
                    buffer.position(frameOffset + SPDY_HEADER_SIZE);

                    boolean control = (buffer.get(frameOffset) & 0x80) != 0;

                    int version;
                    int type;
                    if (control) {
                        // Decode control frame common header
                        version = getUnsignedShort(buffer, frameOffset) & 0x7FFF;
                        type = getUnsignedShort(buffer, frameOffset + SPDY_HEADER_TYPE_OFFSET);
                        streamId = SPDY_SESSION_STREAM_ID; // Default to session Stream-ID
                    } else {
                        // Decode data frame common header
                        version = spdyVersion; // Default to expected version
                        type = SPDY_DATA_FRAME;
                        streamId = getUnsignedInt(buffer, frameOffset);
                    }

                    flags  = buffer.get(flagsOffset);
                    length = getUnsignedMedium(buffer, lengthOffset);

                    // Check version first then validity
                    if (version != spdyVersion) {
                        state = State.FRAME_ERROR;
                        delegate.readFrameError("Invalid SPDY Version");
                    } else if (!isValidFrameHeader(streamId, type, flags, length)) {
                        state = State.FRAME_ERROR;
                        delegate.readFrameError("Invalid Frame Error");
                    } else {
                        state = getNextState(type, length);
                    }
                    break;

                case READ_DATA_FRAME:
                    if (length == 0) {
                        state = State.READ_COMMON_HEADER;
                        delegate.readDataFrame(streamId, hasFlag(flags, SPDY_DATA_FLAG_FIN), ByteBuffer.allocate(0));
                        break;
                    }

                    // Generate data frames that do not exceed maxChunkSize and generally exceed
                    // minChunkSize
                    int bytesToRead = Math.min(maxChunkSize, length);
                    int bytesBuffered = buffer.remaining();
                    if (bytesBuffered < bytesToRead) {
                        if (bytesBuffered < minChunkSize) return;
                        bytesToRead = bytesBuffered;
                    }

                    ByteBuffer data = ByteBuffer.allocate(bytesToRead);
                    int oldLimit = buffer.limit();
                    buffer.limit(buffer.position() + bytesToRead);
                    data.put(buffer);
                    data.flip();
                    buffer.limit(oldLimit);
                    length -= bytesToRead;

                    if (length == 0) {
                        state = State.READ_COMMON_HEADER;
                    }

                    last = length == 0 && hasFlag(flags, SPDY_DATA_FLAG_FIN);

                    delegate.readDataFrame(streamId, last, data);
                    break;

                case READ_SYN_STREAM_FRAME:
                    if (buffer.remaining() < 10) {
                        return;
                    }

                    int offset = buffer.position();
                    streamId = getUnsignedInt(buffer, offset);
                    int associatedToStreamId = getUnsignedInt(buffer, offset + 4);
                    byte priority = (byte) (buffer.get(offset + 8) >> 5 & 0x07);
                    last = hasFlag(flags, SPDY_FLAG_FIN);
                    boolean unidirectional = hasFlag(flags, SPDY_FLAG_UNIDIRECTIONAL);
                    buffer.position(offset + 10);
                    length -= 10;

                    if (streamId == 0) {
                        state = State.FRAME_ERROR;
                        delegate.readFrameError("Invalid SYN_STREAM Frame");
                    } else {
                        state = State.READ_HEADER_BLOCK;
                        delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, last, unidirectional);
                    }
                    break;

                case READ_SYN_REPLY_FRAME:
                    if (buffer.remaining() < 4) {
                        return;
                    }

                    streamId = getUnsignedInt(buffer);
                    last = hasFlag(flags, SPDY_FLAG_FIN);
                    length -= 4;

                    if (streamId == 0) {
                        state = State.FRAME_ERROR;
                        delegate.readFrameError("Invalid SYN_REPLY Frame");
                    } else {
                        state = State.READ_HEADER_BLOCK;
                        delegate.readSynReplyFrame(streamId, last);
                    }
                    break;

                case READ_RST_STREAM_FRAME:
                    if (buffer.remaining() < 8) {
                        return;
                    }

                    streamId = getUnsignedInt(buffer);
                    statusCode = buffer.getInt();

                    if (streamId == 0 || statusCode == 0) {
                        state = State.FRAME_ERROR;
                        delegate.readFrameError("Invalid RST_STREAM Frame");
                    } else {
                        state = State.READ_COMMON_HEADER;
                        delegate.readRstStreamFrame(streamId, statusCode);
                    }
                    break;

                case READ_SETTINGS_FRAME:
                    if (buffer.remaining() < 4) {
                        return;
                    }

                    boolean clear = hasFlag(flags, SPDY_SETTINGS_CLEAR);

                    numSettings = getUnsignedInt(buffer);
                    length -= 4;

                    // Validate frame length against number of entries. Each ID/Value entry is 8 bytes.
                    if ((length & 0x07) != 0 || length >> 3 != numSettings) {
                        state = State.FRAME_ERROR;
                        delegate.readFrameError("Invalid SETTINGS Frame");
                    } else {
                        state = State.READ_SETTING;
                        delegate.readSettingsFrame(clear);
                    }
                    break;

                case READ_SETTING:
                    if (numSettings == 0) {
                        state = State.READ_COMMON_HEADER;
                        delegate.readSettingsEnd();
                        break;
                    }

                    if (buffer.remaining() < 8) {
                        return;
                    }

                    byte settingsFlags = buffer.get();
                    int id = getUnsignedMedium(buffer);
                    int value = buffer.getInt();
                    boolean persistValue = hasFlag(settingsFlags, SPDY_SETTINGS_PERSIST_VALUE);
                    boolean persisted = hasFlag(settingsFlags, SPDY_SETTINGS_PERSISTED);
                    --numSettings;

                    delegate.readSetting(id, value, persistValue, persisted);
                    break;

                case READ_PING_FRAME:
                    if (buffer.remaining() < 4) {
                        return;
                    }

                    int pingId = buffer.getInt();

                    state = State.READ_COMMON_HEADER;
                    delegate.readPingFrame(pingId);
                    break;

                case READ_GOAWAY_FRAME:
                    if (buffer.remaining() < 8) {
                        return;
                    }

                    int lastGoodStreamId = getUnsignedInt(buffer);
                    statusCode = buffer.getInt();

                    state = State.READ_COMMON_HEADER;
                    delegate.readGoAwayFrame(lastGoodStreamId, statusCode);
                    break;

                case READ_HEADERS_FRAME:
                    if (buffer.remaining() < 4) {
                        return;
                    }

                    streamId = getUnsignedInt(buffer);
                    last = hasFlag(flags, SPDY_FLAG_FIN);
                    length -= 4;

                    if (streamId == 0) {
                        state = State.FRAME_ERROR;
                        delegate.readFrameError("Invalid HEADERS Frame");
                    } else {
                        state = State.READ_HEADER_BLOCK;
                        delegate.readHeadersFrame(streamId, last);
                    }
                    break;

                case READ_WINDOW_UPDATE_FRAME:
                    if (buffer.remaining() < 8) {
                        return;
                    }

                    streamId = getUnsignedInt(buffer);
                    int deltaWindowSize = getUnsignedInt(buffer);

                    if (deltaWindowSize == 0) {
                        state = State.FRAME_ERROR;
                        delegate.readFrameError("Invalid WINDOW_UPDATE Frame");
                    } else {
                        state = State.READ_COMMON_HEADER;
                        delegate.readWindowUpdateFrame(streamId, deltaWindowSize);
                    }
                    break;

                case READ_HEADER_BLOCK:
                    if (length == 0) {
                        state = State.READ_COMMON_HEADER;
                        headerBlockDecoder.endHeaderBlock();
                        delegate.readHeadersEnd(streamId);
                        break;
                    }

                    if (!buffer.hasRemaining()) {
                        return;
                    }

                    int headerBytes = Math.min(buffer.remaining(), length);

                    ByteBuffer headerBlock = buffer.slice();
                    headerBlock.limit(headerBytes);

                    try {
                        headerBlockDecoder.decode(headerBlock, streamId);
                    } catch (Exception e) {
                        state = State.FRAME_ERROR;
                    }

                    int bytesRead = headerBytes - headerBlock.remaining();
                    buffer.position(buffer.position() + bytesRead);
                    length -= bytesRead;
                    break;

                case DISCARD_FRAME:
                    int numBytes = Math.min(buffer.remaining(), length);
                    buffer.position(buffer.position() + numBytes);
                    length -= numBytes;
                    if (length == 0) {
                        state = State.READ_COMMON_HEADER;
                        break;
                    }
                    return;

                case FRAME_ERROR:
                    buffer.position(buffer.limit());
                    return;

                default:
                    throw new Error("Shouldn't reach here.");
            }
        }
    }