Skip to content

Commit 5cc6323

Browse files
committed
Updates for forwarded prefix header.
Adjusts all path related filters to add the original uri and set the request url See gh-3443
1 parent d6d8a28 commit 5cc6323

File tree

5 files changed

+70
-46
lines changed

5 files changed

+70
-46
lines changed

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,13 +256,11 @@ public static void setRequestUrl(ServerRequest request, URI url) {
256256
request.servletRequest().setAttribute(GATEWAY_REQUEST_URL_ATTR, url);
257257
}
258258

259+
@SuppressWarnings("unchecked")
259260
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-
}
261+
LinkedHashSet<URI> urls = (LinkedHashSet<URI>) request.attributes()
262+
.computeIfAbsent(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, s -> new LinkedHashSet<>());
264263
urls.add(url);
265-
putAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR, urls);
266264
}
267265

268266
private record ByteArrayInputMessage(ServerRequest request, ByteArrayInputStream body) implements HttpInputMessage {

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,14 @@ public static Function<ServerRequest, ServerRequest> prefixPath(String prefix) {
189189
final UriTemplate uriTemplate = new UriTemplate(prefix);
190190

191191
return request -> {
192+
MvcUtils.addOriginalRequestUrl(request, request.uri());
192193
Map<String, Object> uriVariables = MvcUtils.getUriTemplateVariables(request);
193194
URI uri = uriTemplate.expand(uriVariables);
194195

195196
String newPath = uri.getRawPath() + request.uri().getRawPath();
196197

197198
URI prefixedUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).build().toUri();
199+
MvcUtils.setRequestUrl(request, prefixedUri);
198200
return ServerRequest.from(request).uri(prefixedUri).build();
199201
};
200202
}
@@ -326,16 +328,15 @@ public static Function<ServerRequest, ServerRequest> rewritePath(String regexp,
326328
String normalizedReplacement = replacement.replace("$\\", "$");
327329
Pattern pattern = Pattern.compile(regexp);
328330
return request -> {
329-
// TODO: original request url
331+
MvcUtils.addOriginalRequestUrl(request, request.uri());
330332
String path = request.uri().getRawPath();
331333
String newPath = pattern.matcher(path).replaceAll(normalizedReplacement);
332334

333335
URI rewrittenUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).build().toUri();
334336

335337
ServerRequest modified = ServerRequest.from(request).uri(rewrittenUri).build();
336338

337-
// TODO: can this be restored at some point?
338-
// MvcUtils.setRequestUrl(modified, modified.uri());
339+
MvcUtils.setRequestUrl(request, rewrittenUri);
339340
return modified;
340341
};
341342
}
@@ -351,14 +352,13 @@ public static Function<ServerRequest, ServerRequest> setPath(String path) {
351352
UriTemplate uriTemplate = new UriTemplate(path);
352353

353354
return request -> {
355+
MvcUtils.addOriginalRequestUrl(request, request.uri());
354356
Map<String, Object> uriVariables = MvcUtils.getUriTemplateVariables(request);
355357
URI uri = uriTemplate.expand(uriVariables);
356358

357-
URI prefixedUri = UriComponentsBuilder.fromUri(request.uri())
358-
.replacePath(uri.getRawPath())
359-
.build(true)
360-
.toUri();
361-
return ServerRequest.from(request).uri(prefixedUri).build();
359+
URI newUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(uri.getRawPath()).build(true).toUri();
360+
MvcUtils.setRequestUrl(request, newUri);
361+
return ServerRequest.from(request).uri(newUri).build();
362362
};
363363
}
364364

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ public static HandlerFilterFunction<ServerResponse, ServerResponse> lb(String se
6363
public static HandlerFilterFunction<ServerResponse, ServerResponse> lb(String serviceId,
6464
BiFunction<ServiceInstance, URI, URI> reconstructUriFunction) {
6565
return (request, next) -> {
66+
MvcUtils.addOriginalRequestUrl(request, request.uri());
67+
6668
LoadBalancerClientFactory clientFactory = getApplicationContext(request)
6769
.getBean(LoadBalancerClientFactory.class);
6870
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator

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

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -235,21 +235,21 @@ public void setPathPostWorks() {
235235
public void stripPrefixWorks() {
236236
restClient.get()
237237
.uri("/long/path/to/get")
238+
.header("Host", "www.stripprefix.org")
238239
.exchange()
239240
.expectStatus()
240241
.isOk()
241242
.expectBody(Map.class)
242243
.consumeWith(res -> {
243244
Map<String, Object> map = res.getResponseBody();
244245
Map<String, Object> headers = getMap(map, "headers");
245-
assertThat(headers).containsKeys(
246-
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
246+
assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
247247
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
248248
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
249249
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
250250
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
251-
assertThat(headers).containsEntry(
252-
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to");
251+
assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
252+
"/long/path/to");
253253
assertThat(headers).containsEntry("X-Test", "stripPrefix");
254254
});
255255
}
@@ -268,18 +268,40 @@ public void stripPrefixPostWorks() {
268268
Map<String, Object> map = res.getResponseBody();
269269
assertThat(map).containsEntry("data", "hello");
270270
Map<String, Object> headers = getMap(map, "headers");
271-
assertThat(headers).containsKeys(
272-
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
271+
assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
273272
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
274273
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
275274
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
276275
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
277-
assertThat(headers).containsEntry(
278-
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to");
276+
assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
277+
"/long/path/to");
279278
assertThat(headers).containsEntry("X-Test", "stripPrefixPost");
280279
});
281280
}
282281

282+
@Test
283+
public void stripPrefixLbWorks() {
284+
restClient.get()
285+
.uri("/long/path/to/get")
286+
.header("Host", "www.stripprefixlb.org")
287+
.exchange()
288+
.expectStatus()
289+
.isOk()
290+
.expectBody(Map.class)
291+
.consumeWith(res -> {
292+
Map<String, Object> map = res.getResponseBody();
293+
Map<String, Object> headers = getMap(map, "headers");
294+
assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
295+
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
296+
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
297+
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
298+
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
299+
assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
300+
"/long/path/to");
301+
assertThat(headers).containsEntry("X-Test", "stripPrefix");
302+
});
303+
}
304+
283305
@Test
284306
public void setStatusGatewayRouterFunctionWorks() {
285307
restClient.get()
@@ -1074,20 +1096,21 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsSetPathPost() {
10741096
// @formatter:off
10751097
return route("testsetpath")
10761098
.route(POST("/mycustompath{extra}").and(host("**.setpathpost.org")), http())
1077-
.filter(new HttpbinUriResolver())
10781099
.filter(setPath("/{extra}"))
1100+
.filter(new HttpbinUriResolver())
10791101
.build();
10801102
// @formatter:on
10811103
}
10821104

10831105
@Bean
10841106
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefix() {
10851107
// @formatter:off
1086-
return route(GET("/long/path/to/get"), http())
1108+
return route("teststripprefix")
1109+
.route(GET("/long/path/to/get").and(host("**.stripprefix.org")), http())
10871110
.filter(stripPrefix(3))
10881111
.filter(addRequestHeader("X-Test", "stripPrefix"))
1089-
.filter(new HttpbinUriResolver(true))
1090-
.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, "teststripprefix");
1112+
.filter(new HttpbinUriResolver())
1113+
.build();
10911114
// @formatter:on
10921115
}
10931116

@@ -1098,7 +1121,19 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefixPost() {
10981121
.route(POST("/long/path/to/post").and(host("**.stripprefixpost.org")), http())
10991122
.filter(stripPrefix(3))
11001123
.filter(addRequestHeader("X-Test", "stripPrefixPost"))
1101-
.filter(new HttpbinUriResolver(true))
1124+
.filter(new HttpbinUriResolver())
1125+
.build();
1126+
// @formatter:on
1127+
}
1128+
1129+
@Bean
1130+
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefixLb() {
1131+
// @formatter:off
1132+
return route("teststripprefix")
1133+
.route(GET("/long/path/to/get").and(host("**.stripprefixlb.org")), http())
1134+
.filter(stripPrefix(3))
1135+
.filter(addRequestHeader("X-Test", "stripPrefix"))
1136+
.filter(lb("httpbin"))
11021137
.build();
11031138
// @formatter:on
11041139
}
@@ -1433,8 +1468,8 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsRequestHeaderToReque
14331468
return route("requestheadertorequesturi")
14341469
.route(cloudFoundryRouteService().and(host("**.requestheadertorequesturi.org")), http())
14351470
//.before(new HttpbinUriResolver()) NO URI RESOLVER!
1436-
.before(requestHeaderToRequestUri("X-CF-Forwarded-Url"))
14371471
.filter(setPath("/hello"))
1472+
.before(requestHeaderToRequestUri("X-CF-Forwarded-Url"))
14381473
.build();
14391474
// @formatter:on
14401475
}

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

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.cloud.gateway.server.mvc.test;
1818

19+
import java.lang.reflect.UndeclaredThrowableException;
1920
import java.net.URI;
2021
import java.net.URISyntaxException;
2122
import java.util.function.Function;
@@ -31,33 +32,21 @@
3132
public class HttpbinUriResolver
3233
implements Function<ServerRequest, ServerRequest>, HandlerFilterFunction<ServerResponse, ServerResponse> {
3334

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-
4435
protected URI uri(ServerRequest request) {
4536
ApplicationContext context = MvcUtils.getApplicationContext(request);
4637
Integer port = context.getEnvironment().getProperty("httpbin.port", Integer.class);
4738
String host = context.getEnvironment().getProperty("httpbin.host");
4839
Assert.hasText(host, "httpbin.host is not set, did you initialize HttpbinTestcontainers?");
4940
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-
}
41+
URI original = request.uri();
42+
try {
43+
return new URI("http", original.getUserInfo(), host, port, original.getPath(), original.getQuery(),
44+
original.getFragment());
45+
}
46+
catch (URISyntaxException e) {
47+
throw new UndeclaredThrowableException(e);
5848
}
5949

60-
return URI.create(String.format("http://%s:%d", host, port));
6150
}
6251

6352
@Override

0 commit comments

Comments
 (0)