Skip to content

Commit 5a08abd

Browse files
committed
Correct Encoding and restore decodeSlash in QueryTemplate
Fixes OpenFeign#1156 Collection Format was encoding query string values unnecessarily due to changes introduced in OpenFeign#1138 and OpenFeign#1139 that encode template values before appending them to the query string. In addition, `decodeSlash` flags that were accidentally removed, have been restored in QueryTemplate.
1 parent 49e1373 commit 5a08abd

File tree

8 files changed

+68
-34
lines changed

8 files changed

+68
-34
lines changed

core/src/main/java/feign/CollectionFormat.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2012-2019 The Feign Authors
2+
* Copyright 2012-2020 The Feign Authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
55
* in compliance with the License. You may obtain a copy of the License at
@@ -77,7 +77,7 @@ public CharSequence join(String field, Collection<String> values, Charset charse
7777
builder.append(UriUtils.encode(field, charset));
7878
if (value != null) {
7979
builder.append('=');
80-
builder.append(UriUtils.encode(value, charset));
80+
builder.append(value);
8181
}
8282
} else {
8383
// delimited with a separator character
@@ -87,8 +87,8 @@ public CharSequence join(String field, Collection<String> values, Charset charse
8787
if (value == null) {
8888
continue;
8989
}
90-
builder.append(valueCount++ == 0 ? "=" : separator);
91-
builder.append(UriUtils.encode(value, charset));
90+
builder.append(valueCount++ == 0 ? "=" : UriUtils.encode(separator, charset));
91+
builder.append(value);
9292
}
9393
}
9494
return builder;

