You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2015/06/04 13:39:51 UTC
svn commit: r1683524 - in /tomcat/trunk/java/org/apache/coyote/http2:
Http2Parser.java LocalStrings.properties
Author: markt
Date: Thu Jun 4 11:39:51 2015
New Revision: 1683524
URL: http://svn.apache.org/r1683524
Log:
Add support for continuation frames, refactoring the now common header block processing into a separate method.
Modified:
tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java
tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties
Modified: tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java?rev=1683524&r1=1683523&r2=1683524&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java Thu Jun 4 11:39:51 2015
@@ -40,6 +40,7 @@ class Http2Parser {
private static final int FRAME_TYPE_PING = 6;
private static final int FRAME_TYPE_GOAWAY = 7;
private static final int FRAME_TYPE_WINDOW_UPDATE = 8;
+ private static final int FRAME_TYPE_CONTINUATION = 9;
private final String connectionId;
private final Input input;
@@ -48,6 +49,8 @@ class Http2Parser {
private volatile HpackDecoder hpackDecoder;
private final ByteBuffer headerReadBuffer = ByteBuffer.allocate(1024);
+ private volatile int headersCurrentStream = -1;
+ private volatile boolean headersEndStream = false;
private volatile boolean readPreface = false;
private volatile int maxPayloadSize = ConnectionSettings.DEFAULT_MAX_FRAME_SIZE;
@@ -98,6 +101,19 @@ class Http2Parser {
streamId, ErrorCode.FRAME_SIZE_ERROR);
}
+ if (headersCurrentStream != -1) {
+ if (headersCurrentStream != streamId) {
+ throw new Http2Exception(sm.getString("http2Parser.headers.wrongStream",
+ connectionId, Integer.toString(headersCurrentStream),
+ Integer.toString(streamId)), streamId, ErrorCode.COMPRESSION_ERROR);
+ }
+ if (frameType != FRAME_TYPE_CONTINUATION) {
+ throw new Http2Exception(sm.getString("http2Parser.headers.wrongFrameType",
+ connectionId, Integer.toString(headersCurrentStream),
+ Integer.toString(frameType)), streamId, ErrorCode.COMPRESSION_ERROR);
+ }
+ }
+
switch (frameType) {
case FRAME_TYPE_DATA:
readDataFrame(streamId, flags, payloadSize);
@@ -120,6 +136,9 @@ class Http2Parser {
case FRAME_TYPE_WINDOW_UPDATE:
readWindowUpdateFrame(streamId, flags, payloadSize);
break;
+ case FRAME_TYPE_CONTINUATION:
+ readContinuationFrame(streamId, flags, payloadSize);
+ break;
// TODO: Missing types
default:
readUnknownFrame(streamId, frameType, flags, payloadSize);
@@ -145,10 +164,9 @@ class Http2Parser {
// Process the Stream
int padLength = 0;
- boolean endOfStream = (flags & 0x01) > 0;
- boolean padding = (flags & 0x08) > 0;
+ boolean endOfStream = Flags.isEndOfStream(flags);
- if (padding) {
+ if (Flags.hasPadding(flags)) {
byte[] b = new byte[1];
input.fill(true, b);
padLength = b[0] & 0xFF;
@@ -187,18 +205,14 @@ class Http2Parser {
0, ErrorCode.PROTOCOL_ERROR);
}
- // TODO Handle end of headers flag
- // TODO Handle end of stream flag
- // TODO Handle continuation frames
-
if (hpackDecoder == null) {
hpackDecoder = output.getHpackDecoder();
}
hpackDecoder.setHeaderEmitter(output.headersStart(streamId));
int padLength = 0;
- boolean padding = (flags & 0x08) > 0;
- boolean priority = (flags & 0x20) > 0;
+ boolean padding = Flags.hasPadding(flags);
+ boolean priority = Flags.hasPriority(flags);
int optionalLen = 0;
if (padding) {
optionalLen = 1;
@@ -223,33 +237,25 @@ class Http2Parser {
payloadSize -= optionalLen;
}
- while (payloadSize > 0) {
- int toRead = Math.min(headerReadBuffer.remaining(), payloadSize);
- // headerReadBuffer in write mode
- input.fill(true, headerReadBuffer, toRead);
- // switch to read mode
- headerReadBuffer.flip();
- try {
- hpackDecoder.decode(headerReadBuffer);
- } catch (HpackException hpe) {
- throw new Http2Exception(
- sm.getString("http2Parser.processFrameHeaders.decodingFailed"),
- 0, ErrorCode.COMPRESSION_ERROR);
- }
- // switches to write mode
- headerReadBuffer.compact();
- payloadSize -= toRead;
- }
- // Should be empty at this point
- if (headerReadBuffer.position() > 0) {
- throw new Http2Exception(
- sm.getString("http2Parser.processFrameHeaders.decodingDataLeft"),
- 0, ErrorCode.COMPRESSION_ERROR);
- }
+ boolean endOfHeaders = Flags.isEndOfHeaders(flags);
+
+ readHeaderBlock(payloadSize, endOfHeaders);
swallow(padLength);
- output.headersEnd(streamId);
+ if (endOfHeaders) {
+ output.headersEnd(streamId);
+ } else {
+ headersCurrentStream = streamId;
+ }
+
+ if (Flags.isEndOfStream(flags)) {
+ if (headersCurrentStream == -1) {
+ output.endOfStream(streamId);
+ } else {
+ headersEndStream = true;
+ }
+ }
}
@@ -296,7 +302,7 @@ class Http2Parser {
throw new Http2Exception(sm.getString("http2Parser.processFrameSettings.invalidPayloadSize",
Integer.toString(payloadSize)), 0, ErrorCode.FRAME_SIZE_ERROR);
}
- boolean ack = (flags & 0x1) != 0;
+ boolean ack = Flags.isAck(flags);
if (payloadSize > 0 && ack) {
throw new Http2Exception(sm.getString("http2Parser.processFrameSettings.ackWithNonZeroPayload"),
0, ErrorCode.FRAME_SIZE_ERROR);
@@ -332,7 +338,7 @@ class Http2Parser {
throw new Http2Exception(sm.getString("http2Parser.processFramePing.invalidPayloadSize",
Integer.toString(payloadSize)), 0, ErrorCode.FRAME_SIZE_ERROR);
}
- if ((flags & 0x1) == 0) {
+ if (Flags.isAck(flags)) {
// Read the payload
byte[] payload = new byte[8];
input.fill(true, payload);
@@ -407,6 +413,68 @@ class Http2Parser {
}
+ private void readContinuationFrame(int streamId, int flags, int payloadSize)
+ throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http2Parser.processFrame", connectionId,
+ Integer.toString(streamId), Integer.toString(flags),
+ Integer.toString(payloadSize)));
+ }
+
+ if (streamId == 0) {
+ throw new Http2Exception(sm.getString(
+ "http2Parser.processFrameContinuation.invalidStream", connectionId),
+ 0, ErrorCode.PROTOCOL_ERROR);
+ }
+
+ if (headersCurrentStream == -1) {
+ // No headers to continue
+ throw new Http2Exception(sm.getString(
+ "http2Parser.processFrameContinuation.notExpected", connectionId,
+ Integer.toString(streamId)), 0, ErrorCode.PROTOCOL_ERROR);
+ }
+
+ boolean endOfHeaders = Flags.isEndOfHeaders(flags);
+ readHeaderBlock(payloadSize, endOfHeaders);
+
+ if (endOfHeaders) {
+ output.headersEnd(streamId);
+ headersCurrentStream = -1;
+ if (headersEndStream) {
+ output.endOfStream(streamId);
+ headersEndStream = false;
+ }
+ }
+ }
+
+
+ private void readHeaderBlock(int payloadSize, boolean endOfHeaders) throws IOException {
+ while (payloadSize > 0) {
+ int toRead = Math.min(headerReadBuffer.remaining(), payloadSize);
+ // headerReadBuffer in write mode
+ input.fill(true, headerReadBuffer, toRead);
+ // switch to read mode
+ headerReadBuffer.flip();
+ try {
+ hpackDecoder.decode(headerReadBuffer);
+ } catch (HpackException hpe) {
+ throw new Http2Exception(
+ sm.getString("http2Parser.processFrameHeaders.decodingFailed"),
+ 0, ErrorCode.COMPRESSION_ERROR);
+ }
+ // switches to write mode
+ headerReadBuffer.compact();
+ payloadSize -= toRead;
+ }
+
+ if (headerReadBuffer.position() > 0 && endOfHeaders) {
+ throw new Http2Exception(
+ sm.getString("http2Parser.processFrameHeaders.decodingDataLeft"),
+ 0, ErrorCode.COMPRESSION_ERROR);
+ }
+ }
+
+
private void readUnknownFrame(int streamId, int frameType, int flags, int payloadSize)
throws IOException {
output.swallow(streamId, frameType, flags, payloadSize);
Modified: tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties?rev=1683524&r1=1683523&r2=1683524&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties Thu Jun 4 11:39:51 2015
@@ -32,11 +32,15 @@ hpackdecoder.zeroNotValidHeaderTableInde
hpackhuffman.huffmanEncodedHpackValueDidNotEndWithEOS=Huffman encoded value in HPACK headers did not end with EOS padding
+http2Parser.headers.wrongFrameType=Connection [{0}], headers in progress for stream [{1}] but a frame of type [{2}] was received
+http2Parser.headers.wrongStream=Connection [{0}], headers in progress for stream [{1}] but a frame for stream [{2}] was received
http2Parser.payloadTooBig=The payload is [{0}] bytes long but the maximum frame size is [{1}]
http2Parser.preface.invalid=Invalid connection preface [{0}] presented
http2Parser.preface.io=Unable to read connection preface
http2Parser.processFrame=Connection [{0}], Stream [{1}], Flags [{2}], Payload size [{3}]
http2Parser.processFrame.unexpectedType=Expected frame type [{0}] but received frame type [{1}]
+http2Parser.processFrameContinuation.invalidStream=Connection [{0}], Continuation frame received for stream [0]
+http2Parser.processFrameContinuation.notExpected=Connection [{0}], Continuation frame received for stream [{1}] when no headers were in progress
http2Parser.processFrameData.invalidStream=Data frame received for stream [0]
http2Parser.processFrameGoaway.invalidStream=Goaway frame received for stream [{0}]
http2Parser.processFrameGoaway.payloadTooSmall=Connection [{0}]: Goaway payload size was [{1}] which is less than the minimum 8
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org