Author: markt
Date: Wed May 20 22:21:41 2015
New Revision: 1680690

URL: http://svn.apache.org/r1680690
Log:
Get responses without bodies working (e.g. redirect)
Lots of TODOs remain including fixing a couple of hacks that will only work 
when there is no response body.
Modified:
    tomcat/trunk/java/org/apache/coyote/http2/ByteUtil.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/ByteUtil.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/ByteUtil.java?rev=1680690&r1=1680689&r2=1680690&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/ByteUtil.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/ByteUtil.java Wed May 20 22:21:41 
2015
@@ -37,6 +37,14 @@ public class ByteUtil {
     }
 
 
+    public static void set31Bits(byte[] input, int firstByte, int value) {
+        input[firstByte] = (byte) ((value & 0x7F000000) >> 24);
+        input[firstByte + 1] = (byte) ((value & 0xFF0000) >> 16);
+        input[firstByte + 2] = (byte) ((value & 0xFF00) >> 8);
+        input[firstByte + 3] = (byte) (value & 0xFF);
+    }
+
+
     public static int getOneByte(byte[] input, int pos) {
         return (input[pos] & 0xFF);
     }
@@ -53,6 +61,13 @@ public class ByteUtil {
     }
 
 
+    public static void setThreeBytes(byte[] input, int firstByte, int value) {
+        input[firstByte] = (byte) ((value & 0xFF0000) >> 16);
+        input[firstByte + 1] = (byte) ((value & 0xFF00) >> 8);
+        input[firstByte + 2] = (byte) (value & 0xFF);
+    }
+
+
     public static int getFourBytes(byte[] input, int firstByte) {
         return ((input[firstByte] & 0xFF) << 24) + ((input[firstByte + 1] & 
0xFF) << 16) +
                 ((input[firstByte + 2] & 0xFF) << 8) + (input[firstByte + 3] & 
0xFF);

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=1680690&r1=1680689&r2=1680690&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java 
(original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java Wed May 
20 22:21:41 2015
@@ -29,10 +29,13 @@ import java.util.concurrent.atomic.Atomi
 import javax.servlet.http.WebConnection;
 
 import org.apache.coyote.Adapter;
+import org.apache.coyote.Response;
 import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
+import org.apache.coyote.http2.HpackEncoder.State;
 import org.apache.coyote.http2.WriteStateMachine.WriteState;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.http.MimeHeaders;
 import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
 import org.apache.tomcat.util.net.SocketStatus;
 import org.apache.tomcat.util.net.SocketWrapperBase;
@@ -59,13 +62,18 @@ public class Http2UpgradeHandler extends
     private static final AtomicInteger connectionIdGenerator = new 
AtomicInteger(0);
     private static final Integer STREAM_ID_ZERO = Integer.valueOf(0);
 
+    private static final int FLAG_END_OF_STREAM = 1;
+    private static final int FLAG_END_OF_HEADERS = 4;
+
     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;
+    private static final int FRAME_TYPE_CONTINUATION = 9;
 
     private static final byte[] SETTINGS_EMPTY = { 0x00, 0x00, 0x00, 0x04, 
0x00, 0x00, 0x00, 0x00, 0x00 };
     private static final byte[] SETTINGS_ACK = { 0x00, 0x00, 0x00, 0x04, 0x01, 
0x00, 0x00, 0x00, 0x00 };
+
     private static final byte[] GOAWAY = { 0x07, 0x00, 0x00, 0x00, 0x00 };
 
     private final int connectionId;
@@ -159,6 +167,13 @@ public class Http2UpgradeHandler extends
             try {
                 while (processFrame()) {
                 }
+
+                // We are on a container thread. There is no more data to read
+                // so check for writes (more efficient than dispatching to a 
new
+                // thread).
+                if (writeStateMachine.endRead()) {
+                    processWrites();
+                }
             } catch (Http2Exception h2e) {
                 if (h2e.getStreamId() == 0) {
                     // Connection error
@@ -171,23 +186,41 @@ public class Http2UpgradeHandler extends
                 }
             } catch (IOException ioe) {
                 if (log.isDebugEnabled()) {
-                    
log.debug(sm.getString("upgradeHandler.processFrame.ioerror"), ioe);
+                    log.debug(sm.getString("upgradeHandler.ioerror",
+                            Long.toString(connectionId)), ioe);
                 }
                 close();
                 result = SocketState.CLOSED;
                 break;
             }
 
-            if (writeStateMachine.endRead()) {
-                processWrites();
-            }
-
             result = SocketState.UPGRADED;
             break;
 
         case OPEN_WRITE:
             if (writeStateMachine.startWrite()) {
-                processWrites();
+                try {
+                    processWrites();
+                } catch (Http2Exception h2e) {
+                    if (h2e.getStreamId() == 0) {
+                        // Connection error
+                        
log.warn(sm.getString("upgradeHandler.connectionError"), h2e);
+                        close(h2e);
+                        break;
+                    } else {
+                        // Stream error
+                        // TODO Reset stream
+                    }
+                } catch (IOException ioe) {
+                    if (log.isDebugEnabled()) {
+                        log.debug(sm.getString("upgradeHandler.ioerror",
+                                Long.toString(connectionId)), ioe);
+                    }
+                    close();
+                    result = SocketState.CLOSED;
+                    break;
+                }
+
             }
             result = SocketState.UPGRADED;
             break;
@@ -611,7 +644,52 @@ public class Http2UpgradeHandler extends
     }
 
 
-    private void processWrites() {
+    void writeHeaders(Stream stream, Response coyoteResponse) throws 
IOException {
+        if (log.isDebugEnabled()) {
+            log.debug(sm.getString("upgradeHandler.writeHeaders",
+                    Integer.toString(connectionId), stream.getIdentifier()));
+        }
+        MimeHeaders headers = coyoteResponse.getMimeHeaders();
+        // Add the pseudo header for status
+        
headers.addValue(":status").setString(Integer.toString(coyoteResponse.getStatus()));
+        // This ensures the Stream processing thread has control of the socket.
+        synchronized (socketWrapper) {
+            // Frame sizes are allowed to be bigger than 4k but for headers 
that
+            // should be plenty
+            byte[] header = new byte[9];
+            ByteBuffer target = ByteBuffer.allocate(4 * 1024);
+            boolean first = true;
+            State state = null;
+            while (state != State.COMPLETE) {
+                state = hpackEncoder.encode(coyoteResponse.getMimeHeaders(), 
target);
+                target.flip();
+                ByteUtil.setThreeBytes(header, 0, target.limit());
+                if (first) {
+                    header[3] = FRAME_TYPE_HEADERS;
+                } else {
+                    header[3] = FRAME_TYPE_CONTINUATION;
+                }
+                if (state == State.COMPLETE) {
+                    // TODO Determine end of stream correctly
+                    header[4] = FLAG_END_OF_HEADERS + FLAG_END_OF_STREAM;
+                }
+                if (log.isDebugEnabled()) {
+                    log.debug(target.limit() + " bytes");
+                }
+                ByteUtil.set31Bits(header, 5, 
stream.getIdentifier().intValue());
+                socketWrapper.write(true, header, 0, header.length);
+                socketWrapper.write(true, target.array(), 
target.arrayOffset(), target.limit());
+                socketWrapper.flush(true);
+            }
+        }
+    }
+
+    private void processWrites() throws IOException {
+        if (socketWrapper.flush(false)) {
+            socketWrapper.registerWriteInterest();
+            return;
+        }
+
         Object obj;
         while ((obj = getThingToWrite()) != null) {
             // TODO

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=1680690&r1=1680689&r2=1680690&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties Wed May 
20 22:21:41 2015
@@ -38,9 +38,9 @@ streamProcessor.httpupgrade.notsupported
 
 upgradeHandler.connectionError=An error occurred that requires the HTTP/2 
connection to be closed.
 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.processFrame.ioerror=An I/O error occurred while reading an 
incoming HTTP/2 frame
 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
@@ -59,7 +59,7 @@ upgradeHandler.unexpectedEos=Unexpected
 upgradeHandler.unexpectedStatus=An unexpected value of status ([{0}]) was 
passed to this method
 upgradeHandler.upgradeDispatch.entry=Entry, Connection [{0}], SocketStatus 
[{1}]
 upgradeHandler.upgradeDispatch.exit=Exit, Connection [{0}], SocketState [{1}]
-
+upgradeHandler.writeHeaders=Connection [{0}], Stream [{1}]
 
 writeStateMachine.endWrite.ise=It is illegal to specify [{0}] for the new 
state once a write has completed
 writeStateMachine.ise=It is illegal to call [{0}()] in state [{1}]
\ No newline at end of file

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=1680690&r1=1680689&r2=1680690&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Stream.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Stream.java Wed May 20 22:21:41 
2015
@@ -96,13 +96,12 @@ public class Stream extends AbstractStre
 
 
     void writeHeaders() {
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("stream.write",
-                    Long.toString(getConnectionId()), getIdentifier()));
+        try {
+            handler.writeHeaders(this, coyoteResponse);
+        } catch (IOException e) {
+            // TODO Handle this
+            e.printStackTrace();
         }
-        // Format the frames.
-        // TODO
-        handler.addWrite("HEADERS");
     }
 
 



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

Reply via email to