Author: markt Date: Wed May 13 10:10:08 2015 New Revision: 1679173 URL: http://svn.apache.org/r1679173 Log: Complete handling of connection preface. Now see several (currently ignored) frames of different types in the logs that need to processed.
Added: tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java (with props) Modified: tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties Added: 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=1679173&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java (added) +++ tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java Wed May 13 10:10:08 2015 @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.coyote.http2; + +import java.io.IOException; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.res.StringManager; + +public class ConnectionSettings { + + private final Log log = LogFactory.getLog(ConnectionSettings.class); + private final StringManager sm = StringManager.getManager(ConnectionSettings.class); + + 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 = (1 << 16) - 1; + private volatile long maxFrameSize = MIN_MAX_FRAME_SIZE; + private volatile long maxHeaderListSize = UNLIMITED; + + public void set(int parameterId, long value) throws IOException { + switch(parameterId) { + case 1: + setHeaderTableSize(value); + break; + case 2: + setEnablePush(value); + break; + case 3: + setMaxConcurrentStreams(value); + break; + case 4: + setInitialWindowSize(value); + break; + case 5: + setMaxFrameSize(value); + break; + case 6: + setMaxHeaderListSize(value); + break; + default: + // Unrecognised. Ignore it. + log.warn(sm.getString("connectionSettings.unknown", + Integer.toString(parameterId), Long.toString(value))); + } + } + + + public long getHeaderTableSize() { + return headerTableSize; + } + public void setHeaderTableSize(long headerTableSize) { + this.headerTableSize = headerTableSize; + } + + + public long getEnablePush() { + return enablePush; + } + public void setEnablePush(long enablePush) throws IOException { + // Can't be less than zero since the result of the byte->long conversion + // will never be negative + if (enablePush > 1) { + // TODO i18n + throw new Http2Exception("", 0, Http2Exception.PROTOCOL_ERROR); + } + this.enablePush = enablePush; + } + + + public long getMaxConcurrentStreams() { + return maxConcurrentStreams; + } + public void setMaxConcurrentStreams(long maxConcurrentStreams) { + this.maxConcurrentStreams = maxConcurrentStreams; + } + + + public long getInitialWindowSize() { + return initialWindowSize; + } + public void setInitialWindowSize(long initialWindowSize) throws IOException { + if (initialWindowSize > MAX_WINDOW_SIZE) { + // TODO i18n + throw new Http2Exception("", 0, Http2Exception.PROTOCOL_ERROR); + } + this.initialWindowSize = initialWindowSize; + } + + + public long getMaxFrameSize() { + return maxFrameSize; + } + public void setMaxFrameSize(long maxFrameSize) throws IOException { + if (maxFrameSize < MIN_MAX_FRAME_SIZE || maxFrameSize > MAX_MAX_FRAME_SIZE) { + // TODO i18n + throw new Http2Exception("", 0, Http2Exception.PROTOCOL_ERROR); + } + this.maxFrameSize = maxFrameSize; + } + + + public long getMaxHeaderListSize() { + return maxHeaderListSize; + } + public void setMaxHeaderListSize(long maxHeaderListSize) { + this.maxHeaderListSize = maxHeaderListSize; + } +} Propchange: tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java ------------------------------------------------------------------------------ svn:eol-style = native 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=1679173&r1=1679172&r2=1679173&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java Wed May 13 10:10:08 2015 @@ -49,6 +49,9 @@ public class Http2UpgradeHandler impleme private static final int FRAME_SETTINGS = 4; + 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 volatile SocketWrapperBase<?> socketWrapper; private volatile boolean initialized = false; private volatile ConnectionPrefaceParser connectionPrefaceParser = @@ -56,12 +59,20 @@ public class Http2UpgradeHandler impleme private volatile boolean firstFrame = true; private volatile boolean open = true; - private volatile int settingsMaxFrameSize = 16 * 1024; - + private final ConnectionSettings remoteSettings = new ConnectionSettings(); @Override public void init(WebConnection unused) { initialized = true; + + // Send the initial settings frame + try { + socketWrapper.write(true, SETTINGS_EMPTY, 0, SETTINGS_EMPTY.length); + socketWrapper.flush(true); + } catch (IOException ioe) { + // TODO i18n + throw new IllegalStateException("Failed to send preface to client", ioe); + } } @@ -147,31 +158,70 @@ public class Http2UpgradeHandler impleme } int frameType = getFrameType(frameHeader); + int flags = frameHeader[4] & 0xFF; int streamId = getStreamIdentifier(frameHeader); int payloadSize = getPayloadSize(streamId, frameHeader); switch (frameType) { case FRAME_SETTINGS: - processFrameSettings(streamId, payloadSize); + processFrameSettings(flags, streamId, payloadSize); break; default: // Unknown frame type. processFrameUnknown(streamId, frameType, payloadSize); } - return false; + return true; } - private void processFrameSettings(int streamId, int payloadSize) throws IOException { + private void processFrameSettings(int flags, int streamId, int payloadSize) throws IOException { + if (log.isDebugEnabled()) { + log.debug(sm.getString("upgradeHandler.processFrameSettings", Integer.toString(flags), + Integer.toString(streamId), Integer.toString(payloadSize))); + } + // Validate the frame if (streamId != 0) { // TODO i18n throw new Http2Exception("", 0, Http2Exception.FRAME_SIZE_ERROR); } - if (payloadSize % 6 != 0) { // TODO i18n throw new Http2Exception("", 0, Http2Exception.FRAME_SIZE_ERROR); } + if (payloadSize > 0 && (flags & 0x1) != 0) { + // TODO i18n + throw new Http2Exception("", 0, Http2Exception.FRAME_SIZE_ERROR); + } + + if (payloadSize == 0) { + // Either an ACK or an empty settings frame + if ((flags & 0x1) != 0) { + // TODO process ACK + } + } else { + // Process the settings + byte[] setting = new byte[6]; + for (int i = 0; i < payloadSize / 6; i++) { + int read = 0; + while (read < 6) { + int thisTime = socketWrapper.read(true, setting, read, setting.length - read); + if (thisTime == -1) { + // TODO i18n + throw new EOFException(); + } + read += thisTime; + } + int id = ((setting[0] & 0xFF) << 8) + (setting[1] & 0xFF); + long value = ((setting[2] & 0xFF) << 24) + ((setting[3] & 0xFF) << 16) + + ((setting[4] & 0xFF) << 8) + (setting[5] & 0xFF); + remoteSettings.set(id, value); + } + } + + // Acknowledge the settings + // TODO Need to coordinate writes with other threads + socketWrapper.write(true, SETTINGS_ACK, 0, SETTINGS_ACK.length); + socketWrapper.flush(true); } @@ -208,7 +258,7 @@ public class Http2UpgradeHandler impleme // Partial header read. Blocking within a frame to block while the // remainder is read. - if (headerBytesRead < frameHeader.length) { + while (headerBytesRead < frameHeader.length) { int read = socketWrapper.read(true, frameHeader, headerBytesRead, frameHeader.length - headerBytesRead); if (read == -1) { @@ -249,7 +299,7 @@ public class Http2UpgradeHandler impleme ((frameHeader[1] & 0xFF) << 8) + (frameHeader[2] & 0xFF); - if (payloadSize > settingsMaxFrameSize) { + if (payloadSize > remoteSettings.getMaxFrameSize()) { swallowPayload(payloadSize); // TODO i18n throw new Http2Exception("", streamId, Http2Exception.FRAME_SIZE_ERROR); 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=1679173&r1=1679172&r2=1679173&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/coyote/http2/LocalStrings.properties Wed May 13 10:10:08 2015 @@ -17,11 +17,14 @@ connectionPrefaceParser.eos=Unexpected e connectionPrefaceParser.ioError=Failed to read opening client preface byte sequence connectionPrefaceParser.mismatch=An unexpected byte sequence was received at the start of the client preface [{0}] +connectionSettings.unknown=An unknown setting with identifier [{0}] and value [{1}] was ignored + hpack.integerEncodedOverTooManyOctets=HPACK variable length integer encoded over too many octets, max is {0} hpackdecoder.zeroNotValidHeaderTableIndex=Zero is not a valid header table index hpackhuffman.huffmanEncodedHpackValueDidNotEndWithEOS=Huffman encoded value in HPACK headers did not end with EOS padding +upgradeHandler.processFrameSettings=Processing settings frame for stream [{1}] with flags [{0}] and payload size [{2}] upgradeHandler.socketCloseFailed=Error closing socket upgradeHandler.unexpectedStatus=An unexpected value of status ([{0}]) was passed to this method \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org