remm        01/01/29 20:14:49

  Modified:    catalina/src/share/org/apache/catalina/connector
                        HttpRequestBase.java
               catalina/src/share/org/apache/catalina/core
                        ApplicationHttpRequest.java
               catalina/src/share/org/apache/catalina/util RequestUtil.java
  Log:
  - Merge Tim Tye patch.
  - This patch addresses the decoding of parameters in a POST request. Both
    the parameters on the URL and in the request body had the character
    decoding done before the url-form decoding (which is incorrect).
  - The patch will have a small impact performance wise. Some modifications in
    the connector API will be introduced in the TC 4.1 timeframe to avoid any
    unnecessary character conversion.
  
  Revision  Changes    Path
  1.15      +11 -19    
jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/HttpRequestBase.java
  
  Index: HttpRequestBase.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/HttpRequestBase.java,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -r1.14 -r1.15
  --- HttpRequestBase.java      2001/01/18 01:39:53     1.14
  +++ HttpRequestBase.java      2001/01/30 04:14:49     1.15
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/HttpRequestBase.java,v
 1.14 2001/01/18 01:39:53 remm Exp $
  - * $Revision: 1.14 $
  - * $Date: 2001/01/18 01:39:53 $
  + * $Header: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/HttpRequestBase.java,v
 1.15 2001/01/30 04:14:49 remm Exp $
  + * $Revision: 1.15 $
  + * $Date: 2001/01/30 04:14:49 $
    *
    * ====================================================================
    *
  @@ -98,7 +98,7 @@
    * be implemented.
    *
    * @author Craig R. McClanahan
  - * @version $Revision: 1.14 $ $Date: 2001/01/18 01:39:53 $
  + * @version $Revision: 1.15 $ $Date: 2001/01/30 04:14:49 $
    */
   
   public class HttpRequestBase
  @@ -577,15 +577,14 @@
               results = new ParameterMap();
           results.setLocked(false);
   
  +        String encoding = getCharacterEncoding();
  +
        // Parse any parameters specified in the query string
        String queryString = getQueryString();
  -     if ((queryString != null) && (queryString.length() > 0)) {
  -         try {
  -             RequestUtil.parseParameters(results, queryString, true);
  -         } catch (Throwable t) {
  -             ;
  -         }
  -     }
  +        try {
  +            RequestUtil.parseParameters(results, queryString, encoding);
  +        } catch (Throwable t) {
  +        }
   
        // Parse any parameters specified in the input stream
           String contentType = getContentType();
  @@ -607,14 +606,7 @@
                       len += next;
                   }
                is.close();
  -             String data = null;
  -             String encoding = getCharacterEncoding();
  -             if (encoding == null)
  -                 RequestUtil.parseParameters
  -                        (results, new String(buf), false);
  -             else
  -                 RequestUtil.parseParameters
  -                        (results, new String(buf, encoding), false);
  +                RequestUtil.parseParameters(results, buf, encoding);
            } catch (Throwable t) {
                ;
            }
  
  
  
  1.3       +11 -5     
jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/ApplicationHttpRequest.java
  
  Index: ApplicationHttpRequest.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/ApplicationHttpRequest.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ApplicationHttpRequest.java       2000/11/15 00:52:50     1.2
  +++ ApplicationHttpRequest.java       2001/01/30 04:14:49     1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/ApplicationHttpRequest.java,v
 1.2 2000/11/15 00:52:50 remm Exp $
  - * $Revision: 1.2 $
  - * $Date: 2000/11/15 00:52:50 $
  + * $Header: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/ApplicationHttpRequest.java,v
 1.3 2001/01/30 04:14:49 remm Exp $
  + * $Revision: 1.3 $
  + * $Date: 2001/01/30 04:14:49 $
    *
    * ====================================================================
    *
  @@ -92,7 +92,7 @@
    * keep these two classes in synchronization when making changes!
    *
    * @author Craig R. McClanahan
  - * @version $Revision: 1.2 $ $Date: 2000/11/15 00:52:50 $
  + * @version $Revision: 1.3 $ $Date: 2001/01/30 04:14:49 $
    */
   
   class ApplicationHttpRequest extends HttpServletRequestWrapper {
  @@ -450,7 +450,13 @@
            return;
   
        HashMap queryParameters = new HashMap();
  -     RequestUtil.parseParameters(queryParameters, queryString, true);
  +        String encoding = getCharacterEncoding();
  +        try {
  +         RequestUtil.parseParameters
  +                (queryParameters, queryString, encoding);
  +        } catch (Exception e) {
  +            ;
  +        }
        synchronized (parameters) {
            Iterator keys = parameters.keySet().iterator();
            while (keys.hasNext()) {
  
  
  
  1.12      +139 -127  
jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/util/RequestUtil.java
  
  Index: RequestUtil.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/util/RequestUtil.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- RequestUtil.java  2000/12/17 01:03:58     1.11
  +++ RequestUtil.java  2001/01/30 04:14:49     1.12
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/util/RequestUtil.java,v
 1.11 2000/12/17 01:03:58 craigmcc Exp $
  - * $Revision: 1.11 $
  - * $Date: 2000/12/17 01:03:58 $
  + * $Header: 
/home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/util/RequestUtil.java,v
 1.12 2001/01/30 04:14:49 remm Exp $
  + * $Revision: 1.12 $
  + * $Date: 2001/01/30 04:14:49 $
    *
    * ====================================================================
    *
  @@ -64,8 +64,7 @@
   
   package org.apache.catalina.util;
   
  -
  -import java.net.URLDecoder;
  +import java.io.UnsupportedEncodingException;
   import java.text.SimpleDateFormat;
   import java.util.ArrayList;
   import java.util.Date;
  @@ -78,7 +77,8 @@
    * General purpose request parsing and encoding utility methods.
    *
    * @author Craig R. McClanahan
  - * @version $Revision: 1.11 $ $Date: 2000/12/17 01:03:58 $
  + * @author Tim Tye
  + * @version $Revision: 1.12 $ $Date: 2001/01/30 04:14:49 $
    */
   
   public final class RequestUtil {
  @@ -230,144 +230,156 @@
        *
        * @exception IllegalArgumentException if the data is malformed
        */
  -    public static void parseParameters(Map map, String data, 
  -                                       boolean urlParameters) {
  -
  -        if ((data == null) || (data.length() < 1))
  -         return;
  -
  -     // Initialize the variables we will require
  -     StringParser parser = new StringParser(data);
  -     boolean first = true;
  -     int nameStart = 0;
  -     int nameEnd = 0;
  -     int valueStart = 0;
  -     int valueEnd = 0;
  -     String name = null;
  -     String value = null;
  -     String oldValues[] = null;
  -     String newValues[] = null;
  -
  -     // Loop through the "name=value" entries in the input data
  -     while (true) {
  -
  -         // Extract the name and value components
  -         if (first)
  -             first = false;
  -         else
  -             parser.advance();
  -         nameStart = parser.getIndex();
  -         nameEnd = parser.findChar('=');
  -         parser.advance();
  -         valueStart = parser.getIndex();
  -         valueEnd = parser.findChar('&');
  -         name = parser.extract(nameStart, nameEnd);
  -         value = parser.extract(valueStart, valueEnd);
  -
  -         // A zero-length name means we are done
  -         if (name.length() < 1)
  -             break;
  -
  -            // Decode the name and value if required
  -            if ((name.indexOf('%') >= 0) || (name.indexOf('+') >= 0)) {
  -                try {
  -                    if (urlParameters) {
  -                        name = URLDecode(name);
  -                    } else {
  -                        name = URLDecoder.decode(name);
  -                    }
  -                } catch (Throwable t) {
  -                    ;
  -                }
  -            }
  -            if ((value.indexOf('%') >= 0) || (value.indexOf('+') >= 0)) {
  -                try {
  -                    if (urlParameters) {
  -                        value = URLDecode(value);
  -                    } else {
  -                        value = URLDecoder.decode(value);
  -                    }
  -                } catch (Throwable t) {
  -                    ;
  -                }
  -            }
  -
  -         // Create or update the array of values for this name
  -         oldValues = (String[]) map.get(name);
  -         if (oldValues == null)
  -             oldValues = new String[0];
  -         newValues = new String[oldValues.length + 1];
  -         System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
  -         newValues[oldValues.length] = value;
  -         map.put(name, newValues);
  -
  -     }
  -
  +    public static void parseParameters(Map map, String data, String encoding) 
  +        throws UnsupportedEncodingException {
  +        
  +        if ((data != null) && (data.length() > 0)) {
  +            int len = data.length();
  +            byte[] bytes = new byte[len];
  +            data.getBytes(0, len, bytes, 0);
  +            parseParameters(map, bytes, encoding);
  +        }
  +        
       }
   
   
       /**
        * Decode and return the specified URL-encoded String.
  +     * When the byte array is converted to a string, the system default 
  +     * character encoding is used...  This may be different than some other
  +     * servers.
        *
        * @param str The url-encoded string
        *
        * @exception IllegalArgumentException if a '%' character is not followed
        * by a valid 2-digit hexadecimal number
        */
  -    public static String URLDecode(String str)
  -     throws IllegalArgumentException {
  +    public static String URLDecode(String str) {
  +        
  +     if (str != null) {
  +            int len = str.length();
  +            byte[] bytes = new byte[len];
  +            str.getBytes(0, len, bytes, 0);
  +            int ix = 0;
  +            int ox = 0;
  +            
  +            while (ix < len) {
  +                byte b = bytes[ix++];     // Get byte to test
  +                if (b == '+') {
  +                    b = (byte)' ';
  +                } else if (b == '%') {
  +                    b = (byte) ((convertHexDigit(bytes[ix++]) << 4)
  +                                + convertHexDigit(bytes[ix++]));
  +                } 
  +                bytes[ox++] = b;
  +            }
  +            return new String(bytes, 0, ox);
  +        }
  +        return null;
  +        
  +    }
   
  -     if (str == null)
  -         return (null);
   
  -     StringBuffer dec = new StringBuffer();
  -     int pos = 0;
  -     int len = str.length();
  -     dec.ensureCapacity(str.length());
  -
  -     while (pos < len) {
  -         int lookahead;      // Look-ahead position
  -
  -         // Look ahead to the next URLencoded metacharacter, if any
  -         for (lookahead = pos; lookahead < len; lookahead++) {
  -             char ch = str.charAt(lookahead);
  -             if ((ch == '+') || (ch == '%'))
  -                 break;
  -         }
  +    /**
  +     * Convert a byte character value to hexidecimal digit value.
  +     *
  +     * @param b the character value byte
  +     */
  +    private static byte convertHexDigit( byte b ) {
  +        if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
  +        if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
  +        if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
  +        return 0;
  +    }
   
  -         // If there were non-metacharacters, copy them as a block
  -         if (lookahead > pos) {
  -             dec.append(str.substring(pos, lookahead));
  -             pos = lookahead;
  -         }
   
  -         // Shortcut out if we are at the end of the string
  -         if (pos >= len)
  -             break;
  -
  -         // Process the next metacharacter
  -         char meta = str.charAt(pos);
  -         if (meta == '+') {
  -             dec.append(' ');
  -             pos++;
  -         } else if (meta == '%') {
  -             try {
  -                 dec.append((char) Integer.parseInt
  -                            (str.substring(pos+1, pos+3), 16));
  -             } catch (NumberFormatException e) {
  -                 throw new IllegalArgumentException
  -                     ("Invalid hexadecimal '" + str.substring(pos+1, pos+3)
  -                      + " in URLencoded string");
  -             } catch (StringIndexOutOfBoundsException e) {
  -                 throw new IllegalArgumentException
  -                     ("Invalid unescaped '%' in URLcoded string");
  -             }
  -             pos += 3;
  -         }
  -     }
  -     return (dec.toString());
  +    /**
  +     * Put name value pair in map.
  +     *
  +     * @param b the character value byte
  +     *
  +     * Put name and value pair in map.  When name already exist, add value 
  +     * to array of values.
  +     */
  +    private static void putMapEntry( Map map, String name, String value) {
  +        String[] newValues = null;
  +        String[] oldValues = (String[]) map.get(name);
  +        if (oldValues == null) {
  +            newValues = new String[1];
  +            newValues[0] = value;
  +        } else {
  +            newValues = new String[oldValues.length + 1];
  +            System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
  +            newValues[oldValues.length] = value;
  +        }
  +        map.put(name, newValues);
  +    }
  +
  +
  +    /**
  +     * Append request parameters from the specified String to the specified
  +     * Map.  It is presumed that the specified Map is not accessed from any
  +     * other thread, so no synchronization is performed.
  +     * <p>
  +     * <strong>IMPLEMENTATION NOTE</strong>:  URL decoding is performed
  +     * individually on the parsed name and value elements, rather than on
  +     * the entire query string ahead of time, to properly deal with the case
  +     * where the name or value includes an encoded "=" or "&" character
  +     * that would otherwise be interpreted as a delimiter.
  +     *
  +     * NOTE: byte array data is modified by this method.  Caller beware.
  +     *
  +     * @param map Map that accumulates the resulting parameters
  +     * @param data Input string containing request parameters
  +     * @param urlParameters true if we're parsing parameters on the URL
  +     *
  +     * @exception UnsupportedEncodingException if the data is malformed
  +     */
  +    public static void parseParameters(Map map, byte[] data, String encoding) 
  +        throws UnsupportedEncodingException {
   
  +        if (data != null && data.length > 0) {
  +            int    pos = 0;
  +            int    ix = 0;
  +            int    ox = 0;
  +            String key = null;
  +            String value = null;
  +            while (ix < data.length) {
  +                byte c = data[ix++];
  +                switch (c) {
  +                case '&':
  +                    value = new String(data, 0, ox, encoding);
  +                    if (key != null) {
  +                        putMapEntry(map, key, value);
  +                        key = null;
  +                    }
  +                    ox = 0;
  +                    break;
  +                case '=':
  +                    key = new String(data, 0, ox, encoding);
  +                    ox = 0;
  +                    break;
  +                case '+':
  +                    data[ox++] = (byte)' ';
  +                    break;
  +                case '%':
  +                    data[ox++] = (byte)((convertHexDigit(data[ix++]) << 4)
  +                                    + convertHexDigit(data[ix++]));
  +                    break;
  +                default:
  +                    data[ox++] = c;
  +                }
  +            }
  +            //The last value does not end in '&'.  So save it now.
  +            if (key != null) {
  +                value = new String(data, 0, ox, encoding);
  +                putMapEntry(map, key, value);
  +            }
  +        }
  +
       }
  +
  +
   
   }
   
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, email: [EMAIL PROTECTED]

Reply via email to