Skip to content

Commit 60ba4ea

Browse files
authored
use %20 to escpae spaces in URI templates (#973)
1 parent 198453b commit 60ba4ea

File tree

3 files changed

+68
-32
lines changed

3 files changed

+68
-32
lines changed

Diff for: google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java

+23-28
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
*/
5656
public class UriTemplate {
5757

58-
static final Map<Character, CompositeOutput> COMPOSITE_PREFIXES =
58+
private static final Map<Character, CompositeOutput> COMPOSITE_PREFIXES =
5959
new HashMap<Character, CompositeOutput>();
6060

6161
static {
@@ -98,14 +98,14 @@ private enum CompositeOutput {
9898
private final boolean reservedExpansion;
9999

100100
/**
101-
* @param propertyPrefix The prefix of a parameter or {@code null} for none. In {+var} the
101+
* @param propertyPrefix the prefix of a parameter or {@code null} for none. In {+var} the
102102
* prefix is '+'
103-
* @param outputPrefix The string that should be prefixed to the expanded template.
104-
* @param explodeJoiner The delimiter used to join composite values.
105-
* @param requiresVarAssignment Denotes whether or not the expanded template should contain an
106-
* assignment with the variable.
107-
* @param reservedExpansion Reserved expansion allows pct-encoded triplets and characters in the
108-
* reserved set.
103+
* @param outputPrefix the string that should be prefixed to the expanded template.
104+
* @param explodeJoiner the delimiter used to join composite values.
105+
* @param requiresVarAssignment denotes whether or not the expanded template should contain an
106+
* assignment with the variable
107+
* @param reservedExpansion reserved expansion allows percent-encoded triplets and characters in the
108+
* reserved set
109109
*/
110110
CompositeOutput(
111111
Character propertyPrefix,
@@ -149,26 +149,22 @@ int getVarNameStartIndex() {
149149
}
150150

151151
/**
152-
* Encodes the specified value. If reserved expansion is turned on then pct-encoded triplets and
152+
* Encodes the specified value. If reserved expansion is turned on, then percent-encoded triplets and
153153
* characters are allowed in the reserved set.
154154
*
155-
* @param value The string to be encoded.
156-
* @return The encoded string.
155+
* @param value the string to be encoded
156+
* @return the encoded string
157157
*/
158-
String getEncodedValue(String value) {
158+
private String getEncodedValue(String value) {
159159
String encodedValue;
160160
if (reservedExpansion) {
161-
// Reserved expansion allows pct-encoded triplets and characters in the reserved set.
161+
// Reserved expansion allows percent-encoded triplets and characters in the reserved set.
162162
encodedValue = CharEscapers.escapeUriPathWithoutReserved(value);
163163
} else {
164-
encodedValue = CharEscapers.escapeUri(value);
164+
encodedValue = CharEscapers.escapeUriConformant(value);
165165
}
166166
return encodedValue;
167167
}
168-
169-
boolean getReservedExpansion() {
170-
return reservedExpansion;
171-
}
172168
}
173169

174170
static CompositeOutput getCompositeOutput(String propertyName) {
@@ -334,12 +330,12 @@ private static String getSimpleValue(String name, String value, CompositeOutput
334330
* Expand the template of a composite list property. Eg: If d := ["red", "green", "blue"] then
335331
* {/d*} is expanded to "/red/green/blue"
336332
*
337-
* @param varName The name of the variable the value corresponds to. Eg: "d"
338-
* @param iterator The iterator over list values. Eg: ["red", "green", "blue"]
339-
* @param containsExplodeModifier Set to true if the template contains the explode modifier "*"
340-
* @param compositeOutput An instance of CompositeOutput. Contains information on how the
333+
* @param varName the name of the variable the value corresponds to. E.g. "d"
334+
* @param iterator the iterator over list values. E.g. ["red", "green", "blue"]
335+
* @param containsExplodeModifiersSet to true if the template contains the explode modifier "*"
336+
* @param compositeOutput an instance of CompositeOutput. Contains information on how the
341337
* expansion should be done
342-
* @return The expanded list template
338+
* @return the expanded list template
343339
* @throws IllegalArgumentException if the required list path parameter is empty
344340
*/
345341
private static String getListPropertyValue(
@@ -378,12 +374,11 @@ private static String getListPropertyValue(
378374
* Expand the template of a composite map property. Eg: If d := [("semi", ";"),("dot",
379375
* "."),("comma", ",")] then {/d*} is expanded to "/semi=%3B/dot=./comma=%2C"
380376
*
381-
* @param varName The name of the variable the value corresponds to. Eg: "d"
382-
* @param map The map property value. Eg: [("semi", ";"),("dot", "."),("comma", ",")]
377+
* @param varName the name of the variable the value corresponds to. Eg: "d"
378+
* @param map the map property value. Eg: [("semi", ";"),("dot", "."),("comma", ",")]
383379
* @param containsExplodeModifier Set to true if the template contains the explode modifier "*"
384-
* @param compositeOutput An instance of CompositeOutput. Contains information on how the
385-
* expansion should be done
386-
* @return The expanded map template
380+
* @param compositeOutput contains information on how the expansion should be done
381+
* @return the expanded map template
387382
* @throws IllegalArgumentException if the required list path parameter is map
388383
*/
389384
private static String getMapPropertyValue(

Diff for: google-http-client/src/main/java/com/google/api/client/util/escape/CharEscapers.java

+38-3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public final class CharEscapers {
2929
private static final Escaper APPLICATION_X_WWW_FORM_URLENCODED =
3030
new PercentEscaper(PercentEscaper.SAFECHARS_URLENCODER, true);
3131

32+
private static final Escaper URI_ESCAPER =
33+
new PercentEscaper(PercentEscaper.SAFECHARS_URLENCODER, false);
34+
3235
private static final Escaper URI_PATH_ESCAPER =
3336
new PercentEscaper(PercentEscaper.SAFEPATHCHARS_URLENCODER);
3437

@@ -42,8 +45,13 @@ public final class CharEscapers {
4245
new PercentEscaper(PercentEscaper.SAFEQUERYSTRINGCHARS_URLENCODER);
4346

4447
/**
45-
* Escapes the string value so it can be safely included in URIs. For details on escaping URIs,
46-
* see <a href="http://tools.ietf.org/html/rfc3986#section-2.4">RFC 3986 - section 2.4</a>.
48+
* Escapes the string value so it can be safely included in application/x-www-form-urlencoded
49+
* data. This is not appropriate for generic URI escaping. In particular it encodes
50+
* the space character as a plus #stead of percent escaping it, in
51+
* contravention of the URI specification.
52+
* For details on application/x-www-form-urlencoded encoding see the
53+
* see <a href="https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1">HTML 4
54+
* specification, section 17.13.4.1</a>.
4755
*
4856
* <p>When encoding a String, the following rules apply:
4957
*
@@ -68,9 +76,36 @@ public final class CharEscapers {
6876
* <li>{@link java.net.URLEncoder#encode(String, String)} with the encoding name "UTF-8"
6977
* </ul>
7078
*/
79+
@Deprecated
7180
public static String escapeUri(String value) {
7281
return APPLICATION_X_WWW_FORM_URLENCODED.escape(value);
7382
}
83+
84+
/**
85+
* Escapes the string value so it can be safely included in any part of a URI.
86+
* For details on escaping URIs,
87+
* see <a href="http://tools.ietf.org/html/rfc3986#section-2.4">RFC 3986 - section 2.4</a>.
88+
*
89+
* <p>When encoding a String, the following rules apply:
90+
*
91+
* <ul>
92+
* <li>The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain
93+
* the same.
94+
* <li>The special characters ".", "-", "*", and "_" remain the same.
95+
* <li>The space character " " is converted into "%20".
96+
* <li>All other characters are converted into one or more bytes using UTF-8 encoding and each
97+
* byte is then represented by the 3-character string "%XY", where "XY" is the two-digit,
98+
* uppercase, hexadecimal representation of the byte value.
99+
* </ul>
100+
*
101+
* <p><b>Note</b>: Unlike other escapers, URI escapers produce uppercase hexadecimal sequences.
102+
* From <a href="http://tools.ietf.org/html/rfc3986">RFC 3986</a>:<br>
103+
* <i>"URI producers and normalizers should use uppercase hexadecimal digits for all
104+
* percent-encodings."</i>
105+
*/
106+
public static String escapeUriConformant(String value) {
107+
return URI_ESCAPER.escape(value);
108+
}
74109

75110
/**
76111
* Decodes application/x-www-form-urlencoded strings. The UTF-8 character set determines
@@ -144,7 +179,7 @@ public static String escapeUriPath(String value) {
144179

145180
/**
146181
* Escapes a URI path but retains all reserved characters, including all general delimiters. That
147-
* is the same as {@link #escapeUriPath(String)} except that it keeps '?', '+', and '/' unescaped.
182+
* is the same as {@link #escapeUriPath(String)} except that it does not escape '?', '+', and '/'.
148183
*/
149184
public static String escapeUriPathWithoutReserved(String value) {
150185
return URI_RESERVED_ESCAPER.escape(value);

Diff for: google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,19 @@ public void testExpandTemplates_basic() {
5858
assertTrue(requestMap.containsKey("unused"));
5959
}
6060

61-
public void testExpanTemplates_basicEncodeValue() {
61+
public void testExpandTemplates_basicEncodeValue() {
6262
SortedMap<String, Object> requestMap = Maps.newTreeMap();
6363
requestMap.put("abc", "xyz;def");
6464
assertEquals(";abc=xyz%3Bdef", UriTemplate.expand("{;abc}", requestMap, false));
6565
assertEquals("xyz;def", UriTemplate.expand("{+abc}", requestMap, false));
6666
}
6767

68+
public void testExpandTemplates_encodeSpace() {
69+
SortedMap<String, Object> requestMap = Maps.newTreeMap();
70+
requestMap.put("abc", "xyz def");
71+
assertEquals(";abc=xyz%20def", UriTemplate.expand("{;abc}", requestMap, false));
72+
}
73+
6874
public void testExpandTemplates_noExpansionsWithQueryParams() {
6975
SortedMap<String, Object> requestMap = Maps.newTreeMap();
7076
requestMap.put("abc", "xyz");

0 commit comments

Comments
 (0)