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 2011/10/27 20:02:28 UTC
svn commit: r1189899 - in /tomcat/tc7.0.x/trunk: ./
java/org/apache/catalina/connector/ java/org/apache/tomcat/util/buf/
java/org/apache/tomcat/util/http/ webapps/docs/ webapps/docs/config/
Author: markt
Date: Thu Oct 27 18:02:27 2011
New Revision: 1189899
URL: http://svn.apache.org/viewvc?rev=1189899&view=rev
Log:
Re-factor parameter parsing to improve performance
Added:
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties (with props)
Modified:
tomcat/tc7.0.x/trunk/ (props changed)
tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Connector.java
tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Request.java
tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/StringCache.java
tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/Parameters.java
tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml
tomcat/tc7.0.x/trunk/webapps/docs/config/ajp.xml
tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml
Propchange: tomcat/tc7.0.x/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Oct 27 18:02:27 2011
@@ -1 +1 @@
-/tomcat/trunk:1156115,1156171,1156276,1156304,1156519,1156530,1156602,1157015,1157018,1157151,1157198,1157204,1157810,1157832,1157834,1157847,1157908,1157939,1158155,1158160,1158176,1158195,1158198-1158199,1158227,1158331,1158334-1158335,1158426,1160347,1160592,1160611,1160619,1160626,1160639,1160652,1160720-1160721,1160772,1160774,1160776,1161303,1161310,1161322,1161339,1161486,1161540,1161549,1161584,1162082,1162149,1162169,1162721,1162769,1162836,1162932,1163630,1164419,1164438,1164469,1164480,1164567,1165234,1165247-1165248,1165253,1165273,1165282,1165309,1165331,1165338,1165347,1165360-1165361,1165367-1165368,1165602,1165608,1165677,1165693,1165721,1165723,1165728,1165730,1165738,1165746,1165765,1165777,1165918,1165921,1166077,1166150-1166151,1166290,1166366,1166620,1166686,1166693,1166752,1166757,1167368,1167394,1169447,1170647,1171692,1172233-1172234,1172236,1172269,1172278,1172282,1172556,1172610,1172664,1172689,1172711,1173020-1173021,1173082,1173088,1173090,1173096
,1173241,1173256,1173288,1173333,1173342,1173461,1173614,1173630,1173659,1173722,1174061,1174239,1174322,1174325,1174329-1174330,1174337-1174339,1174343,1174353,1174799,1174882,1174884,1174983,1175155,1175158,1175167,1175182,1175190,1175201,1175272,1175275,1175283,1175582,1175589-1175590,1175594,1175602,1175613,1175633,1175690,1175713,1175889,1175896,1175907,1176584,1176590,1176799,1177050,1177060,1177125,1177152,1177160,1177245,1177850,1177862,1177978,1178209,1178228,1178233,1178449,1178542,1178681,1178684,1178721,1179268,1179274,1180261,1180865,1180891,1180894,1180907,1181028,1181123,1181125,1181136,1181291,1181743,1182796,1183078,1183105,1183142,1183328,1183339-1183340,1183492-1183494,1183605,1184917,1184919,1185018,1185020,1185200,1185588,1185626,1185756,1185758,1186011,1186042-1186045,1186104,1186123,1186137,1186153,1186254,1186257,1186377-1186379,1186479-1186480,1186712,1186743,1186750,1186763,1186890-1186892,1186894,1186949,1187018,1187027-1187028,1187381,1187755,1187
775,1187827,1188301,1188303-1188305,1188399,1188822,1188930-1188931,1189116,1189129,1189183,1189240,1189256,1189386,1189413-1189414,1189477,1189685,1189805,1189857,1189864
+/tomcat/trunk:1156115,1156171,1156276,1156304,1156519,1156530,1156602,1157015,1157018,1157151,1157198,1157204,1157810,1157832,1157834,1157847,1157908,1157939,1158155,1158160,1158176,1158195,1158198-1158199,1158227,1158331,1158334-1158335,1158426,1160347,1160592,1160611,1160619,1160626,1160639,1160652,1160720-1160721,1160772,1160774,1160776,1161303,1161310,1161322,1161339,1161486,1161540,1161549,1161584,1162082,1162149,1162169,1162721,1162769,1162836,1162932,1163630,1164419,1164438,1164469,1164480,1164567,1165234,1165247-1165248,1165253,1165273,1165282,1165309,1165331,1165338,1165347,1165360-1165361,1165367-1165368,1165602,1165608,1165677,1165693,1165721,1165723,1165728,1165730,1165738,1165746,1165765,1165777,1165918,1165921,1166077,1166150-1166151,1166290,1166366,1166620,1166686,1166693,1166752,1166757,1167368,1167394,1169447,1170647,1171692,1172233-1172234,1172236,1172269,1172278,1172282,1172556,1172610,1172664,1172689,1172711,1173020-1173021,1173082,1173088,1173090,1173096
,1173241,1173256,1173288,1173333,1173342,1173461,1173614,1173630,1173659,1173722,1174061,1174239,1174322,1174325,1174329-1174330,1174337-1174339,1174343,1174353,1174799,1174882,1174884,1174983,1175155,1175158,1175167,1175182,1175190,1175201,1175272,1175275,1175283,1175582,1175589-1175590,1175594,1175602,1175613,1175633,1175690,1175713,1175889,1175896,1175907,1176584,1176590,1176799,1177050,1177060,1177125,1177152,1177160,1177245,1177850,1177862,1177978,1178209,1178228,1178233,1178449,1178542,1178681,1178684,1178721,1179268,1179274,1180261,1180865,1180891,1180894,1180907,1181028,1181123,1181125,1181136,1181291,1181743,1182796,1183078,1183105,1183142,1183328,1183339-1183340,1183492-1183494,1183605,1184917,1184919,1185018,1185020,1185200,1185588,1185626,1185756,1185758,1186011,1186042-1186045,1186104,1186123,1186137,1186153,1186254,1186257,1186377-1186379,1186479-1186480,1186712,1186743,1186750,1186763,1186890-1186892,1186894,1186949,1187018,1187027-1187028,1187381,1187755,1187
775,1187827,1188301,1188303-1188305,1188399,1188822,1188930-1188931,1189116,1189129,1189183,1189240,1189256,1189386,1189413-1189414,1189477,1189685,1189805,1189857,1189864,1189882
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Connector.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Connector.java?rev=1189899&r1=1189898&r2=1189899&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Connector.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Connector.java Thu Oct 27 18:02:27 2011
@@ -172,6 +172,13 @@ public class Connector extends Lifecycle
/**
+ * The maximum number of parameters (GET plus POST) which will be
+ * automatically parsed by the container. 10000 by default. A value of less
+ * than 0 means no limit.
+ */
+ protected int maxParameterCount = 10000;
+
+ /**
* Maximum size of a POST which will be automatically parsed by the
* container. 2MB by default.
*/
@@ -400,14 +407,34 @@ public class Connector extends Lifecycle
}
- /**
- * Return the mapper.
- */
- public Mapper getMapper() {
+ /**
+ * Return the mapper.
+ */
+ public Mapper getMapper() {
+ return (mapper);
+ }
- return (mapper);
- }
+ /**
+ * Return the maximum number of parameters (GET plus POST) that will be
+ * automatically parsed by the container. A value of less than 0 means no
+ * limit.
+ */
+ public int getMaxParameterCount() {
+ return maxParameterCount;
+ }
+
+
+ /**
+ * Set the maximum number of parameters (GET plus POST) that will be
+ * automatically parsed by the container. A value of less than 0 means no
+ * limit.
+ *
+ * @param maxParameterCount The new setting
+ */
+ public void setMaxParameterCount(int maxParameterCount) {
+ this.maxParameterCount = maxParameterCount;
+ }
/**
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Request.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Request.java?rev=1189899&r1=1189898&r2=1189899&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Request.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Request.java Thu Oct 27 18:02:27 2011
@@ -2875,6 +2875,8 @@ public class Request
parametersParsed = true;
Parameters parameters = coyoteRequest.getParameters();
+ // Set this every time in case limit has been changed via JMX
+ parameters.setLimit(getConnector().getMaxParameterCount());
// getCharacterEncoding() may have been overridden to search for
// hidden form field containing request encoding
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml?rev=1189899&r1=1189898&r2=1189899&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml Thu Oct 27 18:02:27 2011
@@ -81,6 +81,10 @@
description="Maximum number of Keep-Alive requests to honor per connection"
type="int"/>
+ <attribute name="maxParameterCount"
+ description="The maximum number of parameters (GET plus POST) which will be automatically parsed by the container. 10000 by default. A value of less than 0 means no limit."
+ type="int"/>
+
<attribute name="maxPostSize"
description="Maximum size in bytes of a POST which will be handled by the servlet API provided features"
type="int"/>
Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java?rev=1189899&r1=1189898&r2=1189899&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java Thu Oct 27 18:02:27 2011
@@ -19,6 +19,10 @@ package org.apache.tomcat.util.buf;
import java.io.IOException;
import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
/*
* In a server it is very important to be able to operate on
@@ -97,15 +101,23 @@ public final class ByteChunk implements
as most standards seem to converge, but the servlet API requires
8859_1, and this object is used mostly for servlets.
*/
- public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
+ public static Charset DEFAULT_CHARSET = null;
+ static {
+ try {
+ DEFAULT_CHARSET = B2CConverter.getCharset("ISO-8859-1");
+ } catch (UnsupportedEncodingException e) {
+ // Should never happen since all JVMs must support ISO-8859-1
+ }
+ }
+
// byte[]
private byte[] buff;
private int start=0;
private int end;
- private String enc;
+ private Charset charset;
private boolean isSet=false; // XXX
@@ -146,7 +158,7 @@ public final class ByteChunk implements
*/
public void recycle() {
// buff = null;
- enc=null;
+ charset=null;
start=0;
end=0;
isSet=false;
@@ -186,13 +198,15 @@ public final class ByteChunk implements
this.optimizedWrite = optimizedWrite;
}
- public void setEncoding( String enc ) {
- this.enc=enc;
+ public void setCharset(Charset charset) {
+ this.charset = charset;
}
- public String getEncoding() {
- if (enc == null)
- enc=DEFAULT_CHARACTER_ENCODING;
- return enc;
+
+ public Charset getCharset() {
+ if (charset == null) {
+ charset = DEFAULT_CHARSET;
+ }
+ return charset;
}
/**
@@ -497,31 +511,15 @@ public final class ByteChunk implements
}
public String toStringInternal() {
- String strValue=null;
- try {
- if (enc == null) {
- enc = DEFAULT_CHARACTER_ENCODING;
- }
- strValue = new String(buff, start, end-start,
- B2CConverter.getCharset(enc));
- /*
- Does not improve the speed too much on most systems,
- it's safer to use the "classical" new String().
-
- Most overhead is in creating char[] and copying,
- the internal implementation of new String() is very close to
- what we do. The decoder is nice for large buffers and if
- we don't go to String ( so we can take advantage of reduced GC)
-
- // Method is commented out, in:
- return B2CConverter.decodeString( enc );
- */
- } catch (java.io.UnsupportedEncodingException e) {
- // Use the platform encoding in that case; the usage of a bad
- // encoding will have been logged elsewhere already
- strValue = new String(buff, start, end-start);
+ if (charset == null) {
+ charset = DEFAULT_CHARSET;
}
- return strValue;
+ // new String(byte[], int, int, Charset) takes a defensive copy of the
+ // entire byte array. This is expensive if only a small subset of the
+ // bytes will be used. The code below is from Apache Harmony.
+ CharBuffer cb;
+ cb = charset.decode(ByteBuffer.wrap(buff, start, end-start));
+ return new String(cb.array());
}
public int getInt()
Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java?rev=1189899&r1=1189898&r2=1189899&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java Thu Oct 27 18:02:27 2011
@@ -130,13 +130,13 @@ public final class MessageBytes implemen
* previous conversion is reset.
* If no encoding is set, we'll use 8859-1.
*/
- public void setEncoding( String enc ) {
+ public void setCharset(Charset charset) {
if( !byteC.isNull() ) {
// if the encoding changes we need to reset the conversion results
charC.recycle();
hasStrValue=false;
}
- byteC.setEncoding(enc);
+ byteC.setCharset(charset);
}
/**
Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/StringCache.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/StringCache.java?rev=1189899&r1=1189898&r2=1189899&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/StringCache.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/StringCache.java Thu Oct 27 18:02:27 2011
@@ -17,6 +17,7 @@
package org.apache.tomcat.util.buf;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
@@ -299,7 +300,7 @@ public class StringCache {
System.arraycopy(bc.getBuffer(), start, entry.name,
0, end - start);
// Set encoding
- entry.enc = bc.getEncoding();
+ entry.charset = bc.getCharset();
// Initialize occurrence count to one
count = new int[1];
count[0] = 1;
@@ -485,7 +486,7 @@ public class StringCache {
protected static final String find(ByteChunk name) {
int pos = findClosest(name, bcCache, bcCache.length);
if ((pos < 0) || (compare(name, bcCache[pos].name) != 0)
- || !(name.getEncoding().equals(bcCache[pos].enc))) {
+ || !(name.getCharset().equals(bcCache[pos].charset))) {
return null;
} else {
return bcCache[pos].value;
@@ -640,7 +641,7 @@ public class StringCache {
public static class ByteEntry {
public byte[] name = null;
- public String enc = null;
+ public Charset charset = null;
public String value = null;
@Override
Added: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties?rev=1189899&view=auto
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties (added)
+++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties Thu Oct 27 18:02:27 2011
@@ -0,0 +1,23 @@
+# 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.
+
+parameters.bytes=Start processing with input [{0}]
+paramerers.copyFail=Failed to create copy of original parameter values for debug logging purposes
+parameters.decodeFail.debug=Character decoding failed. Parameter [{0}] with value [{1}] has been ignored.
+parameters.decodeFail.info=Character decoding failed. Parameter [{0}] with value [{1}] has been ignored. Note that the name and value quoted here may be corrupted due to the failed decoding. Use debug level logging to see the original, non-corrupted values.
+parameters.invalidChunk=Invalid chunk starting at byte [{0}] and ending at byte [{1}] with a value of [{2}] ignored
+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
Propchange: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties
------------------------------------------------------------------------------
svn:eol-style = native
Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/Parameters.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/Parameters.java?rev=1189899&r1=1189898&r2=1189899&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/Parameters.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/Parameters.java Thu Oct 27 18:02:27 2011
@@ -18,14 +18,18 @@
package org.apache.tomcat.util.http;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
+import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.CharChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.buf.UDecoder;
+import org.apache.tomcat.util.res.StringManager;
/**
*
@@ -33,15 +37,15 @@ import org.apache.tomcat.util.buf.UDecod
*/
public final class Parameters {
-
- private static final org.apache.juli.logging.Log log=
+ private static final org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(Parameters.class );
-
- // Transition: we'll use the same Hashtable( String->String[] )
- // for the beginning. When we are sure all accesses happen through
- // this class - we can switch to MultiMap
- private Hashtable<String,String[]> paramHashStringArray =
- new Hashtable<String,String[]>();
+
+ protected static final StringManager sm =
+ StringManager.getManager("org.apache.tomcat.util.http");
+
+ private final Hashtable<String,ArrayList<String>> paramHashValues =
+ new Hashtable<String,ArrayList<String>>();
+
private boolean didQueryParameters=false;
MessageBytes queryMB;
@@ -52,6 +56,9 @@ public final class Parameters {
String encoding=null;
String queryStringEncoding=null;
+ private int limit = -1;
+ private int parameterCount = 0;
+
public Parameters() {
// NO-OP
}
@@ -60,6 +67,10 @@ public final class Parameters {
this.queryMB=queryMB;
}
+ public void setLimit(int limit) {
+ this.limit = limit;
+ }
+
public String getEncoding() {
return encoding;
}
@@ -79,7 +90,8 @@ public final class Parameters {
}
public void recycle() {
- paramHashStringArray.clear();
+ parameterCount = 0;
+ paramHashValues.clear();
didQueryParameters=false;
encoding=null;
decodedQuery.recycle();
@@ -93,41 +105,39 @@ public final class Parameters {
public void addParameterValues( String key, String[] newValues) {
if ( key==null ) return;
- String values[];
- if (paramHashStringArray.containsKey(key)) {
- String oldValues[] = paramHashStringArray.get(key);
- values = new String[oldValues.length + newValues.length];
- for (int i = 0; i < oldValues.length; i++) {
- values[i] = oldValues[i];
- }
- for (int i = 0; i < newValues.length; i++) {
- values[i+ oldValues.length] = newValues[i];
- }
+ ArrayList<String> values;
+ if (paramHashValues.containsKey(key)) {
+ values = paramHashValues.get(key);
} else {
- values = newValues;
+ values = new ArrayList<String>(1);
+ paramHashValues.put(key, values);
+ }
+ values.ensureCapacity(values.size() + newValues.length);
+ for (String newValue : newValues) {
+ values.add(newValue);
}
-
- paramHashStringArray.put(key, values);
}
public String[] getParameterValues(String name) {
handleQueryParameters();
// no "facade"
- String values[] = paramHashStringArray.get(name);
- return values;
+ ArrayList<String> values = paramHashValues.get(name);
+ return values.toArray(new String[values.size()]);
}
public Enumeration<String> getParameterNames() {
handleQueryParameters();
- return paramHashStringArray.keys();
+ return paramHashValues.keys();
}
// Shortcut.
public String getParameter(String name ) {
- String[] values = getParameterValues(name);
+ ArrayList<String> values = paramHashValues.get(name);
if (values != null) {
- if( values.length==0 ) return "";
- return values[0];
+ if(values.size() == 0) {
+ return "";
+ }
+ return values.get(0);
} else {
return null;
}
@@ -161,21 +171,14 @@ public final class Parameters {
// until we test the new one
private void addParam( String key, String value ) {
if( key==null ) return;
- String values[];
- if (paramHashStringArray.containsKey(key)) {
- String oldValues[] = paramHashStringArray.get(key);
- values = new String[oldValues.length + 1];
- for (int i = 0; i < oldValues.length; i++) {
- values[i] = oldValues[i];
- }
- values[oldValues.length] = value;
+ ArrayList<String> values;
+ if (paramHashValues.containsKey(key)) {
+ values = paramHashValues.get(key);
} else {
- values = new String[1];
- values[0] = value;
+ values = new ArrayList<String>(1);
+ paramHashValues.put(key, values);
}
-
-
- paramHashStringArray.put(key, values);
+ values.add(value);
}
public void setURLDecoder( UDecoder u ) {
@@ -196,104 +199,158 @@ public final class Parameters {
public void processParameters( byte bytes[], int start, int len ) {
- processParameters(bytes, start, len, encoding);
+ processParameters(bytes, start, len, getCharset(encoding));
}
- public void processParameters( byte bytes[], int start, int len,
- String enc ) {
- int end=start+len;
- int pos=start;
+ public void processParameters(byte bytes[], int start, int len,
+ Charset charset) {
if(log.isDebugEnabled()) {
- log.debug("Bytes: " +
- new String(bytes, start, len, DEFAULT_CHARSET));
+ log.debug(sm.getString("parameters.bytes",
+ new String(bytes, start, len, DEFAULT_CHARSET)));
}
- do {
- boolean noEq=false;
- int valStart=-1;
- int valEnd=-1;
+ int decodeFailCount = 0;
- int nameStart=pos;
- int nameEnd=ByteChunk.indexOf(bytes, nameStart, end, '=' );
- // Workaround for a&b&c encoding
- int nameEnd2=ByteChunk.indexOf(bytes, nameStart, end, '&' );
- if( (nameEnd2!=-1 ) &&
- ( nameEnd==-1 || nameEnd > nameEnd2) ) {
- nameEnd=nameEnd2;
- noEq=true;
- valStart=nameEnd;
- valEnd=nameEnd;
- if(log.isDebugEnabled()) {
- log.debug("no equal " + nameStart + " " + nameEnd + " " +
- new String(bytes, nameStart, nameEnd-nameStart,
- DEFAULT_CHARSET));
- }
+ int pos = start;
+ int end = start + len;
+
+ while(pos < end) {
+ parameterCount ++;
+
+ if (limit > -1 && parameterCount >= limit) {
+ log.warn(sm.getString("parameters.maxCountFail",
+ Integer.valueOf(limit)));
+ break;
}
- if( nameEnd== -1 )
- nameEnd=end;
+ int nameStart = pos;
+ int nameEnd = -1;
+ int valueStart = -1;
+ int valueEnd = -1;
+
+ boolean parsingName = true;
+ boolean decodeName = false;
+ boolean decodeValue = false;
+ boolean parameterComplete = false;
+
+ do {
+ switch(bytes[pos]) {
+ case '=':
+ if (parsingName) {
+ // Name finished. Value starts from next character
+ nameEnd = pos;
+ parsingName = false;
+ valueStart = ++pos;
+ } else {
+ // Equals character in value
+ pos++;
+ }
+ break;
+ case '&':
+ if (parsingName) {
+ // Name finished. No value.
+ nameEnd = pos;
+ } else {
+ // Value finished
+ valueEnd = pos;
+ }
+ parameterComplete = true;
+ pos++;
+ break;
+ case '%':
+ // Decoding required
+ if (parsingName) {
+ decodeName = true;
+ } else {
+ decodeValue = true;
+ }
+ pos ++;
+ break;
+ default:
+ pos ++;
+ break;
+ }
+ } while (!parameterComplete && pos < end);
- if( ! noEq ) {
- valStart= (nameEnd < end) ? nameEnd+1 : end;
- valEnd=ByteChunk.indexOf(bytes, valStart, end, '&');
- if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
+ if (pos == end) {
+ if (nameEnd == -1) {
+ nameEnd = pos;
+ } else if (valueStart > -1 && valueEnd == -1){
+ valueEnd = pos;
+ }
}
- pos=valEnd+1;
+ if (log.isDebugEnabled() && valueStart == -1) {
+ log.debug(sm.getString("parameters.noequal",
+ Integer.valueOf(nameStart), Integer.valueOf(nameEnd),
+ new String(bytes, nameStart, nameEnd-nameStart,
+ DEFAULT_CHARSET)));
+ }
- if( nameEnd<=nameStart ) {
+ if (nameEnd <= nameStart ) {
if (log.isInfoEnabled()) {
- StringBuilder msg = new StringBuilder("Parameters: Invalid chunk ");
- // No name eg ...&=xx&... will trigger this
- if (valEnd >= nameStart) {
- msg.append('\'');
- msg.append(new String(bytes, nameStart,
- valEnd - nameStart, DEFAULT_CHARSET));
- msg.append("' ");
+ String extract;
+ if (valueEnd >= nameStart) {
+ extract = new String(bytes, nameStart,
+ valueEnd - nameStart, DEFAULT_CHARSET);
+ log.info(sm.getString("parameters.invalidChunk",
+ Integer.valueOf(nameStart),
+ Integer.valueOf(valueEnd),
+ extract));
+ } else {
+ log.info(sm.getString("parameters.invalidChunk",
+ Integer.valueOf(nameStart),
+ Integer.valueOf(nameEnd),
+ null));
}
- msg.append("ignored.");
- log.info(msg);
}
continue;
// invalid chunk - it's better to ignore
}
- tmpName.setBytes( bytes, nameStart, nameEnd-nameStart );
- tmpValue.setBytes( bytes, valStart, valEnd-valStart );
+ tmpName.setBytes(bytes, nameStart, nameEnd - nameStart);
+ tmpValue.setBytes(bytes, valueStart, valueEnd - valueStart);
+
// Take copies as if anything goes wrong originals will be
// corrupted. This means original values can be logged.
// For performance - only done for debug
if (log.isDebugEnabled()) {
try {
- origName.append(bytes, nameStart, nameEnd-nameStart);
- origValue.append(bytes, valStart, valEnd-valStart);
+ origName.append(bytes, nameStart, nameEnd - nameStart);
+ origValue.append(bytes, valueStart, valueEnd - valueStart);
} catch (IOException ioe) {
// Should never happen...
- log.error("Error copying parameters", ioe);
+ log.error(sm.getString("paramerers.copyFail"), ioe);
}
}
try {
- addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) );
+ String name;
+ String value;
+
+ if (decodeName) {
+ name = urlDecode(tmpName, charset);
+ } else {
+ name = tmpName.toString();
+ }
+
+ if (decodeValue) {
+ value = urlDecode(tmpValue, charset);
+ } else {
+ value = tmpValue.toString();
+ }
+
+ addParam(name, value);
} catch (IOException e) {
- StringBuilder msg =
- new StringBuilder("Parameters: Character decoding failed.");
- msg.append(" Parameter '");
- if (log.isDebugEnabled()) {
- msg.append(origName.toString());
- msg.append("' with value '");
- msg.append(origValue.toString());
- msg.append("' has been ignored.");
- log.debug(msg, e);
- } else if (log.isInfoEnabled()) {
- msg.append(tmpName.toString());
- msg.append("' with value '");
- msg.append(tmpValue.toString());
- msg.append("' has been ignored. Note that the name and ");
- msg.append("value quoted here may be corrupted due to ");
- msg.append("the failed decoding. Use debug level logging ");
- msg.append("to see the original, non-corrupted values.");
- log.info(msg);
+ decodeFailCount++;
+ if (decodeFailCount == 1 || log.isDebugEnabled()) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("parameters.decodeFail.debug",
+ origName.toString(), origValue.toString()), e);
+ } else if (log.isInfoEnabled()) {
+ log.info(sm.getString("parameters.decodeFail.info",
+ tmpName.toString(), tmpValue.toString()), e);
+ }
}
}
@@ -304,35 +361,22 @@ public final class Parameters {
origName.recycle();
origValue.recycle();
}
- } while( pos<end );
+ }
+
+ if (decodeFailCount > 1 && !log.isDebugEnabled()) {
+ log.info(sm.getString("parameters.multipleDecodingFail",
+ Integer.valueOf(decodeFailCount)));
+ }
}
- private String urlDecode(ByteChunk bc, String enc)
+ private String urlDecode(ByteChunk bc, Charset charset)
throws IOException {
if( urlDec==null ) {
urlDec=new UDecoder();
}
urlDec.convert(bc);
- String result = null;
- if (enc != null) {
- bc.setEncoding(enc);
- result = bc.toString();
- } else {
- CharChunk cc = tmpNameC;
- int length = bc.getLength();
- cc.allocate(length, -1);
- // Default encoding: fast conversion
- byte[] bbuf = bc.getBuffer();
- char[] cbuf = cc.getBuffer();
- int start = bc.getStart();
- for (int i = 0; i < length; i++) {
- cbuf[i] = (char) (bbuf[i + start] & 0xff);
- }
- cc.setChars(cbuf, 0, length);
- result = cc.toString();
- cc.recycle();
- }
- return result;
+ bc.setCharset(charset);
+ return bc.toString();
}
public void processParameters( MessageBytes data, String encoding ) {
@@ -343,23 +387,34 @@ public final class Parameters {
}
ByteChunk bc=data.getByteChunk();
processParameters( bc.getBytes(), bc.getOffset(),
- bc.getLength(), encoding);
+ bc.getLength(), getCharset(encoding));
+ }
+
+ private Charset getCharset(String encoding) {
+ if (encoding == null) {
+ return DEFAULT_CHARSET;
+ }
+ try {
+ return B2CConverter.getCharset(encoding);
+ } catch (UnsupportedEncodingException e) {
+ return DEFAULT_CHARSET;
+ }
}
/** Debug purpose
*/
public String paramsAsString() {
StringBuilder sb=new StringBuilder();
- Enumeration<String> en= paramHashStringArray.keys();
+ Enumeration<String> en= paramHashValues.keys();
while( en.hasMoreElements() ) {
String k = en.nextElement();
sb.append( k ).append("=");
- String v[] = paramHashStringArray.get( k );
- for( int i=0; i<v.length; i++ )
- sb.append( v[i] ).append(",");
- sb.append("\n");
+ ArrayList<String> values = paramHashValues.get(k);
+ for(String value : values) {
+ sb.append(value).append(",");
+ sb.append("\n");
+ }
}
return sb.toString();
}
-
}
Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1189899&r1=1189898&r2=1189899&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Thu Oct 27 18:02:27 2011
@@ -141,6 +141,11 @@
in the failure of the valve or filter rather than just a warning
message. (markt)
</add>
+ <add>
+ Improve performance of parameter processing for GET and POST requests.
+ Also add an option to limit the maximum number of parameters processed
+ per request. This defaults to 10000. (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">
Modified: tomcat/tc7.0.x/trunk/webapps/docs/config/ajp.xml
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/config/ajp.xml?rev=1189899&r1=1189898&r2=1189899&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/docs/config/ajp.xml (original)
+++ tomcat/tc7.0.x/trunk/webapps/docs/config/ajp.xml Thu Oct 27 18:02:27 2011
@@ -95,6 +95,12 @@
By default, DNS lookups are disabled.</p>
</attribute>
+ <attribute name="maxParameterCount" required="false">
+ <p>The maximum number of parameters (GET plus POST) which will be
+ automatically parsed by the container. A value of less than 0 means no
+ limit. If not specified, a default of 10000 is used.</p>
+ </attribute>
+
<attribute name="maxPostSize" required="false">
<p>The maximum size in bytes of the POST which will be handled by
the container FORM URL parameter parsing. The limit can be disabled by
Modified: tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml?rev=1189899&r1=1189898&r2=1189899&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml (original)
+++ tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml Thu Oct 27 18:02:27 2011
@@ -93,6 +93,12 @@
By default, DNS lookups are disabled.</p>
</attribute>
+ <attribute name="maxParameterCount" required="false">
+ <p>The maximum number of parameters (GET plus POST) which will be
+ automatically parsed by the container. A value of less than 0 means no
+ limit. If not specified, a default of 10000 is used.</p>
+ </attribute>
+
<attribute name="maxPostSize" required="false">
<p>The maximum size in bytes of the POST which will be handled by
the container FORM URL parameter parsing. The limit can be disabled by
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org
Re: svn commit: r1189899 - in /tomcat/tc7.0.x/trunk: ./
java/org/apache/catalina/connector/ java/org/apache/tomcat/util/buf/
java/org/apache/tomcat/util/http/ webapps/docs/ webapps/docs/config/
Posted by Konstantin Kolinko <kn...@gmail.com>.
2011/10/27 <ma...@apache.org>:
> Author: markt
> Date: Thu Oct 27 18:02:27 2011
> New Revision: 1189899
>
> URL: http://svn.apache.org/viewvc?rev=1189899&view=rev
> Log:
> Re-factor parameter parsing to improve performance
>
> Added:
> tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties (with props)
> Modified:
> tomcat/tc7.0.x/trunk/ (props changed)
> tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Connector.java
> tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/Request.java
> tomcat/tc7.0.x/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml
> tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java
> tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java
> tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/buf/StringCache.java
> tomcat/tc7.0.x/trunk/java/org/apache/tomcat/util/http/Parameters.java
> tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml
> tomcat/tc7.0.x/trunk/webapps/docs/config/ajp.xml
> tomcat/tc7.0.x/trunk/webapps/docs/config/http.xml
>
> @@ -97,15 +101,23 @@ public final class ByteChunk implements
> as most standards seem to converge, but the servlet API requires
> 8859_1, and this object is used mostly for servlets.
> */
> - public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
> + public static Charset DEFAULT_CHARSET = null;
1. The above public variable must declared as final.
> + static {
> + try {
> + DEFAULT_CHARSET = B2CConverter.getCharset("ISO-8859-1");
> + } catch (UnsupportedEncodingException e) {
> + // Should never happen since all JVMs must support ISO-8859-1
> + }
> + }
IIRC, it might be done using a helper variable in the static{} block.
public static final Charset DEFAULT_CHARSET;
static {
Charset defaultCharset = null;
try {
defaultCharset = B2CConverter.getCharset("ISO-8859-1");
} catch (UnsupportedEncodingException e) {
// Should never happen since all JVMs must support ISO-8859-1
}
DEFAULT_CHARSET = defaultCharset;
}
2) In TestParameters.java
in http://svn.apache.org/viewvc?view=revision&revision=1189876
UEncoder uencoder = new UEncoder();
field can be private final, like the others.
Best regards,
Konstantin Kolinko
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org