package io.undertow.protocols.http2;
import static io.undertow.protocols.http2.Http2Channel.DATA_FLAG_END_STREAM;
import static io.undertow.protocols.http2.Http2Channel.FRAME_TYPE_CONTINUATION;
import static io.undertow.protocols.http2.Http2Channel.FRAME_TYPE_DATA;
import static io.undertow.protocols.http2.Http2Channel.FRAME_TYPE_GOAWAY;
import static io.undertow.protocols.http2.Http2Channel.FRAME_TYPE_HEADERS;
import static io.undertow.protocols.http2.Http2Channel.FRAME_TYPE_PRIORITY;
import static io.undertow.protocols.http2.Http2Channel.FRAME_TYPE_PUSH_PROMISE;
import static io.undertow.protocols.http2.Http2Channel.FRAME_TYPE_RST_STREAM;
import static io.undertow.protocols.http2.Http2Channel.FRAME_TYPE_SETTINGS;
import static io.undertow.protocols.http2.Http2Channel.FRAME_TYPE_WINDOW_UPDATE;
import static io.undertow.protocols.http2.Http2Channel.HEADERS_FLAG_END_HEADERS;
import static org.xnio.Bits.allAreClear;
import static org.xnio.Bits.anyAreClear;
import static org.xnio.Bits.anyAreSet;
import java.io.IOException;
import java.nio.ByteBuffer;
import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;
import io.undertow.server.protocol.framed.AbstractFramedStreamSourceChannel;
import io.undertow.server.protocol.framed.FrameHeaderData;
class implements FrameHeaderData {
final byte[] = new byte[9];
int = 0;
int ;
int ;
int ;
int ;
Http2PushBackParser = null;
Http2HeadersParser = null;
private static final int = ~(1 << 7);
private Http2Channel ;
(Http2Channel http2Channel, Http2HeadersParser continuationParser) {
this.http2Channel = http2Channel;
this.continuationParser = continuationParser;
}
public boolean handle(final ByteBuffer byteBuffer) throws IOException {
if (parser == null) {
if (!parseFrameHeader(byteBuffer)) {
return false;
}
if(continuationParser != null && type != FRAME_TYPE_CONTINUATION) {
throw new ConnectionErrorException(Http2Channel.ERROR_PROTOCOL_ERROR, UndertowMessages.MESSAGES.expectedContinuationFrame());
}
switch (type) {
case FRAME_TYPE_DATA: {
if (streamId == 0) {
throw new ConnectionErrorException(Http2Channel.ERROR_PROTOCOL_ERROR, UndertowMessages.MESSAGES.streamIdMustNotBeZeroForFrameType(Http2Channel.FRAME_TYPE_DATA));
}
parser = new Http2DataFrameParser(length);
break;
}
case FRAME_TYPE_HEADERS: {
if (streamId == 0) {
throw new ConnectionErrorException(Http2Channel.ERROR_PROTOCOL_ERROR, UndertowMessages.MESSAGES.streamIdMustNotBeZeroForFrameType(Http2Channel.FRAME_TYPE_HEADERS));
}
parser = new Http2HeadersParser(length, http2Channel.getDecoder(), http2Channel.isClient(), http2Channel.getMaxHeaders(), streamId, http2Channel.getMaxHeaderListSize());
if(allAreClear(flags, Http2Channel.HEADERS_FLAG_END_HEADERS)) {
continuationParser = (Http2HeadersParser) parser;
}
break;
}
case FRAME_TYPE_RST_STREAM: {
if(length != 4) {
throw new ConnectionErrorException(Http2Channel.ERROR_FRAME_SIZE_ERROR, UndertowMessages.MESSAGES.incorrectFrameSize());
}
parser = new Http2RstStreamParser(length);
break;
}
case FRAME_TYPE_CONTINUATION: {
if(continuationParser == null) {
http2Channel.sendGoAway(Http2Channel.ERROR_PROTOCOL_ERROR);
throw UndertowMessages.MESSAGES.http2ContinuationFrameNotExpected();
}
if(continuationParser.getStreamId() != streamId) {
http2Channel.sendGoAway(Http2Channel.ERROR_PROTOCOL_ERROR);
throw UndertowMessages.MESSAGES.http2ContinuationFrameNotExpected();
}
parser = continuationParser;
continuationParser.moreData(length);
break;
}
case FRAME_TYPE_PUSH_PROMISE: {
parser = new Http2PushPromiseParser(length, http2Channel.getDecoder(), http2Channel.isClient(), http2Channel.getMaxHeaders(), streamId, http2Channel.getMaxHeaderListSize());
if(allAreClear(flags, Http2Channel.HEADERS_FLAG_END_HEADERS)) {
continuationParser = (Http2HeadersParser) parser;
}
break;
}
case FRAME_TYPE_GOAWAY: {
if (streamId != 0) {
throw new ConnectionErrorException(Http2Channel.ERROR_PROTOCOL_ERROR, UndertowMessages.MESSAGES.streamIdMustBeZeroForFrameType(Http2Channel.FRAME_TYPE_GOAWAY));
}
parser = new Http2GoAwayParser(length);
break;
}
case Http2Channel.FRAME_TYPE_PING: {
if (length != 8) {
throw new ConnectionErrorException(Http2Channel.ERROR_FRAME_SIZE_ERROR, UndertowMessages.MESSAGES.invalidPingSize());
}
if (streamId != 0) {
throw new ConnectionErrorException(Http2Channel.ERROR_PROTOCOL_ERROR, UndertowMessages.MESSAGES.streamIdMustBeZeroForFrameType(Http2Channel.FRAME_TYPE_PING));
}
parser = new Http2PingParser(length);
break;
}
case FRAME_TYPE_SETTINGS: {
if(length % 6 != 0) {
throw new ConnectionErrorException(Http2Channel.ERROR_FRAME_SIZE_ERROR, UndertowMessages.MESSAGES.incorrectFrameSize());
}
if (streamId != 0) {
throw new ConnectionErrorException(Http2Channel.ERROR_PROTOCOL_ERROR, UndertowMessages.MESSAGES.streamIdMustBeZeroForFrameType(Http2Channel.FRAME_TYPE_SETTINGS));
}
parser = new Http2SettingsParser(length);
break;
}
case FRAME_TYPE_WINDOW_UPDATE: {
if(length != 4) {
throw new ConnectionErrorException(Http2Channel.ERROR_FRAME_SIZE_ERROR, UndertowMessages.MESSAGES.incorrectFrameSize());
}
parser = new Http2WindowUpdateParser(length);
break;
}
case FRAME_TYPE_PRIORITY: {
if(length != 5) {
throw new ConnectionErrorException(Http2Channel.ERROR_FRAME_SIZE_ERROR, UndertowMessages.MESSAGES.incorrectFrameSize());
}
if (streamId == 0) {
throw new ConnectionErrorException(Http2Channel.ERROR_PROTOCOL_ERROR, UndertowMessages.MESSAGES.streamIdMustNotBeZeroForFrameType(Http2Channel.FRAME_TYPE_PRIORITY));
}
parser = new Http2PriorityParser(length);
break;
}
default: {
parser = new Http2DiscardParser(length);
break;
}
}
}
parser.parse(byteBuffer, this);
if(continuationParser != null) {
if(anyAreSet(flags, HEADERS_FLAG_END_HEADERS)) {
continuationParser = null;
}
}
return parser.isFinished();
}
private boolean (ByteBuffer byteBuffer) {
while (read < 9 && byteBuffer.hasRemaining()) {
header[read++] = byteBuffer.get();
}
if (read != 9) {
return false;
}
length = (header[0] & 0xFF) << 16;
length += (header[1] & 0xff) << 8;
length += header[2] & 0xff;
type = header[3] & 0xff;
flags = header[4] & 0xff;
streamId = (header[5] & SECOND_RESERVED_MASK & 0xFF) << 24;
streamId += (header[6] & 0xFF) << 16;
streamId += (header[7] & 0xFF) << 8;
streamId += (header[8] & 0xFF);
return true;
}
@Override
public long () {
if (type != FRAME_TYPE_DATA) {
return 0;
}
return length;
}
int () {
return length;
}
@Override
public AbstractFramedStreamSourceChannel<?, ?, ?> () {
Http2StreamSourceChannel http2StreamSourceChannel;
if (type == FRAME_TYPE_DATA ||
type == Http2Channel.FRAME_TYPE_CONTINUATION ||
type == Http2Channel.FRAME_TYPE_PRIORITY ) {
if (anyAreSet(flags, Http2Channel.DATA_FLAG_END_STREAM)) {
http2StreamSourceChannel = http2Channel.removeStreamSource(streamId);
} else if (type == FRAME_TYPE_CONTINUATION) {
http2StreamSourceChannel = http2Channel.getIncomingStream(streamId);
if(http2StreamSourceChannel != null && http2StreamSourceChannel.isHeadersEndStream() && anyAreSet(flags, Http2Channel.CONTINUATION_FLAG_END_HEADERS)) {
http2Channel.removeStreamSource(streamId);
}
} else {
http2StreamSourceChannel = http2Channel.getIncomingStream(streamId);
}
if(type == FRAME_TYPE_DATA && http2StreamSourceChannel != null) {
Http2DataFrameParser dataFrameParser = (Http2DataFrameParser) parser;
http2StreamSourceChannel.updateContentSize(getFrameLength() - dataFrameParser.getPadding(), anyAreSet(flags, DATA_FLAG_END_STREAM));
}
return http2StreamSourceChannel;
} else if(type == FRAME_TYPE_HEADERS) {
Http2StreamSourceChannel channel = http2Channel.getIncomingStream(streamId);
if(channel != null) {
if(anyAreClear(flags, Http2Channel.HEADERS_FLAG_END_STREAM)) {
UndertowLogger.REQUEST_IO_LOGGER.debug("Received HTTP/2 trailers header without end stream set");
http2Channel.sendGoAway(Http2Channel.ERROR_PROTOCOL_ERROR);
}
if (!channel.isHeadersEndStream() && anyAreSet(flags, Http2Channel.HEADERS_FLAG_END_HEADERS)) {
http2Channel.removeStreamSource(streamId);
}
}
return channel;
}
return null;
}
Http2PushBackParser () {
return parser;
}
Http2HeadersParser () {
return continuationParser;
}
}