Skip to content

Commit b82da55

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 b8200a3 commit b82da55

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
}
@@ -372,14 +373,13 @@ public static Function<ServerRequest, ServerRequest> setPath(String path) {
372373
UriTemplate uriTemplate = new UriTemplate(path);
373374

374375
return request -> {
376+
MvcUtils.addOriginalRequestUrl(request, request.uri());
375377
Map<String, Object> uriVariables = MvcUtils.getUriTemplateVariables(request);
376378
URI uri = uriTemplate.expand(uriVariables);
377379

378-
URI prefixedUri = UriComponentsBuilder.fromUri(request.uri())
379-
.replacePath(uri.getRawPath())
380-
.build(true)
381-
.toUri();
382-
return ServerRequest.from(request).uri(prefixedUri).build();
380+
URI newUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(uri.getRawPath()).build(true).toUri();
381+
MvcUtils.setRequestUrl(request, newUri);
382+
return ServerRequest.from(request).uri(newUri).build();
383383
};
384384
}
385385

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
@@ -239,21 +239,21 @@ public void setPathPostWorks() {
239239
public void stripPrefixWorks() {
240240
restClient.get()
241241
.uri("/long/path/to/get")
242+
.header("Host", "www.stripprefix.org")
242243
.exchange()
243244
.expectStatus()
244245
.isOk()
245246
.expectBody(Map.class)
246247
.consumeWith(res -> {
247248
Map<String, Object> map = res.getResponseBody();
248249
Map<String, Object> headers = getMap(map, "headers");
249-
assertThat(headers).containsKeys(
250-
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
250+
assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
251251
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
252252
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
253253
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
254254
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
255-
assertThat(headers).containsEntry(
256-
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to");
255+
assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
256+
"/long/path/to");
257257
assertThat(headers).containsEntry("X-Test", "stripPrefix");
258258
});
259259
}
@@ -272,18 +272,40 @@ public void stripPrefixPostWorks() {
272272
Map<String, Object> map = res.getResponseBody();
273273
assertThat(map).containsEntry("data", "hello");
274274
Map<String, Object> headers = getMap(map, "headers");
275-
assertThat(headers).containsKeys(
276-
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
275+
assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
277276
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
278277
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
279278
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
280279
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
281-
assertThat(headers).containsEntry(
282-
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to");
280+
assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
281+
"/long/path/to");
283282
assertThat(headers).containsEntry("X-Test", "stripPrefixPost");
284283
});
285284
}
286285

286+
@Test
287+
public void stripPrefixLbWorks() {
288+
restClient.get()
289+
.uri("/long/path/to/get")
290+
.header("Host", "www.stripprefixlb.org")
291+
.exchange()
292+
.expectStatus()
293+
.isOk()
294+
.expectBody(Map.class)
295+
.consumeWith(res -> {
296+
Map<String, Object> map = res.getResponseBody();
297+
Map<String, Object> headers = getMap(map, "headers");
298+
assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
299+
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
300+
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
301+
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
302+
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
303+
assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
304+
"/long/path/to");
305+
assertThat(headers).containsEntry("X-Test", "stripPrefix");
306+
});
307+
}
308+
287309
@Test
288310
public void setStatusGatewayRouterFunctionWorks() {
289311
restClient.get()
@@ -1083,20 +1105,21 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsSetPathPost() {
10831105
// @formatter:off
10841106
return route("testsetpath")
10851107
.route(POST("/mycustompath{extra}").and(host("**.setpathpost.org")), http())
1086-
.filter(new HttpbinUriResolver())
10871108
.filter(setPath("/{extra}"))
1109+
.filter(new HttpbinUriResolver())
10881110
.build();
10891111
// @formatter:on
10901112
}
10911113

10921114
@Bean
10931115
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefix() {
10941116
// @formatter:off
1095-
return route(GET("/long/path/to/get"), http())
1117+
return route("teststripprefix")
1118+
.route(GET("/long/path/to/get").and(host("**.stripprefix.org")), http())
10961119
.filter(stripPrefix(3))
10971120
.filter(addRequestHeader("X-Test", "stripPrefix"))
1098-
.filter(new HttpbinUriResolver(true))
1099-
.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, "teststripprefix");
1121+
.filter(new HttpbinUriResolver())
1122+
.build();
11001123
// @formatter:on
11011124
}
11021125

@@ -1107,7 +1130,19 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefixPost() {
11071130
.route(POST("/long/path/to/post").and(host("**.stripprefixpost.org")), http())
11081131
.filter(stripPrefix(3))
11091132
.filter(addRequestHeader("X-Test", "stripPrefixPost"))
1110-
.filter(new HttpbinUriResolver(true))
1133+
.filter(new HttpbinUriResolver())
1134+
.build();
1135+
// @formatter:on
1136+
}
1137+
1138+
@Bean
1139+
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefixLb() {
1140+
// @formatter:off
1141+
return route("teststripprefix")
1142+
.route(GET("/long/path/to/get").and(host("**.stripprefixlb.org")), http())
1143+
.filter(stripPrefix(3))
1144+
.filter(addRequestHeader("X-Test", "stripPrefix"))
1145+
.filter(lb("httpbin"))
11111146
.build();
11121147
// @formatter:on
11131148
}
@@ -1442,8 +1477,8 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsRequestHeaderToReque
14421477
return route("requestheadertorequesturi")
14431478
.route(cloudFoundryRouteService().and(host("**.requestheadertorequesturi.org")), http())
14441479
//.before(new HttpbinUriResolver()) NO URI RESOLVER!
1445-
.before(requestHeaderToRequestUri("X-CF-Forwarded-Url"))
14461480
.filter(setPath("/hello"))
1481+
.before(requestHeaderToRequestUri("X-CF-Forwarded-Url"))
14471482
.build();
14481483
// @formatter:on
14491484
}

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)