cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From vgritse...@apache.org
Subject svn commit: r577497 - in /cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src: main/java/org/apache/cocoon/servletservice/util/ test/java/org/apache/cocoon/servletservice/ test/java/org/apache/cocoon/servletservice/util/
Date Thu, 20 Sep 2007 01:44:06 GMT
Author: vgritsenko
Date: Wed Sep 19 18:44:05 2007
New Revision: 577497

URL: http://svn.apache.org/viewvc?rev=577497&view=rev
Log:
Fix several issues with request query string parsing.
Implement getParametersMap.
Remove insane implementation of getParameterValues :)

Added:
    cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/util/
    cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/util/RequestParametersTestCase.java
  (with props)
Modified:
    cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/main/java/org/apache/cocoon/servletservice/util/RequestParameters.java
    cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/ServletServiceContextTestCase.java

Modified: cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/main/java/org/apache/cocoon/servletservice/util/RequestParameters.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/main/java/org/apache/cocoon/servletservice/util/RequestParameters.java?rev=577497&r1=577496&r2=577497&view=diff
==============================================================================
--- cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/main/java/org/apache/cocoon/servletservice/util/RequestParameters.java
(original)
+++ cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/main/java/org/apache/cocoon/servletservice/util/RequestParameters.java
Wed Sep 19 18:44:05 2007
@@ -19,76 +19,26 @@
 import org.apache.commons.collections.iterators.IteratorEnumeration;
 
 import java.io.Serializable;
