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: [email protected]
For additional commands, e-mail: [email protected]