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/05/29 10:48:57 UTC

svn commit: r1682395 - in /tomcat/trunk/java/org/apache/coyote/http2: Http2UpgradeHandler.java LocalStrings.properties Stream.java

Author: markt
Date: Fri May 29 08:48:56 2015
New Revision: 1682395

URL: http://svn.apache.org/r1682395
Log:
Plumb in basic support for reading request bodies.

Modified:
    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

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=1682395&r1=1682394&r2=1682395&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java Fri May 29 08:48:56 2015
@@ -275,6 +275,9 @@ public class Http2UpgradeHandler extends
         int payloadSize = getPayloadSize(streamId, frameHeader);
 
         switch (frameType) {
+        case FRAME_TYPE_DATA:
+            processFrameData(flags, streamId, payloadSize);
+            break;
         case FRAME_TYPE_HEADERS:
             processFrameHeaders(flags, streamId, payloadSize);
             break;
@@ -298,6 +301,46 @@ public class Http2UpgradeHandler extends
     }
 
 
+    private void processFrameData(int flags, int streamId, int payloadSize) throws IOException {
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("upgradeHandler.processFrame",
+                    Long.toString(connectionId), Integer.toString(streamId),
+                    Integer.toString(flags), Integer.toString(payloadSize)));
+        }
+
+        // Validate the stream
+        if (streamId == 0) {
+            throw new Http2Exception(sm.getString("upgradeHandler.processFrameData.invalidStream"),
+                    0, Http2Exception.PROTOCOL_ERROR);
+        }
+
+        // Process the Stream
+        // TODO Handle end of stream flag
+        int padLength = 0;
+
+        boolean endOfStream = (flags & 0x01) > 0;
+        boolean padding = (flags & 0x08) > 0;
+
+        if (padding) {
+            byte[] b = new byte[1];
+            readFully(b);
+            padLength = b[0] & 0xFF;
+        }
+
+        Stream stream = getStream(streamId);
+        ByteBuffer dest = stream.getInputByteBuffer();
+        synchronized (dest) {
+            readFully(dest, payloadSize);
+            if (endOfStream) {
+                stream.setEndOfStream();
+            }
+            dest.notifyAll();
+        }
+
+        swallow(padLength);
+    }
+
+
     private void processFrameHeaders(int flags, int streamId, int payloadSize) throws IOException {
         if (log.isDebugEnabled()) {
             log.debug(sm.getString("upgradeHandler.processFrame",
@@ -373,9 +416,7 @@ public class Http2UpgradeHandler extends
                     0, Http2Exception.PROTOCOL_ERROR);
         }
 
-        if (padLength > 0) {
-            swallow(padLength);
-        }
+        swallow(padLength);
 
         // Process this stream on a container thread
         StreamProcessor streamProcessor = new StreamProcessor(stream, adapter, socketWrapper);
@@ -557,6 +598,9 @@ public class Http2UpgradeHandler extends
 
 
     private void swallow(int len) throws IOException {
+        if (len == 0) {
+            return;
+        }
         int read = 0;
         byte[] buffer = new byte[1024];
         while (read < len) {

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=1682395&r1=1682394&r2=1682395&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties Fri May 29 08:48:56 2015
@@ -41,6 +41,7 @@ upgradeHandler.init=Connection [{0}]
 upgradeHandler.ioerror=Connection [{0}]
 upgradeHandler.payloadTooBig=The payload is [{0}] bytes long but the maximum frame size is [{1}]
 upgradeHandler.processFrame=Connection [{0}], Stream [{1}], Flags [{2}], Payload size [{3}]
+upgradeHandler.processFrameData.invalidStream=Data frame received for stream [0]
 upgradeHandler.processFrameHeaders.invalidStream=Headers frame received for stream [0]
 upgradeHandler.processFrameHeaders.decodingFailed=There was an error during the HPACK decoding of HTTP headers
 upgradeHandler.processFrameHeaders.decodingDataLeft=Data left over after HPACK decoding - it should have been consumed

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=1682395&r1=1682394&r2=1682395&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Stream.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Stream.java Fri May 29 08:48:56 2015
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Iterator;
 
+import org.apache.coyote.InputBuffer;
 import org.apache.coyote.OutputBuffer;
 import org.apache.coyote.Request;
 import org.apache.coyote.Response;
@@ -39,6 +40,7 @@ public class Stream extends AbstractStre
     private final Http2UpgradeHandler handler;
     private final Request coyoteRequest = new Request();
     private final Response coyoteResponse = new Response();
+    private final StreamInputBuffer inputBuffer = new StreamInputBuffer();
     private final StreamOutputBuffer outputBuffer = new StreamOutputBuffer();
 
 
@@ -47,6 +49,7 @@ public class Stream extends AbstractStre
         this.handler = handler;
         setParentStream(handler);
         setWindowSize(handler.getRemoteSettings().getInitialWindowSize());
+        coyoteRequest.setInputBuffer(inputBuffer);
         coyoteResponse.setOutputBuffer(outputBuffer);
     }
 
@@ -199,6 +202,17 @@ public class Stream extends AbstractStre
     }
 
 
+    ByteBuffer getInputByteBuffer() {
+        return inputBuffer.getInBuffer();
+    }
+
+
+    void setEndOfStream() {
+        // TODO This is temporary until the state machine for a stream is
+        // implemented
+        inputBuffer.endOfStream = true;
+    }
+
     StreamOutputBuffer getOutputBuffer() {
         return outputBuffer;
     }
@@ -206,7 +220,7 @@ public class Stream extends AbstractStre
 
     class StreamOutputBuffer implements OutputBuffer {
 
-        private volatile ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
+        private final ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
         private volatile long written = 0;
         private volatile boolean finished = false;
 
@@ -307,4 +321,72 @@ public class Stream extends AbstractStre
             return ((written == 0) && finished);
         }
     }
+
+
+    class StreamInputBuffer implements InputBuffer {
+
+        /* Two buffers are required to avoid various multi-threading issues.
+         * These issues arise from the fact that the Stream (or the
+         * Request/Response) used by the application is processed in one thread
+         * but the connection is processed in another. Therefore it is possible
+         * that a request body frame could be received before the application
+         * is ready to read it. If it isn't buffered, processing of the
+         * connection (and hence all streams) would block until the application
+         * read the data. Hence the incoming data has to be buffered.
+         * If only one buffer was used then it could become corrupted if the
+         * connection thread is trying to add to it at the same time as the
+         * application is read it. While it should be possible to avoid this
+         * corruption by careful use of the buffer it would still require the
+         * same copies as using two buffers and the behaviour would be less
+         * clear.
+         */
+        // This buffer is used to populate the ByteChunk passed in to the read
+        // method
+        private final byte[] outBuffer = new byte[8 * 1024];
+        // This buffer is the destination for incoming data. It is normally is
+        // 'write mode'.
+        private final ByteBuffer inBuffer = ByteBuffer.allocate(8 * 1024);
+
+        private boolean endOfStream = false;
+
+        @Override
+        public int doRead(ByteChunk chunk) throws IOException {
+
+            int written = 0;
+
+            // Ensure that only one thread accesses inBuffer at a time
+            synchronized (inBuffer) {
+                while (inBuffer.position() == 0 && !endOfStream) {
+                    // Need to block until some data is written
+                    try {
+                        inBuffer.wait();
+                    } catch (InterruptedException e) {
+                        // TODO: Possible shutdown?
+                    }
+                }
+
+                if (inBuffer.position() > 0) {
+                    // Data remains in the in buffer. Copy it to the out buffer.
+                    inBuffer.flip();
+                    written = inBuffer.remaining();
+                    inBuffer.get(outBuffer, 0, written);
+                    inBuffer.clear();
+                } else if (endOfStream) {
+                    return -1;
+                } else {
+                    // TODO Should never happen
+                    throw new IllegalStateException();
+                }
+            }
+
+            chunk.setBytes(outBuffer, 0,  written);
+
+            return written;
+        }
+
+
+        public ByteBuffer getInBuffer() {
+            return inBuffer;
+        }
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org