You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by gg...@apache.org on 2013/06/06 18:00:06 UTC

svn commit: r1490331 - in /httpcomponents/httpclient/trunk: RELEASE_NOTES.txt httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java

Author: ggregory
Date: Thu Jun  6 16:00:05 2013
New Revision: 1490331

URL: http://svn.apache.org/r1490331
Log:
[HTTPCLIENT-1366] org.apache.http.client.utils.URLEncodedUtils should parse the semicolon as a query parameter separator. Contributed by Gary Gregory <ggregory at apache.org>

Modified:
    httpcomponents/httpclient/trunk/RELEASE_NOTES.txt
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java

Modified: httpcomponents/httpclient/trunk/RELEASE_NOTES.txt
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/RELEASE_NOTES.txt?rev=1490331&r1=1490330&r2=1490331&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/RELEASE_NOTES.txt (original)
+++ httpcomponents/httpclient/trunk/RELEASE_NOTES.txt Thu Jun  6 16:00:05 2013
@@ -1,6 +1,9 @@
 Changes since release 4.3 BETA2
 -------------------
 
+* [HTTPCLIENT-1366] org.apache.http.client.utils.URLEncodedUtils should parse the semicolon as a query parameter separator.
+  Contributed by Gary Gregory <ggregory at apache.org>
+
 * [HTTPCLIENT-1365] NPE when ManagedHttpClientConnectionFactory.create(ConnectionConfig) is called with null.
   Contributed by Gary Gregory <ggregory at apache.org>
 

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java?rev=1490331&r1=1490330&r2=1490331&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java Thu Jun  6 16:00:05 2013
@@ -60,48 +60,54 @@ import org.apache.http.util.EntityUtils;
 @Immutable
 public class URLEncodedUtils {
 
+    /**
+     * The default HTML form content type.
+     */
     public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
-    private static final String PARAMETER_SEPARATOR = "&";
+
+    private static final char QP_SEP_A = '&';
+    private static final char QP_SEP_S = ';';
     private static final String NAME_VALUE_SEPARATOR = "=";
 
     /**
-     * Returns a list of {@link NameValuePair NameValuePairs} as built from the
-     * URI's query portion. For example, a URI of
-     * http://example.org/path/to/file?a=1&b=2&c=3 would return a list of three
-     * NameValuePairs, one for a=1, one for b=2, and one for c=3.
+     * Returns a list of {@link NameValuePair NameValuePairs} as built from the URI's query portion. For example, a URI
+     * of http://example.org/path/to/file?a=1&b=2&c=3 would return a list of three NameValuePairs, one for a=1, one for
+     * b=2, and one for c=3. By convention, {@code '&'} and {@code ';'} are accepted as parameter separators.
      * <p>
      * This is typically useful while parsing an HTTP PUT.
      *
+     * This API is currently only used for testing.
+     *
      * @param uri
      *            URI to parse
      * @param charset
-     *            charset name to use while parsing the query
+     *            Charset name to use while parsing the query
+     * @return a list of {@link NameValuePair} as built from the URI's query portion.
      */
-    public static List <NameValuePair> parse (final URI uri, final String charset) {
+    public static List <NameValuePair> parse(final URI uri, final String charset) {
         final String query = uri.getRawQuery();
         if (query != null && query.length() > 0) {
             final List<NameValuePair> result = new ArrayList<NameValuePair>();
             final Scanner scanner = new Scanner(query);
-            parse(result, scanner, charset);
+            parse(result, scanner, QP_SEP_PATTERN, charset);
             return result;
-        } else {
-            return Collections.emptyList();
         }
+        return Collections.emptyList();
     }
 
     /**
-     * Returns a list of {@link NameValuePair NameValuePairs} as parsed from an
-     * {@link HttpEntity}. The encoding is taken from the entity's
-     * Content-Encoding header.
+     * Returns a list of {@link NameValuePair NameValuePairs} as parsed from an {@link HttpEntity}. The encoding is
+     * taken from the entity's Content-Encoding header.
      * <p>
      * This is typically used while parsing an HTTP POST.
      *
      * @param entity
      *            The entity to parse
+     * @return a list of {@link NameValuePair} as built from the URI's query portion.
      * @throws IOException
      *             If there was an exception getting the entity's data.
      */
-    public static List <NameValuePair> parse (
+    public static List <NameValuePair> parse(
             final HttpEntity entity) throws IOException {
         final ContentType contentType = ContentType.get(entity);
         if (contentType != null && contentType.getMimeType().equalsIgnoreCase(CONTENT_TYPE)) {
@@ -111,7 +117,7 @@ public class URLEncodedUtils {
                 if (charset == null) {
                     charset = HTTP.DEF_CONTENT_CHARSET;
                 }
-                return parse(content, charset);
+                return parse(content, charset, QP_SEPS);
             }
         }
         return Collections.emptyList();
@@ -121,7 +127,7 @@ public class URLEncodedUtils {
      * Returns true if the entity's Content-Type header is
      * <code>application/x-www-form-urlencoded</code>.
      */
-    public static boolean isEncoded (final HttpEntity entity) {
+    public static boolean isEncoded(final HttpEntity entity) {
         final Header h = entity.getContentType();
         if (h != null) {
             final HeaderElement[] elems = h.getElements();
@@ -137,6 +143,26 @@ public class URLEncodedUtils {
     }
 
     /**
+     * Adds all parameters within the Scanner to the list of <code>parameters</code>, as encoded by
+     * <code>encoding</code>. For example, a scanner containing the string <code>a=1&b=2&c=3</code> would add the
+     * {@link NameValuePair NameValuePairs} a=1, b=2, and c=3 to the list of parameters. By convention, {@code '&'} and
+     * {@code ';'} are accepted as parameter separators.
+     *
+     * @param parameters
+     *            List to add parameters to.
+     * @param scanner
+     *            Input that contains the parameters to parse.
+     * @param charset
+     *            Encoding to use when decoding the parameters.
+     */
+    public static void parse(
+            final List <NameValuePair> parameters,
+            final Scanner scanner,
+            final String charset) {
+        parse(parameters, scanner, QP_SEP_PATTERN, charset);
+    }
+
+    /**
      * Adds all parameters within the Scanner to the list of
      * <code>parameters</code>, as encoded by <code>encoding</code>. For
      * example, a scanner containing the string <code>a=1&b=2&c=3</code> would
@@ -147,14 +173,17 @@ public class URLEncodedUtils {
      *            List to add parameters to.
      * @param scanner
      *            Input that contains the parameters to parse.
+     * @param parameterSepartorPattern
+     *            The Pattern string for parameter separators, by convention {@code "[&;]"}
      * @param charset
      *            Encoding to use when decoding the parameters.
      */
-    public static void parse (
+    public static void parse(
             final List <NameValuePair> parameters,
             final Scanner scanner,
+            final String parameterSepartorPattern,
             final String charset) {
-        scanner.useDelimiter(PARAMETER_SEPARATOR);
+        scanner.useDelimiter(parameterSepartorPattern);
         while (scanner.hasNext()) {
             String name = null;
             String value = null;
@@ -170,20 +199,47 @@ public class URLEncodedUtils {
         }
     }
 
-    private static final char[] DELIM = new char[] { '&' };
+    /**
+     * Query parameter separators.
+     */
+    private static final char[] QP_SEPS = new char[] { QP_SEP_A, QP_SEP_S };
 
     /**
-     * Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string
-     * using the given character encoding.
+     * Query parameter separator pattern.
+     */
+    private static final String QP_SEP_PATTERN = "[" + new String(QP_SEPS) + "]";
+
+    /**
+     * Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string using the given character
+     * encoding. By convention, {@code '&'} and {@code ';'} are accepted as parameter separators.
      *
      * @param s
      *            text to parse.
      * @param charset
      *            Encoding to use when decoding the parameters.
+     * @return a list of {@link NameValuePair} as built from the URI's query portion.
      *
      * @since 4.2
      */
-    public static List<NameValuePair> parse (final String s, final Charset charset) {
+    public static List<NameValuePair> parse(final String s, final Charset charset) {
+        return parse(s, charset, QP_SEPS);
+    }
+
+    /**
+     * Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string using the given character
+     * encoding.
+     *
+     * @param s
+     *            text to parse.
+     * @param charset
+     *            Encoding to use when decoding the parameters.
+     * @param parameterSeparator
+     *            The characters used to separate parameters, by convention, {@code '&'} and {@code ';'}.
+     * @return a list of {@link NameValuePair} as built from the URI's query portion.
+     *
+     * @since 4.3
+     */
+    public static List<NameValuePair> parse(final String s, final Charset charset, final char... parameterSeparator) {
         if (s == null) {
             return Collections.emptyList();
         }
@@ -193,7 +249,7 @@ public class URLEncodedUtils {
         final ParserCursor cursor = new ParserCursor(0, buffer.length());
         final List<NameValuePair> list = new ArrayList<NameValuePair>();
         while (!cursor.atEnd()) {
-            final NameValuePair nvp = parser.parseNameValuePair(buffer, cursor, DELIM);
+            final NameValuePair nvp = parser.parseNameValuePair(buffer, cursor, parameterSeparator);
             if (nvp.getName().length() > 0) {
                 list.add(new BasicNameValuePair(
                         decodeFormFields(nvp.getName(), charset),
@@ -204,21 +260,40 @@ public class URLEncodedUtils {
     }
 
     /**
-     * Returns a String that is suitable for use as an <code>application/x-www-form-urlencoded</code>
+     * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded}
      * list of parameters in an HTTP PUT or HTTP POST.
      *
      * @param parameters  The parameters to include.
-     * @param encoding The encoding to use.
+     * @param charset The encoding to use.
+     * @return An {@code application/x-www-form-urlencoded} string
      */
-    public static String format (
+    public static String format(
             final List <? extends NameValuePair> parameters,
-            final String encoding) {
+            final String charset) {
+        return format(parameters, QP_SEP_A, charset);
+    }
+
+    /**
+     * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded}
+     * list of parameters in an HTTP PUT or HTTP POST.
+     *
+     * @param parameters  The parameters to include.
+     * @param parameterSeparator The parameter separator, by convention, {@code '&'} or {@code ';'}.
+     * @param charset The encoding to use.
+     * @return An {@code application/x-www-form-urlencoded} string
+     *
+     * @since 4.3
+     */
+    public static String format(
+            final List <? extends NameValuePair> parameters,
+            final char parameterSeparator,
+            final String charset) {
         final StringBuilder result = new StringBuilder();
         for (final NameValuePair parameter : parameters) {
-            final String encodedName = encodeFormFields(parameter.getName(), encoding);
-            final String encodedValue = encodeFormFields(parameter.getValue(), encoding);
+            final String encodedName = encodeFormFields(parameter.getName(), charset);
+            final String encodedValue = encodeFormFields(parameter.getValue(), charset);
             if (result.length() > 0) {
-                result.append(PARAMETER_SEPARATOR);
+                result.append(parameterSeparator);
             }
             result.append(encodedName);
             if (encodedValue != null) {
@@ -230,23 +305,42 @@ public class URLEncodedUtils {
     }
 
     /**
-     * Returns a String that is suitable for use as an <code>application/x-www-form-urlencoded</code>
+     * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded}
      * list of parameters in an HTTP PUT or HTTP POST.
      *
      * @param parameters  The parameters to include.
      * @param charset The encoding to use.
+     * @return An {@code application/x-www-form-urlencoded} string
      *
      * @since 4.2
      */
-    public static String format (
+    public static String format(
+            final Iterable<? extends NameValuePair> parameters,
+            final Charset charset) {
+        return format(parameters, QP_SEP_A, charset);
+    }
+
+    /**
+     * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded}
+     * list of parameters in an HTTP PUT or HTTP POST.
+     *
+     * @param parameters  The parameters to include.
+     * @param parameterSeparator The parameter separator, by convention, {@code '&'} or {@code ';'}.
+     * @param charset The encoding to use.
+     * @return An {@code application/x-www-form-urlencoded} string
+     *
+     * @since 4.3
+     */
+    public static String format(
             final Iterable<? extends NameValuePair> parameters,
+            final char parameterSeparator,
             final Charset charset) {
         final StringBuilder result = new StringBuilder();
         for (final NameValuePair parameter : parameters) {
             final String encodedName = encodeFormFields(parameter.getName(), charset);
             final String encodedValue = encodeFormFields(parameter.getValue(), charset);
             if (result.length() > 0) {
-                result.append(PARAMETER_SEPARATOR);
+                result.append(parameterSeparator);
             }
             result.append(encodedName);
             if (encodedValue != null) {
@@ -470,12 +564,11 @@ public class URLEncodedUtils {
      * @param charset the charset to use
      * @return encoded string
      */
-    private static String encodeFormFields (final String content, final String charset) {
+    private static String encodeFormFields(final String content, final String charset) {
         if (content == null) {
             return null;
         }
-        return urlEncode(content, charset != null ? Charset.forName(charset) :
-            Consts.UTF_8, URLENCODER, true);
+        return urlEncode(content, charset != null ? Charset.forName(charset) : Consts.UTF_8, URLENCODER, true);
     }
 
     /**

Modified: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java?rev=1490331&r1=1490330&r2=1490331&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java Thu Jun  6 16:00:05 2013
@@ -223,7 +223,7 @@ public class TestURLEncodedUtils {
     }
 
     @Test
-    public void testParseUTF8String() throws Exception {
+    public void testParseUTF8Ampersand1String() throws Exception {
         final String ru_hello = constructString(RUSSIAN_HELLO);
         final String ch_hello = constructString(SWISS_GERMAN_HELLO);
         final List <NameValuePair> parameters = new ArrayList<NameValuePair>();
@@ -239,6 +239,34 @@ public class TestURLEncodedUtils {
     }
 
     @Test
+    public void testParseUTF8Ampersand2String() throws Exception {
+        testParseUTF8String('&');
+    }
+
+    @Test
+    public void testParseUTF8SemicolonString() throws Exception {
+        testParseUTF8String(';');
+    }
+
+    private void testParseUTF8String(final char parameterSeparator) throws Exception {
+        final String ru_hello = constructString(RUSSIAN_HELLO);
+        final String ch_hello = constructString(SWISS_GERMAN_HELLO);
+        final List <NameValuePair> parameters = new ArrayList<NameValuePair>();
+        parameters.add(new BasicNameValuePair("russian", ru_hello));
+        parameters.add(new BasicNameValuePair("swiss", ch_hello));
+
+        final String s = URLEncodedUtils.format(parameters, parameterSeparator, Consts.UTF_8);
+
+        final List <NameValuePair> result1 = URLEncodedUtils.parse(s, Consts.UTF_8);
+        Assert.assertEquals(2, result1.size());
+        assertNameValuePair(result1.get(0), "russian", ru_hello);
+        assertNameValuePair(result1.get(1), "swiss", ch_hello);
+
+        final List <NameValuePair> result2 = URLEncodedUtils.parse(s, Consts.UTF_8, parameterSeparator);
+        Assert.assertEquals(result1, result2);
+    }
+
+    @Test
     public void testParseEntityDefaultContentType() throws Exception {
         final String ch_hello = constructString(SWISS_GERMAN_HELLO);
         final String us_hello = "hi there";
@@ -311,6 +339,8 @@ public class TestURLEncodedUtils {
         params.add(new BasicNameValuePair("Name7", "b,b"));
         params.add(new BasicNameValuePair("Name7", "ccc"));
         Assert.assertEquals("Name7=aaa&Name7=b%2Cb&Name7=ccc", URLEncodedUtils.format(params, Consts.ASCII));
+        Assert.assertEquals("Name7=aaa&Name7=b%2Cb&Name7=ccc", URLEncodedUtils.format(params, '&', Consts.ASCII));
+        Assert.assertEquals("Name7=aaa;Name7=b%2Cb;Name7=ccc", URLEncodedUtils.format(params, ';', Consts.ASCII));
 
         params.clear();
         params.add(new BasicNameValuePair("Name8", "xx,  yy  ,zz"));