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: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to