Author: markt
Date: Tue May 12 22:29:45 2015
New Revision: 1679116
URL: http://svn.apache.org/r1679116
Log:
Start to process the initial settings frame sent by the client.
Added:
tomcat/trunk/java/org/apache/coyote/http2/Http2Exception.java (with props)
Modified:
tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java
Added: tomcat/trunk/java/org/apache/coyote/http2/Http2Exception.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Http2Exception.java?rev=1679116&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2Exception.java (added)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2Exception.java Tue May 12
22:29:45 2015
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+public class Http2Exception extends IOException {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final byte[] NO_ERROR = { 0x00, 0x00, 0x00, 0x00 };
+ public static final byte[] PROTOCOL_ERROR = { 0x00, 0x00, 0x00, 0x01 };
+ public static final byte[] INTERNAL_ERROR = { 0x00, 0x00, 0x00, 0x02 };
+ public static final byte[] FLOW_CONTROL_ERROR = { 0x00, 0x00, 0x00, 0x03 };
+ public static final byte[] SETTINGS_TIMEOUT = { 0x00, 0x00, 0x00, 0x04 };
+ public static final byte[] STREAM_CLOSED = { 0x00, 0x00, 0x00, 0x05 };
+ public static final byte[] FRAME_SIZE_ERROR = { 0x00, 0x00, 0x00, 0x06};
+ public static final byte[] REFUSED_STREAM = { 0x00, 0x00, 0x00, 0x07};
+ public static final byte[] CANCEL = { 0x00, 0x00, 0x00, 0x08};
+ public static final byte[] COMPRESSION_ERROR= { 0x00, 0x00, 0x00, 0x09};
+ public static final byte[] CONNECT_ERROR = { 0x00, 0x00, 0x00, 0x0a};
+ public static final byte[] ENHANCE_YOUR_CALM = { 0x00, 0x00, 0x00, 0x0b};
+ public static final byte[] INADEQUATE_SECURITY = { 0x00, 0x00, 0x00, 0x0c};
+ public static final byte[] HTTP_1_1_REQUIRED = { 0x00, 0x00, 0x00, 0x0d};
+
+
+ private final int streamId;
+ private final byte[] errorCode;
+
+
+ public Http2Exception(String msg, int streamId, byte[] errorCode) {
+ super(msg);
+ this.streamId = streamId;
+ this.errorCode = errorCode;
+ }
+
+
+ public int getStreamId() {
+ return streamId;
+ }
+
+
+ public byte[] getErrorCode() {
+ return errorCode;
+ }
+}
Propchange: tomcat/trunk/java/org/apache/coyote/http2/Http2Exception.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=1679116&r1=1679115&r2=1679116&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java
(original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java Tue May
12 22:29:45 2015
@@ -16,6 +16,7 @@
*/
package org.apache.coyote.http2;
+import java.io.EOFException;
import java.io.IOException;
import javax.servlet.http.WebConnection;
@@ -46,11 +47,16 @@ public class Http2UpgradeHandler impleme
private static final Log log =
LogFactory.getLog(Http2UpgradeHandler.class);
private static final StringManager sm =
StringManager.getManager(Http2UpgradeHandler.class);
+ private static final int FRAME_SETTINGS = 4;
+
private volatile SocketWrapperBase<?> socketWrapper;
private volatile boolean initialized = false;
private volatile ConnectionPrefaceParser connectionPrefaceParser =
new ConnectionPrefaceParser();
- private volatile boolean readFirstFrame = false;
+ private volatile boolean firstFrame = true;
+ private volatile boolean open = true;
+
+ private volatile int settingsMaxFrameSize = 16 * 1024;
@Override
@@ -89,10 +95,19 @@ public class Http2UpgradeHandler impleme
}
connectionPrefaceParser = null;
- while (processFrame()) {
+ try {
+ while (processFrame()) {
+ }
+ // TODO Catch the Http2Exception and reset the stream / close
+ // the connection as appropriate
+ } catch (IOException ioe) {
+ log.error("TODO: i18n - Frame processing error", ioe);
+ open = false;
}
- // TODO: CLOSED (GO_AWAY + no open streams apart from 0?) vs LONG
+ if (open) {
+ return SocketState.LONG;
+ }
break;
case OPEN_WRITE:
@@ -124,11 +139,126 @@ public class Http2UpgradeHandler impleme
}
- private boolean processFrame() {
+ private boolean processFrame() throws IOException {
+ // TODO: Consider refactoring and making this a field to reduce GC.
+ byte[] frameHeader = new byte[9];
+ if (!getFrameHeader(frameHeader)) {
+ return false;
+ }
+
+ int frameType = getFrameType(frameHeader);
+ int streamId = getStreamIdentifier(frameHeader);
+ int payloadSize = getPayloadSize(streamId, frameHeader);
+
+ switch (frameType) {
+ case FRAME_SETTINGS:
+ processFrameSettings(streamId, payloadSize);
+ break;
+ default:
+ // Unknown frame type.
+ processFrameUnknown(streamId, frameType, payloadSize);
+ }
return false;
}
+ private void processFrameSettings(int streamId, int payloadSize) throws
IOException {
+ 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);
+ }
+ }
+
+
+ private void processFrameUnknown(int streamId, int type, int payloadSize)
throws IOException {
+ // Swallow the payload
+ log.info("Swallowing [" + payloadSize + "] bytes of unknown frame type
+ [" + type +
+ "] from stream [" + streamId + "]");
+ swallowPayload(payloadSize);
+ }
+
+
+ private void swallowPayload(int payloadSize) throws IOException {
+ int read = 0;
+ byte[] buffer = new byte[8 * 1024];
+ while (read < payloadSize) {
+ int toRead = Math.min(buffer.length, payloadSize - read);
+ int thisTime = socketWrapper.read(true, buffer, 0, toRead);
+ if (thisTime == -1) {
+ throw new IOException("TODO: i18n");
+ }
+ read += thisTime;
+ }
+ }
+
+
+ private boolean getFrameHeader(byte[] frameHeader) throws IOException {
+ // All frames start with a fixed size header.
+ int headerBytesRead = socketWrapper.read(false, frameHeader, 0,
frameHeader.length);
+
+ // No frame header read. Non-blocking between frames, so return.
+ if (headerBytesRead == 0) {
+ return false;
+ }
+
+ // Partial header read. Blocking within a frame to block while the
+ // remainder is read.
+ if (headerBytesRead < frameHeader.length) {
+ int read = socketWrapper.read(true, frameHeader, headerBytesRead,
+ frameHeader.length - headerBytesRead);
+ if (read == -1) {
+ // TODO i18n
+ throw new EOFException();
+ }
+ }
+
+ return true;
+ }
+
+
+ private int getFrameType(byte[] frameHeader) throws IOException {
+ int frameType = frameHeader[3] & 0xFF;
+ // Make sure the first frame is a settings frame
+ if (firstFrame) {
+ if (frameType != FRAME_SETTINGS) {
+ // TODO i18n
+ throw new Http2Exception("", 0, Http2Exception.PROTOCOL_ERROR);
+ } else {
+ firstFrame = false;
+ }
+ }
+ return frameType;
+ }
+
+
+ private int getStreamIdentifier(byte[] frameHeader) {
+ // MSB of [5] is reserved and must be ignored.
+ return ((frameHeader[5] & 0x7F) << 24) + ((frameHeader[6] & 0xFF) <<
16) +
+ ((frameHeader[7] & 0xFF) << 6) + (frameHeader[8] & 0xFF);
+ }
+
+
+ private int getPayloadSize(int streamId, byte[] frameHeader) throws
IOException {
+ // Make sure the payload size is valid
+ int payloadSize = ((frameHeader[0] & 0xFF) << 16) +
+ ((frameHeader[1] & 0xFF) << 8) +
+ (frameHeader[2] & 0xFF);
+
+ if (payloadSize > settingsMaxFrameSize) {
+ swallowPayload(payloadSize);
+ // TODO i18n
+ throw new Http2Exception("", streamId,
Http2Exception.FRAME_SIZE_ERROR);
+ }
+
+ return payloadSize;
+ }
+
+
@Override
public void destroy() {
// NO-OP
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]