public void decode()

in src/main/java/com/twitter/http2/HttpFrameDecoder.java [122:454]


    public void decode(ByteBuf buffer) {
        boolean endStream;
        boolean endSegment;
        int minLength;
        int dependency;
        int weight;
        boolean exclusive;
        int errorCode;

        while (true) {
            switch (state) {
            case READ_CONNECTION_HEADER:
                while (buffer.isReadable()) {
                    byte b = buffer.readByte();
                    if (b != CLIENT_CONNECTION_PREFACE[length++]) {
                        state = State.FRAME_ERROR;
                        delegate.readFrameError("Invalid Connection Header");
                        return;
                    }

                    if (length == CLIENT_CONNECTION_PREFACE.length) {
                        state = State.READ_FRAME_HEADER;
                        break;
                    }
                }
                if (buffer.isReadable()) {
                    break;
                } else {
                    return;
                }

            case READ_FRAME_HEADER:
                // Wait until entire header is readable
                if (buffer.readableBytes() < HTTP_FRAME_HEADER_SIZE) {
                    return;
                }

                // Read frame header fields
                readFrameHeader(buffer);

                // TODO(jpinner) Sec 4.2 FRAME_SIZE_ERROR

                if (!isValidFrameHeader(length, type, flags, streamId)) {
                    state = State.FRAME_ERROR;
                    delegate.readFrameError("Invalid Frame Header");
                } else if (frameHasPadding(type, flags)) {
                    state = State.READ_PADDING_LENGTH;
                } else {
                    paddingLength = 0;
                    state = getNextState(length, type);
                }
                break;

            case READ_PADDING_LENGTH:
                if (buffer.readableBytes() < 1) {
                    return;
                }

                paddingLength = buffer.readUnsignedByte();
                --length;

                if (!isValidPaddingLength(length, type, flags, paddingLength)) {
                    state = State.FRAME_ERROR;
                    delegate.readFrameError("Invalid Frame Padding Length");
                } else {
                    state = getNextState(length, type);
                }
                break;

            case READ_DATA_FRAME:
                endStream = hasFlag(flags, HTTP_FLAG_END_STREAM);
                state = State.READ_DATA;
                if (hasFlag(flags, HTTP_FLAG_PADDED)) {
                    delegate.readDataFramePadding(streamId, endStream, paddingLength + 1);
                }
                break;

            case READ_DATA:
                // Generate data frames that do not exceed maxChunkSize
                // maxChunkSize must be > 0 so we cannot infinitely loop
                int dataLength = Math.min(maxChunkSize, length - paddingLength);

                // Wait until entire frame is readable
                if (buffer.readableBytes() < dataLength) {
                    return;
                }

                ByteBuf data = buffer.readBytes(dataLength);
                length -= dataLength;

                if (length == paddingLength) {
                    if (paddingLength == 0) {
                        state = State.READ_FRAME_HEADER;
                    } else {
                        state = State.SKIP_FRAME_PADDING;
                    }
                }

                endStream = length == paddingLength && hasFlag(flags, HTTP_FLAG_END_STREAM);
                endSegment = length == paddingLength && hasFlag(flags, HTTP_FLAG_END_SEGMENT);

                delegate.readDataFrame(streamId, endStream, endSegment, data);
                break;

            case READ_HEADERS_FRAME:
                minLength = 0;
                if (hasFlag(flags, HTTP_FLAG_PRIORITY)) {
                    minLength = 5;
                }
                if (buffer.readableBytes() < minLength) {
                    return;
                }

                endStream = hasFlag(flags, HTTP_FLAG_END_STREAM);
                endSegment = hasFlag(flags, HTTP_FLAG_END_SEGMENT);

                exclusive = false;
                dependency = 0;
                weight = 16;
                if (hasFlag(flags, HTTP_FLAG_PRIORITY)) {
                    dependency = getSignedInt(buffer, buffer.readerIndex());
                    buffer.skipBytes(4);
                    weight = buffer.readUnsignedByte() + 1;
                    if (dependency < 0) {
                        dependency = dependency & 0x7FFFFFFF;
                        exclusive = true;
                    }
                    length -= 5;
                }

                state = State.READ_HEADER_BLOCK;
                delegate.readHeadersFrame(streamId, endStream, endSegment, exclusive, dependency, weight);
                break;

            case READ_PRIORITY_FRAME:
                if (buffer.readableBytes() < length) {
                    return;
                }

                exclusive = false;
                dependency = getSignedInt(buffer, buffer.readerIndex());
                buffer.skipBytes(4);
                weight = buffer.readUnsignedByte() + 1;
                if (dependency < 0) {
                    dependency = dependency & 0x7FFFFFFF;
                    exclusive = true;
                }

                state = State.READ_FRAME_HEADER;
                delegate.readPriorityFrame(streamId, exclusive, dependency, weight);
                break;

            case READ_RST_STREAM_FRAME:
                if (buffer.readableBytes() < length) {
                    return;
                }

                errorCode = getSignedInt(buffer, buffer.readerIndex());
                buffer.skipBytes(length);

                state = State.READ_FRAME_HEADER;
                delegate.readRstStreamFrame(streamId, errorCode);
                break;

            case READ_SETTINGS_FRAME:
                boolean ack = hasFlag(flags, HTTP_FLAG_ACK);

                state = State.READ_SETTING;
                delegate.readSettingsFrame(ack);
                break;

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

                if (buffer.readableBytes() < 6) {
                    return;
                }

                int id = getUnsignedShort(buffer, buffer.readerIndex());
                int value = getSignedInt(buffer, buffer.readerIndex() + 2);
                buffer.skipBytes(6);
                length -= 6;

                delegate.readSetting(id, value);
                break;

            case READ_PUSH_PROMISE_FRAME:
                if (buffer.readableBytes() < 4) {
                    return;
                }

                int promisedStreamId = getUnsignedInt(buffer, buffer.readerIndex());
                buffer.skipBytes(4);
                length -= 4;

                if (promisedStreamId == 0) {
                    state = State.FRAME_ERROR;
                    delegate.readFrameError("Invalid Promised-Stream-ID");
                } else {
                    state = State.READ_HEADER_BLOCK;
                    delegate.readPushPromiseFrame(streamId, promisedStreamId);
                }
                break;

            case READ_PING_FRAME:
                if (buffer.readableBytes() < length) {
                    return;
                }

                long ping = getSignedLong(buffer, buffer.readerIndex());
                buffer.skipBytes(length);

                boolean pong = hasFlag(flags, HTTP_FLAG_ACK);

                state = State.READ_FRAME_HEADER;
                delegate.readPingFrame(ping, pong);
                break;

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

                int lastStreamId = getUnsignedInt(buffer, buffer.readerIndex());
                errorCode = getSignedInt(buffer, buffer.readerIndex() + 4);
                buffer.skipBytes(8);
                length -= 8;

                if (length == 0) {
                    state = State.READ_FRAME_HEADER;
                } else {
                    paddingLength = length;
                    state = State.SKIP_FRAME_PADDING;
                }
                delegate.readGoAwayFrame(lastStreamId, errorCode);
                break;

            case READ_WINDOW_UPDATE_FRAME:
                // Wait until entire frame is readable
                if (buffer.readableBytes() < length) {
                    return;
                }

                int windowSizeIncrement = getUnsignedInt(buffer, buffer.readerIndex());
                buffer.skipBytes(length);

                if (windowSizeIncrement == 0) {
                    state = State.FRAME_ERROR;
                    delegate.readFrameError("Invalid Window Size Increment");
                } else {
                    state = State.READ_FRAME_HEADER;
                    delegate.readWindowUpdateFrame(streamId, windowSizeIncrement);
                }
                break;

            case READ_CONTINUATION_FRAME_HEADER:
                // Wait until entire frame header is readable
                if (buffer.readableBytes() < HTTP_FRAME_HEADER_SIZE) {
                    return;
                }

                // Read and validate continuation frame header fields
                int prevStreamId = streamId;
                readFrameHeader(buffer);

                // TODO(jpinner) Sec 4.2 FRAME_SIZE_ERROR
                // TODO(jpinner) invalid flags

                if (type != HTTP_CONTINUATION_FRAME || streamId != prevStreamId) {
                    state = State.FRAME_ERROR;
                    delegate.readFrameError("Invalid Continuation Frame");
                } else {
                    paddingLength = 0;
                    state = State.READ_HEADER_BLOCK;
                }
                break;

            case READ_HEADER_BLOCK:
                if (length == paddingLength) {
                    boolean endHeaders = hasFlag(flags, HTTP_FLAG_END_HEADERS);
                    if (endHeaders) {
                        state = State.SKIP_FRAME_PADDING;
                        delegate.readHeaderBlockEnd();
                    } else {
                        state = State.SKIP_FRAME_PADDING_CONTINUATION;
                    }
                    break;
                }

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

                int readableBytes = Math.min(buffer.readableBytes(), length - paddingLength);
                ByteBuf headerBlockFragment = buffer.readBytes(readableBytes);
                length -= readableBytes;

                delegate.readHeaderBlock(headerBlockFragment);
                break;

            case SKIP_FRAME_PADDING:
                int numBytes = Math.min(buffer.readableBytes(), length);
                buffer.skipBytes(numBytes);
                length -= numBytes;
                if (length == 0) {
                    state = State.READ_FRAME_HEADER;
                    break;
                }
                return;

            case SKIP_FRAME_PADDING_CONTINUATION:
                int numPaddingBytes = Math.min(buffer.readableBytes(), length);
                buffer.skipBytes(numPaddingBytes);
                length -= numPaddingBytes;
                if (length == 0) {
                    state = State.READ_CONTINUATION_FRAME_HEADER;
                    break;
                }
                return;

            case FRAME_ERROR:
                buffer.skipBytes(buffer.readableBytes());
                return;

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