Skip to content

Commit d6d8a28

Browse files
jrhenderson1988spencergibb
authored andcommitted
Adds support for saving original request URL to request attributes
This enables X-Forwarded-Prefix. Fixes gh-3354 Fixes gh-3443
1 parent a830c8e commit d6d8a28

File tree

5 files changed

+61
-8
lines changed

5 files changed

+61
-8
lines changed

spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Collection;
2626
import java.util.Collections;
2727
import java.util.HashMap;
28+
import java.util.LinkedHashSet;
2829
import java.util.List;
2930
import java.util.Map;
3031
import java.util.Optional;
@@ -72,6 +73,11 @@ public abstract class MvcUtils {
7273
*/
7374
public static final String GATEWAY_ATTRIBUTES_ATTR = qualify("gatewayAttributes");
7475

76+
/**
77+
* Gateway original request URL attribute name.
78+
*/
79+
public static final String GATEWAY_ORIGINAL_REQUEST_URL_ATTR = qualify("gatewayOriginalRequestUrl");
80+
7581
/**
7682
* Gateway request URL attribute name.
7783
*/
@@ -250,6 +256,15 @@ public static void setRequestUrl(ServerRequest request, URI url) {
250256
request.servletRequest().setAttribute(GATEWAY_REQUEST_URL_ATTR, url);
251257
}
252258

259+
public static void addOriginalRequestUrl(ServerRequest request, URI url) {
260+
LinkedHashSet<URI> urls = getAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
261+
if (urls == null) {
262+
urls = new LinkedHashSet<>();
263+
}
264+
urls.add(url);
265+
putAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR, urls);
266+
}
267+
253268
private record ByteArrayInputMessage(ServerRequest request, ByteArrayInputStream body) implements HttpInputMessage {
254269

255270
@Override

spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ public static Function<ServerRequest, ServerRequest> stripPrefix() {
389389

390390
public static Function<ServerRequest, ServerRequest> stripPrefix(int parts) {
391391
return request -> {
392+
MvcUtils.addOriginalRequestUrl(request, request.uri());
392393
// TODO: gateway url attributes
393394
String path = request.uri().getRawPath();
394395
// TODO: begin duplicate code from StripPrefixGatewayFilterFactory
@@ -414,6 +415,8 @@ public static Function<ServerRequest, ServerRequest> stripPrefix(int parts) {
414415
.replacePath(newPath.toString())
415416
.build(true)
416417
.toUri();
418+
MvcUtils.setRequestUrl(request, prefixedUri);
419+
417420
return ServerRequest.from(request).uri(prefixedUri).build();
418421
};
419422
}

spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/XForwardedRequestHeadersFilter.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.boot.context.properties.ConfigurationProperties;
2626
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
2727
import org.springframework.boot.context.properties.PropertyMapper;
28+
import org.springframework.cloud.gateway.server.mvc.common.MvcUtils;
2829
import org.springframework.core.Ordered;
2930
import org.springframework.http.HttpHeaders;
3031
import org.springframework.util.ObjectUtils;
@@ -397,18 +398,15 @@ public HttpHeaders apply(HttpHeaders input, ServerRequest request) {
397398
// - see XForwardedHeadersFilterTests, so first get uris, then extract paths
398399
// and remove one from another if it's the ending part.
399400

400-
LinkedHashSet<URI> originalUris = null; // TODO:
401-
// exchange.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
402-
URI requestUri = null; // TODO:
403-
// exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
401+
LinkedHashSet<URI> originalUris = MvcUtils.getAttribute(request,
402+
MvcUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
403+
URI requestUri = MvcUtils.getAttribute(request, MvcUtils.GATEWAY_REQUEST_URL_ATTR);
404404

405405
if (originalUris != null && requestUri != null) {
406406

407407
originalUris.forEach(originalUri -> {
408408

409409
if (originalUri != null && originalUri.getPath() != null) {
410-
String prefix = originalUri.getPath();
411-
412410
// strip trailing slashes before checking if request path is end
413411
// of original path
414412
String originalUriPath = stripTrailingSlash(originalUri);

spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,14 @@ public void stripPrefixWorks() {
242242
.consumeWith(res -> {
243243
Map<String, Object> map = res.getResponseBody();
244244
Map<String, Object> headers = getMap(map, "headers");
245+
assertThat(headers).containsKeys(
246+
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
247+
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
248+
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
249+
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
250+
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
251+
assertThat(headers).containsEntry(
252+
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to");
245253
assertThat(headers).containsEntry("X-Test", "stripPrefix");
246254
});
247255
}
@@ -260,6 +268,14 @@ public void stripPrefixPostWorks() {
260268
Map<String, Object> map = res.getResponseBody();
261269
assertThat(map).containsEntry("data", "hello");
262270
Map<String, Object> headers = getMap(map, "headers");
271+
assertThat(headers).containsKeys(
272+
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
273+
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
274+
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
275+
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
276+
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
277+
assertThat(headers).containsEntry(
278+
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to");
263279
assertThat(headers).containsEntry("X-Test", "stripPrefixPost");
264280
});
265281
}
@@ -1068,9 +1084,9 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsSetPathPost() {
10681084
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefix() {
10691085
// @formatter:off
10701086
return route(GET("/long/path/to/get"), http())
1071-
.filter(new HttpbinUriResolver())
10721087
.filter(stripPrefix(3))
10731088
.filter(addRequestHeader("X-Test", "stripPrefix"))
1089+
.filter(new HttpbinUriResolver(true))
10741090
.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, "teststripprefix");
10751091
// @formatter:on
10761092
}
@@ -1080,9 +1096,9 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefixPost() {
10801096
// @formatter:off
10811097
return route("teststripprefixpost")
10821098
.route(POST("/long/path/to/post").and(host("**.stripprefixpost.org")), http())
1083-
.filter(new HttpbinUriResolver())
10841099
.filter(stripPrefix(3))
10851100
.filter(addRequestHeader("X-Test", "stripPrefixPost"))
1101+
.filter(new HttpbinUriResolver(true))
10861102
.build();
10871103
// @formatter:on
10881104
}

spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinUriResolver.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.cloud.gateway.server.mvc.test;
1818

1919
import java.net.URI;
20+
import java.net.URISyntaxException;
2021
import java.util.function.Function;
2122

2223
import org.springframework.cloud.gateway.server.mvc.common.MvcUtils;
@@ -30,12 +31,32 @@
3031
public class HttpbinUriResolver
3132
implements Function<ServerRequest, ServerRequest>, HandlerFilterFunction<ServerResponse, ServerResponse> {
3233

34+
private final boolean preservePath;
35+
36+
public HttpbinUriResolver(boolean preservePath) {
37+
this.preservePath = preservePath;
38+
}
39+
40+
public HttpbinUriResolver() {
41+
this(false);
42+
}
43+
3344
protected URI uri(ServerRequest request) {
3445
ApplicationContext context = MvcUtils.getApplicationContext(request);
3546
Integer port = context.getEnvironment().getProperty("httpbin.port", Integer.class);
3647
String host = context.getEnvironment().getProperty("httpbin.host");
3748
Assert.hasText(host, "httpbin.host is not set, did you initialize HttpbinTestcontainers?");
3849
Assert.notNull(port, "httpbin.port is not set, did you initialize HttpbinTestcontainers?");
50+
if (preservePath) {
51+
URI original = request.uri();
52+
try {
53+
return new URI("http", original.getUserInfo(), host, port, original.getPath(),
54+
original.getQuery(), original.getFragment());
55+
} catch (URISyntaxException e) {
56+
throw new IllegalArgumentException(e.getMessage(), e);
57+
}
58+
}
59+
3960
return URI.create(String.format("http://%s:%d", host, port));
4061
}
4162

0 commit comments

Comments
 (0)