From 41c364002fe2e0d9e9c1f59ccf4a9c530dcde04a Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 23 Feb 2016 15:19:23 +0000 Subject: [PATCH] Encode query params in Zuul filter Some slightly tricky manipulation involved here, but hopefully UriTemplate to the rescue. Fixes gh-682 --- .../zuul/filters/ProxyRequestHelper.java | 18 ++++++++++-- .../zuul/SampleZuulProxyApplicationTests.java | 28 +++++++++++++++++++ .../cloud/netflix/zuul/ZuulProxyTestBase.java | 6 ++-- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java index 4e7c04a408..b8fc3a6662 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java @@ -22,6 +22,7 @@ import java.nio.charset.Charset; import java.util.Collection; import java.util.Enumeration; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -36,6 +37,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriTemplate; import org.springframework.web.util.UriUtils; import org.springframework.web.util.WebUtils; @@ -290,17 +292,27 @@ private void debugRequestEntity(Map info, InputStream inputStrea } public String getQueryString(MultiValueMap params) { + if (params.isEmpty()) { + return ""; + } StringBuilder query = new StringBuilder(); + Map singles = new HashMap<>(); for (String param : params.keySet()) { + int i = 0; for (String value : params.get(param)) { query.append("&"); query.append(param); if (!"".equals(value)) { - query.append("="); - query.append(value); + singles.put(param + i, value); + query.append("={"); + query.append(param + i); + query.append("}"); } + i++; } } - return (query.length() > 0) ? "?" + query.substring(1) : ""; + + UriTemplate template = new UriTemplate("?" + query.toString().substring(1)); + return template.expand(singles).toString(); } } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SampleZuulProxyApplicationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SampleZuulProxyApplicationTests.java index c8eb7bfe99..401442128a 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SampleZuulProxyApplicationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SampleZuulProxyApplicationTests.java @@ -51,6 +51,7 @@ import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.netflix.client.ClientException; @@ -109,6 +110,28 @@ public void simpleHostRouteDefaultIgnoredHeader() { result.getHeaders().get("X-Application-Context").toString()); } + @Test + public void simpleHostRouteWithQuery() { + this.routes.addRoute("/self/**", "http://localhost:" + this.port + "/"); + this.endpoint.reset(); + ResponseEntity result = new TestRestTemplate().exchange( + "http://localhost:" + this.port + "/self/query?foo=bar", HttpMethod.GET, + new HttpEntity<>((Void) null), String.class); + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertEquals("/query?foo=bar", result.getBody()); + } + + @Test + public void simpleHostRouteWithEncodedQuery() { + this.routes.addRoute("/self/**", "http://localhost:" + this.port + "/"); + this.endpoint.reset(); + ResponseEntity result = new TestRestTemplate().exchange( + "http://localhost:" + this.port + "/self/query?foo={foo}", HttpMethod.GET, + new HttpEntity<>((Void) null), String.class, "weird#chars"); + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertEquals("/query?foo=weird#chars", result.getBody()); + } + @Test public void ribbonCommandForbidden() { ResponseEntity result = new TestRestTemplate().exchange( @@ -166,6 +189,11 @@ public ResponseEntity addHeader(HttpServletRequest request) { return result; } + @RequestMapping(value = "/query") + public String addQuery(HttpServletRequest request, @RequestParam String foo) { + return request.getRequestURI() + "?foo=" + foo; + } + @Bean public RibbonCommandFactory ribbonCommandFactory( SpringClientFactory clientFactory) { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/ZuulProxyTestBase.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/ZuulProxyTestBase.java index bb4784c47e..dae18b17ba 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/ZuulProxyTestBase.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/ZuulProxyTestBase.java @@ -54,8 +54,8 @@ public abstract class ZuulProxyTestBase { @Before public void setTestRequestcontext() { - RequestContext context = new RequestContext(); - RequestContext.testSetCurrentContext(context); + RequestContext.testSetCurrentContext(null); + RequestContext.getCurrentContext().unset(); } protected String getRoute(String path) { @@ -157,7 +157,7 @@ public void simpleHostRouteWithSpace() { } @Test - public void simpleHostRouteWithOriginalQString() { + public void simpleHostRouteWithOriginalQueryString() { this.routes.addRoute("/self/**", "http://localhost:" + this.port); this.endpoint.reset();