Author: markt Date: Thu Oct 27 17:34:58 2011 New Revision: 1189882 URL: http://svn.apache.org/viewvc?rev=1189882&view=rev Log: Refactor parameter parsing for performance.
Added: tomcat/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties Modified: tomcat/trunk/java/org/apache/catalina/connector/Connector.java tomcat/trunk/java/org/apache/catalina/connector/Request.java tomcat/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml tomcat/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java tomcat/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java tomcat/trunk/java/org/apache/tomcat/util/buf/StringCache.java tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java tomcat/trunk/webapps/docs/config/ajp.xml tomcat/trunk/webapps/docs/config/http.xml Modified: tomcat/trunk/java/org/apache/catalina/connector/Connector.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/Connector.java?rev=1189882&r1=1189881&r2=1189882&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/connector/Connector.java (original) +++ tomcat/trunk/java/org/apache/catalina/connector/Connector.java Thu Oct 27 17:34:58 2011 @@ -163,6 +163,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. */ @@ -381,14 +388,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/trunk/java/org/apache/catalina/connector/Request.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/Request.java?rev=1189882&r1=1189881&r2=1189882&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/connector/Request.java (original) +++ tomcat/trunk/java/org/apache/catalina/connector/Request.java Thu Oct 27 17:34:58 2011 @@ -2732,6 +2732,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/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml?rev=1189882&r1=1189881&r2=1189882&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml (original) +++ tomcat/trunk/java/org/apache/catalina/connector/mbeans-descriptors.xml Thu Oct 27 17:34:58 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/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java?rev=1189882&r1=1189881&r2=1189882&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/buf/ByteChunk.java Thu Oct 27 17:34:58 2011 @@ -18,6 +18,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 @@ -96,7 +100,15 @@ 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; @@ -104,7 +116,7 @@ public final class ByteChunk implements private int start=0; private int end; - private String enc; + private Charset charset; private boolean isSet=false; // XXX @@ -145,7 +157,7 @@ public final class ByteChunk implements */ public void recycle() { // buff = null; - enc=null; + charset=null; start=0; end=0; isSet=false; @@ -185,14 +197,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; + + public Charset getCharset() { + if (charset == null) { + charset = DEFAULT_CHARSET; } - return enc; + return charset; } /** @@ -512,31 +525,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/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java?rev=1189882&r1=1189881&r2=1189882&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/buf/MessageBytes.java Thu Oct 27 17:34:58 2011 @@ -129,13 +129,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/trunk/java/org/apache/tomcat/util/buf/StringCache.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/buf/StringCache.java?rev=1189882&r1=1189881&r2=1189882&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/buf/StringCache.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/buf/StringCache.java Thu Oct 27 17:34:58 2011 @@ -16,6 +16,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; @@ -298,7 +299,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; @@ -484,7 +485,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; @@ -639,7 +640,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/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties?rev=1189882&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties (added) +++ tomcat/trunk/java/org/apache/tomcat/util/http/LocalStrings.properties Thu Oct 27 17:34:58 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 Modified: tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java?rev=1189882&r1=1189881&r2=1189882&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java Thu Oct 27 17:34:58 2011 @@ -17,14 +17,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; /** * @@ -32,11 +36,14 @@ 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 ); - private final 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; @@ -47,6 +54,9 @@ public final class Parameters { String encoding=null; String queryStringEncoding=null; + private int limit = -1; + private int parameterCount = 0; + public Parameters() { // NO-OP } @@ -55,6 +65,10 @@ public final class Parameters { this.queryMB=queryMB; } + public void setLimit(int limit) { + this.limit = limit; + } + public String getEncoding() { return encoding; } @@ -74,7 +88,8 @@ public final class Parameters { } public void recycle() { - paramHashStringArray.clear(); + parameterCount = 0; + paramHashValues.clear(); didQueryParameters=false; encoding=null; decodedQuery.recycle(); @@ -90,43 +105,39 @@ public final class Parameters { 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 ) { + if(values.size() == 0) { return ""; } - return values[0]; + return values.get(0); } else { return null; } @@ -165,21 +176,14 @@ public final class Parameters { 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 ) { @@ -200,107 +204,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 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 decodeFailCount = 0; + + 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; + } + 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; } - } - if( nameEnd== -1 ) { - nameEnd=end; - } + } 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); + } } } @@ -311,35 +366,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 ) { @@ -352,20 +394,31 @@ 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(","); + ArrayList<String> values = paramHashValues.get(k); + for(String value : values) { + sb.append(value).append(","); } sb.append("\n"); } Modified: tomcat/trunk/webapps/docs/config/ajp.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/ajp.xml?rev=1189882&r1=1189881&r2=1189882&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/ajp.xml (original) +++ tomcat/trunk/webapps/docs/config/ajp.xml Thu Oct 27 17:34:58 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/trunk/webapps/docs/config/http.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/http.xml?rev=1189882&r1=1189881&r2=1189882&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/http.xml (original) +++ tomcat/trunk/webapps/docs/config/http.xml Thu Oct 27 17:34:58 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-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org