core/src/main/java/feign/RequestTemplate.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -636,9 +636,9 @@ private RequestTemplate appendQuery(String name,
636636
/* create a new query template out of the information here */
637637
this.queries.compute(name, (key, queryTemplate) -> {
638638
if (queryTemplate == null) {
639-
return QueryTemplate.create(name, values, this.charset, collectionFormat);
639+
return QueryTemplate.create(name, values, this.charset, collectionFormat, this.decodeSlash);
640640
} else {
641-
return QueryTemplate.append(queryTemplate, values, collectionFormat);
641+
return QueryTemplate.append(queryTemplate, values, collectionFormat, this.decodeSlash);
642642
}
643643
});
644644
return this;

core/src/main/java/feign/template/QueryTemplate.java

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2012-2019 The Feign Authors
2+
* Copyright 2012-2020 The Feign Authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
55
* in compliance with the License. You may obtain a copy of the License at
@@ -13,6 +13,10 @@
1313
*/
1414
package feign.template;
1515

16+
import feign.CollectionFormat;
17+
import feign.Util;
18+
import feign.template.Template.EncodingOptions;
19+
import feign.template.Template.ExpansionOptions;
1620
import java.nio.charset.Charset;
1721
import java.nio.charset.StandardCharsets;
1822
import java.util.ArrayList;
@@ -24,10 +28,6 @@
2428
import java.util.concurrent.CopyOnWriteArrayList;
2529
import java.util.stream.Collectors;
2630
import java.util.stream.StreamSupport;
27-
import feign.CollectionFormat;
28-
import feign.Util;
29-
import feign.template.Template.EncodingOptions;
30-
import feign.template.Template.ExpansionOptions;
3131

3232
/**
3333
* Template for a Query String parameter.
@@ -49,7 +49,14 @@ public final class QueryTemplate {
4949
* @return a QueryTemplate.
5050
*/
5151
public static QueryTemplate create(String name, Iterable<String> values, Charset charset) {
52-
return create(name, values, charset, CollectionFormat.EXPLODED);
52+
return create(name, values, charset, CollectionFormat.EXPLODED, true);
53+
}
54+
55+
public static QueryTemplate create(String name,
56+
Iterable<String> values,
57+
Charset charset,
58+
CollectionFormat collectionFormat) {
59+
return create(name, values, charset, collectionFormat, true);
5360
}
5461

5562
/**
@@ -59,12 +66,14 @@ public static QueryTemplate create(String name, Iterable<String> values, Charset
5966
* @param values in the template.
6067
* @param charset for the template.
6168
* @param collectionFormat to use.
69+
* @param decodeSlash if slash characters should be decoded
6270
* @return a QueryTemplate
6371
*/
6472
public static QueryTemplate create(String name,
6573
Iterable<String> values,
6674
Charset charset,
67-
CollectionFormat collectionFormat) {
75+
CollectionFormat collectionFormat,
76+
boolean decodeSlash) {
6877
if (Util.isBlank(name)) {
6978
throw new IllegalArgumentException("name is required.");
7079
}
@@ -78,7 +87,7 @@ public static QueryTemplate create(String name,
7887
.filter(Util::isNotBlank)
7988
.collect(Collectors.toList());
8089

81-
return new QueryTemplate(name, remaining, charset, collectionFormat);
90+
return new QueryTemplate(name, remaining, charset, collectionFormat, decodeSlash);
8291
}
8392

8493
/**
@@ -90,13 +99,14 @@ public static QueryTemplate create(String name,
9099
*/
91100
public static QueryTemplate append(QueryTemplate queryTemplate,
92101
Iterable<String> values,
93-
CollectionFormat collectionFormat) {
102+
CollectionFormat collectionFormat,
103+
boolean decodeSlash) {
94104
List<String> queryValues = new ArrayList<>(queryTemplate.getValues());
95105
queryValues.addAll(StreamSupport.stream(values.spliterator(), false)
96106
.filter(Util::isNotBlank)
97107
.collect(Collectors.toList()));
98108
return create(queryTemplate.getName(), queryValues, StandardCharsets.UTF_8,
99-
collectionFormat);
109+
collectionFormat, decodeSlash);
100110
}
101111

102112
/**
@@ -110,10 +120,11 @@ private QueryTemplate(
110120
String name,
111121
Iterable<String> values,
112122
Charset charset,
113-
CollectionFormat collectionFormat) {
123+
CollectionFormat collectionFormat,
124+
boolean decodeSlash) {
114125
this.values = new CopyOnWriteArrayList<>();
115126
this.name = new Template(name, ExpansionOptions.ALLOW_UNRESOLVED, EncodingOptions.REQUIRED,
116-
false, charset);
127+
!decodeSlash, charset);
117128
this.collectionFormat = collectionFormat;
118129

119130
/* parse each value into a template chunk for resolution later */
@@ -128,7 +139,7 @@ private QueryTemplate(
128139
value,
129140
ExpansionOptions.REQUIRED,
130141
EncodingOptions.REQUIRED,
131-
false,
142+
!decodeSlash,
132143
charset));
133144
}
134145

core/src/main/java/feign/template/UriUtils.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2012-2019 The Feign Authors
2+
* Copyright 2012-2020 The Feign Authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
55
* in compliance with the License. You may obtain a copy of the License at
@@ -15,6 +15,7 @@
1515

1616
import feign.Util;
1717
import java.io.ByteArrayOutputStream;
18+
import java.io.IOException;
1819
import java.io.UnsupportedEncodingException;
1920
import java.net.URLDecoder;
2021
import java.nio.charset.Charset;
@@ -153,16 +154,21 @@ private static String encodeChunk(String value, Charset charset, boolean allowRe
153154
}
154155

155156
byte[] data = value.getBytes(charset);
156-
ByteArrayOutputStream encoded = new ByteArrayOutputStream();
157-
for (byte b : data) {
158-
if (isUnreserved(b) || (isReserved(b) && allowReserved)) {
159-
encoded.write(b);
160-
} else {
161-
/* percent encode the byte */
162-
pctEncode(b, encoded);
157+
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
158+
for (byte b : data) {
159+
if (isUnreserved((char) b)) {
160+
bos.write(b);
161+
} else if (isReserved((char) b) && allowReserved) {
162+
bos.write(b);
163+
} else {
164+
pctEncode(b, bos);
165+
}
163166
}
167+
return new String(bos.toByteArray());
168+
} catch (IOException ioe) {
169+
throw new IllegalStateException("Error occurred during encoding of the uri: "
170+
+ ioe.getMessage(), ioe);
164171
}
165-
return new String(encoded.toByteArray());
166172
}
167173