-import java.util.ArrayList;
+import java.io.UnsupportedEncodingException;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.StringTokenizer;
 
 /**
  * This class is used by the <code>RequestWrapper</code>. It parses
- * a query string and creates a parameter representation required
- * for the <code>Request</code> object.
+ * a query string, decodes request parameters, and creates a parameter map
+ * ready for use by the <code>Request</code> object.
  *
  * @version $Id$
  */
 public final class RequestParameters implements Serializable {
 
     /**
-     * The parameter names are the keys and the value is a List object
+     * The parameter names are the keys and the value is a String array object
      */
-    private Map names;
-
-    /**
-     * Decodes URL encoded string
-     *
-     * @param s URL encoded string
-     * @return decoded string
-     */
-    private String decode(String s) {
-        StringBuffer sb = new StringBuffer();
-        for (int i = 0; i < s.length(); i++) {
-            char c = s.charAt(i);
-            switch (c) {
-                case '+':
-                    sb.append(' ');
-                    break;
-
-                case '%':
-                    try {
-                        if (s.charAt(i + 1) == 'u') {
-                            // working with multi-byte symbols in format %uXXXX
-                            sb.append((char) Integer.parseInt(s.substring(i + 2, i + 6),
16));
-                            i += 5; // 4 digits and 1 symbol u
-                        } else {
-                            // FIXME: w3c recommends to use UTF-8 instead of platform default
encoding
-                            // http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars
-
-                            // working with single-byte symbols in format %YY
-                            sb.append((char) Integer.parseInt(s.substring(i + 1, i + 3),
16));
-                            i += 2;
-                        }
-                    } catch (NumberFormatException e) {
-                        throw new IllegalArgumentException();
-                    } catch (StringIndexOutOfBoundsException e) {
-                        String rest = s.substring(i);
-                        sb.append(rest);
-                        if (rest.length() == 2) {
-                            i++;
-                        }
-                    }
-
-                    break;
-
-                default:
-                    sb.append(c);
-                    break;
-            }
-        }
-
-        return sb.toString();
-    }
+    private final Map values;
 
     /**
      * Construct a new object from a queryString
@@ -96,7 +46,8 @@
      * @param queryString request query string
      */
     public RequestParameters(String queryString) {
-        this.names = new HashMap(5);
+        this.values = new HashMap(5);
+        
         if (queryString != null) {
             StringTokenizer st = new StringTokenizer(queryString, "&");
             while (st.hasMoreTokens()) {
@@ -105,6 +56,8 @@
                 if (pos != -1) {
                     setParameter(decode(pair.substring(0, pos)),
                                  decode(pair.substring(pos + 1, pair.length())));
+                } else {
+                    setParameter(decode(pair), "");
                 }
             }
         }
@@ -118,14 +71,17 @@
      * @param value  The value of the parameter.
      */
     private void setParameter(String name, String value) {
-        ArrayList list;
-        if (names.containsKey(name)) {
-            list = (ArrayList) names.get(name);
+        String[] values = (String[]) this.values.get(name);
+        if (values == null) {
+            values = new String[] { value };
         } else {
-            list = new ArrayList(3);
-            names.put(name, list);
+            String[] v2 = new String[values.length + 1];
+            System.arraycopy(values, 0, v2, 0, values.length);
+            v2[values.length] = value;
+            values = v2;
         }
-        list.add(value);
+
+        this.values.put(name, values);
     }
 
     /**
@@ -136,8 +92,9 @@
      *               or <CODE>null</CODE>
      */
     public String getParameter(String name) {
-        if (names.containsKey(name)) {
-            return (String) ((ArrayList) names.get(name)).get(0);
+        String[] values = (String[]) this.values.get(name);
+        if (values != null) {
+            return values[0];
         }
 
         return null;
@@ -152,8 +109,9 @@
      *               or <CODE>defaultValue</CODE>
      */
     public String getParameter(String name, String defaultValue) {
-        if (names.containsKey(name)) {
-            return (String) ((ArrayList) names.get(name)).get(0);
+        String[] values = (String[]) this.values.get(name);
+        if (values != null) {
+            return values[0];
         }
 
         return defaultValue;
@@ -167,25 +125,7 @@
      *               is not defined.
      */
     public String[] getParameterValues(String name) {
-        if (names.containsKey(name)) {
-            String values[] = null;
-            ArrayList list = (ArrayList) names.get(name);
-            Iterator iter = list.iterator();
-            while (iter.hasNext()) {
-                if (values == null) {
-                    values = new String[1];
-                } else {
-                    String[] copy = new String[values.length + 1];
-                    System.arraycopy(values, 0, copy, 0, values.length);
-                    values = copy;
-                }
-                values[values.length - 1] = (String) iter.next();
-            }
-
-            return values;
-        }
-
-        return null;
+        return (String[]) values.get(name);
     }
 
     /**
@@ -194,6 +134,97 @@
      * @return  Enumeration for the (String) parameter names.
      */
     public Enumeration getParameterNames() {
-        return new IteratorEnumeration(names.keySet().iterator());
+        return new IteratorEnumeration(values.keySet().iterator());
+    }
+
+    /**
+     * Get map of parameter names to String array values
+     *
+     * @return map of parameter values
+     */
+    public Map getParameterMap() {
+        return Collections.unmodifiableMap(values);
+    }
+
+    /**
+     * Decodes URL encoded string. Supports decoding of '+', '%XX' encoding,
+     * and '%uXXXX' encoding.
+     *
+     * @param s URL encoded string
+     * @return decoded string
+     */
+    private static String decode(String s) {
+        byte[] decoded = new byte[s.length() / 3 + 1];
+        int decodedLength = 0;
+
+        final int length = s.length();
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < length; ) {
+            char c = s.charAt(i);
+            switch (c) {
+                case '+':
+                    sb.append(' ');
+                    i++;
+                    break;
+
+                case '%':
+                    // Check if this is a non-standard %u9999 encoding
+                    // (see COCOON-1950)
+                    try {
+                        if (s.charAt(i + 1) == 'u') {
+                            sb.append((char) Integer.parseInt(s.substring(i + 2, i + 6),
16));
+                            i += 6;
+                            break;
+                        }
+                    } catch (NumberFormatException e) {
+                        throw new IllegalArgumentException("Invalid query string. " +
+                                                           "Illegal hex characters in pattern
%" + s.substring(i + 1, i + 6));
+                    } catch (StringIndexOutOfBoundsException e) {
+                        throw new IllegalArgumentException("Invalid query string. " +
+                                                           "% character should be followed
by 2 hexadecimal characters.");
+                    }
+
+                    // Process sequence of %XX encoded bytes in one go since several bytes
can represent
+                    // single character.
+                    while (i < length && s.charAt(i) == '%') {
+                        if (i + 2 >= length) {
+                            throw new IllegalArgumentException("Invalid query string. " +
+                                                               "% character should be followed
by 2 hexadecimal characters.");
+                        }
+
+                        // If found %u9999, rollback '%' and finish this loop
+                        if (s.charAt(i + 1) == 'u') {
+                            i--;
+                            break;
+                        }
+
+                        try {
+                            decoded[decodedLength++] = (byte) Integer.parseInt(s.substring(i
+ 1, i + 3), 16);
+                        } catch (NumberFormatException e) {
+                            throw new IllegalArgumentException("Invalid query string. " +
+                                                               "Illegal hex characters in
pattern %" + s.substring(i + 1, i + 3));
+                        }
+                        i += 3;
+                    }
+                    if (decodedLength > 0) {
+                        try {
+                            sb.append(new String(decoded, 0, decodedLength, "UTF-8"));
+                        } catch (UnsupportedEncodingException e) {
+                            // The situation that UTF-8 is not supported is quite theoretical,
so throw a runtime exception
+                            throw new RuntimeException("Problem in decode: UTF-8 encoding
not supported.");
+                        }
+                        decodedLength = 0;
+                    }
+
+                    break;
+
+                default:
+                    sb.append(c);
+                    i++;
+                    break;
+            }
+        }
+
+        return sb.toString();
     }
 }

Modified: cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/ServletServiceContextTestCase.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/ServletServiceContextTestCase.java?rev=577497&r1=577496&r2=577497&view=diff
==============================================================================
--- cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/ServletServiceContextTestCase.java
(original)
+++ cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/ServletServiceContextTestCase.java
Wed Sep 19 18:44:05 2007
@@ -35,6 +35,9 @@
 import org.apache.cocoon.servletservice.util.BlockCallHttpServletRequest;
 import org.apache.cocoon.servletservice.util.BlockCallHttpServletResponse;
 
+/**
+ * @version $Id$
+ */
 public class ServletServiceContextTestCase extends TestCase {
     
     private ServletServiceContext mainContext;
@@ -58,7 +61,6 @@
         this.mainContext = new ServletServiceContext();
         
         request = new BlockCallHttpServletRequest(new URI("dummy"), null);
-        request.setMethod("GET");
         response = new BlockCallHttpServletResponse();
         
         //creating ServletContexts
@@ -70,7 +72,7 @@
     /**
      * Tests basic connection to the servlet.
      * 
-     * @throws Exception
+     * @throws Exception if test fails
      */
     public void testBasicConnection() throws Exception {
         servletA = new HttpServlet() {
@@ -100,7 +102,7 @@
      *       |
      *    ServletA
      * </pre>
-     * @throws Exception
+     * @throws Exception if test fails
      */
     public void testExplicitSuperCall() throws Exception {
         servletA = new HttpServlet() {
@@ -147,7 +149,7 @@
      *    ServletA
      * </pre>
      * 
-     * @throws Exception
+     * @throws Exception if test fails
      */
     public void testContextInServletCalledFromExplicitSuperCall() throws Exception {
         servletA = new HttpServlet() {
@@ -206,7 +208,7 @@
      *    ServletA
      * </pre>
      * 
-     * @throws Exception
+     * @throws Exception if test fails
      */
     public void testTrueObjectOrientedBehaviour() throws Exception {
         servletA = new HttpServlet() {
@@ -267,7 +269,7 @@
      *    ServletA
      * </pre>
      * 
-     * @throws Exception
+     * @throws Exception if test fails
      */
     public void testThreeLevelInheritance() throws Exception {
         servletA = new HttpServlet() {

Added: cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/util/RequestParametersTestCase.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/util/RequestParametersTestCase.java?rev=577497&view=auto
==============================================================================
--- cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/util/RequestParametersTestCase.java
(added)
+++ cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/util/RequestParametersTestCase.java
Wed Sep 19 18:44:05 2007
@@ -0,0 +1,85 @@
+/*
+* 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.cocoon.servletservice.util;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Id$
+ */
+public class RequestParametersTestCase extends TestCase {
+
+    public void testParseMultiple() {
+        RequestParameters p = new RequestParameters("a=1&a=2&a=3&b=11&b=22");
+        String[] a = p.getParameterValues("a");
+        assertEquals(3, a.length);
+        assertEquals("1", a[0]);
+        assertEquals("2", a[1]);
+        assertEquals("3", a[2]);
+
+        String[] b = p.getParameterValues("b");
+        assertEquals(2, b.length);
+        assertEquals("11", b[0]);
+        assertEquals("22", b[1]);
+    }
+
+    public void testParseEncoded() {
+        RequestParameters p = new RequestParameters("a=one+two&b=one%3Ftwo&c=");
+        assertEquals("one two", p.getParameter("a"));
+        assertEquals("one?two", p.getParameter("b"));
+    }
+
+    public void testParseEncodedWrong() {
+        try {
+            new RequestParameters("a=one%");
+            fail();
+        } catch (IllegalArgumentException e) { }
+        try {
+            new RequestParameters("a=one%3");
+            fail();
+        } catch (IllegalArgumentException e) { }
+        try {
+            new RequestParameters("a=one%u");
+            fail();
+        } catch (IllegalArgumentException e) { }
+        try {
+            new RequestParameters("a=one%u0");
+            fail();
+        } catch (IllegalArgumentException e) { }
+        try {
+            new RequestParameters("a=one%u00");
+            fail();
+        } catch (IllegalArgumentException e) { }
+        try {
+            new RequestParameters("a=one%u003");
+            fail();
+        } catch (IllegalArgumentException e) { }
+    }
+
+    public void testParseUnicode() {
+        final char u = '\u11F3'; // Hangul character
+
+        RequestParameters p = new RequestParameters("a=a%E1%87%B3b&b=a%u11f3b");
+        assertEquals("a" + u + "b", p.getParameter("a"));
+        assertEquals("a" + u + "b", p.getParameter("b"));
+    }
+
+    public void testParseWSDL() {
+        assertEquals("", new RequestParameters("foo").getParameter("foo"));
+    }
+}

Propchange: cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/util/RequestParametersTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/trunk/core/cocoon-servlet-service/cocoon-servlet-service-impl/src/test/java/org/apache/cocoon/servletservice/util/RequestParametersTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision Author Date



Mime
View raw message