Added option to not decode URL. Fixes gh-2178 (#3306)

* Added a way for consumers to override the default behavior or decoding and re-encoding URL using zuul.decodeUrl property.
* Fixed gh-2178
This commit is contained in:
bhattankit
2018-12-06 15:22:53 -05:00
committed by Ryan Baxter
parent 60bb89293c
commit a1f7bab91a
5 changed files with 53 additions and 1 deletions

16
docs/src/main/asciidoc/spring-cloud-netflix.adoc Normal file → Executable file
View File

@@ -1491,6 +1491,22 @@ NOTE: This special flag works only with `SimpleHostRoutingFilter`. Also, you loo
query parameters with `RequestContext.getCurrentContext().setRequestQueryParams(someOverriddenParameters)`, because
the query string is now fetched directly on the original `HttpServletRequest`.
=== Request URI Encoding
When processing the incoming request, request URI is decoded before matching them to routes.
The request URI is then re-encoded when the back end request is rebuilt in the route filters.
This can cause some unexpected behavior if your URI includes the encoded "/" character.
To use the original request URI, it is possible to pass a special flag to 'ZuulProperties' so that the URI will be taken as is with the `HttpServletRequest::getRequestURI` method, as shown in the following example:
.application.yml
[source,yaml]
----
zuul:
decodeUrl: false
----
NOTE: If you are overriding request URI using `requestURI` RequestContext attribute and this flag is set to false, then the URL set in the request context will not be encoded. It will be your responsibility to make sure the URL is already encoded.
=== Plain Embedded Zuul
If you use `@EnableZuulServer` (instead of `@EnableZuulProxy`), you can also run a Zuul server without proxying or selectively switch on parts of the proxying platform.

View File

@@ -78,6 +78,8 @@ public class ProxyRequestHelper {
private boolean addHostHeader = false;
private boolean urlDecoded = true;
@Deprecated
//TODO Remove in 2.1.x
public ProxyRequestHelper() {}
@@ -86,6 +88,7 @@ public class ProxyRequestHelper {
this.ignoredHeaders.addAll(zuulProperties.getIgnoredHeaders());
this.traceRequestBody = zuulProperties.isTraceRequestBody();
this.addHostHeader = zuulProperties.isAddHostHeader();
this.urlDecoded = zuulProperties.isDecodeUrl();
}
public void setWhitelistHosts(Set<String> whitelistHosts) {
@@ -114,7 +117,10 @@ public class ProxyRequestHelper {
String contextURI = (String) context.get(REQUEST_URI_KEY);
if (contextURI != null) {
try {
uri = UriUtils.encodePath(contextURI, characterEncoding(request));
uri = contextURI;
if (this.urlDecoded) {
uri = UriUtils.encodePath(contextURI, characterEncoding(request));
}
}
catch (Exception e) {
log.debug(

View File

@@ -140,6 +140,11 @@ public class ZuulProperties {
*/
private boolean removeSemicolonContent = true;
/**
* Flag to indicate whether to decode the matched URL or use it as is.
*/
private boolean decodeUrl = true;
/**
* List of sensitive headers that are not passed to downstream requests. Defaults to a
* "safe" set of headers that commonly contain user credentials. It's OK to remove
@@ -764,6 +769,14 @@ public class ZuulProperties {
this.removeSemicolonContent = removeSemicolonContent;
}
public boolean isDecodeUrl() {
return decodeUrl;
}
public void setDecodeUrl(boolean decodeUrl) {
this.decodeUrl = decodeUrl;
}
public Set<String> getSensitiveHeaders() {
return sensitiveHeaders;
}

View File

@@ -88,6 +88,7 @@ public class PreDecorationFilter extends ZuulFilter {
this.routeLocator = routeLocator;
this.properties = properties;
this.urlPathHelper.setRemoveSemicolonContent(properties.isRemoveSemicolonContent());
this.urlPathHelper.setUrlDecode(properties.isDecodeUrl());
this.dispatcherServletPath = dispatcherServletPath;
this.proxyRequestHelper = proxyRequestHelper;
}

View File

@@ -329,6 +329,22 @@ public class PreDecorationFilterTests {
getHeader(ctx.getOriginResponseHeaders(), "x-zuul-serviceid"));
}
@Test
public void dontDecodeUrl() {
this.properties.setPrefix("/api");
this.properties.setStripPrefix(true);
this.properties.setDecodeUrl(false);
this.request.setRequestURI("/api/foo/encoded%2Fpath");
this.request.setContextPath("/context-path");
this.routeLocator.addRoute(
new ZuulRoute("foo", "/foo/**", "foo", null, false, null, null));
this.filter = new PreDecorationFilter(this.routeLocator, "/", this.properties,
this.proxyRequestHelper);
this.filter.run();
RequestContext ctx = RequestContext.getCurrentContext();
assertEquals("/foo/encoded%2Fpath", ctx.get(REQUEST_URI_KEY));
}
@Test
public void routeIgnoreContextPathIfPrefixHeader() {
this.properties.setStripPrefix(false);