168174
/**

core/src/test/java/feign/RequestTemplateTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,15 @@ public void slashShouldNotBeAppendedForMatrixParams() {
476476
.uri("/path;key1=value1;key2=value2", true);
477477

478478
assertThat(template.url()).isEqualTo("/path;key1=value1;key2=value2");
479+
}
479480

481+
@Test
482+
public void encodedReserved() {
483+
RequestTemplate template = new RequestTemplate();
484+
template.uri("/get?url={url}");
485+
template.method(HttpMethod.GET);
486+
template = template.resolve(Collections.singletonMap("url", "https://www.google.com"));
487+
Request request = template.request();
488+
assertThat(request).isNotNull();
480489
}
481490
}

core/src/test/java/feign/TargetTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public void baseCaseQueryParamsArePercentEncoded() throws InterruptedException {
4141

4242
Feign.builder().target(TestQuery.class, baseUrl).get("slash/foo", "slash/bar");
4343

44-
assertThat(server.takeRequest()).hasPath("/default/slash/foo?query=slash%2Fbar");
44+
assertThat(server.takeRequest()).hasPath("/default/slash/foo?query=slash/bar");
4545
}
4646

4747
/**

core/src/test/java/feign/template/QueryTemplateTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2012-2019 The Feign Authors
2+
* Copyright 2012-2020 The Feign Authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
55
* in compliance with the License. You may obtain a copy of the License at
@@ -93,7 +93,7 @@ public void collectionFormat() {
9393
QueryTemplate
9494
.create("name", Arrays.asList("James", "Jason"), Util.UTF_8, CollectionFormat.CSV);
9595
String expanded = template.expand(Collections.emptyMap());
96-
assertThat(expanded).isEqualToIgnoringCase("name=James,Jason");
96+
assertThat(expanded).isEqualToIgnoringCase("name=James%2CJason");
9797
}
9898

9999
@Test
@@ -183,7 +183,7 @@ public void expandCollectionValueWithBrackets() {
183183
String expanded = template.expand(Collections.singletonMap("collection[]",
184184
Arrays.asList("1", "2")));
185185
/* brackets will be pct-encoded */
186-
assertThat(expanded).isEqualToIgnoringCase("collection%5B%5D=1,2");
186+
assertThat(expanded).isEqualToIgnoringCase("collection%5B%5D=1%2C2");
187187
}
188188

189189
@Test
@@ -194,6 +194,6 @@ public void expandCollectionValueWithDollar() {
194194
String expanded = template.expand(Collections.singletonMap("$collection",
195195
Arrays.asList("1", "2")));
196196
/* dollar will be pct-encoded */
197-
assertThat(expanded).isEqualToIgnoringCase("%24collection=1,2");
197+
assertThat(expanded).isEqualToIgnoringCase("%24collection=1%2C2");
198198
}
199199
}

core/src/test/java/feign/template/UriTemplateTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2012-2019 The Feign Authors
2+
* Copyright 2012-2020 The Feign Authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
55
* in compliance with the License. You may obtain a copy of the License at
@@ -291,4 +291,12 @@ public void testLiteralTemplateWithQueryString() {
291291
String expanded = uriTemplate.expand(Collections.emptyMap());
292292
assertThat(expanded).isEqualToIgnoringCase("https://api.example.com?wsdl");
293293
}
294+
295+
@Test
296+
public void encodeReserved() {
297+
String template = "/get?url={url}";
298+
UriTemplate uriTemplate = UriTemplate.create(template, Util.UTF_8);
299+
String expanded = uriTemplate.expand(Collections.singletonMap("url", "https://www.google.com"));
300+
assertThat(expanded).isEqualToIgnoringCase("/get?url=https%3A%2F%2Fwww.google.com");
301+
}
294302
}

0 commit comments

Comments
 (0)