This commit is contained in:
Mengqi Xu 2025-07-01 21:25:01 +05:00 committed by GitHub
commit 8521008f99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 219 additions and 3 deletions

View File

@ -57,6 +57,8 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
private @Nullable InetSocketAddress remoteAddress; private @Nullable InetSocketAddress remoteAddress;
private @Nullable InetSocketAddress localAddress;
private final Flux<DataBuffer> body; private final Flux<DataBuffer> body;
private final ServerHttpRequest originalRequest; private final ServerHttpRequest originalRequest;
@ -131,10 +133,16 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
return this; return this;
} }
@Override
public ServerHttpRequest.Builder localAddress(InetSocketAddress localAddress) {
this.localAddress = localAddress;
return this;
}
@Override @Override
public ServerHttpRequest build() { public ServerHttpRequest build() {
return new MutatedServerHttpRequest(getUriToUse(), this.contextPath, return new MutatedServerHttpRequest(getUriToUse(), this.contextPath,
this.httpMethod, this.sslInfo, this.remoteAddress, this.headers, this.body, this.originalRequest); this.httpMethod, this.sslInfo, this.remoteAddress, this.localAddress, this.headers, this.body, this.originalRequest);
} }
private URI getUriToUse() { private URI getUriToUse() {
@ -182,16 +190,19 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
private final @Nullable InetSocketAddress remoteAddress; private final @Nullable InetSocketAddress remoteAddress;
private final @Nullable InetSocketAddress localAddress;
private final Flux<DataBuffer> body; private final Flux<DataBuffer> body;
private final ServerHttpRequest originalRequest; private final ServerHttpRequest originalRequest;
public MutatedServerHttpRequest(URI uri, @Nullable String contextPath, public MutatedServerHttpRequest(URI uri, @Nullable String contextPath,
HttpMethod method, @Nullable SslInfo sslInfo, @Nullable InetSocketAddress remoteAddress, HttpMethod method, @Nullable SslInfo sslInfo, @Nullable InetSocketAddress remoteAddress, @Nullable InetSocketAddress localAddress,
HttpHeaders headers, Flux<DataBuffer> body, ServerHttpRequest originalRequest) { HttpHeaders headers, Flux<DataBuffer> body, ServerHttpRequest originalRequest) {
super(method, uri, contextPath, headers); super(method, uri, contextPath, headers);
this.remoteAddress = (remoteAddress != null ? remoteAddress : originalRequest.getRemoteAddress()); this.remoteAddress = (remoteAddress != null ? remoteAddress : originalRequest.getRemoteAddress());
this.localAddress = (localAddress != null ? localAddress : originalRequest.getLocalAddress());
this.sslInfo = (sslInfo != null ? sslInfo : originalRequest.getSslInfo()); this.sslInfo = (sslInfo != null ? sslInfo : originalRequest.getSslInfo());
this.body = body; this.body = body;
this.originalRequest = originalRequest; this.originalRequest = originalRequest;
@ -204,7 +215,7 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
@Override @Override
public @Nullable InetSocketAddress getLocalAddress() { public @Nullable InetSocketAddress getLocalAddress() {
return this.originalRequest.getLocalAddress(); return this.localAddress;
} }
@Override @Override

View File

@ -184,6 +184,12 @@ public interface ServerHttpRequest extends HttpRequest, ReactiveHttpInputMessage
*/ */
Builder remoteAddress(InetSocketAddress remoteAddress); Builder remoteAddress(InetSocketAddress remoteAddress);
/**
* Set the address of the local client.
* @since 7.x
*/
Builder localAddress(InetSocketAddress localAddress);
/** /**
* Build a {@link ServerHttpRequest} decorator with the mutated properties. * Build a {@link ServerHttpRequest} decorator with the mutated properties.
*/ */

View File

@ -73,6 +73,7 @@ import org.springframework.web.util.WebUtils;
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Rob Winch * @author Rob Winch
* @author Brian Clozel * @author Brian Clozel
* @author Mengqi Xu
* @since 4.3 * @since 4.3
* @see <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a> * @see <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a>
* @see <a href="https://docs.spring.io/spring-framework/reference/web/webmvc/filters.html#filters-forwarded-headers">Forwarded Headers</a> * @see <a href="https://docs.spring.io/spring-framework/reference/web/webmvc/filters.html#filters-forwarded-headers">Forwarded Headers</a>
@ -92,6 +93,7 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter {
FORWARDED_HEADER_NAMES.add("X-Forwarded-Prefix"); FORWARDED_HEADER_NAMES.add("X-Forwarded-Prefix");
FORWARDED_HEADER_NAMES.add("X-Forwarded-Ssl"); FORWARDED_HEADER_NAMES.add("X-Forwarded-Ssl");
FORWARDED_HEADER_NAMES.add("X-Forwarded-For"); FORWARDED_HEADER_NAMES.add("X-Forwarded-For");
FORWARDED_HEADER_NAMES.add("X-Forwarded-By");
} }
@ -255,6 +257,8 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter {
private final @Nullable InetSocketAddress remoteAddress; private final @Nullable InetSocketAddress remoteAddress;
private final @Nullable InetSocketAddress localAddress;
private final ForwardedPrefixExtractor forwardedPrefixExtractor; private final ForwardedPrefixExtractor forwardedPrefixExtractor;
ForwardedHeaderExtractingRequest(HttpServletRequest servletRequest) { ForwardedHeaderExtractingRequest(HttpServletRequest servletRequest) {
@ -272,6 +276,7 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter {
this.port = (port == -1 ? (this.secure ? 443 : 80) : port); this.port = (port == -1 ? (this.secure ? 443 : 80) : port);
this.remoteAddress = ForwardedHeaderUtils.parseForwardedFor(uri, headers, request.getRemoteAddress()); this.remoteAddress = ForwardedHeaderUtils.parseForwardedFor(uri, headers, request.getRemoteAddress());
this.localAddress = ForwardedHeaderUtils.parseForwardedBy(uri, headers, request.getLocalAddress());
// Use Supplier as Tomcat updates delegate request on FORWARD // Use Supplier as Tomcat updates delegate request on FORWARD
Supplier<HttpServletRequest> requestSupplier = () -> (HttpServletRequest) getRequest(); Supplier<HttpServletRequest> requestSupplier = () -> (HttpServletRequest) getRequest();
@ -330,6 +335,16 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter {
return (this.remoteAddress != null ? this.remoteAddress.getPort() : super.getRemotePort()); return (this.remoteAddress != null ? this.remoteAddress.getPort() : super.getRemotePort());
} }
@Override
public @Nullable String getLocalAddr() {
return (this.localAddress != null ? this.localAddress.getHostString() : super.getLocalAddr());
}
@Override
public int getLocalPort() {
return (this.localAddress != null ? this.localAddress.getPort() : super.getLocalPort());
}
@SuppressWarnings("DataFlowIssue") @SuppressWarnings("DataFlowIssue")
@Override @Override
public @Nullable Object getAttribute(String name) { public @Nullable Object getAttribute(String name) {

View File

@ -55,6 +55,7 @@ import org.springframework.web.util.UriComponents;
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Mengqi Xu
* @since 5.1 * @since 5.1
* @see <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a> * @see <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a>
* @see <a href="https://docs.spring.io/spring-framework/reference/web/webflux/reactive-spring.html#webflux-forwarded-headers">Forwarded Headers</a> * @see <a href="https://docs.spring.io/spring-framework/reference/web/webflux/reactive-spring.html#webflux-forwarded-headers">Forwarded Headers</a>
@ -72,6 +73,7 @@ public class ForwardedHeaderTransformer implements Function<ServerHttpRequest, S
FORWARDED_HEADER_NAMES.add("X-Forwarded-Prefix"); FORWARDED_HEADER_NAMES.add("X-Forwarded-Prefix");
FORWARDED_HEADER_NAMES.add("X-Forwarded-Ssl"); FORWARDED_HEADER_NAMES.add("X-Forwarded-Ssl");
FORWARDED_HEADER_NAMES.add("X-Forwarded-For"); FORWARDED_HEADER_NAMES.add("X-Forwarded-For");
FORWARDED_HEADER_NAMES.add("X-Forwarded-By");
} }
@ -119,6 +121,11 @@ public class ForwardedHeaderTransformer implements Function<ServerHttpRequest, S
if (remoteAddress != null) { if (remoteAddress != null) {
builder.remoteAddress(remoteAddress); builder.remoteAddress(remoteAddress);
} }
InetSocketAddress localAddress = request.getLocalAddress();
localAddress = ForwardedHeaderUtils.parseForwardedBy(originalUri, headers, localAddress);
if (localAddress != null) {
builder.localAddress(localAddress);
}
} }
removeForwardedHeaders(builder); removeForwardedHeaders(builder);
request = builder.build(); request = builder.build();

View File

@ -54,6 +54,7 @@ public abstract class ForwardedHeaderUtils {
private static final Pattern FORWARDED_FOR_PATTERN = Pattern.compile("(?i:for)=" + FORWARDED_VALUE); private static final Pattern FORWARDED_FOR_PATTERN = Pattern.compile("(?i:for)=" + FORWARDED_VALUE);
private static final Pattern FORWARDED_BY_PATTERN = Pattern.compile("(?i:by)=" + FORWARDED_VALUE);
/** /**
* Adapt the scheme+host+port of the given {@link URI} from the "Forwarded" header * Adapt the scheme+host+port of the given {@link URI} from the "Forwarded" header
@ -189,4 +190,57 @@ public abstract class ForwardedHeaderUtils {
return null; return null;
} }
/**
* Parse the first "Forwarded: by=..." or "X-Forwarded-By" header value to
* an {@code InetSocketAddress} representing the address of the server.
* @param uri the request {@code URI}
* @param headers the request headers that may contain forwarded headers
* @param localAddress the current local address
* @return an {@code InetSocketAddress} with the extracted host and port, or
* {@code null} if the headers are not present
* @see <a href="https://tools.ietf.org/html/rfc7239#section-5.1">RFC 7239, Section 5.1</a>
*/
public static @Nullable InetSocketAddress parseForwardedBy(
URI uri, HttpHeaders headers, @Nullable InetSocketAddress localAddress) {
int port = (localAddress != null ?
localAddress.getPort() : "https".equals(uri.getScheme()) ? 443 : 80);
String forwardedHeader = headers.getFirst("Forwarded");
if (StringUtils.hasText(forwardedHeader)) {
String forwardedToUse = StringUtils.tokenizeToStringArray(forwardedHeader, ",")[0];
Matcher matcher = FORWARDED_BY_PATTERN.matcher(forwardedToUse);
if (matcher.find()) {
String value = matcher.group(1).trim();
String host = value;
int portSeparatorIdx = value.lastIndexOf(':');
int squareBracketIdx = value.lastIndexOf(']');
if (portSeparatorIdx > squareBracketIdx) {
if (squareBracketIdx == -1 && value.indexOf(':') != portSeparatorIdx) {
throw new IllegalArgumentException("Invalid IPv4 address: " + value);
}
host = value.substring(0, portSeparatorIdx);
try {
port = Integer.parseInt(value, portSeparatorIdx + 1, value.length(), 10);
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException(
"Failed to parse a port from \"forwarded\"-type header value: " + value);
}
}
return InetSocketAddress.createUnresolved(host, port);
}
}
String byHeader = headers.getFirst("X-Forwarded-By");
if (StringUtils.hasText(byHeader)) {
String host = StringUtils.tokenizeToStringArray(byHeader, ",")[0];
boolean ipv6 = (host.indexOf(':') != -1);
host = (ipv6 && !host.startsWith("[") && !host.endsWith("]") ? "[" + host + "]" : host);
return InetSocketAddress.createUnresolved(host, port);
}
return null;
}
} }

View File

@ -49,6 +49,7 @@ import static org.mockito.Mockito.mock;
* @author Rob Winch * @author Rob Winch
* @author Brian Clozel * @author Brian Clozel
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Mengqi Xu
*/ */
class ForwardedHeaderFilterTests { class ForwardedHeaderFilterTests {
@ -66,6 +67,8 @@ class ForwardedHeaderFilterTests {
private static final String X_FORWARDED_FOR = "x-forwarded-for"; private static final String X_FORWARDED_FOR = "x-forwarded-for";
private static final String X_FORWARDED_BY = "x-forwarded-by";
private final ForwardedHeaderFilter filter = new ForwardedHeaderFilter(); private final ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
@ -93,6 +96,7 @@ class ForwardedHeaderFilterTests {
testShouldFilter(X_FORWARDED_SSL); testShouldFilter(X_FORWARDED_SSL);
testShouldFilter(X_FORWARDED_PREFIX); testShouldFilter(X_FORWARDED_PREFIX);
testShouldFilter(X_FORWARDED_FOR); testShouldFilter(X_FORWARDED_FOR);
testShouldFilter(X_FORWARDED_BY);
} }
private void testShouldFilter(String headerName) { private void testShouldFilter(String headerName) {
@ -115,6 +119,7 @@ class ForwardedHeaderFilterTests {
this.request.addHeader(X_FORWARDED_PORT, "443"); this.request.addHeader(X_FORWARDED_PORT, "443");
this.request.addHeader("foo", "bar"); this.request.addHeader("foo", "bar");
this.request.addHeader(X_FORWARDED_FOR, "[203.0.113.195]"); this.request.addHeader(X_FORWARDED_FOR, "[203.0.113.195]");
this.request.addHeader(X_FORWARDED_BY, "[203.0.113.196]");
this.filter.doFilter(this.request, new MockHttpServletResponse(), this.filterChain); this.filter.doFilter(this.request, new MockHttpServletResponse(), this.filterChain);
HttpServletRequest actual = (HttpServletRequest) this.filterChain.getRequest(); HttpServletRequest actual = (HttpServletRequest) this.filterChain.getRequest();
@ -126,11 +131,13 @@ class ForwardedHeaderFilterTests {
assertThat(actual.getServerPort()).isEqualTo(443); assertThat(actual.getServerPort()).isEqualTo(443);
assertThat(actual.isSecure()).isTrue(); assertThat(actual.isSecure()).isTrue();
assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("[203.0.113.195]"); assertThat(actual.getRemoteAddr()).isEqualTo(actual.getRemoteHost()).isEqualTo("[203.0.113.195]");
assertThat(actual.getLocalAddr()).isEqualTo(actual.getLocalAddr()).isEqualTo("[203.0.113.196]");
assertThat(actual.getHeader(X_FORWARDED_PROTO)).isNull(); assertThat(actual.getHeader(X_FORWARDED_PROTO)).isNull();
assertThat(actual.getHeader(X_FORWARDED_HOST)).isNull(); assertThat(actual.getHeader(X_FORWARDED_HOST)).isNull();
assertThat(actual.getHeader(X_FORWARDED_PORT)).isNull(); assertThat(actual.getHeader(X_FORWARDED_PORT)).isNull();
assertThat(actual.getHeader(X_FORWARDED_FOR)).isNull(); assertThat(actual.getHeader(X_FORWARDED_FOR)).isNull();
assertThat(actual.getHeader(X_FORWARDED_BY)).isNull();
assertThat(actual.getHeader("foo")).isEqualTo("bar"); assertThat(actual.getHeader("foo")).isEqualTo("bar");
} }
@ -143,6 +150,7 @@ class ForwardedHeaderFilterTests {
this.request.addHeader(X_FORWARDED_SSL, "on"); this.request.addHeader(X_FORWARDED_SSL, "on");
this.request.addHeader("foo", "bar"); this.request.addHeader("foo", "bar");
this.request.addHeader(X_FORWARDED_FOR, "203.0.113.195"); this.request.addHeader(X_FORWARDED_FOR, "203.0.113.195");
this.request.addHeader(X_FORWARDED_BY, "203.0.113.196");
this.filter.setRemoveOnly(true); this.filter.setRemoveOnly(true);
this.filter.doFilter(this.request, new MockHttpServletResponse(), this.filterChain); this.filter.doFilter(this.request, new MockHttpServletResponse(), this.filterChain);
@ -156,12 +164,14 @@ class ForwardedHeaderFilterTests {
assertThat(actual.isSecure()).isFalse(); assertThat(actual.isSecure()).isFalse();
assertThat(actual.getRemoteAddr()).isEqualTo(MockHttpServletRequest.DEFAULT_REMOTE_ADDR); assertThat(actual.getRemoteAddr()).isEqualTo(MockHttpServletRequest.DEFAULT_REMOTE_ADDR);
assertThat(actual.getRemoteHost()).isEqualTo(MockHttpServletRequest.DEFAULT_REMOTE_HOST); assertThat(actual.getRemoteHost()).isEqualTo(MockHttpServletRequest.DEFAULT_REMOTE_HOST);
assertThat(actual.getLocalAddr()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_ADDR);
assertThat(actual.getHeader(X_FORWARDED_PROTO)).isNull(); assertThat(actual.getHeader(X_FORWARDED_PROTO)).isNull();
assertThat(actual.getHeader(X_FORWARDED_HOST)).isNull(); assertThat(actual.getHeader(X_FORWARDED_HOST)).isNull();
assertThat(actual.getHeader(X_FORWARDED_PORT)).isNull(); assertThat(actual.getHeader(X_FORWARDED_PORT)).isNull();
assertThat(actual.getHeader(X_FORWARDED_SSL)).isNull(); assertThat(actual.getHeader(X_FORWARDED_SSL)).isNull();
assertThat(actual.getHeader(X_FORWARDED_FOR)).isNull(); assertThat(actual.getHeader(X_FORWARDED_FOR)).isNull();
assertThat(actual.getHeader(X_FORWARDED_BY)).isNull();
assertThat(actual.getHeader("foo")).isEqualTo("bar"); assertThat(actual.getHeader("foo")).isEqualTo("bar");
} }
@ -541,6 +551,83 @@ class ForwardedHeaderFilterTests {
} }
@Nested
class ForwardedBy {
@Test
void xForwardedForEmpty() throws Exception {
request.addHeader(X_FORWARDED_BY, "");
HttpServletRequest actual = filterAndGetWrappedRequest();
assertThat(actual.getLocalAddr()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_ADDR);
assertThat(actual.getLocalPort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT);
}
@Test
void xForwardedForSingleIdentifier() throws Exception {
request.addHeader(X_FORWARDED_BY, "203.0.113.195");
HttpServletRequest actual = filterAndGetWrappedRequest();
assertThat(actual.getLocalAddr()).isEqualTo(actual.getLocalAddr()).isEqualTo("203.0.113.195");
assertThat(actual.getLocalPort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT);
}
@Test
void xForwardedForMultipleIdentifiers() throws Exception {
request.addHeader(X_FORWARDED_BY, "203.0.113.195, 70.41.3.18, 150.172.238.178");
HttpServletRequest actual = filterAndGetWrappedRequest();
assertThat(actual.getLocalAddr()).isEqualTo(actual.getLocalAddr()).isEqualTo("203.0.113.195");
assertThat(actual.getLocalPort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT);
}
@Test
void forwardedForIpV4Identifier() throws Exception {
request.addHeader(FORWARDED, "By=203.0.113.195");
HttpServletRequest actual = filterAndGetWrappedRequest();
assertThat(actual.getLocalAddr()).isEqualTo(actual.getLocalAddr()).isEqualTo("203.0.113.195");
assertThat(actual.getLocalPort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT);
}
@Test
void forwardedForIpV6Identifier() throws Exception {
request.addHeader(FORWARDED, "By=\"[2001:db8:cafe::17]\"");
HttpServletRequest actual = filterAndGetWrappedRequest();
assertThat(actual.getLocalAddr()).isEqualTo(actual.getLocalAddr()).isEqualTo("[2001:db8:cafe::17]");
assertThat(actual.getLocalPort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT);
}
@Test
void forwardedForIpV4IdentifierWithPort() throws Exception {
request.addHeader(FORWARDED, "By=\"203.0.113.195:47011\"");
HttpServletRequest actual = filterAndGetWrappedRequest();
assertThat(actual.getLocalAddr()).isEqualTo(actual.getLocalAddr()).isEqualTo("203.0.113.195");
assertThat(actual.getLocalPort()).isEqualTo(47011);
}
@Test
void forwardedForIpV6IdentifierWithPort() throws Exception {
request.addHeader(FORWARDED, "By=\"[2001:db8:cafe::17]:47011\"");
HttpServletRequest actual = filterAndGetWrappedRequest();
assertThat(actual.getLocalAddr()).isEqualTo(actual.getLocalAddr()).isEqualTo("[2001:db8:cafe::17]");
assertThat(actual.getLocalPort()).isEqualTo(47011);
}
@Test
void forwardedForMultipleIdentifiers() throws Exception {
request.addHeader(FORWARDED, "by=203.0.113.195;proto=http, by=\"[2001:db8:cafe::17]\", by=unknown");
HttpServletRequest actual = filterAndGetWrappedRequest();
assertThat(actual.getLocalAddr()).isEqualTo(actual.getLocalAddr()).isEqualTo("203.0.113.195");
assertThat(actual.getLocalPort()).isEqualTo(MockHttpServletRequest.DEFAULT_SERVER_PORT);
}
}
@Nested @Nested
class SendRedirect { class SendRedirect {

View File

@ -33,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Mengqi Xu
*/ */
class ForwardedHeaderTransformerTests { class ForwardedHeaderTransformerTests {
@ -52,6 +53,7 @@ class ForwardedHeaderTransformerTests {
headers.add("X-Forwarded-Prefix", "prefix"); headers.add("X-Forwarded-Prefix", "prefix");
headers.add("X-Forwarded-Ssl", "on"); headers.add("X-Forwarded-Ssl", "on");
headers.add("X-Forwarded-For", "203.0.113.195"); headers.add("X-Forwarded-For", "203.0.113.195");
headers.add("X-Forwarded-By", "203.0.113.196");
ServerHttpRequest request = this.requestMutator.apply(getRequest(headers)); ServerHttpRequest request = this.requestMutator.apply(getRequest(headers));
assertForwardedHeadersRemoved(request); assertForwardedHeadersRemoved(request);
@ -233,6 +235,40 @@ class ForwardedHeaderTransformerTests {
assertThat(request.getRemoteAddress().getHostName()).isEqualTo("203.0.113.195"); assertThat(request.getRemoteAddress().getHostName()).isEqualTo("203.0.113.195");
} }
@Test
void forwardedBy() {
HttpHeaders headers = new HttpHeaders();
headers.add("Forwarded", "by=\"203.0.113.195:4711\";host=84.198.58.199;proto=https");
InetSocketAddress localAddress = new InetSocketAddress("example.client", 47011);
ServerHttpRequest request = MockServerHttpRequest
.method(HttpMethod.GET, URI.create("https://example.com/a%20b?q=a%2Bb"))
.localAddress(localAddress)
.headers(headers)
.build();
request = this.requestMutator.apply(request);
assertThat(request.getLocalAddress()).isNotNull();
assertThat(request.getLocalAddress().getHostName()).isEqualTo("203.0.113.195");
assertThat(request.getLocalAddress().getPort()).isEqualTo(4711);
}
@Test
void xForwardedBy() {
HttpHeaders headers = new HttpHeaders();
headers.add("x-forwarded-by", "203.0.113.195, 70.41.3.18, 150.172.238.178");
ServerHttpRequest request = MockServerHttpRequest
.method(HttpMethod.GET, URI.create("https://example.com/a%20b?q=a%2Bb"))
.headers(headers)
.build();
request = this.requestMutator.apply(request);
assertThat(request.getLocalAddress()).isNotNull();
assertThat(request.getLocalAddress().getHostName()).isEqualTo("203.0.113.195");
}
private MockServerHttpRequest getRequest(HttpHeaders headers) { private MockServerHttpRequest getRequest(HttpHeaders headers) {
return MockServerHttpRequest.get(BASE_URL).headers(headers).build(); return MockServerHttpRequest.get(BASE_URL).headers(headers).build();