Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
Please login to access the resource
openSUSE:Evergreen:11.4
tomcat6.import5619
apache-tomcat-parameter-processing-performance....
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File apache-tomcat-parameter-processing-performance.patch of Package tomcat6.import5619
--- conf/web.xml | 21 java/org/apache/catalina/Globals.java | 11 java/org/apache/catalina/connector/Connector.java | 39 java/org/apache/catalina/connector/Request.java | 95 + java/org/apache/catalina/connector/mbeans-descriptors.xml | 4 java/org/apache/catalina/filters/FailedRequestFilter.java | 92 + java/org/apache/coyote/Request.java | 1 java/org/apache/tomcat/util/buf/B2CConverter.java | 11 java/org/apache/tomcat/util/buf/ByteChunk.java | 72 - java/org/apache/tomcat/util/buf/MessageBytes.java | 15 java/org/apache/tomcat/util/buf/StringCache.java | 7 java/org/apache/tomcat/util/http/LocalStrings.properties | 23 java/org/apache/tomcat/util/http/Parameters.java | 750 ++++---------- webapps/docs/changelog.xml | 11 webapps/docs/config/ajp.xml | 8 webapps/docs/config/filter.xml | 40 webapps/docs/config/http.xml | 8 17 files changed, 617 insertions(+), 591 deletions(-) Index: apache-tomcat-6.0.32-src/java/org/apache/coyote/Request.java =================================================================== --- apache-tomcat-6.0.32-src.orig/java/org/apache/coyote/Request.java 2011-02-02 20:07:32.000000000 +0100 +++ apache-tomcat-6.0.32-src/java/org/apache/coyote/Request.java 2012-01-05 14:11:44.479426602 +0100 @@ -73,7 +73,6 @@ parameters.setQuery(queryMB); parameters.setURLDecoder(urlDecoder); - parameters.setHeaders(headers); } Index: apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/http/LocalStrings.properties =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/http/LocalStrings.properties 2012-01-05 14:11:44.479426602 +0100 @@ -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}] +parameters.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 Index: apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/http/Parameters.java =================================================================== --- apache-tomcat-6.0.32-src.orig/java/org/apache/tomcat/util/http/Parameters.java 2011-02-02 20:07:33.000000000 +0100 +++ apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/http/Parameters.java 2012-01-05 14:54:01.081966677 +0100 @@ -19,228 +19,144 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; -import java.util.Hashtable; +import java.util.HashMap; +import java.util.Map; +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.collections.MultiMap; +import org.apache.tomcat.util.res.StringManager; /** * * @author Costin Manolache */ -public final class Parameters extends MultiMap { +public final class Parameters { - - private static 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 HashMap<String,ArrayList<String>> paramHashValues = + new HashMap<String,ArrayList<String>>(); + private boolean didQueryParameters=false; - private boolean didMerge=false; MessageBytes queryMB; - MimeHeaders headers; UDecoder urlDec; MessageBytes decodedQuery=MessageBytes.newInstance(); - - public static final int INITIAL_SIZE=4; - - // Garbage-less parameter merging. - // In a sub-request with parameters, the new parameters - // will be stored in child. When a getParameter happens, - // the 2 are merged togheter. The child will be altered - // to contain the merged values - the parent is allways the - // original request. - private Parameters child=null; - private Parameters parent=null; - private Parameters currentChild=null; String encoding=null; String queryStringEncoding=null; + private int limit = -1; + private int parameterCount = 0; + /** - * + * Is set to <code>true</code> if there were failures during parameter + * parsing. */ + private boolean parseFailed = false; + public Parameters() { - super( INITIAL_SIZE ); + // NO-OP } public void setQuery( MessageBytes queryMB ) { this.queryMB=queryMB; } - public void setHeaders( MimeHeaders headers ) { - this.headers=headers; + public void setLimit(int limit) { + this.limit = limit; + } + + public String getEncoding() { + return encoding; } public void setEncoding( String s ) { encoding=s; - if(debug>0) log( "Set encoding to " + s ); + if(log.isDebugEnabled()) { + log.debug( "Set encoding to " + s ); + } } public void setQueryStringEncoding( String s ) { queryStringEncoding=s; - if(debug>0) log( "Set query string encoding to " + s ); + if(log.isDebugEnabled()) { + log.debug( "Set query string encoding to " + s ); + } + } + + public boolean isParseFailed() { + return parseFailed; + } + + public void setParseFailed(boolean parseFailed) { + this.parseFailed = parseFailed; } public void recycle() { - super.recycle(); - paramHashStringArray.clear(); + parameterCount = 0; + paramHashValues.clear(); didQueryParameters=false; - currentChild=null; - didMerge=false; encoding=null; decodedQuery.recycle(); - } - - // -------------------- Sub-request support -------------------- - - public Parameters getCurrentSet() { - if( currentChild==null ) - return this; - return currentChild; - } - - /** Create ( or reuse ) a child that will be used during a sub-request. - All future changes ( setting query string, adding parameters ) - will affect the child ( the parent request is never changed ). - Both setters and getters will return the data from the deepest - child, merged with data from parents. - */ - public void push() { - // We maintain a linked list, that will grow to the size of the - // longest include chain. - // The list has 2 points of interest: - // - request.parameters() is the original request and head, - // - request.parameters().currentChild() is the current set. - // The ->child and parent<- links are preserved ( currentChild is not - // the last in the list ) - - // create a new element in the linked list - // note that we reuse the child, if any - pop will not - // set child to null ! - if( currentChild==null ) { - currentChild=new Parameters(); - currentChild.setURLDecoder( urlDec ); - currentChild.parent=this; - return; - } - if( currentChild.child==null ) { - currentChild.child=new Parameters(); - currentChild.setURLDecoder( urlDec ); - currentChild.child.parent=currentChild; - } // it is not null if this object already had a child - // i.e. a deeper include() ( we keep it ) - - // the head will be the new element. - currentChild=currentChild.child; - currentChild.setEncoding( encoding ); + parseFailed = false; } - /** Discard the last child. This happens when we return from a - sub-request and the parameters are locally modified. - */ - public void pop() { - if( currentChild==null ) { - throw new RuntimeException( "Attempt to pop without a push" ); - } - currentChild.recycle(); - currentChild=currentChild.parent; - // don't remove the top. - } - // -------------------- Data access -------------------- // Access to the current name/values, no side effect ( processing ). - // You must explicitely call handleQueryParameters and the post methods. + // You must explicitly call handleQueryParameters and the post methods. - // This is the original data representation ( hash of String->String[]) - - public void addParameterValues( String key, String[] newValues) { - if ( key==null ) return; - String values[]; - if (paramHashStringArray.containsKey(key)) { - String oldValues[] = (String[])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]; - } + public void addParameterValues(String key, String[] newValues) { + if (key == null) { + return; + } + ArrayList<String> values = paramHashValues.get(key); + if (values == null) { + values = new ArrayList<String>(newValues.length); + paramHashValues.put(key, values); } else { - values = newValues; + values.ensureCapacity(values.size() + newValues.length); + } + for (String newValue : newValues) { + values.add(newValue); } - - paramHashStringArray.put(key, values); } public String[] getParameterValues(String name) { handleQueryParameters(); - // sub-request - if( currentChild!=null ) { - currentChild.merge(); - return (String[])currentChild.paramHashStringArray.get(name); - } - // no "facade" - String values[]=(String[])paramHashStringArray.get(name); - return values; + ArrayList<String> values = paramHashValues.get(name); + if (values == null) { + return null; + } + return values.toArray(new String[values.size()]); } - public Enumeration getParameterNames() { + public Enumeration<String> getParameterNames() { handleQueryParameters(); - // Slow - the original code - if( currentChild!=null ) { - currentChild.merge(); - return currentChild.paramHashStringArray.keys(); - } - - // merge in child - return paramHashStringArray.keys(); + return Collections.enumeration(paramHashValues.keySet()); } - /** Combine the parameters from parent with our local ones - */ - private void merge() { - // recursive - if( debug > 0 ) { - log("Before merging " + this + " " + parent + " " + didMerge ); - log( paramsAsString()); - } - // Local parameters first - they take precedence as in spec. - handleQueryParameters(); - - // we already merged with the parent - if( didMerge ) return; - - // we are the top level - if( parent==null ) return; - - // Add the parent props to the child ( lower precedence ) - parent.merge(); - Hashtable<String,String[]> parentProps=parent.paramHashStringArray; - merge2( paramHashStringArray , parentProps); - didMerge=true; - if(debug > 0 ) - log("After " + paramsAsString()); - } - - // Shortcut. public String getParameter(String name ) { - String[] values = getParameterValues(name); + handleQueryParameters(); + 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; } @@ -256,8 +172,10 @@ if( queryMB==null || queryMB.isNull() ) return; - if( debug > 0 ) - log( "Decoding query " + decodedQuery + " " + queryStringEncoding); + if(log.isDebugEnabled()) { + log.debug("Decoding query " + decodedQuery + " " + + queryStringEncoding); + } try { decodedQuery.duplicate( queryMB ); @@ -268,63 +186,15 @@ processParameters( decodedQuery, queryStringEncoding ); } - // -------------------- - - /** Combine 2 hashtables into a new one. - * ( two will be added to one ). - * Used to combine child parameters ( RequestDispatcher's query ) - * with parent parameters ( original query or parent dispatcher ) - */ - private static void merge2(Hashtable<String,String[]> one, - Hashtable<String,String[]> two ) { - Enumeration e = two.keys(); - - while (e.hasMoreElements()) { - String name = (String) e.nextElement(); - String[] oneValue = one.get(name); - String[] twoValue = two.get(name); - String[] combinedValue; - if (twoValue == null) { - continue; - } else { - if( oneValue==null ) { - combinedValue = new String[twoValue.length]; - System.arraycopy(twoValue, 0, combinedValue, - 0, twoValue.length); - } else { - combinedValue = new String[oneValue.length + - twoValue.length]; - System.arraycopy(oneValue, 0, combinedValue, 0, - oneValue.length); - System.arraycopy(twoValue, 0, combinedValue, - oneValue.length, twoValue.length); - } - one.put(name, combinedValue); - } - } - } - - // incredibly inefficient data representation for 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[] = (String[])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; - } else { - values = new String[1]; - values[0] = value; + ArrayList<String> values = paramHashValues.get(key); + if (values == null) { + values = new ArrayList<String>(1); + paramHashValues.put(key, values); } - - - paramHashStringArray.put(key, values); + values.add(value); } public void setURLDecoder( UDecoder u ) { @@ -332,132 +202,189 @@ } // -------------------- Parameter parsing -------------------- - - // This code is not used right now - it's the optimized version - // of the above. - // we are called from a single thread - we can do it the hard way // if needed ByteChunk tmpName=new ByteChunk(); ByteChunk tmpValue=new ByteChunk(); private ByteChunk origName=new ByteChunk(); private ByteChunk origValue=new ByteChunk(); - CharChunk tmpNameC=new CharChunk(1024); - CharChunk tmpValueC=new CharChunk(1024); private static final String DEFAULT_ENCODING = "ISO-8859-1"; + private static final Charset DEFAULT_CHARSET = + Charset.forName(DEFAULT_ENCODING); + 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; + private void processParameters(byte bytes[], int start, int len, + Charset charset) { if(log.isDebugEnabled()) { try { - log.debug("Bytes: " + - new String(bytes, start, len, DEFAULT_ENCODING)); - } catch (UnsupportedEncodingException e) { - // Should never happen... - log.error("Unable to convert bytes", e); - } - } - - 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()) { - try { - log.debug("no equal " + nameStart + " " + nameEnd + " " + - new String(bytes, nameStart, nameEnd-nameStart, - DEFAULT_ENCODING) ); - } catch (UnsupportedEncodingException e) { - // Should never happen... - log.error("Unable to convert bytes", e); - } + log.debug(sm.getString("parameters.bytes", + new String(bytes, start, len, DEFAULT_CHARSET.name()))); + } catch (UnsupportedEncodingException uee) { + // Not possible. All JVMs must support ISO-8859-1 + } + } + + int decodeFailCount = 0; + + int pos = start; + int end = start + len; + + while(pos < end) { + parameterCount ++; + + if (limit > -1 && parameterCount >= limit) { + parseFailed = true; + 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( nameEnd<=nameStart ) { - StringBuilder msg = new StringBuilder("Parameters: Invalid chunk "); - // No name eg ...&=xx&... will trigger this - if (valEnd >= nameStart) { - msg.append('\''); + if (log.isDebugEnabled() && valueStart == -1) { + try { + log.debug(sm.getString("parameters.noequal", + Integer.valueOf(nameStart), + Integer.valueOf(nameEnd), + new String(bytes, nameStart, nameEnd-nameStart, + DEFAULT_CHARSET.name()))); + } catch (UnsupportedEncodingException uee) { + // Not possible. All JVMs must support ISO-8859-1 + } + } + + if (nameEnd <= nameStart ) { + if (log.isInfoEnabled()) { + if (valueEnd >= nameStart && log.isDebugEnabled()) { + String extract = null; try { - msg.append(new String(bytes, nameStart, - valEnd - nameStart, DEFAULT_ENCODING)); - } catch (UnsupportedEncodingException e) { - // Should never happen... - log.error("Unable to convert bytes", e); + extract = new String(bytes, nameStart, + valueEnd - nameStart, + DEFAULT_CHARSET.name()); + } catch (UnsupportedEncodingException uee) { + // Not possible. All JVMs must support ISO-8859-1 } - msg.append("' "); + 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.warn(msg); + parseFailed = true; 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("parameters.copyFail"), ioe); } } try { - addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) ); + String name; + String value; + + if (decodeName) { + urlDecode(tmpName); + } + tmpName.setCharset(charset); + name = tmpName.toString(); + + if (decodeValue) { + urlDecode(tmpValue); + } + tmpValue.setCharset(charset); + 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 { - 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 corrupted due to the "); - msg.append("failed decoding. Use debug level logging to "); - msg.append("see the original, non-corrupted values."); - log.warn(msg); + parseFailed = true; + 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); + } } } @@ -468,217 +395,58 @@ 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 void urlDecode(ByteChunk bc) 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; - } - - public void processParameters( char chars[], int start, int len ) { - int end=start+len; - int pos=start; - - if( debug>0 ) - log( "Chars: " + new String( chars, start, len )); - do { - boolean noEq=false; - int nameStart=pos; - int valStart=-1; - int valEnd=-1; - - int nameEnd=CharChunk.indexOf(chars, nameStart, end, '=' ); - int nameEnd2=CharChunk.indexOf(chars, nameStart, end, '&' ); - if( (nameEnd2!=-1 ) && - ( nameEnd==-1 || nameEnd > nameEnd2) ) { - nameEnd=nameEnd2; - noEq=true; - valStart=nameEnd; - valEnd=nameEnd; - if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + new String(chars, nameStart, nameEnd-nameStart) ); - } - if( nameEnd== -1 ) nameEnd=end; - - if( ! noEq ) { - valStart= (nameEnd < end) ? nameEnd+1 : end; - valEnd=CharChunk.indexOf(chars, valStart, end, '&'); - if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart; - } - - pos=valEnd+1; - - if( nameEnd<=nameStart ) { - continue; - // invalid chunk - no name, it's better to ignore - // XXX log it ? - } - - try { - tmpNameC.append( chars, nameStart, nameEnd-nameStart ); - tmpValueC.append( chars, valStart, valEnd-valStart ); - - if( debug > 0 ) - log( tmpNameC + "= " + tmpValueC); - - if( urlDec==null ) { - urlDec=new UDecoder(); - } - - urlDec.convert( tmpNameC ); - urlDec.convert( tmpValueC ); - - if( debug > 0 ) - log( tmpNameC + "= " + tmpValueC); - - addParam( tmpNameC.toString(), tmpValueC.toString() ); - } catch( IOException ex ) { - ex.printStackTrace(); - } - - tmpNameC.recycle(); - tmpValueC.recycle(); - - } while( pos<end ); - } - - public void processParameters( MessageBytes data ) { - processParameters(data, encoding); } public void processParameters( MessageBytes data, String encoding ) { if( data==null || data.isNull() || data.getLength() <= 0 ) return; - if( data.getType() == MessageBytes.T_BYTES ) { - ByteChunk bc=data.getByteChunk(); - processParameters( bc.getBytes(), bc.getOffset(), - bc.getLength(), encoding); - } else { - if (data.getType()!= MessageBytes.T_CHARS ) - data.toChars(); - CharChunk cc=data.getCharChunk(); - processParameters( cc.getChars(), cc.getOffset(), - cc.getLength()); + if( data.getType() != MessageBytes.T_BYTES ) { + data.toBytes(); } + ByteChunk bc=data.getByteChunk(); + processParameters( bc.getBytes(), bc.getOffset(), + bc.getLength(), getCharset(encoding)); } - /** Debug purpose - */ - public String paramsAsString() { - StringBuffer sb=new StringBuffer(); - Enumeration en= paramHashStringArray.keys(); - while( en.hasMoreElements() ) { - String k=(String)en.nextElement(); - sb.append( k ).append("="); - String v[]=(String[])paramHashStringArray.get( k ); - for( int i=0; i<v.length; i++ ) - sb.append( v[i] ).append(","); - sb.append("\n"); + private Charset getCharset(String encoding) { + if (encoding == null) { + return DEFAULT_CHARSET; + } + try { + return B2CConverter.getCharset(encoding); + } catch (UnsupportedEncodingException e) { + return DEFAULT_CHARSET; } - return sb.toString(); } - private static int debug=0; - private void log(String s ) { - if (log.isDebugEnabled()) - log.debug("Parameters: " + s ); - } - - // -------------------- Old code, needs rewrite -------------------- - - /** Used by RequestDispatcher + /** + * Debug purpose */ - public void processParameters( String str ) { - int end=str.length(); - int pos=0; - if( debug > 0) - log("String: " + str ); - - do { - boolean noEq=false; - int valStart=-1; - int valEnd=-1; - - int nameStart=pos; - int nameEnd=str.indexOf('=', nameStart ); - int nameEnd2=str.indexOf('&', nameStart ); - if( nameEnd2== -1 ) nameEnd2=end; - if( (nameEnd2!=-1 ) && - ( nameEnd==-1 || nameEnd > nameEnd2) ) { - nameEnd=nameEnd2; - noEq=true; - valStart=nameEnd; - valEnd=nameEnd; - if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + str.substring(nameStart, nameEnd) ); - } - - if( nameEnd== -1 ) nameEnd=end; - - if( ! noEq ) { - valStart=nameEnd+1; - valEnd=str.indexOf('&', valStart); - if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart; - } - - pos=valEnd+1; - - if( nameEnd<=nameStart ) { - continue; - } - if( debug>0) - log( "XXX " + nameStart + " " + nameEnd + " " - + valStart + " " + valEnd ); - - try { - tmpNameC.append(str, nameStart, nameEnd-nameStart ); - tmpValueC.append(str, valStart, valEnd-valStart ); - - if( debug > 0 ) - log( tmpNameC + "= " + tmpValueC); - - if( urlDec==null ) { - urlDec=new UDecoder(); - } - - urlDec.convert( tmpNameC ); - urlDec.convert( tmpValueC ); - - if( debug > 0 ) - log( tmpNameC + "= " + tmpValueC); - - addParam( tmpNameC.toString(), tmpValueC.toString() ); - } catch( IOException ex ) { - ex.printStackTrace(); + public String paramsAsString() { + StringBuilder sb = new StringBuilder(); + for (Map.Entry<String, ArrayList<String>> e : paramHashValues.entrySet()) { + sb.append(e.getKey()).append('='); + ArrayList<String> values = e.getValue(); + for (String value : values) { + sb.append(value).append(','); } - - tmpNameC.recycle(); - tmpValueC.recycle(); - - } while( pos<end ); + sb.append('\n'); + } + return sb.toString(); } - } Index: apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/buf/StringCache.java =================================================================== --- apache-tomcat-6.0.32-src.orig/java/org/apache/tomcat/util/buf/StringCache.java 2011-02-02 20:07:33.000000000 +0100 +++ apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/buf/StringCache.java 2012-01-05 14:11:44.485426810 +0100 @@ -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.Iterator; @@ -292,7 +293,7 @@ entry.name = new byte[bc.getLength()]; 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; @@ -472,7 +473,7 @@ 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; @@ -624,7 +625,7 @@ public static class ByteEntry { public byte[] name = null; - public String enc = null; + public Charset charset = null; public String value = null; public String toString() { Index: apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/buf/MessageBytes.java =================================================================== --- apache-tomcat-6.0.32-src.orig/java/org/apache/tomcat/util/buf/MessageBytes.java 2011-02-02 20:07:33.000000000 +0100 +++ apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/buf/MessageBytes.java 2012-01-05 14:11:44.486426845 +0100 @@ -21,6 +21,7 @@ import java.util.*; import java.io.Serializable; import java.io.IOException; +import java.nio.charset.Charset; /** * This class is used to represent a subarray of bytes in an HTTP message. @@ -140,13 +141,13 @@ * previous conversion is reset. * If no encoding is set, we'll use 8859-1. */ - public void setEncoding( String enc ) { - if( !byteC.isNull() ) { - // if the encoding changes we need to reset the converion results - charC.recycle(); - hasStrValue=false; - } - byteC.setEncoding(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.setCharset(charset); } /** Index: apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/buf/ByteChunk.java =================================================================== --- apache-tomcat-6.0.32-src.orig/java/org/apache/tomcat/util/buf/ByteChunk.java 2012-01-05 14:11:16.000000000 +0100 +++ apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/buf/ByteChunk.java 2012-01-05 14:51:18.760301846 +0100 @@ -19,7 +19,9 @@ 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; /* @@ -96,20 +98,25 @@ 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 final Charset DEFAULT_CHARSET; + + static { + Charset c = null; + try { + c = B2CConverter.getCharset("ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + // Should never happen since all JVMs must support ISO-8859-1 + } + DEFAULT_CHARSET = c; + } - /** Default Charset to use for interpreting byte[] as as String - */ - public static final Charset DEFAULT_CHARSET = - Charset.forName(DEFAULT_CHARACTER_ENCODING); - // byte[] private byte[] buff; private int start=0; private int end; - private String enc; + private Charset charset; private boolean isSet=false; // XXX @@ -149,7 +156,7 @@ */ public void recycle() { // buff = null; - enc=null; + charset=null; start=0; end=0; isSet=false; @@ -189,13 +196,15 @@ 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; } /** @@ -498,34 +507,15 @@ } public String toStringInternal() { - String strValue=null; - try { - Charset charset; - if (enc == null) { - charset = DEFAULT_CHARSET; - } else { - charset = B2CConverter.getCharset(enc); - } - strValue = charset.decode( - ByteBuffer.wrap(buff, start, end-start)).toString(); - /* - Does not improve the speed too much on most systems, - it's safer to use the "clasical" 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(), cb.arrayOffset(), cb.length()); } public int getInt() Index: apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/buf/B2CConverter.java =================================================================== --- apache-tomcat-6.0.32-src.orig/java/org/apache/tomcat/util/buf/B2CConverter.java 2012-01-05 14:11:16.000000000 +0100 +++ apache-tomcat-6.0.32-src/java/org/apache/tomcat/util/buf/B2CConverter.java 2012-01-05 14:11:44.492427055 +0100 @@ -110,7 +110,14 @@ convert(bb, cb, cb.getBuffer().length - cb.getEnd()); } - + /** + * Convert a buffer of bytes into a chars. + * + * @param bb Input byte buffer + * @param cb Output char buffer + * @param limit Number of bytes to convert + * @throws IOException + */ public void convert( ByteChunk bb, CharChunk cb, int limit) throws IOException { @@ -118,7 +125,7 @@ try { // read from the reader int bbLengthBeforeRead = 0; - while( limit > 0 ) { // conv.ready() ) { + while( limit > 0 ) { int size = limit < BUFFER_SIZE ? limit : BUFFER_SIZE; bbLengthBeforeRead = bb.getLength(); int cnt=conv.read( result, 0, size ); Index: apache-tomcat-6.0.32-src/java/org/apache/catalina/Globals.java =================================================================== --- apache-tomcat-6.0.32-src.orig/java/org/apache/catalina/Globals.java 2011-02-02 20:07:31.000000000 +0100 +++ apache-tomcat-6.0.32-src/java/org/apache/catalina/Globals.java 2012-01-05 14:11:44.492427055 +0100 @@ -327,6 +327,17 @@ /** + * The request attribute that is set to <code>Boolean.TRUE</code> if some request + * parameters have been ignored during request parameters parsing. It can + * happen, for example, if there is a limit on the total count of parseable + * parameters, or if parameter cannot be decoded, or any other error + * happened during parameter parsing. + */ + public static final String PARAMETER_PARSE_FAILED_ATTR = + "org.apache.catalina.parameter_parse_failed"; + + + /** * The master flag which controls strict servlet specification * compliance. */ Index: apache-tomcat-6.0.32-src/java/org/apache/catalina/filters/FailedRequestFilter.java =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ apache-tomcat-6.0.32-src/java/org/apache/catalina/filters/FailedRequestFilter.java 2012-01-05 14:11:44.494427125 +0100 @@ -0,0 +1,92 @@ +/* + * 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.catalina.filters; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +import org.apache.catalina.Globals; +import org.apache.catalina.CometEvent; +import org.apache.catalina.CometFilter; +import org.apache.catalina.CometFilterChain; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +/** + * Filter that will reject requests if there was a failure during parameter + * parsing. This filter can be used to ensure that none parameter values + * submitted by client are lost. + * + * <p> + * Note that it has side effect that it triggers parameter parsing and thus + * consumes the body for POST requests. Parameter parsing does check content + * type of the request, so there should not be problems with addresses that use + * <code>request.getInputStream()</code> and <code>request.getReader()</code>, + * if requests parsed by them do not use standard value for content mime-type. + */ +public class FailedRequestFilter extends FilterBase implements CometFilter { + + private static final Log log = LogFactory.getLog(FailedRequestFilter.class); + + @Override + protected Log getLogger() { + return log; + } + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + if (!isGoodRequest(request)) { + ((HttpServletResponse) response) + .sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + chain.doFilter(request, response); + } + + public void doFilterEvent(CometEvent event, CometFilterChain chain) + throws IOException, ServletException { + if (event.getEventType() == CometEvent.EventType.BEGIN + && !isGoodRequest(event.getHttpServletRequest())) { + event.getHttpServletResponse().sendError( + HttpServletResponse.SC_BAD_REQUEST); + event.close(); + return; + } + chain.doFilterEvent(event); + } + + private boolean isGoodRequest(ServletRequest request) { + // Trigger parsing of parameters + request.getParameter("none"); + // Detect failure + if (request.getAttribute(Globals.PARAMETER_PARSE_FAILED_ATTR) != null) { + return false; + } + return true; + } + + @Override + protected boolean isConfigProblemFatal() { + return true; + } + +} Index: apache-tomcat-6.0.32-src/java/org/apache/catalina/connector/mbeans-descriptors.xml =================================================================== --- apache-tomcat-6.0.32-src.orig/java/org/apache/catalina/connector/mbeans-descriptors.xml 2011-02-02 20:07:31.000000000 +0100 +++ apache-tomcat-6.0.32-src/java/org/apache/catalina/connector/mbeans-descriptors.xml 2012-01-05 14:11:44.494427125 +0100 @@ -118,6 +118,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"/> Index: apache-tomcat-6.0.32-src/java/org/apache/catalina/connector/Request.java =================================================================== --- apache-tomcat-6.0.32-src.orig/java/org/apache/catalina/connector/Request.java 2012-01-05 14:11:16.000000000 +0100 +++ apache-tomcat-6.0.32-src/java/org/apache/catalina/connector/Request.java 2012-01-05 14:11:44.499427298 +0100 @@ -905,6 +905,11 @@ return (requestDispatcherPath == null) ? getRequestPathMB().toString() : requestDispatcherPath.toString(); + } else if (name.equals(Globals.PARAMETER_PARSE_FAILED_ATTR)) { + if (coyoteRequest.getParameters().isParseFailed()) { + return Boolean.TRUE; + } + return null; } Object attr=attributes.get(name); @@ -961,12 +966,12 @@ * <ul> * <li>{@link Globals.DISPATCHER_TYPE_ATTR}</li> * <li>{@link Globals.DISPATCHER_REQUEST_PATH_ATTR}</li> - * <li>{@link Globals.ASYNC_SUPPORTED_ATTR}</li> * <li>{@link Globals.CERTIFICATES_ATTR} (SSL connections only)</li> * <li>{@link Globals.CIPHER_SUITE_ATTR} (SSL connections only)</li> * <li>{@link Globals.KEY_SIZE_ATTR} (SSL connections only)</li> * <li>{@link Globals.SSL_SESSION_ID_ATTR} (SSL connections only)</li> * <li>{@link Globals.SSL_SESSION_MGR_ATTR} (SSL connections only)</li> + * <li>{@link Globals#PARAMETER_PARSE_FAILED_ATTR}</li> * </ul> * The underlying connector may also expose request attributes. These all * have names starting with "org.apache.tomcat" and include: @@ -2550,6 +2555,8 @@ 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 @@ -2590,53 +2597,61 @@ if (!("application/x-www-form-urlencoded".equals(contentType))) return; + boolean success = false; int len = getContentLength(); - if (len > 0) { - int maxPostSize = connector.getMaxPostSize(); - if ((maxPostSize > 0) && (len > maxPostSize)) { - if (context.getLogger().isDebugEnabled()) { - context.getLogger().debug( - sm.getString("coyoteRequest.postTooLarge")); + try { + if (len > 0) { + int maxPostSize = connector.getMaxPostSize(); + if ((maxPostSize > 0) && (len > maxPostSize)) { + if (context.getLogger().isDebugEnabled()) { + context.getLogger().debug( + sm.getString("coyoteRequest.postTooLarge")); + } + return; } - return; - } - byte[] formData = null; - if (len < CACHED_POST_LEN) { - if (postData == null) - postData = new byte[CACHED_POST_LEN]; - formData = postData; - } else { - formData = new byte[len]; - } - try { - if (readPostBody(formData, len) != len) { + byte[] formData = null; + if (len < CACHED_POST_LEN) { + if (postData == null) + postData = new byte[CACHED_POST_LEN]; + formData = postData; + } else { + formData = new byte[len]; + } + try { + if (readPostBody(formData, len) != len) { + return; + } + } catch (IOException e) { + // Client disconnect + if (context.getLogger().isDebugEnabled()) { + context.getLogger().debug( + sm.getString("coyoteRequest.parseParameters"), e); + } return; } - } catch (IOException e) { - // Client disconnect - if (context.getLogger().isDebugEnabled()) { - context.getLogger().debug( - sm.getString("coyoteRequest.parseParameters"), e); + parameters.processParameters(formData, 0, len); + } else if ("chunked".equalsIgnoreCase( + coyoteRequest.getHeader("transfer-encoding"))) { + byte[] formData = null; + try { + formData = readChunkedPostBody(); + } catch (IOException e) { + // Client disconnect + if (context.getLogger().isDebugEnabled()) { + context.getLogger().debug( + sm.getString("coyoteRequest.parseParameters"), e); + } + return; } - return; - } - parameters.processParameters(formData, 0, len); - } else if ("chunked".equalsIgnoreCase( - coyoteRequest.getHeader("transfer-encoding"))) { - byte[] formData = null; - try { - formData = readChunkedPostBody(); - } catch (IOException e) { - // Client disconnect - if (context.getLogger().isDebugEnabled()) { - context.getLogger().debug( - sm.getString("coyoteRequest.parseParameters"), e); + if (formData != null) { + parameters.processParameters(formData, 0, formData.length); } - return; } - if (formData != null) { - parameters.processParameters(formData, 0, formData.length); + success = true; + } finally { + if (!success) { + parameters.setParseFailed(true); } } Index: apache-tomcat-6.0.32-src/java/org/apache/catalina/connector/Connector.java =================================================================== --- apache-tomcat-6.0.32-src.orig/java/org/apache/catalina/connector/Connector.java 2011-02-02 20:07:31.000000000 +0100 +++ apache-tomcat-6.0.32-src/java/org/apache/catalina/connector/Connector.java 2012-01-05 14:11:44.500427333 +0100 @@ -192,6 +192,13 @@ /** + * 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. */ @@ -502,14 +509,34 @@ } - /** - * 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; + } /** Index: apache-tomcat-6.0.32-src/conf/web.xml =================================================================== --- apache-tomcat-6.0.32-src.orig/conf/web.xml 2011-02-02 20:07:31.000000000 +0100 +++ apache-tomcat-6.0.32-src/conf/web.xml 2012-01-05 14:11:44.500427333 +0100 @@ -421,6 +421,19 @@ <!-- ================== Built In Filter Definitions ===================== --> + <!-- A filter that triggers request parameters parsing and rejects the --> + <!-- request if some parameters were skipped because of parsing errors or --> + <!-- request size limitations. --> +<!-- + <filter> + <filter-name>failedRequestFilter</filter-name> + <filter-class> + org.apache.catalina.filters.FailedRequestFilter + </filter-class> + </filter> +--> + + <!-- NOTE: An SSI Servlet is also available as an alternative SSI --> <!-- implementation. Use either the Servlet or the Filter but NOT both. --> <!-- --> @@ -481,6 +494,14 @@ <!-- ==================== Built In Filter Mappings ====================== --> + <!-- The mapping for the Failed Request Filter --> +<!-- + <filter-mapping> + <filter-name>failedRequestFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> +--> + <!-- The mapping for the SSI Filter --> <!-- <filter-mapping> Index: apache-tomcat-6.0.32-src/webapps/docs/changelog.xml =================================================================== --- apache-tomcat-6.0.32-src.orig/webapps/docs/changelog.xml 2012-01-05 14:11:17.000000000 +0100 +++ apache-tomcat-6.0.32-src/webapps/docs/changelog.xml 2012-01-05 14:11:44.504427473 +0100 @@ -571,6 +571,17 @@ <update> Improve documentation of database connection factory. (rjung) </update> + <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. Excessive parameters are ignored. + Note that <code>FailedRequestFilter</code> can be used to reject the + request if some parameters were ignored. (markt/kkolinko) + </add> + <add> + New filter <code>FailedRequestFilter</code> that will reject a request + if there were errors during HTTP parameter parsing. (kkolinko) + </add> </changelog> </subsection> <subsection name="Other"> Index: apache-tomcat-6.0.32-src/webapps/docs/config/filter.xml =================================================================== --- apache-tomcat-6.0.32-src.orig/webapps/docs/config/filter.xml 2011-02-02 20:07:33.000000000 +0100 +++ apache-tomcat-6.0.32-src/webapps/docs/config/filter.xml 2012-01-05 14:11:44.508427613 +0100 @@ -112,6 +112,46 @@ </section> +<section name="Failed Request Filter"> + + <subsection name="Introduction"> + + <p>This filter triggers parameters parsing in a request and rejects the + request if some parameters were skipped during parameter parsing because + of parsing errors or request size limitations (such as + <code>maxParameterCount</code> attribute in a + <a href="http.html">Connector</a>). + This filter can be used to ensure that none parameter values submitted by + client are lost.</p> + + <p>Note that parameter parsing may consume the body of an HTTP request, so + caution is needed if the servlet protected by this filter uses + <code>request.getInputStream()</code> or <code>request.getReader()</code> + calls. In general the risk of breaking a web application by adding this + filter is not so high, because parameter parsing does check content type + of the request before consuming the request body.</p> + + <p>The request is rejected with HTTP status code 400 (Bad Request).</p> + + </subsection> + + <subsection name="Filter Class Name"> + + <p>The filter class name for the Failed Request Filter is + <strong><code>org.apache.catalina.filters.FailedRequestFilter</code> + </strong>.</p> + + </subsection> + + <subsection name="Initialisation parameters"> + + <p>The Failed Request Filter does not support any initialization parameters.</p> + + </subsection> + +</section> + + </body> Index: apache-tomcat-6.0.32-src/webapps/docs/config/ajp.xml =================================================================== --- apache-tomcat-6.0.32-src.orig/webapps/docs/config/ajp.xml 2011-02-02 20:07:33.000000000 +0100 +++ apache-tomcat-6.0.32-src/webapps/docs/config/ajp.xml 2012-01-05 14:11:44.508427613 +0100 @@ -93,6 +93,14 @@ By default, DNS lookups are enabled.</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. Note that + <code>FailedRequestFilter</code> <a href="filter.html">filter</a> can be + used to reject requests that hit the limit.</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 feature can be disabled by Index: apache-tomcat-6.0.32-src/webapps/docs/config/http.xml =================================================================== --- apache-tomcat-6.0.32-src.orig/webapps/docs/config/http.xml 2011-02-02 20:07:33.000000000 +0100 +++ apache-tomcat-6.0.32-src/webapps/docs/config/http.xml 2012-01-05 14:11:44.509427647 +0100 @@ -100,6 +100,14 @@ By default, DNS lookups are enabled.</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. Note that + <code>FailedRequestFilter</code> <a href="filter.html">filter</a> can be + used to reject requests that hit the limit.</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
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor