diff --git a/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedContent.java b/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedContent.java index eb6428b18..ea08b1e18 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedContent.java +++ b/google-http-client/src/main/java/com/google/api/client/http/UrlEncodedContent.java @@ -42,20 +42,43 @@ * *

Implementation is not thread-safe. * - * @since 1.0 * @author Yaniv Inbar + * @since 1.0 */ public class UrlEncodedContent extends AbstractHttpContent { /** Key name/value data. */ private Object data; - /** @param data key name/value data */ + /** Use URI Path encoder flag. False by default (use legacy and deprecated escapeUri) */ + private boolean uriPathEncodingFlag; + + /** + * Initialize the UrlEncodedContent with the legacy and deprecated escapeUri encoder + * + * @param data key name/value data + */ public UrlEncodedContent(Object data) { super(UrlEncodedParser.MEDIA_TYPE); setData(data); + this.uriPathEncodingFlag = false; } + /** + * Initialize the UrlEncodedContent with or without the legacy and deprecated escapeUri encoder + * + * @param data key name/value data + * @param useUriPathEncoding escapes the string value so it can be safely included in URI path + * segments. For details on escaping URIs, see RFC 3986 - section 2.4 + */ + public UrlEncodedContent(Object data, boolean useUriPathEncoding) { + super(UrlEncodedParser.MEDIA_TYPE); + setData(data); + this.uriPathEncodingFlag = useUriPathEncoding; + } + + @Override public void writeTo(OutputStream out) throws IOException { Writer writer = new BufferedWriter(new OutputStreamWriter(out, getCharset())); boolean first = true; @@ -66,10 +89,10 @@ public void writeTo(OutputStream out) throws IOException { Class valueClass = value.getClass(); if (value instanceof Iterable || valueClass.isArray()) { for (Object repeatedValue : Types.iterableOf(value)) { - first = appendParam(first, writer, name, repeatedValue); + first = appendParam(first, writer, name, repeatedValue, this.uriPathEncodingFlag); } } else { - first = appendParam(first, writer, name, value); + first = appendParam(first, writer, name, value, this.uriPathEncodingFlag); } } } @@ -125,7 +148,8 @@ public static UrlEncodedContent getContent(HttpRequest request) { return result; } - private static boolean appendParam(boolean first, Writer writer, String name, Object value) + private static boolean appendParam( + boolean first, Writer writer, String name, Object value, boolean uriPathEncodingFlag) throws IOException { // ignore nulls if (value == null || Data.isNull(value)) { @@ -139,8 +163,13 @@ private static boolean appendParam(boolean first, Writer writer, String name, Ob } writer.write(name); String stringValue = - CharEscapers.escapeUri( - value instanceof Enum ? FieldInfo.of((Enum) value).getName() : value.toString()); + value instanceof Enum ? FieldInfo.of((Enum) value).getName() : value.toString(); + + if (uriPathEncodingFlag) { + stringValue = CharEscapers.escapeUriPath(stringValue); + } else { + stringValue = CharEscapers.escapeUri(stringValue); + } if (stringValue.length() != 0) { writer.write("="); writer.write(stringValue); diff --git a/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedContentTest.java b/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedContentTest.java index f0e0768a9..059d9e2c9 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedContentTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/UrlEncodedContentTest.java @@ -33,19 +33,32 @@ public class UrlEncodedContentTest extends TestCase { public void testWriteTo() throws IOException { - subtestWriteTo("a=x", ArrayMap.of("a", "x")); - subtestWriteTo("noval", ArrayMap.of("noval", "")); - subtestWriteTo("multi=a&multi=b&multi=c", ArrayMap.of("multi", Arrays.asList("a", "b", "c"))); - subtestWriteTo("multi=a&multi=b&multi=c", ArrayMap.of("multi", new String[] {"a", "b", "c"})); + subtestWriteTo("a=x", ArrayMap.of("a", "x"), false); + subtestWriteTo("noval", ArrayMap.of("noval", ""), false); + subtestWriteTo( + "multi=a&multi=b&multi=c", ArrayMap.of("multi", Arrays.asList("a", "b", "c")), false); + subtestWriteTo( + "multi=a&multi=b&multi=c", ArrayMap.of("multi", new String[] {"a", "b", "c"}), false); // https://github.com/googleapis/google-http-java-client/issues/202 final Map params = new LinkedHashMap(); params.put("username", "un"); params.put("password", "password123;{}"); - subtestWriteTo("username=un&password=password123%3B%7B%7D", params); + subtestWriteTo("username=un&password=password123%3B%7B%7D", params, false); + subtestWriteTo("additionkey=add%2Btion", ArrayMap.of("additionkey", "add+tion"), false); + + subtestWriteTo("a=x", ArrayMap.of("a", "x"), true); + subtestWriteTo("noval", ArrayMap.of("noval", ""), true); + subtestWriteTo( + "multi=a&multi=b&multi=c", ArrayMap.of("multi", Arrays.asList("a", "b", "c")), true); + subtestWriteTo( + "multi=a&multi=b&multi=c", ArrayMap.of("multi", new String[] {"a", "b", "c"}), true); + subtestWriteTo("username=un&password=password123;%7B%7D", params, true); + subtestWriteTo("additionkey=add+tion", ArrayMap.of("additionkey", "add+tion"), true); } - private void subtestWriteTo(String expected, Object data) throws IOException { - UrlEncodedContent content = new UrlEncodedContent(data); + private void subtestWriteTo(String expected, Object data, boolean useEscapeUriPathEncoding) + throws IOException { + UrlEncodedContent content = new UrlEncodedContent(data, useEscapeUriPathEncoding); ByteArrayOutputStream out = new ByteArrayOutputStream(); content.writeTo(out); assertEquals(expected, out.toString());