You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2016/10/25 13:54:09 UTC
svn commit: r1766533 - in /tomcat/trunk: java/org/apache/coyote/
java/org/apache/coyote/http2/ test/org/apache/coyote/http2/ webapps/docs/
webapps/docs/config/
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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org