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.");
}
}
}