Author: markt
Date: Thu Jun 4 15:07:16 2015
New Revision: 1683572
URL: http://svn.apache.org/r1683572
Log:
Add initial tests for section 5.1
Add placeholder for PUSH_PROMISE frames (should never be received)
Expand state machine to start tracking allowed frames for each state
Stream now informs the state machine if a data or Window update frame is
received
Added:
tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_5_1.java (with
props)
Modified:
tomcat/trunk/java/org/apache/coyote/http2/AbstractStream.java
tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java
tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java
tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties
tomcat/trunk/java/org/apache/coyote/http2/Stream.java
tomcat/trunk/java/org/apache/coyote/http2/StreamStateMachine.java
tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java
Modified: tomcat/trunk/java/org/apache/coyote/http2/AbstractStream.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/AbstractStream.java?rev=1683572&r1=1683571&r2=1683572&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/AbstractStream.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/AbstractStream.java Thu Jun 4
15:07:16 2015
@@ -100,7 +100,11 @@ abstract class AbstractStream {
}
- protected void incrementWindowSize(int increment) {
+ /**
+s * @param increment
+ * @throws Http2Exception
+ */
+ protected void incrementWindowSize(int increment) throws Http2Exception {
windowSize.addAndGet(increment);
}
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=1683572&r1=1683571&r2=1683572&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java Thu Jun 4
15:07:16 2015
@@ -37,6 +37,7 @@ class Http2Parser {
private static final int FRAME_TYPE_HEADERS = 1;
private static final int FRAME_TYPE_PRIORITY = 2;
private static final int FRAME_TYPE_SETTINGS = 4;
+ private static final int FRAME_TYPE_PUSH_PROMISE = 5;
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;
@@ -127,6 +128,8 @@ class Http2Parser {
case FRAME_TYPE_SETTINGS:
readSettingsFrame(streamId, flags, payloadSize);
break;
+ case FRAME_TYPE_PUSH_PROMISE:
+ readPushPromiseFrame(streamId, flags, payloadSize);
case FRAME_TYPE_PING:
readPingFrame(streamId, flags, payloadSize);
break;
@@ -322,6 +325,17 @@ class Http2Parser {
}
+ private void readPushPromiseFrame(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)));
+ }
+
+ // TODO: Should never be received by a server
+ }
+
+
private void readPingFrame(int streamId, int flags, int payloadSize)
throws IOException {
if (log.isDebugEnabled()) {
@@ -586,7 +600,7 @@ class Http2Parser {
HpackDecoder getHpackDecoder();
// Data frames
- ByteBuffer getInputByteBuffer(int streamId, int payloadSize);
+ ByteBuffer getInputByteBuffer(int streamId, int payloadSize) throws
Http2Exception;
void endOfStream(int streamId);
// Header frames
@@ -606,7 +620,7 @@ class Http2Parser {
void goaway(int lastStreamId, long errorCode, String debugData);
// Window size
- void incrementWindowSize(int streamId, int increment);
+ void incrementWindowSize(int streamId, int increment) throws
Http2Exception;
// Testing
void swallow(int streamId, int frameType, int flags, int size) throws
IOException;
Modified: tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java?rev=1683572&r1=1683571&r2=1683572&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java
(original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java Thu Jun
4 15:07:16 2015
@@ -453,7 +453,7 @@ public class Http2UpgradeHandler extends
}
- int reserveWindowSize(Stream stream, int toWrite) {
+ int reserveWindowSize(Stream stream, int toWrite) throws Http2Exception {
int result;
synchronized (backLogLock) {
long windowSize = getWindowSize();
@@ -488,7 +488,7 @@ public class Http2UpgradeHandler extends
@Override
- protected void incrementWindowSize(int increment) {
+ protected void incrementWindowSize(int increment) throws Http2Exception {
synchronized (backLogLock) {
if (getWindowSize() == 0) {
releaseBackLog(increment);
@@ -706,7 +706,7 @@ public class Http2UpgradeHandler extends
@Override
- public ByteBuffer getInputByteBuffer(int streamId, int payloadSize) {
+ public ByteBuffer getInputByteBuffer(int streamId, int payloadSize) throws
Http2Exception {
Stream stream = getStream(streamId);
if (stream == null) {
return null;
@@ -802,7 +802,7 @@ public class Http2UpgradeHandler extends
@Override
- public void incrementWindowSize(int streamId, int increment) {
+ public void incrementWindowSize(int streamId, int increment) throws
Http2Exception {
AbstractStream stream = getStream(streamId);
if (stream != null) {
stream.incrementWindowSize(increment);
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=1683572&r1=1683571&r2=1683572&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties Thu Jun
4 15:07:16 2015
@@ -63,6 +63,9 @@ stream.write=Connection [{0}], Stream [{
streamProcessor.httpupgrade.notsupported=HTTP upgrade is not supported within
HTTP/2 streams
+streamStateMachine.invalidFrame.windowUpdate=Connection [{0}], Received Data
frame for stream [{1}] in state [{2}]
+streamStateMachine.invalidFrame.windowUpdate=Connection [{0}], Received Window
Update frame for stream [{1}] in state [{2}]
+
upgradeHandler.connectionError=An error occurred that requires the HTTP/2
connection to be closed.
upgradeHandler.goaway.debug=Connection [{0}], Goaway, Last stream [{1}], Error
code [{2}], Debug data [{3}]
upgradeHandler.init=Connection [{0}]
Modified: tomcat/trunk/java/org/apache/coyote/http2/Stream.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Stream.java?rev=1683572&r1=1683571&r2=1683572&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Stream.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Stream.java Thu Jun 4 15:07:16
2015
@@ -42,7 +42,7 @@ public class Stream extends AbstractStre
private final Response coyoteResponse = new Response();
private final StreamInputBuffer inputBuffer = new StreamInputBuffer();
private final StreamOutputBuffer outputBuffer = new StreamOutputBuffer();
- private final StreamStateMachine state = new StreamStateMachine();
+ private final StreamStateMachine state;
public Stream(Integer identifier, Http2UpgradeHandler handler) {
@@ -55,6 +55,7 @@ public class Stream extends AbstractStre
this.handler = handler;
setParentStream(handler);
setWindowSize(handler.getRemoteSettings().getInitialWindowSize());
+ state = new StreamStateMachine(this);
this.coyoteRequest = coyoteRequest;
this.coyoteRequest.setInputBuffer(inputBuffer);
this.coyoteResponse.setOutputBuffer(outputBuffer);
@@ -96,11 +97,14 @@ public class Stream extends AbstractStre
@Override
- public void incrementWindowSize(int windowSizeIncrement) {
+ public void incrementWindowSize(int windowSizeIncrement) throws
Http2Exception {
// If this is zero then any thread that has been trying to write for
// this stream will be waiting. Notify that thread it can continue. Use
// notify all even though only one thread is waiting to be on the safe
// side.
+ if (windowSizeIncrement > 0) {
+ state.receivedWindowUpdate();
+ }
boolean notify = getWindowSize() == 0;
super.incrementWindowSize(windowSizeIncrement);
if (notify) {
@@ -214,7 +218,8 @@ public class Stream extends AbstractStre
}
- ByteBuffer getInputByteBuffer() {
+ ByteBuffer getInputByteBuffer() throws Http2Exception {
+ state.receivedData();
return inputBuffer.getInBuffer();
}
Modified: tomcat/trunk/java/org/apache/coyote/http2/StreamStateMachine.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/StreamStateMachine.java?rev=1683572&r1=1683571&r2=1683572&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/StreamStateMachine.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/StreamStateMachine.java Thu Jun
4 15:07:16 2015
@@ -16,6 +16,8 @@
*/
package org.apache.coyote.http2;
+import org.apache.tomcat.util.res.StringManager;
+
/**
* See <a href="https://tools.ietf.org/html/rfc7540#section-5.1">state
* diagram</a> in RFC 7540.
@@ -23,15 +25,22 @@ package org.apache.coyote.http2;
* The following additions are supported by this state machine:
* <ul>
* <li>differentiate between closed (normal) and closed caused by reset</li>
- * <li>allow a transition from idle to closed if reset is sent or received</li>
* </ul>
*
*/
public class StreamStateMachine {
+ private static final StringManager sm =
StringManager.getManager(StreamStateMachine.class);
+
+ private final Stream stream;
private State state = State.IDLE;
+ public StreamStateMachine(Stream stream) {
+ this.stream = stream;
+ }
+
+
public synchronized void sendPushPromise() {
if (state == State.IDLE) {
state = State.RESERVED_LOCAL;
@@ -111,6 +120,11 @@ public class StreamStateMachine {
public synchronized void recieveReset() {
+ if (state == State.IDLE) {
+ // This should never happen
+ // TODO: ProtocolExcpetion? i18n
+ throw new IllegalStateException();
+ }
state = State.CLOSED_RESET;
}
@@ -130,14 +144,52 @@ public class StreamStateMachine {
}
+ public synchronized void receivedWindowUpdate() throws Http2Exception {
+ // No state change. Just checks state is valid for receiving window
+ // update.
+ if (!state.isWindowUpdatePermitted()) {
+ throw new
Http2Exception(sm.getString("streamStateMachine.invalidFrame.windowUpdate",
+ stream.getConnectionId(), stream.getIdentifier(), state),
+ 0, ErrorCode.PROTOCOL_ERROR);
+ }
+ }
+
+
+ public synchronized void receivedData() throws Http2Exception {
+ // No state change. Just checks state is valid for receiving window
+ // update.
+ if (!state.isDataPermitted()) {
+ throw new
Http2Exception(sm.getString("streamStateMachine.invalidFrame.data",
+ stream.getConnectionId(), stream.getIdentifier(), state),
+ 0, ErrorCode.PROTOCOL_ERROR);
+ }
+ }
+
+
private enum State {
- IDLE,
- OPEN,
- RESERVED_LOCAL,
- RESERVED_REMOTE,
- HALF_CLOSED_LOCAL,
- HALF_CLOSED_REMOTE,
- CLOSED,
- CLOSED_RESET
+ IDLE (false, false),
+ OPEN ( true, true),
+ RESERVED_LOCAL ( true, false),
+ RESERVED_REMOTE (false, false),
+ HALF_CLOSED_LOCAL ( true, true),
+ HALF_CLOSED_REMOTE ( true, false),
+ CLOSED (false, false),
+ CLOSED_RESET ( true, true);
+
+ private final boolean windowUpdatePermitted;
+ private final boolean dataPermitted;
+
+ private State(boolean windowUpdatePermitted, boolean dataPermitted) {
+ this.windowUpdatePermitted = windowUpdatePermitted;
+ this.dataPermitted = dataPermitted;
+ }
+
+ public boolean isWindowUpdatePermitted() {
+ return windowUpdatePermitted;
+ }
+
+ public boolean isDataPermitted() {
+ return dataPermitted;
+ }
}
}
Modified: tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java?rev=1683572&r1=1683571&r2=1683572&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java (original)
+++ tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java Thu Jun 4
15:07:16 2015
@@ -361,6 +361,37 @@ public abstract class Http2TestBase exte
}
+ void sendWindowUpdate(int streamId, int increment) throws IOException {
+ byte[] updateFrame = new byte[13];
+ // length is always 4
+ updateFrame[2] = 0x04;
+ // type is always 8
+ updateFrame[3] = 0x08;
+ // no flags
+ // Stream ID
+ ByteUtil.set31Bits(updateFrame, 5, streamId);
+ // Payload
+ ByteUtil.set31Bits(updateFrame, 9, increment);
+
+ os.write(updateFrame);
+ os.flush();
+ }
+
+
+ void sendData(int streamId, byte[] payload) throws IOException {
+ byte[] header = new byte[9];
+ // length
+ ByteUtil.setThreeBytes(header, 0, payload.length);
+ // Type is zero
+ // No flags
+ // Stream ID
+ ByteUtil.set31Bits(header, 5, streamId);
+
+ os.write(header);
+ os.write(payload);
+ os.flush();
+ }
+
private static class TestInput implements Http2Parser.Input {
private final InputStream is;
Added: tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_5_1.java?rev=1683572&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_5_1.java (added)
+++ tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_5_1.java Thu Jun
4 15:07:16 2015
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.coyote.http2;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for Section 5.§ of
+ * <a href="https://tools.ietf.org/html/rfc7540">RFC 7540</a>.
+ * <br>
+ * The order of tests in this class is aligned with the order of the
+ * requirements in the RFC.
+ */
+public class TestHttp2Section_5_1 extends Http2TestBase {
+
+ @Test
+ public void testIdleStateInvalidFrame01() throws Exception {
+ http2Connect();
+
+ sendWindowUpdate(3, 200);
+
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(),
+ output.getTrace().startsWith("0-Goaway-[2147483647]-[" +
+ ErrorCode.PROTOCOL_ERROR.getErrorCode() + "]-["));
+ }
+
+
+ @Test
+ public void testIdleStateInvalidFrame02() throws Exception {
+ http2Connect();
+
+ sendData(3, new byte[] {});
+
+ parser.readFrame(true);
+
+ Assert.assertTrue(output.getTrace(),
+ output.getTrace().startsWith("0-Goaway-[2147483647]-[" +
+ ErrorCode.PROTOCOL_ERROR.getErrorCode() + "]-["));
+ }
+
+
+ // TODO: Invalid frames for each of the remaining states
+}
Propchange: tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
------------------------------------------------------------------------------
svn:eol-style = native
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]