Author: markt Date: Mon Jun 8 19:45:12 2015 New Revision: 1684255 URL: http://svn.apache.org/r1684255 Log: Introduce separate exceptions for stream errors and connection errors
Added: tomcat/trunk/java/org/apache/coyote/http2/ConnectionError.java (with props) tomcat/trunk/java/org/apache/coyote/http2/StreamError.java (with props) Modified: tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java tomcat/trunk/java/org/apache/coyote/http2/FrameType.java tomcat/trunk/java/org/apache/coyote/http2/Http2Exception.java tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java tomcat/trunk/java/org/apache/coyote/http2/StreamStateMachine.java tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java Added: tomcat/trunk/java/org/apache/coyote/http2/ConnectionError.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/ConnectionError.java?rev=1684255&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/ConnectionError.java (added) +++ tomcat/trunk/java/org/apache/coyote/http2/ConnectionError.java Mon Jun 8 19:45:12 2015 @@ -0,0 +1,26 @@ +/* + * 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; + +public class ConnectionError extends Http2Exception { + + private static final long serialVersionUID = 1L; + + public ConnectionError(String msg, int streamId, Error error) { + super(msg, streamId, error); + } +} Propchange: tomcat/trunk/java/org/apache/coyote/http2/ConnectionError.java ------------------------------------------------------------------------------ svn:eol-style = native 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=1684255&r1=1684254&r2=1684255&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettings.java Mon Jun 8 19:45:12 2015 @@ -83,7 +83,7 @@ public class ConnectionSettings { public void setHeaderTableSize(long headerTableSize) throws IOException { // Need to put a sensible limit on this. Start with 16k (default is 4k) if (headerTableSize > (16 * 1024)) { - throw new Http2Exception(sm.getString("connectionSettings.headerTableSizeLimit", + throw new ConnectionError(sm.getString("connectionSettings.headerTableSizeLimit", Long.toString(headerTableSize)), 0, Error.PROTOCOL_ERROR); } this.headerTableSize = (int) headerTableSize; @@ -97,7 +97,7 @@ public class ConnectionSettings { // Can't be less than zero since the result of the byte->long conversion // will never be negative if (enablePush > 1) { - throw new Http2Exception(sm.getString("connectionSettings.enablePushInvalid", + throw new ConnectionError(sm.getString("connectionSettings.enablePushInvalid", Long.toString(enablePush)), 0, Error.PROTOCOL_ERROR); } this.enablePush = (enablePush == 1); @@ -117,7 +117,7 @@ public class ConnectionSettings { } public void setInitialWindowSize(long initialWindowSize) throws IOException { if (initialWindowSize > MAX_WINDOW_SIZE) { - throw new Http2Exception(sm.getString("connectionSettings.windowSizeTooBig", + throw new ConnectionError(sm.getString("connectionSettings.windowSizeTooBig", Long.toString(initialWindowSize), Long.toString(MAX_WINDOW_SIZE)), 0, Error.PROTOCOL_ERROR); } @@ -130,7 +130,7 @@ public class ConnectionSettings { } public void setMaxFrameSize(long maxFrameSize) throws IOException { if (maxFrameSize < MIN_MAX_FRAME_SIZE || maxFrameSize > MAX_MAX_FRAME_SIZE) { - throw new Http2Exception(sm.getString("connectionSettings.maxFrameSizeInvalid", + throw new ConnectionError(sm.getString("connectionSettings.maxFrameSizeInvalid", Long.toString(maxFrameSize), Integer.toString(MIN_MAX_FRAME_SIZE), Integer.toString(MAX_MAX_FRAME_SIZE)), 0, Error.PROTOCOL_ERROR); } Modified: tomcat/trunk/java/org/apache/coyote/http2/FrameType.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/FrameType.java?rev=1684255&r1=1684254&r2=1684255&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/FrameType.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/FrameType.java Mon Jun 8 19:45:12 2015 @@ -58,10 +58,10 @@ public enum FrameType { public void checkStream(String connectionId, int streamId) throws Http2Exception { if (streamId == 0 && !streamZero) { - throw new Http2Exception(sm.getString("frameType.checkStream.invalidForZero", + throw new ConnectionError(sm.getString("frameType.checkStream.invalidForZero", connectionId, this), 0, Error.PROTOCOL_ERROR); } else if (streamId != 0 && !streamNonZero) { - throw new Http2Exception(sm.getString("frameType.checkStream.invalidForNonZero", + throw new ConnectionError(sm.getString("frameType.checkStream.invalidForNonZero", connectionId, Integer.valueOf(streamId), this), 0, Error.PROTOCOL_ERROR); } } @@ -70,7 +70,7 @@ public enum FrameType { public void checkPayloadSize(String connectionId, int streamId, int payloadSize) throws Http2Exception { if (payloadSizeValidator != null && !payloadSizeValidator.test(payloadSize)) { - throw new Http2Exception(sm.getString("frameType.checkPayloadSize", + throw new ConnectionError(sm.getString("frameType.checkPayloadSize", connectionId, Integer.toString(streamId), this, Integer.toString(payloadSize)), 0, Error.FRAME_SIZE_ERROR); } Modified: 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=1684255&r1=1684254&r2=1684255&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/Http2Exception.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/Http2Exception.java Mon Jun 8 19:45:12 2015 @@ -16,9 +16,7 @@ */ package org.apache.coyote.http2; -import java.io.IOException; - -public class Http2Exception extends IOException { +public abstract class Http2Exception extends Exception { private static final long serialVersionUID = 1L; Modified: tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java?rev=1684255&r1=1684254&r2=1684255&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java Mon Jun 8 19:45:12 2015 @@ -66,12 +66,14 @@ class Http2Parser { * * @throws IOException If an IO error occurs while trying to read a frame */ - boolean readFrame(boolean block) throws IOException { + boolean readFrame(boolean block) throws Http2Exception, IOException { return readFrame(block, null); } - private boolean readFrame(boolean block, FrameType expected) throws IOException { + private boolean readFrame(boolean block, FrameType expected) + throws IOException, Http2Exception { + if (!input.fill(block, frameHeaderBuffer)) { return false; } @@ -81,7 +83,13 @@ class Http2Parser { int flags = ByteUtil.getOneByte(frameHeaderBuffer, 4); int streamId = ByteUtil.get31Bits(frameHeaderBuffer, 5); - validateFrame(expected, frameType, streamId, flags, payloadSize); + try { + validateFrame(expected, frameType, streamId, flags, payloadSize); + } catch (StreamError se) { + // TODO debug log + swallow(payloadSize); + return true; + } switch (frameType) { case DATA: @@ -122,7 +130,8 @@ class Http2Parser { } - private void readDataFrame(int streamId, int flags, int payloadSize) throws IOException { + private void readDataFrame(int streamId, int flags, int payloadSize) + throws Http2Exception, IOException { // Process the Stream int padLength = 0; @@ -153,7 +162,9 @@ class Http2Parser { } - private void readHeadersFrame(int streamId, int flags, int payloadSize) throws IOException { + private void readHeadersFrame(int streamId, int flags, int payloadSize) + throws Http2Exception, IOException { + if (hpackDecoder == null) { hpackDecoder = output.getHpackDecoder(); } @@ -208,7 +219,7 @@ class Http2Parser { } - private void readPriorityFrame(int streamId) throws IOException { + private void readPriorityFrame(int streamId) throws Http2Exception, IOException { byte[] payload = new byte[5]; input.fill(true, payload); @@ -220,7 +231,7 @@ class Http2Parser { } - private void readRstFrame(int streamId) throws IOException { + private void readRstFrame(int streamId) throws Http2Exception, IOException { byte[] payload = new byte[4]; input.fill(true, payload); @@ -229,10 +240,11 @@ class Http2Parser { } - private void readSettingsFrame(int flags, int payloadSize) throws IOException { + private void readSettingsFrame(int flags, int payloadSize) throws Http2Exception, IOException { boolean ack = Flags.isAck(flags); if (payloadSize > 0 && ack) { - throw new Http2Exception(sm.getString("http2Parser.processFrameSettings.ackWithNonZeroPayload"), + throw new ConnectionError(sm.getString( + "http2Parser.processFrameSettings.ackWithNonZeroPayload"), 0, Error.FRAME_SIZE_ERROR); } @@ -250,8 +262,8 @@ class Http2Parser { } - private void readPushPromiseFrame(int streamId) throws IOException { - throw new Http2Exception(sm.getString("http2Parser.processFramePushPromise", + private void readPushPromiseFrame(int streamId) throws Http2Exception { + throw new ConnectionError(sm.getString("http2Parser.processFramePushPromise", connectionId, Integer.valueOf(streamId)), 0, Error.PROTOCOL_ERROR); } @@ -282,7 +294,7 @@ class Http2Parser { } - private void readWindowUpdateFrame(int streamId) throws IOException { + private void readWindowUpdateFrame(int streamId) throws Http2Exception, IOException { byte[] payload = new byte[4]; input.fill(true, payload); int windowSizeIncrement = ByteUtil.get31Bits(payload, 0); @@ -294,8 +306,15 @@ class Http2Parser { // Validate the data if (windowSizeIncrement == 0) { - throw new Http2Exception("http2Parser.processFrameWindowUpdate.invalidIncrement", - streamId, Error.PROTOCOL_ERROR); + if (streamId == 0) { + throw new ConnectionError( + sm.getString("http2Parser.processFrameWindowUpdate.invalidIncrement"), + 0, Error.PROTOCOL_ERROR); + } else { + throw new StreamError( + sm.getString("http2Parser.processFrameWindowUpdate.invalidIncrement"), + streamId, Error.PROTOCOL_ERROR); + } } output.incrementWindowSize(streamId, windowSizeIncrement); @@ -303,10 +322,10 @@ class Http2Parser { private void readContinuationFrame(int streamId, int flags, int payloadSize) - throws IOException { + throws Http2Exception, IOException { if (headersCurrentStream == -1) { // No headers to continue - throw new Http2Exception(sm.getString( + throw new ConnectionError(sm.getString( "http2Parser.processFrameContinuation.notExpected", connectionId, Integer.toString(streamId)), 0, Error.PROTOCOL_ERROR); } @@ -325,7 +344,9 @@ class Http2Parser { } - private void readHeaderBlock(int payloadSize, boolean endOfHeaders) throws IOException { + private void readHeaderBlock(int payloadSize, boolean endOfHeaders) + throws Http2Exception, IOException { + while (payloadSize > 0) { int toRead = Math.min(headerReadBuffer.remaining(), payloadSize); // headerReadBuffer in write mode @@ -335,7 +356,7 @@ class Http2Parser { try { hpackDecoder.decode(headerReadBuffer); } catch (HpackException hpe) { - throw new Http2Exception( + throw new ConnectionError( sm.getString("http2Parser.processFrameHeaders.decodingFailed"), 0, Error.COMPRESSION_ERROR); } @@ -345,7 +366,7 @@ class Http2Parser { } if (headerReadBuffer.position() > 0 && endOfHeaders) { - throw new Http2Exception( + throw new ConnectionError( sm.getString("http2Parser.processFrameHeaders.decodingDataLeft"), 0, Error.COMPRESSION_ERROR); } @@ -391,24 +412,24 @@ class Http2Parser { } if (expected != null && frameType != expected) { - throw new Http2Exception(sm.getString("http2Parser.processFrame.unexpectedType", + throw new StreamError(sm.getString("http2Parser.processFrame.unexpectedType", expected, frameType), streamId, Error.PROTOCOL_ERROR); } if (payloadSize > maxPayloadSize) { - throw new Http2Exception(sm.getString("http2Parser.payloadTooBig", + throw new ConnectionError(sm.getString("http2Parser.payloadTooBig", Integer.toString(payloadSize), Integer.toString(maxPayloadSize)), streamId, Error.FRAME_SIZE_ERROR); } if (headersCurrentStream != -1) { if (headersCurrentStream != streamId) { - throw new Http2Exception(sm.getString("http2Parser.headers.wrongStream", + throw new ConnectionError(sm.getString("http2Parser.headers.wrongStream", connectionId, Integer.toString(headersCurrentStream), Integer.toString(streamId)), streamId, Error.COMPRESSION_ERROR); } if (frameType != FrameType.CONTINUATION) { - throw new Http2Exception(sm.getString("http2Parser.headers.wrongFrameType", + throw new ConnectionError(sm.getString("http2Parser.headers.wrongFrameType", connectionId, Integer.toString(headersCurrentStream), frameType), streamId, Error.COMPRESSION_ERROR); } @@ -422,7 +443,7 @@ class Http2Parser { /** * Read and validate the connection preface from input using blocking IO. */ - void readConnectionPreface() { + void readConnectionPreface() throws Http2Exception { byte[] data = new byte[CLIENT_PREFACE_START.length]; try { input.fill(true, data); 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=1684255&r1=1684254&r2=1684255&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java Mon Jun 8 19:45:12 2015 @@ -179,7 +179,7 @@ public class Http2UpgradeHandler extends long value = ByteUtil.getFourBytes(settings, (i * 6) + 2); remoteSettings.set(id, value); } - } catch (IOException ioe) { + } catch (Http2Exception | IOException ioe) { // TODO i18n throw new ProtocolException(); } @@ -195,7 +195,12 @@ public class Http2UpgradeHandler extends // Make sure the client has sent a valid connection preface before we // send the response to the original request over HTTP/2. - parser.readConnectionPreface(); + try { + parser.readConnectionPreface(); + } catch (Http2Exception e) { + // TODO i18n + throw new ProtocolException(); + } if (webConnection != null) { // Process the initial request on a container thread @@ -236,18 +241,16 @@ public class Http2UpgradeHandler extends try { while (parser.readFrame(false)) { } - } catch (Http2Exception h2e) { - if (h2e.getStreamId() == 0) { - if (log.isDebugEnabled()) { - log.debug(sm.getString("upgradeHandler.connectionError"), h2e); - } - closeConnecion(h2e); - break; - } else { - - // Stream error - // TODO Reset stream + } catch (StreamError se) { + // Stream error + // TODO Reset stream + } catch (Http2Exception ce) { + // This should be a connection error + if (log.isDebugEnabled()) { + log.debug(sm.getString("upgradeHandler.connectionError"), ce); } + closeConnecion(ce); + break; } catch (IOException ioe) { if (log.isDebugEnabled()) { log.debug(sm.getString("upgradeHandler.ioerror", connectionId), ioe); @@ -263,16 +266,6 @@ public class Http2UpgradeHandler extends case OPEN_WRITE: try { processWrites(); - } catch (Http2Exception h2e) { - if (h2e.getStreamId() == 0) { - // Connection error - log.warn(sm.getString("upgradeHandler.connectionError"), h2e); - closeConnecion(h2e); - break; - } else { - // Stream error - // TODO Reset stream - } } catch (IOException ioe) { if (log.isDebugEnabled()) { log.debug(sm.getString("upgradeHandler.ioerror", connectionId), ioe); @@ -339,13 +332,13 @@ public class Http2UpgradeHandler extends } - private void closeConnecion(Http2Exception h2e) { + private void closeConnecion(Http2Exception ce) { // Write a GOAWAY frame. byte[] fixedPayload = new byte[8]; // TODO needs to be correct value ByteUtil.set31Bits(fixedPayload, 0, (2 << 31) - 1); - ByteUtil.setFourBytes(fixedPayload, 4, h2e.getError().getCode()); - byte[] debugMessage = h2e.getMessage().getBytes(StandardCharsets.UTF_8); + ByteUtil.setFourBytes(fixedPayload, 4, ce.getError().getCode()); + byte[] debugMessage = ce.getMessage().getBytes(StandardCharsets.UTF_8); byte[] payloadLength = new byte[3]; ByteUtil.setThreeBytes(payloadLength, 0, debugMessage.length + 8); Added: tomcat/trunk/java/org/apache/coyote/http2/StreamError.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/StreamError.java?rev=1684255&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/StreamError.java (added) +++ tomcat/trunk/java/org/apache/coyote/http2/StreamError.java Mon Jun 8 19:45:12 2015 @@ -0,0 +1,26 @@ +/* + * 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; + +public class StreamError extends Http2Exception { + + private static final long serialVersionUID = 1L; + + public StreamError(String msg, int streamId, Error error) { + super(msg, streamId, error); + } +} Propchange: tomcat/trunk/java/org/apache/coyote/http2/StreamError.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/java/org/apache/coyote/http2/StreamStateMachine.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/StreamStateMachine.java?rev=1684255&r1=1684254&r2=1684255&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/StreamStateMachine.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/StreamStateMachine.java Mon Jun 8 19:45:12 2015 @@ -107,15 +107,15 @@ public class StreamStateMachine { // No state change. Checks that the frame type is valid for the current // state of this stream. if (!isFrameTypePermitted(frameType)) { - int errorStream; if (state.connectionErrorForInvalidFrame) { - errorStream = 0; + throw new ConnectionError(sm.getString("streamStateMachine.invalidFrame", + stream.getConnectionId(), stream.getIdentifier(), state, frameType), + stream.getIdentifier().intValue(), state.errorCodeForInvalidFrame); } else { - errorStream = stream.getIdentifier().intValue(); + throw new StreamError(sm.getString("streamStateMachine.invalidFrame", + stream.getConnectionId(), stream.getIdentifier(), state, frameType), + stream.getIdentifier().intValue(), state.errorCodeForInvalidFrame); } - throw new Http2Exception(sm.getString("streamStateMachine.invalidFrame", - stream.getConnectionId(), stream.getIdentifier(), state, frameType), - errorStream, state.errorCodeForInvalidFrame); } } Modified: tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java?rev=1684255&r1=1684254&r2=1684255&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java (original) +++ tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java Mon Jun 8 19:45:12 2015 @@ -179,7 +179,7 @@ public abstract class Http2TestBase exte } - protected void readSimpleResponse() throws IOException { + protected void readSimpleResponse() throws Http2Exception, IOException { // Headers parser.readFrame(true); // Body --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org