Adds support for saving original request URL to request attributes
This enables X-Forwarded-Prefix. Fixes gh-3354 Fixes gh-3443
This commit is contained in:
committed by
spencergibb
parent
88fce67f9d
commit
f6e68fac8e
@@ -25,6 +25,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -72,6 +73,11 @@ public abstract class MvcUtils {
|
||||
*/
|
||||
public static final String GATEWAY_ATTRIBUTES_ATTR = qualify("gatewayAttributes");
|
||||
|
||||
/**
|
||||
* Gateway original request URL attribute name.
|
||||
*/
|
||||
public static final String GATEWAY_ORIGINAL_REQUEST_URL_ATTR = qualify("gatewayOriginalRequestUrl");
|
||||
|
||||
/**
|
||||
* Gateway request URL attribute name.
|
||||
*/
|
||||
@@ -250,6 +256,15 @@ public abstract class MvcUtils {
|
||||
request.servletRequest().setAttribute(GATEWAY_REQUEST_URL_ATTR, url);
|
||||
}
|
||||
|
||||
public static void addOriginalRequestUrl(ServerRequest request, URI url) {
|
||||
LinkedHashSet<URI> urls = getAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
|
||||
if (urls == null) {
|
||||
urls = new LinkedHashSet<>();
|
||||
}
|
||||
urls.add(url);
|
||||
putAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR, urls);
|
||||
}
|
||||
|
||||
private record ByteArrayInputMessage(ServerRequest request, ByteArrayInputStream body) implements HttpInputMessage {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -389,6 +389,7 @@ public abstract class BeforeFilterFunctions {
|
||||
|
||||
public static Function<ServerRequest, ServerRequest> stripPrefix(int parts) {
|
||||
return request -> {
|
||||
MvcUtils.addOriginalRequestUrl(request, request.uri());
|
||||
// TODO: gateway url attributes
|
||||
String path = request.uri().getRawPath();
|
||||
// TODO: begin duplicate code from StripPrefixGatewayFilterFactory
|
||||
@@ -414,6 +415,8 @@ public abstract class BeforeFilterFunctions {
|
||||
.replacePath(newPath.toString())
|
||||
.build(true)
|
||||
.toUri();
|
||||
MvcUtils.setRequestUrl(request, prefixedUri);
|
||||
|
||||
return ServerRequest.from(request).uri(prefixedUri).build();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.Map;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.cloud.gateway.server.mvc.common.MvcUtils;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -397,18 +398,15 @@ public class XForwardedRequestHeadersFilter implements HttpHeadersFilter.Request
|
||||
// - see XForwardedHeadersFilterTests, so first get uris, then extract paths
|
||||
// and remove one from another if it's the ending part.
|
||||
|
||||
LinkedHashSet<URI> originalUris = null; // TODO:
|
||||
// exchange.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
|
||||
URI requestUri = null; // TODO:
|
||||
// exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
LinkedHashSet<URI> originalUris = MvcUtils.getAttribute(request,
|
||||
MvcUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
|
||||
URI requestUri = MvcUtils.getAttribute(request, MvcUtils.GATEWAY_REQUEST_URL_ATTR);
|
||||
|
||||
if (originalUris != null && requestUri != null) {
|
||||
|
||||
originalUris.forEach(originalUri -> {
|
||||
|
||||
if (originalUri != null && originalUri.getPath() != null) {
|
||||
String prefix = originalUri.getPath();
|
||||
|
||||
// strip trailing slashes before checking if request path is end
|
||||
// of original path
|
||||
String originalUriPath = stripTrailingSlash(originalUri);
|
||||
|
||||
@@ -242,6 +242,14 @@ public class ServerMvcIntegrationTests {
|
||||
.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");
|
||||
});
|
||||
}
|
||||
@@ -260,6 +268,14 @@ public class ServerMvcIntegrationTests {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).containsEntry("data", "hello");
|
||||
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", "stripPrefixPost");
|
||||
});
|
||||
}
|
||||
@@ -1068,9 +1084,9 @@ public class ServerMvcIntegrationTests {
|
||||
public RouterFunction<ServerResponse> gatewayRouterFunctionsStripPrefix() {
|
||||
// @formatter:off
|
||||
return route(GET("/long/path/to/get"), http())
|
||||
.filter(new HttpbinUriResolver())
|
||||
.filter(stripPrefix(3))
|
||||
.filter(addRequestHeader("X-Test", "stripPrefix"))
|
||||
.filter(new HttpbinUriResolver(true))
|
||||
.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, "teststripprefix");
|
||||
// @formatter:on
|
||||
}
|
||||
@@ -1080,9 +1096,9 @@ public class ServerMvcIntegrationTests {
|
||||
// @formatter:off
|
||||
return route("teststripprefixpost")
|
||||
.route(POST("/long/path/to/post").and(host("**.stripprefixpost.org")), http())
|
||||
.filter(new HttpbinUriResolver())
|
||||
.filter(stripPrefix(3))
|
||||
.filter(addRequestHeader("X-Test", "stripPrefixPost"))
|
||||
.filter(new HttpbinUriResolver(true))
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.gateway.server.mvc.test;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.cloud.gateway.server.mvc.common.MvcUtils;
|
||||
@@ -30,12 +31,32 @@ import org.springframework.web.servlet.function.ServerResponse;
|
||||
public class HttpbinUriResolver
|
||||
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) {
|
||||
ApplicationContext context = MvcUtils.getApplicationContext(request);
|
||||
Integer port = context.getEnvironment().getProperty("httpbin.port", Integer.class);
|
||||
String host = context.getEnvironment().getProperty("httpbin.host");
|
||||
Assert.hasText(host, "httpbin.host 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();
|
||||
try {
|
||||
return new URI("http", original.getUserInfo(), host, port, original.getPath(),
|
||||
original.getQuery(), original.getFragment());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return URI.create(String.format("http://%s:%d", host, port));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user