Skip to content

Commit 9a7e84e

Browse files
author
julian.duniec
committed
Fix for bug in Ribbon-Module, where query strings are not properly encoded
1 parent 93447c6 commit 9a7e84e

2 files changed

Lines changed: 67 additions & 2 deletions

File tree

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

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,13 +463,38 @@ private StringBuilder pullAnyQueriesOutOfUrl(StringBuilder url) {
463463
firstQueries.putAll(queries);
464464
queries.clear();
465465
}
466-
queries.putAll(firstQueries);
466+
//Since we decode all queries, we want to use the
467+
//query()-method to re-add them to ensure that all
468+
//logic (such as url-encoding) are executed, giving
469+
//a valid queryLine()
470+
for(String key : firstQueries.keySet()) {
471+
Collection<String> values = firstQueries.get(key);
472+
if(allValuesAreNull(values)) {
473+
//Queryies where all values are null will
474+
//be ignored by the query(key, value)-method
475+
//So we manually avoid this case here, to ensure that
476+
//we still fulfill the contract (ex. parameters without values)
477+
queries.put(urlEncode(key), values);
478+
}
479+
else {
480+
query(key, values);
481+
}
482+
483+
}
467484
return new StringBuilder(url.substring(0, queryIndex));
468485
}
469486
return url;
470487
}
471488

472-
private static Map<String, Collection<String>> parseAndDecodeQueries(String queryLine) {
489+
private boolean allValuesAreNull(Collection<String> values) {
490+
if(values.isEmpty()) return true;
491+
for(String val : values) {
492+
if(val != null) return false;
493+
}
494+
return true;
495+
}
496+
497+
private static Map<String, Collection<String>> parseAndDecodeQueries(String queryLine) {
473498
Map<String, Collection<String>> map = new LinkedHashMap<String, Collection<String>>();
474499
if (emptyToNull(queryLine) == null)
475500
return map;

ribbon/src/test/java/feign/ribbon/RibbonClientTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,13 @@
3232
import static feign.Util.UTF_8;
3333
import static org.testng.Assert.assertEquals;
3434

35+
import javax.inject.Named;
36+
3537
@Test
3638
public class RibbonClientTest {
3739
interface TestInterface {
3840
@RequestLine("POST /") void post();
41+
@RequestLine("GET /?a={a}") void getWithQueryParameters(@Named("a") String a);
3942

4043
@dagger.Module(injects = Feign.class, overrides = true, addsTo = Feign.Defaults.class)
4144
static class Module {
@@ -108,6 +111,43 @@ public void ioExceptionRetry() throws IOException, InterruptedException {
108111
}
109112
}
110113

114+
/*
115+
This test-case replicates a bug that occurs when using RibbonRequest with a query string.
116+
117+
The querystrings would not be URL-encoded, leading to invalid HTTP-requests if the query string contained
118+
invalid characters (ex. space).
119+
*/
120+
@Test public void urlEncodeQueryStringParameters () throws IOException, InterruptedException {
121+
String client = "RibbonClientTest-urlEncodeQueryStringParameters";
122+
String serverListKey = client + ".ribbon.listOfServers";
123+
124+
String queryStringValue = "some string with space";
125+
String expectedQueryStringValue = "some+string+with+space";
126+
String expectedRequestLine = String.format("GET /?a=%s HTTP/1.1", expectedQueryStringValue);
127+
128+
MockWebServer server = new MockWebServer();
129+
server.enqueue(new MockResponse().setBody("success!".getBytes(UTF_8)));
130+
server.play();
131+
132+
getConfigInstance().setProperty(serverListKey, hostAndPort(server.getUrl("")));
133+
134+
try {
135+
136+
TestInterface api = Feign.create(TestInterface.class, "http://" + client, new TestInterface.Module(), new RibbonModule());
137+
138+
api.getWithQueryParameters(queryStringValue);
139+
140+
final String recordedRequestLine = server.takeRequest().getRequestLine();
141+
142+
assertEquals(recordedRequestLine, expectedRequestLine);
143+
} finally {
144+
server.shutdown();
145+
getConfigInstance().clearProperty(serverListKey);
146+
}
147+
}
148+
149+
150+
111151
static String hostAndPort(URL url) {
112152
// our build slaves have underscores in their hostnames which aren't permitted by ribbon
113153
return "localhost:" + url.getPort();

0 commit comments

Comments
 (0)