Author: markt Date: Tue Oct 25 13:54:09 2016 New Revision: 1766533 URL: http://svn.apache.org/viewvc?rev=1766533&view=rev Log: Add support for limiting trailer header count and size
Added: tomcat/trunk/java/org/apache/coyote/CloseNowException.java (with props) Modified: tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java tomcat/trunk/java/org/apache/coyote/http2/Constants.java tomcat/trunk/java/org/apache/coyote/http2/Http2Protocol.java tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java tomcat/trunk/java/org/apache/coyote/http2/Stream.java tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Limits.java tomcat/trunk/webapps/docs/changelog.xml tomcat/trunk/webapps/docs/config/http2.xml Modified: tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java?rev=1766533&r1=1766532&r2=1766533&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java (original) +++ tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java Tue Oct 25 13:54:09 2016 @@ -269,6 +269,8 @@ public abstract class AbstractProcessor action(ActionCode.COMMIT, null); try { finishResponse(); + } catch (CloseNowException cne) { + setErrorState(ErrorState.CLOSE_NOW, cne); } catch (IOException e) { setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); } Added: tomcat/trunk/java/org/apache/coyote/CloseNowException.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/CloseNowException.java?rev=1766533&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/coyote/CloseNowException.java (added) +++ tomcat/trunk/java/org/apache/coyote/CloseNowException.java Tue Oct 25 13:54:09 2016 @@ -0,0 +1,51 @@ +/* + * 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; + +import java.io.IOException; + +/** + * This exception is thrown to signal to the Tomcat internals that an error has + * occurred that requires the connection to be closed. For multiplexed protocols + * such as HTTP/2, this means the channel must be closed but the connection can + * continue. For non-multiplexed protocols, the connection must be closed. It + * corresponds to {@link ErrorState#CLOSE_NOW}. + */ +public class CloseNowException extends IOException { + + private static final long serialVersionUID = 1L; + + + public CloseNowException() { + super(); + } + + + public CloseNowException(String message, Throwable cause) { + super(message, cause); + } + + + public CloseNowException(String message) { + super(message); + } + + + public CloseNowException(Throwable cause) { + super(cause); + } +} Propchange: tomcat/trunk/java/org/apache/coyote/CloseNowException.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/java/org/apache/coyote/http2/Constants.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Constants.java?rev=1766533&r1=1766532&r2=1766533&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/Constants.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/Constants.java Tue Oct 25 13:54:09 2016 @@ -25,7 +25,9 @@ public class Constants { static final int DEFAULT_HEADER_READ_BUFFER_SIZE = 1024; // Limits + static final int DEFAULT_MAX_COOKIE_COUNT = 200; static final int DEFAULT_MAX_HEADER_COUNT = 100; static final int DEFAULT_MAX_HEADER_SIZE = 8 * 1024; - static final int DEFAULT_MAX_COOKIE_COUNT = 200; + static final int DEFAULT_MAX_TRAILER_COUNT = 100; + static final int DEFAULT_MAX_TRAILER_SIZE = 8 * 1024; } Modified: tomcat/trunk/java/org/apache/coyote/http2/Http2Protocol.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Http2Protocol.java?rev=1766533&r1=1766532&r2=1766533&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/Http2Protocol.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/Http2Protocol.java Tue Oct 25 13:54:09 2016 @@ -67,6 +67,8 @@ public class Http2Protocol implements Up private int maxCookieCount = Constants.DEFAULT_MAX_COOKIE_COUNT; private int maxHeaderCount = Constants.DEFAULT_MAX_HEADER_COUNT; private int maxHeaderSize = Constants.DEFAULT_MAX_HEADER_SIZE; + private int maxTrailerCount = Constants.DEFAULT_MAX_TRAILER_COUNT; + private int maxTrailerSize = Constants.DEFAULT_MAX_TRAILER_SIZE; @Override @@ -111,6 +113,8 @@ public class Http2Protocol implements Up result.setMaxCookieCount(getMaxCookieCount()); result.setMaxHeaderCount(getMaxHeaderCount()); result.setMaxHeaderSize(getMaxHeaderSize()); + result.setMaxTrailerCount(getMaxTrailerCount()); + result.setMaxTrailerSize(getMaxTrailerSize()); return result; } @@ -236,6 +240,16 @@ public class Http2Protocol implements Up } + public void setMaxCookieCount(int maxCookieCount) { + this.maxCookieCount = maxCookieCount; + } + + + public int getMaxCookieCount() { + return maxCookieCount; + } + + public void setMaxHeaderCount(int maxHeaderCount) { this.maxHeaderCount = maxHeaderCount; } @@ -256,12 +270,22 @@ public class Http2Protocol implements Up } - public void setMaxCookieCount(int maxCookieCount) { - this.maxCookieCount = maxCookieCount; + public void setMaxTrailerCount(int maxTrailerCount) { + this.maxTrailerCount = maxTrailerCount; } - public int getMaxCookieCount() { - return maxCookieCount; + public int getMaxTrailerCount() { + return maxTrailerCount; + } + + + public void setMaxTrailerSize(int maxTrailerSize) { + this.maxTrailerSize = maxTrailerSize; + } + + + public int getMaxTrailerSize() { + return maxTrailerSize; } } 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=1766533&r1=1766532&r2=1766533&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/Http2UpgradeHandler.java Tue Oct 25 13:54:09 2016 @@ -37,6 +37,7 @@ import java.util.concurrent.atomic.Atomi import javax.servlet.http.WebConnection; import org.apache.coyote.Adapter; +import org.apache.coyote.CloseNowException; import org.apache.coyote.ProtocolException; import org.apache.coyote.Request; import org.apache.coyote.Response; @@ -148,6 +149,10 @@ class Http2UpgradeHandler extends Abstra // Limits private Set<String> allowedTrailerHeaders = Collections.emptySet(); private int maxCookieCount = Constants.DEFAULT_MAX_COOKIE_COUNT; + private int maxHeaderCount = Constants.DEFAULT_MAX_HEADER_COUNT; + private int maxHeaderSize = Constants.DEFAULT_MAX_HEADER_SIZE; + private int maxTrailerCount = Constants.DEFAULT_MAX_TRAILER_COUNT; + private int maxTrailerSize = Constants.DEFAULT_MAX_TRAILER_SIZE; Http2UpgradeHandler(Adapter adapter, Request coyoteRequest) { @@ -513,6 +518,10 @@ class Http2UpgradeHandler extends Abstra stream.getIdentifier())); } + if (!stream.canWrite()) { + return; + } + prepareHeaders(coyoteResponse); byte[] header = new byte[9]; @@ -633,6 +642,8 @@ class Http2UpgradeHandler extends Abstra log.debug(sm.getString("upgradeHandler.writeBody", connectionId, stream.getIdentifier(), Integer.toString(len))); } + // Need to check this now since sending end of stream will change this. + boolean writeable = stream.canWrite(); byte[] header = new byte[9]; ByteUtil.setThreeBytes(header, 0, len); header[3] = FrameType.DATA.getIdByte(); @@ -643,17 +654,19 @@ class Http2UpgradeHandler extends Abstra activeRemoteStreamCount.decrementAndGet(); } } - ByteUtil.set31Bits(header, 5, stream.getIdentifier().intValue()); - synchronized (socketWrapper) { - try { - socketWrapper.write(true, header, 0, header.length); - int orgLimit = data.limit(); - data.limit(data.position() + len); - socketWrapper.write(true, data); - data.limit(orgLimit); - socketWrapper.flush(true); - } catch (IOException ioe) { - handleAppInitiatedIOException(ioe); + if (writeable) { + ByteUtil.set31Bits(header, 5, stream.getIdentifier().intValue()); + synchronized (socketWrapper) { + try { + socketWrapper.write(true, header, 0, header.length); + int orgLimit = data.limit(); + data.limit(data.position() + len); + socketWrapper.write(true, data); + data.limit(orgLimit); + socketWrapper.flush(true); + } catch (IOException ioe) { + handleAppInitiatedIOException(ioe); + } } } } @@ -681,6 +694,9 @@ class Http2UpgradeHandler extends Abstra */ void writeWindowUpdate(Stream stream, int increment, boolean applicationInitiated) throws IOException { + if (!stream.canWrite()) { + return; + } synchronized (socketWrapper) { // Build window update frame for stream 0 byte[] frame = new byte[13]; @@ -722,8 +738,9 @@ class Http2UpgradeHandler extends Abstra do { synchronized (this) { if (!stream.canWrite()) { - throw new IOException(sm.getString("upgradeHandler.stream.notWritable", - stream.getConnectionId(), stream.getIdentifier())); + throw new CloseNowException( + sm.getString("upgradeHandler.stream.notWritable", + stream.getConnectionId(), stream.getIdentifier())); } long windowSize = getWindowSize(); if (windowSize < 1 || backLogSize > 0) { @@ -1114,23 +1131,53 @@ class Http2UpgradeHandler extends Abstra } + public void setMaxCookieCount(int maxCookieCount) { + this.maxCookieCount = maxCookieCount; + } + + + public int getMaxCookieCount() { + return maxCookieCount; + } + + public void setMaxHeaderCount(int maxHeaderCount) { - getHpackDecoder().setMaxHeaderCount(maxHeaderCount); + this.maxHeaderCount = maxHeaderCount; + } + + + public int getMaxHeaderCount() { + return maxHeaderCount; } public void setMaxHeaderSize(int maxHeaderSize) { - getHpackDecoder().setMaxHeaderSize(maxHeaderSize); + this.maxHeaderSize = maxHeaderSize; } - public void setMaxCookieCount(int maxCookieCount) { - this.maxCookieCount = maxCookieCount; + public int getMaxHeaderSize() { + return maxHeaderSize; } - public int getMaxCookieCount() { - return maxCookieCount; + public void setMaxTrailerCount(int maxTrailerCount) { + this.maxTrailerCount = maxTrailerCount; + } + + + public int getMaxTrailerCount() { + return maxTrailerCount; + } + + + public void setMaxTrailerSize(int maxTrailerSize) { + this.maxTrailerSize = maxTrailerSize; + } + + + public int getMaxTrailerSize() { + return maxTrailerSize; } 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=1766533&r1=1766532&r2=1766533&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/Stream.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/Stream.java Tue Oct 25 13:54:09 2016 @@ -24,6 +24,7 @@ import java.security.PrivilegedException import java.util.Iterator; import org.apache.coyote.ActionCode; +import org.apache.coyote.CloseNowException; import org.apache.coyote.InputBuffer; import org.apache.coyote.OutputBuffer; import org.apache.coyote.Request; @@ -171,8 +172,8 @@ class Stream extends AbstractStream impl long windowSize = getWindowSize(); while (windowSize < 1) { if (!canWrite()) { - throw new IOException(sm.getString("stream.notWritable", getConnectionId(), - getIdentifier())); + throw new CloseNowException(sm.getString("stream.notWritable", + getConnectionId(), getIdentifier())); } try { if (block) { @@ -358,8 +359,12 @@ class Stream extends AbstractStream impl final void receivedStartOfHeaders() { if (headerState == HEADER_STATE_START) { headerState = HEADER_STATE_PSEUDO; + handler.getHpackDecoder().setMaxHeaderCount(handler.getMaxHeaderCount()); + handler.getHpackDecoder().setMaxHeaderSize(handler.getMaxHeaderSize()); } else if (headerState == HEADER_STATE_PSEUDO || headerState == HEADER_STATE_REGULAR) { headerState = HEADER_STATE_TRAILER; + handler.getHpackDecoder().setMaxHeaderCount(handler.getMaxTrailerCount()); + handler.getHpackDecoder().setMaxHeaderSize(handler.getMaxTrailerSize()); } // Parser will catch attempt to send a headers frame after the stream // has closed. Modified: tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Limits.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Limits.java?rev=1766533&r1=1766532&r2=1766533&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Limits.java (original) +++ tomcat/trunk/test/org/apache/coyote/http2/TestHttp2Limits.java Tue Oct 25 13:54:09 2016 @@ -394,4 +394,81 @@ public class TestHttp2Limits extends Htt } } } + + + @Test + public void doTestPostWithTrailerHeadersDefaultLimit() throws Exception{ + doTestPostWithTrailerHeaders(Constants.DEFAULT_MAX_TRAILER_COUNT, + Constants.DEFAULT_MAX_TRAILER_SIZE, true); + } + + + @Test + public void doTestPostWithTrailerHeadersCount0() throws Exception{ + doTestPostWithTrailerHeaders(0, Constants.DEFAULT_MAX_TRAILER_SIZE, false); + } + + + @Test + public void doTestPostWithTrailerHeadersSize0() throws Exception{ + doTestPostWithTrailerHeaders(Constants.DEFAULT_MAX_TRAILER_COUNT, 0, false); + } + + + private void doTestPostWithTrailerHeaders(int maxTrailerCount, int maxTrailerSize, boolean ok) + throws Exception { + enableHttp2(); + + Http2Protocol http2Protocol = + (Http2Protocol) getTomcatInstance().getConnector().findUpgradeProtocols()[0]; + http2Protocol.setAllowedTrailerHeaders(TRAILER_HEADER_NAME); + http2Protocol.setMaxTrailerCount(maxTrailerCount); + http2Protocol.setMaxTrailerSize(maxTrailerSize); + + configureAndStartWebApplication(); + openClientConnection(); + doHttpUpgrade(); + sendClientPreface(); + validateHttp2InitialResponse(); + + byte[] headersFrameHeader = new byte[9]; + ByteBuffer headersPayload = ByteBuffer.allocate(128); + byte[] dataFrameHeader = new byte[9]; + ByteBuffer dataPayload = ByteBuffer.allocate(256); + byte[] trailerFrameHeader = new byte[9]; + ByteBuffer trailerPayload = ByteBuffer.allocate(256); + + buildPostRequest(headersFrameHeader, headersPayload, false, dataFrameHeader, dataPayload, + null, trailerFrameHeader, trailerPayload, 3); + + // Write the headers + writeFrame(headersFrameHeader, headersPayload); + // Body + writeFrame(dataFrameHeader, dataPayload); + // Trailers + writeFrame(trailerFrameHeader, trailerPayload); + + parser.readFrame(true); + if (ok) { + parser.readFrame(true); + parser.readFrame(true); + parser.readFrame(true); + + String len = Integer.toString(256 + TRAILER_HEADER_VALUE.length()); + + Assert.assertEquals("0-WindowSize-[256]\n" + + "3-WindowSize-[256]\n" + + "3-HeadersStart\n" + + "3-Header-[:status]-[200]\n" + + "3-Header-[date]-["+ DEFAULT_DATE + "]\n" + + "3-HeadersEnd\n" + + "3-Body-" + + len + + "\n" + + "3-EndOfStream\n", + output.getTrace()); + } else { + Assert.assertEquals("3-RST-[11]\n", output.getTrace()); + } + } } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1766533&r1=1766532&r2=1766533&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Tue Oct 25 13:54:09 2016 @@ -127,8 +127,10 @@ </fix> <add> Add configuration options to the HTTP/2 implementation to control the - maximum number of headers allowed, the maximum size of headers allowed - and the maximum number of cookies allowed. (markt) + maximum number of headers allowed, the maximum size of headers allowed, + the maximum number of trailer headers allowed, the maximum size of + trailer headers allowed and the maximum number of cookies allowed. + (markt) </add> </changelog> </subsection> Modified: tomcat/trunk/webapps/docs/config/http2.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/http2.xml?rev=1766533&r1=1766532&r2=1766533&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/http2.xml (original) +++ tomcat/trunk/webapps/docs/config/http2.xml Tue Oct 25 13:54:09 2016 @@ -128,6 +128,23 @@ a default of 8192 is used.</p> </attribute> + <attribute name="maxTrailerCount" required="false"> + <p>The maximum number of trailer headers in a request that is allowed by + the container. A request that contains more trailer headers than the + specified limit will be rejected. A value of less than 0 means no limit. + If not specified, a default of 100 is used.</p> + </attribute> + + <attribute name="maxTrailerSize" required="false"> + <p>The maximum total size for all trailer headers in a request that is + allowed by the container. Total size for a header is calculated as the + uncompressed size of the header name in bytes, plus the uncompressed size + of the header value in bytes plus an HTTP/2 overhead of 3 bytes per + header. A request that contains a set of trailer headers that requires + more than the specified limit will be rejected. A value of less than 0 + means no limit. If not specified, a default of 8192 is used.</p> + </attribute> + <attribute name="readTimeout" required="false"> <p>The time, in milliseconds, that Tomcat will wait for additional data when a partial HTTP/2 frame has been received. Negative values will be --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org