Implement maxHeaderCount attribute on HTTP Connectors. It is equivalent of LimitRequestFields directive of Apache HTTPD
git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc5.5.x/trunk@1392247 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/STATUS.txt b/STATUS.txt
index 43fbd91..e69b89b 100644
--- a/STATUS.txt
+++ b/STATUS.txt
@@ -28,36 +28,6 @@
PATCHES PROPOSED TO BACKPORT:
[ New proposals should be added at the end of the list ]
-* Implement maxHeaderCount attribute on HTTP Connectors.
- It is equivalent of LimitRequestFields directive of Apache HTTPD
- See r1356239 in Tomcat 6.
-
- Notes:
- 1. Implemented for HTTP protocol only. (MimeHeaders.setLimit() is called
- by HTTP protocol processors only).
-
- I suppose that users of AJP can leverage the LimitRequestFields directive
- in Apache HTTPD server.
-
- 2. The feature is manageable through JMX on the ProtocolHandler MBean.
-
- Unlike later Tomcat versions, I did not add setter/getter methods to
- Connector class and did not expose the property on Connector MBean.
-
- Note that Catalina MBeans are not visible in Tomcat 5.5 by default.
- See r1356696 for instructions.
-
- 3. To test the feature one can use
- http://localhost:8080/servlets-examples/servlet/RequestHeaderExample
-
- Refreshing the page in Firefox changes the number of headers in incoming request
- (adds 'cache-control' for "F5" refresh, adds 'pragma=no-cache' for "Ctrl+F5" refresh).
-
- Patch:
- http://people.apache.org/~kkolinko/patches/2012-07-03_tc55_maxHeaderCount_v1.patch
- +1: kkolinko, markt, kfujino
- -1:
-
* Various DIGEST improvements ported from Tomcat 7
http://people.apache.org/~markt/patches/2012-08-28-digest-tc5.patch
+1: markt, kkolinko, kfujino
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java
index dd9ca0e..ec43d48 100644
--- a/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java
@@ -803,6 +803,8 @@
if (!disableUploadTimeout) {
Socket.timeoutSet(socket, timeout * 1000);
}
+ // Set this every time in case limit has been changed via JMX
+ request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
inputBuffer.parseHeaders();
} catch (IOException e) {
error = true;
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProtocol.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProtocol.java
index cb1e3bb..96270bc 100644
--- a/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProtocol.java
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProtocol.java
@@ -367,6 +367,15 @@
setAttribute("maxHttpHeaderSize", "" + valueI);
}
+ public int getMaxHeaderCount() {
+ return ep.getMaxHeaderCount();
+ }
+
+ public void setMaxHeaderCount(int maxHeaderCount) {
+ ep.setMaxHeaderCount(maxHeaderCount);
+ setAttribute("maxHeaderCount", "" + maxHeaderCount);
+ }
+
public String getRestrictedUserAgents() {
return restrictedUserAgents;
}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Http11BaseProtocol.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11BaseProtocol.java
index 9fb5ea8..0ef5902 100644
--- a/connectors/http11/src/java/org/apache/coyote/http11/Http11BaseProtocol.java
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11BaseProtocol.java
@@ -377,6 +377,15 @@
setAttribute("maxHttpHeaderSize", "" + valueI);
}
+ public int getMaxHeaderCount() {
+ return ep.getMaxHeaderCount();
+ }
+
+ public void setMaxHeaderCount(int maxHeaderCount) {
+ ep.setMaxHeaderCount(maxHeaderCount);
+ setAttribute("maxHeaderCount", "" + maxHeaderCount);
+ }
+
public String getRestrictedUserAgents() {
return restrictedUserAgents;
}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java
index 473a12a..17999cd 100644
--- a/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java
@@ -839,6 +839,8 @@
if (!disableUploadTimeout) {
socket.setSoTimeout(timeout);
}
+ // Set this every time in case limit has been changed via JMX
+ request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
inputBuffer.parseHeaders();
} catch (IOException e) {
error = true;
diff --git a/connectors/util/java/org/apache/tomcat/util/http/LocalStrings.properties b/connectors/util/java/org/apache/tomcat/util/http/LocalStrings.properties
index 815751d..badd276 100644
--- a/connectors/util/java/org/apache/tomcat/util/http/LocalStrings.properties
+++ b/connectors/util/java/org/apache/tomcat/util/http/LocalStrings.properties
@@ -22,3 +22,5 @@
parameters.maxCountFail=More than the maximum number of request parameters (GET plus POST) for a single request ([{0}]) were detected. Any parameters beyond this limit have been ignored. To change this limit, set the maxParameterCount attribute on the Connector.
parameters.multipleDecodingFail=Character decoding failed. A total of [{0}] failures were detected but only the first was logged. Enable debug level logging for this logger to log all failures.
parameters.noequal=Parameter starting at position [{0}] and ending at position [{1}] with a value of [{0}] was not followed by an '=' character
+
+headers.maxCountFail=More than the maximum allowed number of headers ([{0}]) were detected.
diff --git a/connectors/util/java/org/apache/tomcat/util/http/MimeHeaders.java b/connectors/util/java/org/apache/tomcat/util/http/MimeHeaders.java
index 0640cd5..7d9c217 100644
--- a/connectors/util/java/org/apache/tomcat/util/http/MimeHeaders.java
+++ b/connectors/util/java/org/apache/tomcat/util/http/MimeHeaders.java
@@ -22,6 +22,7 @@
import java.util.Enumeration;
import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.res.StringManager;
/* XXX XXX XXX Need a major rewrite !!!!
*/
@@ -96,7 +97,10 @@
* XXX make it configurable ( fine-tuning of web-apps )
*/
public static final int DEFAULT_HEADER_SIZE=8;
-
+
+ private static final StringManager sm =
+ StringManager.getManager("org.apache.tomcat.util.http");
+
/**
* The header fields.
*/
@@ -109,12 +113,30 @@
private int count;
/**
+ * The limit on the number of header fields.
+ */
+ private int limit = -1;
+
+ /**
* Creates a new MimeHeaders object using a default buffer size.
*/
public MimeHeaders() {
}
/**
+ * Set limit on the number of header fields.
+ */
+ public void setLimit(int limit) {
+ this.limit = limit;
+ if (limit > 0 && headers.length > limit && count < limit) {
+ // shrink header list array
+ MimeHeaderField tmp[] = new MimeHeaderField[limit];
+ System.arraycopy(headers, 0, tmp, 0, count);
+ headers = tmp;
+ }
+ }
+
+ /**
* Clears all header fields.
*/
// [seguin] added for consistency -- most other objects have recycle().
@@ -213,11 +235,19 @@
* field has not had its name or value initialized.
*/
private MimeHeaderField createHeader() {
+ if (limit > -1 && count >= limit) {
+ throw new IllegalStateException(sm.getString(
+ "headers.maxCountFail", new Integer(limit)));
+ }
MimeHeaderField mh;
int len = headers.length;
if (count >= len) {
// expand header list array
- MimeHeaderField tmp[] = new MimeHeaderField[count * 2];
+ int newLength = count * 2;
+ if (limit > 0 && newLength > limit) {
+ newLength = limit;
+ }
+ MimeHeaderField tmp[] = new MimeHeaderField[newLength];
System.arraycopy(headers, 0, tmp, 0, len);
headers = tmp;
}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/AprEndpoint.java b/connectors/util/java/org/apache/tomcat/util/net/AprEndpoint.java
index 01646f6..01b6081 100644
--- a/connectors/util/java/org/apache/tomcat/util/net/AprEndpoint.java
+++ b/connectors/util/java/org/apache/tomcat/util/net/AprEndpoint.java
@@ -455,6 +455,18 @@
public void setSSLVerifyDepth(int SSLVerifyDepth) { this.SSLVerifyDepth = SSLVerifyDepth; }
+ /**
+ * The maximum number of headers in a request that are allowed.
+ * 100 by default. A value of less than 0 means no limit.
+ */
+ private int maxHeaderCount = 100; // as in Apache HTTPD server
+ public int getMaxHeaderCount() {
+ return maxHeaderCount;
+ }
+ public void setMaxHeaderCount(int maxHeaderCount) {
+ this.maxHeaderCount = maxHeaderCount;
+ }
+
// --------------------------------------------------------- Public Methods
diff --git a/connectors/util/java/org/apache/tomcat/util/net/PoolTcpEndpoint.java b/connectors/util/java/org/apache/tomcat/util/net/PoolTcpEndpoint.java
index ee62a28..a8be3d4 100644
--- a/connectors/util/java/org/apache/tomcat/util/net/PoolTcpEndpoint.java
+++ b/connectors/util/java/org/apache/tomcat/util/net/PoolTcpEndpoint.java
@@ -89,7 +89,12 @@
protected int socketTimeout=-1;
private boolean lf = true;
-
+ /**
+ * The maximum number of headers in a request that are allowed.
+ * 100 by default. A value of less than 0 means no limit.
+ */
+ private int maxHeaderCount = 100; // as in Apache HTTPD server
+
// ------ Leader follower fields
@@ -273,6 +278,14 @@
}
}
+ public int getMaxHeaderCount() {
+ return maxHeaderCount;
+ }
+
+ public void setMaxHeaderCount(int maxHeaderCount) {
+ this.maxHeaderCount = maxHeaderCount;
+ }
+
public int getCurrentThreadCount() {
return curThreads;
}
diff --git a/container/webapps/docs/changelog.xml b/container/webapps/docs/changelog.xml
index 580f832..979722e 100644
--- a/container/webapps/docs/changelog.xml
+++ b/container/webapps/docs/changelog.xml
@@ -76,6 +76,9 @@
Ensure that the chunked input filter is correctly recycled between
requests. (kkolinko/jim)
</fix>
+ <add>
+ Implement the maxHeaderCount for the HTTP connectors. (kkolinko)
+ </add>
</changelog>
</subsection>
<subsection name="Webapps">
diff --git a/container/webapps/docs/config/http.xml b/container/webapps/docs/config/http.xml
index f842712..66ff8db 100644
--- a/container/webapps/docs/config/http.xml
+++ b/container/webapps/docs/config/http.xml
@@ -277,6 +277,13 @@
this attribute is set to "true".</p>
</attribute>
+ <attribute name="maxHeaderCount" required="false">
+ <p>The maximum number of headers in a request that are allowed by the
+ container. A request that contains more 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="maxHttpHeaderSize" required="false">
<p>The maximum size of the request and response HTTP header, specified
in bytes.