void decode()

in src/main/java/com/twitter/whiskey/net/SpdyHeaderBlockRawDecoder.java [59:242]


    void decode(ByteBuffer headerBlock, int streamId) throws Exception {
        int skipLength;
        while (headerBlock.hasRemaining()) {
            switch(state) {
                case READ_NUM_HEADERS:
                    if (headerBlock.remaining() < LENGTH_FIELD_SIZE) {
                        return;
                    }

                    numHeaders = readLengthField(headerBlock);

                    if (numHeaders < 0) {
                        errorMessage = "received header block with negative header count";
                        state = State.ERROR;
                    } else if (numHeaders == 0) {
                        state = State.END_HEADER_BLOCK;
                    } else {
                        state = State.READ_NAME_LENGTH;
                    }
                    break;

                case READ_NAME_LENGTH:
                    if (headerBlock.remaining() < LENGTH_FIELD_SIZE) {
                        return;
                    }

                    length = readLengthField(headerBlock);

                    // Recipients of a zero-length name must issue a stream error
                    if (length < 0) {
                        errorMessage = "received header with negative-length name";
                        state = State.ERROR;
                    } else if (length == 0) {
                        errorMessage = "received header with zero-length name";
                        state = State.ERROR;
                    } else if (length > maxHeaderSize || headerSize > maxHeaderSize - length) {
                        headerSize = maxHeaderSize + 1;
                        errorMessage = "header name length is too long";
                        name = null;
                        state = State.SKIP_NAME;
                    } else {
                        headerSize += length;
                        state = State.READ_NAME;
                    }
                    break;

                case READ_NAME:
                    if (headerBlock.remaining() < length) {
                        return;
                    }

                    byte[] nameBytes = new byte[length];
                    headerBlock.get(nameBytes);
                    name = new String(nameBytes, "UTF-8");
                    state = State.READ_VALUE_LENGTH;
                    break;

                case SKIP_NAME:
                    skipLength = Math.min(headerBlock.remaining(), length);
                    headerBlock.position(headerBlock.position() + skipLength);
                    length -= skipLength;

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

                case READ_VALUE_LENGTH:
                    if (headerBlock.remaining() < LENGTH_FIELD_SIZE) {
                        return;
                    }

                    length = readLengthField(headerBlock);

                    // Recipients of illegal value fields must issue a stream error
                    if (length < 0) {
                        errorMessage = "received header with negative-length value";
                        state = State.ERROR;
                    } else if (length == 0) {
                        // SPDY/3 allows zero-length (empty) header values
                        delegate.readHeader(streamId, new Header(name, ""));

                        name = null;
                        if (--numHeaders == 0) {
                            state = State.END_HEADER_BLOCK;
                        } else {
                            state = State.READ_NAME_LENGTH;
                        }

                    } else if (name == null || length > maxHeaderSize || headerSize > maxHeaderSize - length) {
                        headerSize = maxHeaderSize + 1;
                        name = null;
                        state = State.SKIP_VALUE;
                    } else {
                        headerSize += length;
                        state = State.READ_VALUE;
                    }
                    break;

                case READ_VALUE:
                    if (headerBlock.remaining() < length) {
                        return;
                    }

                    byte[] valueBytes = new byte[length];
                    headerBlock.get(valueBytes);

                    // Add Name/Value pair to headers
                    int index = 0;
                    int offset = 0;

                    // Value must not start with a NULL character
                    if (valueBytes[0] == (byte) 0) {
                        state = State.ERROR;
                        break;
                    }

                    while (index < length) {
                        while (index < valueBytes.length && valueBytes[index] != (byte) 0) {
                            index ++;
                        }
                        if (index < valueBytes.length) {
                            // Received NULL character
                            if (index + 1 == valueBytes.length || valueBytes[index + 1] == (byte) 0) {
                                // Value field ended with a NULL character or
                                // received multiple, in-sequence NULL characters.
                                // Recipients of illegal value fields must issue a stream error
                                errorMessage = "received header with invalid value";
                                state = State.ERROR;
                                break;
                            }
                        }
                        String value = new String(valueBytes, offset, index - offset, "UTF-8");

                        delegate.readHeader(streamId, new Header(name, value));
                        // TODO: Handle name that contains NULL or non-ascii characters

                        index ++;
                        offset = index;
                    }

                    name = null;

                    // If we broke out of the add header loop, break here
                    if (state == State.ERROR) {
                        break;
                    }

                    if (--numHeaders == 0) {
                        state = State.END_HEADER_BLOCK;
                    } else {
                        state = State.READ_NAME_LENGTH;
                    }
                    break;

                case SKIP_VALUE:
                    skipLength = Math.min(headerBlock.remaining(), length);
                    headerBlock.position(headerBlock.position() + skipLength);
                    length -= skipLength;

                    if (length == 0) {
                        if (--numHeaders == 0) {
                            state = State.END_HEADER_BLOCK;
                        } else {
                            state = State.READ_NAME_LENGTH;
                        }
                    }
                    break;

                case END_HEADER_BLOCK:
                    errorMessage = "unexpected end of header block";
                    state = State.ERROR;
                    break;

                case ERROR:
                    headerBlock.position(headerBlock.limit());
                    delegate.readFrameError(errorMessage);
                    return;

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