Skip to content
Permalink
Browse files
feat: add flag to allow UrlEncodedContent to use UriPath escaping (#1100
)

Fixes #1098 
The legacy behavior is kept. Test has been updated
  • Loading branch information
guillaumeblaquiere committed Oct 13, 2020
1 parent 407e385 commit 9ab7016032327f6fb0f91970dfbd511b029dd949
@@ -42,20 +42,43 @@
*
* <p>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 <a
* href="http://tools.ietf.org/html/rfc3986#section-2.4">RFC 3986 - section 2.4</a>
*/
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<? extends Object> 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);
@@ -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<String, String> params = new LinkedHashMap<String, String>();
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());

0 comments on commit 9ab7016

Please sign in to comment.