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/13 22:36:27 UTC

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

Author: markt
Date: Wed May 13 20:36:27 2015
New Revision: 1679270

URL: http://svn.apache.org/r1679270
Log:
Start to process Headers frame

Modified:
    tomcat/trunk/java/org/apache/coyote/http2/AbstractStream.java
    tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.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

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=1679270&r1=1679269&r2=1679270&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/AbstractStream.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/AbstractStream.java Wed May 13 20:36:27 2015
@@ -21,7 +21,6 @@ import java.util.Iterator;
 import java.util.Set;
 
 import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.res.StringManager;
 
 /**
@@ -29,7 +28,6 @@ import org.apache.tomcat.util.res.String
  */
 abstract class AbstractStream {
 
-    private static final Log log = LogFactory.getLog(AbstractStream.class);
     private static final StringManager sm = StringManager.getManager(AbstractStream.class);
 
     private final Integer identifier;
@@ -49,8 +47,8 @@ abstract class AbstractStream {
 
 
     public void rePrioritise(AbstractStream parent, boolean exclusive, int weight) {
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("abstractStream.reprioritisation.debug", identifier,
+        if (getLog().isDebugEnabled()) {
+            getLog().debug(sm.getString("abstractStream.reprioritisation.debug", identifier,
                     Boolean.toString(exclusive), parent.getIdentifier(), Integer.toString(weight)));
         }
 
@@ -120,4 +118,6 @@ abstract class AbstractStream {
     Set<AbstractStream> getChildStreams() {
         return childStreams;
     }
+
+    protected abstract Log getLog();
 }

Modified: tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java?rev=1679270&r1=1679269&r2=1679270&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java Wed May 13 20:36:27 2015
@@ -27,20 +27,20 @@ public class ConnectionSettings {
     private final Log log = LogFactory.getLog(ConnectionSettings.class);
     private final StringManager sm = StringManager.getManager(ConnectionSettings.class);
 
-    public static final long DEFAULT_WINDOW_SIZE = (1 << 16) - 1;
-    private static final long UNLIMITED = 1 << 32; // Use the maximum possible
-    private static final long MAX_WINDOW_SIZE = (1 << 31) - 1;
-    private static final long MIN_MAX_FRAME_SIZE = 1 << 14;
-    private static final long MAX_MAX_FRAME_SIZE = (1 << 24) - 1;
-
-    private volatile long headerTableSize = 4096;
-    private volatile long enablePush = 1;
-    private volatile long maxConcurrentStreams = UNLIMITED;
-    private volatile long initialWindowSize = DEFAULT_WINDOW_SIZE;
-    private volatile long maxFrameSize = MIN_MAX_FRAME_SIZE;
-    private volatile long maxHeaderListSize = UNLIMITED;
+    public static final int DEFAULT_WINDOW_SIZE = (1 << 16) - 1;
+    private static final int UNLIMITED = 1 << 32; // Use the maximum possible
+    private static final int MAX_WINDOW_SIZE = (1 << 31) - 1;
+    private static final int MIN_MAX_FRAME_SIZE = 1 << 14;
+    private static final int MAX_MAX_FRAME_SIZE = (1 << 24) - 1;
+
+    private volatile int headerTableSize = 4096;
+    private volatile int enablePush = 1;
+    private volatile int maxConcurrentStreams = UNLIMITED;
+    private volatile int initialWindowSize = DEFAULT_WINDOW_SIZE;
+    private volatile int maxFrameSize = MIN_MAX_FRAME_SIZE;
+    private volatile int maxHeaderListSize = UNLIMITED;
 
-    public void set(int parameterId, long value) throws IOException {
+    public void set(int parameterId, int value) throws IOException {
         if (log.isDebugEnabled()) {
             log.debug(sm.getString("connectionSettings.debug",
                     Integer.toString(parameterId), Long.toString(value)));
@@ -73,18 +73,18 @@ public class ConnectionSettings {
     }
 
 
-    public long getHeaderTableSize() {
+    public int getHeaderTableSize() {
         return headerTableSize;
     }
-    public void setHeaderTableSize(long headerTableSize) {
+    public void setHeaderTableSize(int headerTableSize) {
         this.headerTableSize = headerTableSize;
     }
 
 
-    public long getEnablePush() {
+    public int getEnablePush() {
         return enablePush;
     }
-    public void setEnablePush(long enablePush) throws IOException {
+    public void setEnablePush(int enablePush) throws IOException {
         // Can't be less than zero since the result of the byte->long conversion
         // will never be negative
         if (enablePush > 1) {
@@ -95,18 +95,18 @@ public class ConnectionSettings {
     }
 
 
-    public long getMaxConcurrentStreams() {
+    public int getMaxConcurrentStreams() {
         return maxConcurrentStreams;
     }
-    public void setMaxConcurrentStreams(long maxConcurrentStreams) {
+    public void setMaxConcurrentStreams(int maxConcurrentStreams) {
         this.maxConcurrentStreams = maxConcurrentStreams;
     }
 
 
-    public long getInitialWindowSize() {
+    public int getInitialWindowSize() {
         return initialWindowSize;
     }
-    public void setInitialWindowSize(long initialWindowSize) throws IOException {
+    public void setInitialWindowSize(int initialWindowSize) throws IOException {
         if (initialWindowSize > MAX_WINDOW_SIZE) {
             throw new Http2Exception(sm.getString("connectionSettings.windowSizeTooBig",
                     Long.toString(initialWindowSize), Long.toString(MAX_WINDOW_SIZE)),
@@ -116,10 +116,10 @@ public class ConnectionSettings {
     }
 
 
-    public long getMaxFrameSize() {
+    public int getMaxFrameSize() {
         return maxFrameSize;
     }
-    public void setMaxFrameSize(long maxFrameSize) throws IOException {
+    public void setMaxFrameSize(int maxFrameSize) throws IOException {
         if (maxFrameSize < MIN_MAX_FRAME_SIZE || maxFrameSize > MAX_MAX_FRAME_SIZE) {
             throw new Http2Exception(sm.getString("connectionSettings.maxFrameSizeInvalid",
                     Long.toString(maxFrameSize), Long.toString(MIN_MAX_FRAME_SIZE),
@@ -129,10 +129,10 @@ public class ConnectionSettings {
     }
 
 
-    public long getMaxHeaderListSize() {
+    public int getMaxHeaderListSize() {
         return maxHeaderListSize;
     }
-    public void setMaxHeaderListSize(long maxHeaderListSize) {
+    public void setMaxHeaderListSize(int maxHeaderListSize) {
         this.maxHeaderListSize = maxHeaderListSize;
     }
 }

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=1679270&r1=1679269&r2=1679270&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java Wed May 13 20:36:27 2015
@@ -18,6 +18,7 @@ package org.apache.coyote.http2;
 
 import java.io.EOFException;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Map;
@@ -52,6 +53,7 @@ public class Http2UpgradeHandler extends
 
     private static final Integer STREAM_ID_ZERO = Integer.valueOf(0);
 
+    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_WINDOW_UPDATE = 8;
@@ -67,11 +69,15 @@ public class Http2UpgradeHandler extends
     private volatile boolean firstFrame = true;
 
     private final ConnectionSettings remoteSettings = new ConnectionSettings();
+    private final ConnectionSettings localSettings = new ConnectionSettings();
     private volatile long flowControlWindowSize = ConnectionSettings.DEFAULT_WINDOW_SIZE;
     private volatile int maxRemoteStreamId = 0;
 
-    private final Map<Integer,Stream> streams = new HashMap<>();
+    private HpackDecoder hpackDecoder;
+    private ByteBuffer headerReadBuffer = ByteBuffer.allocate(1024);
+    private HpackEncoder hpackEncoder;
 
+    private final Map<Integer,Stream> streams = new HashMap<>();
 
     public Http2UpgradeHandler() {
         super (STREAM_ID_ZERO);
@@ -188,6 +194,9 @@ public class Http2UpgradeHandler extends
         int payloadSize = getPayloadSize(streamId, frameHeader);
 
         switch (frameType) {
+        case FRAME_TYPE_HEADERS:
+            processFrameHeaders(flags, streamId, payloadSize);
+            break;
         case FRAME_TYPE_PRIORITY:
             processFramePriority(flags, streamId, payloadSize);
             break;
@@ -205,6 +214,85 @@ public class Http2UpgradeHandler extends
     }
 
 
+    private void processFrameHeaders(int flags, int streamId, int payloadSize) throws IOException {
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("upgradeHandler.processFrame",
+                    Integer.toString(FRAME_TYPE_HEADERS), Integer.toString(flags),
+                    Integer.toString(streamId), Integer.toString(payloadSize)));
+        }
+
+        // Validate the stream
+        if (streamId == 0) {
+            throw new Http2Exception(sm.getString("upgradeHandler.processFrameHeaders.invalidStream"),
+                    0, Http2Exception.PROTOCOL_ERROR);
+        }
+
+        // Process the stream
+        // TODO Handle end of headers flag
+        // TODO Handle end of stream flag
+        // TODO Handle continutation frames
+        Stream stream = getStream(streamId);
+        int padLength = 0;
+
+        boolean padding = (flags & 0x08) > 0;
+        boolean priority = (flags & 0x20) > 0;
+        int optionalLen = 0;
+        if (padding) {
+            optionalLen = 1;
+        }
+        if (priority) {
+            optionalLen += 5;
+        }
+        if (optionalLen > 0) {
+            byte[] optional = new byte[optionalLen];
+            readFully(optional);
+            int optionalPos = 0;
+            if (padding) {
+                padLength = ByteUtil.getOneByte(optional, optionalPos++);
+            }
+            if (priority) {
+                boolean exclusive = ByteUtil.isBit7Set(optional[optionalPos]);
+                int parentStreamId = ByteUtil.get31Bits(optional, optionalPos);
+                int weight = ByteUtil.getOneByte(optional, optionalPos + 4) + 1;
+                AbstractStream parentStream = getStream(parentStreamId);
+                if (parentStream == null) {
+                    parentStream = this;
+                }
+                stream.rePrioritise(parentStream, exclusive, weight);
+            }
+
+            payloadSize -= optionalLen;
+        }
+
+        hpackDecoder.setHeaderEmitter(stream);
+        while (payloadSize > 0) {
+            int toRead = Math.min(headerReadBuffer.remaining(), payloadSize);
+            // headerReadBuffer in write mode
+            readFully(headerReadBuffer, toRead);
+            // switch to read mode
+            headerReadBuffer.flip();
+            try {
+                hpackDecoder.decode(headerReadBuffer);
+            } catch (HpackException hpe) {
+                // TODO i18n
+                throw new Http2Exception("", 0, Http2Exception.PROTOCOL_ERROR);
+            }
+            // switches to write mode
+            headerReadBuffer.compact();
+            payloadSize -= toRead;
+        }
+        // Should be empty at this point
+        if (headerReadBuffer.position() > 0) {
+            // TODO i18n
+            throw new Http2Exception("", 0, Http2Exception.PROTOCOL_ERROR);
+        }
+
+        if (padLength > 0) {
+            swallowPayload(padLength);
+        }
+    }
+
+
     private void processFramePriority(int flags, int streamId, int payloadSize) throws IOException {
         if (log.isDebugEnabled()) {
             log.debug(sm.getString("upgradeHandler.processFrame",
@@ -280,10 +368,15 @@ public class Http2UpgradeHandler extends
             for (int i = 0; i < payloadSize / 6; i++) {
                 readFully(setting);
                 int id = ByteUtil.getTwoBytes(setting, 0);
-                long value = ByteUtil.getFourBytes(setting, 2);
+                int value = ByteUtil.getFourBytes(setting, 2);
                 remoteSettings.set(id, value);
             }
         }
+        if (firstFrame) {
+            firstFrame = false;
+            hpackDecoder = new HpackDecoder(remoteSettings.getHeaderTableSize());
+            hpackEncoder = new HpackEncoder(localSettings.getHeaderTableSize());
+        }
 
         // Acknowledge the settings
         // TODO Need to coordinate writes with other threads
@@ -344,7 +437,7 @@ public class Http2UpgradeHandler extends
 
     private void swallowPayload(int payloadSize) throws IOException {
         int read = 0;
-        byte[] buffer = new byte[8 * 1024];
+        byte[] buffer = new byte[1024];
         while (read < payloadSize) {
             int toRead = Math.min(buffer.length, payloadSize - read);
             int thisTime = socketWrapper.read(true, buffer, 0, toRead);
@@ -386,8 +479,6 @@ public class Http2UpgradeHandler extends
             if (frameType != FRAME_TYPE_SETTINGS) {
                 throw new Http2Exception(sm.getString("upgradeHandler.receivePrefaceNotSettings"),
                         0, Http2Exception.PROTOCOL_ERROR);
-            } else {
-                firstFrame = false;
             }
         }
         return frameType;
@@ -463,6 +554,19 @@ public class Http2UpgradeHandler extends
     }
 
 
+    private void readFully(ByteBuffer dest, int len) throws IOException {
+        int read = 0;
+        while (read < len) {
+            int thisTime = socketWrapper.read(true, dest.array(), dest.arrayOffset(), len -read);
+            if (thisTime == -1) {
+                throw new EOFException(sm.getString("upgradeHandler.unexpectedEos"));
+            }
+            read += thisTime;
+        }
+        dest.position(dest.position() + read);
+    }
+
+
     private Stream getStream(int streamId) {
         Integer key = Integer.valueOf(streamId);
 
@@ -485,4 +589,10 @@ public class Http2UpgradeHandler extends
             log.debug(sm.getString("upgradeHandler.socketCloseFailed"), ioe);
         }
     }
+
+
+    @Override
+    protected final Log getLog() {
+        return log;
+    }
 }

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=1679270&r1=1679269&r2=1679270&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties Wed May 13 20:36:27 2015
@@ -30,12 +30,14 @@ hpackdecoder.zeroNotValidHeaderTableInde
 
 hpackhuffman.huffmanEncodedHpackValueDidNotEndWithEOS=Huffman encoded value in HPACK headers did not end with EOS padding
 
+stream.header.debug=Stream [{0}] recieved HTTP header [{1}] with value [{2}]
 upgradeHandler.connectionError=An error occurred that requires the HTTP/2 connection to be closed.
 upgradeHandler.payloadTooBig=The payload is [{0}] bytes long but the maximum frame size is [{1}]
 upgradeHandler.processFrame=Processing frame of type [{0}] for stream [{2}] with flags [{1}] and payload size [{3}]
 upgradeHandler.processFrame.ioerror=An I/O error occurred while reading an incoming HTTP/2 frame
-upgradeHandler.processFramePriority.invalidPayloadSize=Settings frame received with an invalid payload size of [{0}] (should be 5)
-upgradeHandler.processFramePriority.invalidStream=Settings frame received for stream [0]
+upgradeHandler.processFrameHeaders.invalidStream=Headers frame received for stream [0]
+upgradeHandler.processFramePriority.invalidPayloadSize=Priority frame received with an invalid payload size of [{0}] (should be 5)
+upgradeHandler.processFramePriority.invalidStream=Priority frame received for stream [0]
 upgradeHandler.processFrameSettings.ackWithNonZeroPayload=Settings frame received with the ACK flag set and payload present
 upgradeHandler.processFrameSettings.invalidPayloadSize=Settings frame received with a payload size of [{0}] which is not a multiple of 6
 upgradeHandler.processFrameSettings.invalidStream=Settings frame received for stream [{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=1679270&r1=1679269&r2=1679270&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Stream.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Stream.java Wed May 13 20:36:27 2015
@@ -16,7 +16,15 @@
  */
 package org.apache.coyote.http2;
 
-public class Stream extends AbstractStream {
+import org.apache.coyote.http2.HpackDecoder.HeaderEmitter;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+public class Stream extends AbstractStream implements HeaderEmitter {
+
+    private static final Log log = LogFactory.getLog(Stream.class);
+    private static final StringManager sm = StringManager.getManager(Stream.class);
 
     private volatile long flowControlWindowSize;
 
@@ -31,4 +39,20 @@ public class Stream extends AbstractStre
     public void incrementWindowSize(int windowSizeIncrement) {
         flowControlWindowSize += windowSizeIncrement;
     }
+
+
+    @Override
+    public void emitHeader(String name, String value, boolean neverIndex) {
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("stream.header.debug", getIdentifier(), name, value));
+        }
+
+        // TODO: Do something with these headers
+    }
+
+
+    @Override
+    protected final Log getLog() {
+        return log;
+    }
 }



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