Updates for forwarded prefix header.
Adjusts all path related filters to add the original uri and set the request url See gh-3443
This commit is contained in:
@@ -256,13 +256,11 @@ public abstract class MvcUtils {
|
|||||||
request.servletRequest().setAttribute(GATEWAY_REQUEST_URL_ATTR, url);
|
request.servletRequest().setAttribute(GATEWAY_REQUEST_URL_ATTR, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public static void addOriginalRequestUrl(ServerRequest request, URI url) {
|
public static void addOriginalRequestUrl(ServerRequest request, URI url) {
|
||||||
LinkedHashSet<URI> urls = getAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
|
LinkedHashSet<URI> urls = (LinkedHashSet<URI>) request.attributes()
|
||||||
if (urls == null) {
|
.computeIfAbsent(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, s -> new LinkedHashSet<>());
|
||||||
urls = new LinkedHashSet<>();
|
|
||||||
}
|
|
||||||
urls.add(url);
|
urls.add(url);
|
||||||
putAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR, urls);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private record ByteArrayInputMessage(ServerRequest request, ByteArrayInputStream body) implements HttpInputMessage {
|
private record ByteArrayInputMessage(ServerRequest request, ByteArrayInputStream body) implements HttpInputMessage {
|
||||||
|
|||||||
@@ -189,12 +189,14 @@ public abstract class BeforeFilterFunctions {
|
|||||||
final UriTemplate uriTemplate = new UriTemplate(prefix);
|
final UriTemplate uriTemplate = new UriTemplate(prefix);
|
||||||
|
|
||||||
return request -> {
|
return request -> {
|
||||||
|
MvcUtils.addOriginalRequestUrl(request, request.uri());
|
||||||
Map<String, Object> uriVariables = MvcUtils.getUriTemplateVariables(request);
|
Map<String, Object> uriVariables = MvcUtils.getUriTemplateVariables(request);
|
||||||
URI uri = uriTemplate.expand(uriVariables);
|
URI uri = uriTemplate.expand(uriVariables);
|
||||||
|
|
||||||
String newPath = uri.getRawPath() + request.uri().getRawPath();
|
String newPath = uri.getRawPath() + request.uri().getRawPath();
|
||||||
|
|
||||||
URI prefixedUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).build().toUri();
|
URI prefixedUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).build().toUri();
|
||||||
|
MvcUtils.setRequestUrl(request, prefixedUri);
|
||||||
return ServerRequest.from(request).uri(prefixedUri).build();
|
return ServerRequest.from(request).uri(prefixedUri).build();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -326,7 +328,7 @@ public abstract class BeforeFilterFunctions {
|
|||||||
String normalizedReplacement = replacement.replace("$\\", "$");
|
String normalizedReplacement = replacement.replace("$\\", "$");
|
||||||
Pattern pattern = Pattern.compile(regexp);
|
Pattern pattern = Pattern.compile(regexp);
|
||||||
return request -> {
|
return request -> {
|
||||||
// TODO: original request url
|
MvcUtils.addOriginalRequestUrl(request, request.uri());
|
||||||
String path = request.uri().getRawPath();
|
String path = request.uri().getRawPath();
|
||||||
String newPath = pattern.matcher(path).replaceAll(normalizedReplacement);
|
String newPath = pattern.matcher(path).replaceAll(normalizedReplacement);
|
||||||
|
|
||||||
@@ -334,8 +336,7 @@ public abstract class BeforeFilterFunctions {
|
|||||||
|
|
||||||
ServerRequest modified = ServerRequest.from(request).uri(rewrittenUri).build();
|
ServerRequest modified = ServerRequest.from(request).uri(rewrittenUri).build();
|
||||||
|
|
||||||
// TODO: can this be restored at some point?
|
MvcUtils.setRequestUrl(request, rewrittenUri);
|
||||||
// MvcUtils.setRequestUrl(modified, modified.uri());
|
|
||||||
return modified;
|
return modified;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -372,14 +373,13 @@ public abstract class BeforeFilterFunctions {
|
|||||||
UriTemplate uriTemplate = new UriTemplate(path);
|
UriTemplate uriTemplate = new UriTemplate(path);
|
||||||
|
|
||||||
return request -> {
|
return request -> {
|
||||||
|
MvcUtils.addOriginalRequestUrl(request, request.uri());
|
||||||
Map<String, Object> uriVariables = MvcUtils.getUriTemplateVariables(request);
|
Map<String, Object> uriVariables = MvcUtils.getUriTemplateVariables(request);
|
||||||
URI uri = uriTemplate.expand(uriVariables);
|
URI uri = uriTemplate.expand(uriVariables);
|
||||||
|
|
||||||
URI prefixedUri = UriComponentsBuilder.fromUri(request.uri())
|
URI newUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(uri.getRawPath()).build(true).toUri();
|
||||||
.replacePath(uri.getRawPath())
|
MvcUtils.setRequestUrl(request, newUri);
|
||||||
.build(true)
|
return ServerRequest.from(request).uri(newUri).build();
|
||||||
.toUri();
|
|
||||||
return ServerRequest.from(request).uri(prefixedUri).build();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ public abstract class LoadBalancerFilterFunctions {
|
|||||||
public static HandlerFilterFunction<ServerResponse, ServerResponse> lb(String serviceId,
|
public static HandlerFilterFunction<ServerResponse, ServerResponse> lb(String serviceId,
|
||||||
BiFunction<ServiceInstance, URI, URI> reconstructUriFunction) {
|
BiFunction<ServiceInstance, URI, URI> reconstructUriFunction) {
|
||||||
return (request, next) -> {
|
return (request, next) -> {
|
||||||
|
MvcUtils.addOriginalRequestUrl(request, request.uri());
|
||||||
|
|
||||||
LoadBalancerClientFactory clientFactory = getApplicationContext(request)
|
LoadBalancerClientFactory clientFactory = getApplicationContext(request)
|
||||||
.getBean(LoadBalancerClientFactory.class);
|
.getBean(LoadBalancerClientFactory.class);
|
||||||
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
|
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ public class ServerMvcIntegrationTests {
|
|||||||
public void stripPrefixWorks() {
|
public void stripPrefixWorks() {
|
||||||
restClient.get()
|
restClient.get()
|
||||||
.uri("/long/path/to/get")
|
.uri("/long/path/to/get")
|
||||||
|
.header("Host", "www.stripprefix.org")
|
||||||
.exchange()
|
.exchange()
|
||||||
.expectStatus()
|
.expectStatus()
|
||||||
.isOk()
|
.isOk()
|
||||||
@@ -246,14 +247,13 @@ public class ServerMvcIntegrationTests {
|
|||||||
.consumeWith(res -> {
|
.consumeWith(res -> {
|
||||||
Map<String, Object> map = res.getResponseBody();
|
Map<String, Object> map = res.getResponseBody();
|
||||||
Map<String, Object> headers = getMap(map, "headers");
|
Map<String, Object> headers = getMap(map, "headers");
|
||||||
assertThat(headers).containsKeys(
|
assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
|
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
|
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
|
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
|
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
|
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
|
||||||
assertThat(headers).containsEntry(
|
assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to");
|
"/long/path/to");
|
||||||
assertThat(headers).containsEntry("X-Test", "stripPrefix");
|
assertThat(headers).containsEntry("X-Test", "stripPrefix");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -272,18 +272,40 @@ public class ServerMvcIntegrationTests {
|
|||||||
Map<String, Object> map = res.getResponseBody();
|
Map<String, Object> map = res.getResponseBody();
|
||||||
assertThat(map).containsEntry("data", "hello");
|
assertThat(map).containsEntry("data", "hello");
|
||||||
Map<String, Object> headers = getMap(map, "headers");
|
Map<String, Object> headers = getMap(map, "headers");
|
||||||
assertThat(headers).containsKeys(
|
assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
|
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
|
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
|
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
|
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
|
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
|
||||||
assertThat(headers).containsEntry(
|
assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
|
||||||
XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to");
|
"/long/path/to");
|
||||||
assertThat(headers).containsEntry("X-Test", "stripPrefixPost");
|
assertThat(headers).containsEntry("X-Test", "stripPrefixPost");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void stripPrefixLbWorks() {
|
||||||
|
restClient.get()
|
||||||
|
.uri("/long/path/to/get")
|
||||||
|
.header("Host", "www.stripprefixlb.org")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
.expectBody(Map.class)
|
||||||
|
.consumeWith(res -> {
|
||||||
|
Map<String, Object> map = res.getResponseBody();
|
||||||
|
Map<String, Object> headers = getMap(map, "headers");
|
||||||
|
assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
|
||||||
|
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
|
||||||
|
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
|
||||||
|
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER,
|
||||||
|
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER);
|
||||||
|
assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER,
|
||||||
|
"/long/path/to");
|
||||||
|
assertThat(headers).containsEntry("X-Test", "stripPrefix");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setStatusGatewayRouterFunctionWorks() {
|
public void setStatusGatewayRouterFunctionWorks() {
|
||||||
restClient.get()
|
restClient.get()
|
||||||
@@ -1083,8 +1105,8 @@ public class ServerMvcIntegrationTests {
|
|||||||
// @formatter:off
|
// @formatter:off
|
||||||
return route("testsetpath")
|
return route("testsetpath")
|
||||||
.route(POST("/mycustompath{extra}").and(host("**.setpathpost.org")), http())
|
.route(POST("/mycustompath{extra}").and(host("**.setpathpost.org")), http())
|
||||||
.filter(new HttpbinUriResolver())
|
|
||||||
.filter(setPath("/{extra}"))
|
.filter(setPath("/{extra}"))
|
||||||
|
.filter(new HttpbinUriResolver())
|
||||||
.build();
|
.build();
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
@@ -1092,11 +1114,12 @@ public class ServerMvcIntegrationTests {
|
|||||||
@Bean
|
@Bean
|
||||||
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefix() {
|
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefix() {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
return route(GET("/long/path/to/get"), http())
|
return route("teststripprefix")
|
||||||
|
.route(GET("/long/path/to/get").and(host("**.stripprefix.org")), http())
|
||||||
.filter(stripPrefix(3))
|
.filter(stripPrefix(3))
|
||||||
.filter(addRequestHeader("X-Test", "stripPrefix"))
|
.filter(addRequestHeader("X-Test", "stripPrefix"))
|
||||||
.filter(new HttpbinUriResolver(true))
|
.filter(new HttpbinUriResolver())
|
||||||
.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, "teststripprefix");
|
.build();
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1107,7 +1130,19 @@ public class ServerMvcIntegrationTests {
|
|||||||
.route(POST("/long/path/to/post").and(host("**.stripprefixpost.org")), http())
|
.route(POST("/long/path/to/post").and(host("**.stripprefixpost.org")), http())
|
||||||
.filter(stripPrefix(3))
|
.filter(stripPrefix(3))
|
||||||
.filter(addRequestHeader("X-Test", "stripPrefixPost"))
|
.filter(addRequestHeader("X-Test", "stripPrefixPost"))
|
||||||
.filter(new HttpbinUriResolver(true))
|
.filter(new HttpbinUriResolver())
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefixLb() {
|
||||||
|
// @formatter:off
|
||||||
|
return route("teststripprefix")
|
||||||
|
.route(GET("/long/path/to/get").and(host("**.stripprefixlb.org")), http())
|
||||||
|
.filter(stripPrefix(3))
|
||||||
|
.filter(addRequestHeader("X-Test", "stripPrefix"))
|
||||||
|
.filter(lb("httpbin"))
|
||||||
.build();
|
.build();
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
@@ -1442,8 +1477,8 @@ public class ServerMvcIntegrationTests {
|
|||||||
return route("requestheadertorequesturi")
|
return route("requestheadertorequesturi")
|
||||||
.route(cloudFoundryRouteService().and(host("**.requestheadertorequesturi.org")), http())
|
.route(cloudFoundryRouteService().and(host("**.requestheadertorequesturi.org")), http())
|
||||||
//.before(new HttpbinUriResolver()) NO URI RESOLVER!
|
//.before(new HttpbinUriResolver()) NO URI RESOLVER!
|
||||||
.before(requestHeaderToRequestUri("X-CF-Forwarded-Url"))
|
|
||||||
.filter(setPath("/hello"))
|
.filter(setPath("/hello"))
|
||||||
|
.before(requestHeaderToRequestUri("X-CF-Forwarded-Url"))
|
||||||
.build();
|
.build();
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.gateway.server.mvc.test;
|
package org.springframework.cloud.gateway.server.mvc.test;
|
||||||
|
|
||||||
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -31,33 +32,21 @@ import org.springframework.web.servlet.function.ServerResponse;
|
|||||||
public class HttpbinUriResolver
|
public class HttpbinUriResolver
|
||||||
implements Function<ServerRequest, ServerRequest>, HandlerFilterFunction<ServerResponse, ServerResponse> {
|
implements Function<ServerRequest, ServerRequest>, HandlerFilterFunction<ServerResponse, ServerResponse> {
|
||||||
|
|
||||||
private final boolean preservePath;
|
|
||||||
|
|
||||||
public HttpbinUriResolver(boolean preservePath) {
|
|
||||||
this.preservePath = preservePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpbinUriResolver() {
|
|
||||||
this(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected URI uri(ServerRequest request) {
|
protected URI uri(ServerRequest request) {
|
||||||
ApplicationContext context = MvcUtils.getApplicationContext(request);
|
ApplicationContext context = MvcUtils.getApplicationContext(request);
|
||||||
Integer port = context.getEnvironment().getProperty("httpbin.port", Integer.class);
|
Integer port = context.getEnvironment().getProperty("httpbin.port", Integer.class);
|
||||||
String host = context.getEnvironment().getProperty("httpbin.host");
|
String host = context.getEnvironment().getProperty("httpbin.host");
|
||||||
Assert.hasText(host, "httpbin.host is not set, did you initialize HttpbinTestcontainers?");
|
Assert.hasText(host, "httpbin.host is not set, did you initialize HttpbinTestcontainers?");
|
||||||
Assert.notNull(port, "httpbin.port is not set, did you initialize HttpbinTestcontainers?");
|
Assert.notNull(port, "httpbin.port is not set, did you initialize HttpbinTestcontainers?");
|
||||||
if (preservePath) {
|
|
||||||
URI original = request.uri();
|
URI original = request.uri();
|
||||||
try {
|
try {
|
||||||
return new URI("http", original.getUserInfo(), host, port, original.getPath(),
|
return new URI("http", original.getUserInfo(), host, port, original.getPath(), original.getQuery(),
|
||||||
original.getQuery(), original.getFragment());
|
original.getFragment());
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
throw new IllegalArgumentException(e.getMessage(), e);
|
|
||||||
}
|
}
|
||||||
|
catch (URISyntaxException e) {
|
||||||
|
throw new UndeclaredThrowableException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return URI.create(String.format("http://%s:%d", host, port));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user