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