Support multi-value X-Forwarded-Prefix headers

Prior to this commit, the Forwarded headers for Spring MVC and Spring
WebFlux did not support multiple prefix values for the
`"X-Forwarded-Prefix"` HTTP header.

This commit splits and processes multiple prefixes defined in the
dedicated header.

Closes gh-25254
This commit is contained in:
Brian Clozel
2020-06-18 13:12:07 +02:00
parent bad81cef8a
commit 6615e9c5ef
4 changed files with 77 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,9 +47,13 @@ import static org.mockito.Mockito.mock;
public class ForwardedHeaderFilterTests {
private static final String X_FORWARDED_PROTO = "x-forwarded-proto"; // SPR-14372 (case insensitive)
private static final String X_FORWARDED_HOST = "x-forwarded-host";
private static final String X_FORWARDED_PORT = "x-forwarded-port";
private static final String X_FORWARDED_PREFIX = "x-forwarded-prefix";
private static final String X_FORWARDED_SSL = "x-forwarded-ssl";
@@ -350,6 +354,24 @@ public class ForwardedHeaderFilterTests {
assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/prefix/mvc-showcase");
}
@Test
void shouldConcatenatePrefixes() throws Exception {
this.request.addHeader(X_FORWARDED_PREFIX, "/first,/second");
this.request.setRequestURI("/mvc-showcase");
HttpServletRequest actual = filterAndGetWrappedRequest();
assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/first/second/mvc-showcase");
}
@Test
void shouldConcatenatePrefixesWithTrailingSlashes() throws Exception {
this.request.addHeader(X_FORWARDED_PREFIX, "/first/,/second//");
this.request.setRequestURI("/mvc-showcase");
HttpServletRequest actual = filterAndGetWrappedRequest();
assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/first/second/mvc-showcase");
}
@Test
public void requestURLNewStringBuffer() throws Exception {
this.request.addHeader(X_FORWARDED_PREFIX, "/prefix/");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,8 +40,7 @@ public class ForwardedHeaderTransformerTests {
@Test
public void removeOnly() {
void removeOnly() {
this.requestMutator.setRemoveOnly(true);
HttpHeaders headers = new HttpHeaders();
@@ -57,7 +56,7 @@ public class ForwardedHeaderTransformerTests {
}
@Test
public void xForwardedHeaders() throws Exception {
void xForwardedHeaders() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add("X-Forwarded-Host", "84.198.58.199");
headers.add("X-Forwarded-Port", "443");
@@ -70,7 +69,7 @@ public class ForwardedHeaderTransformerTests {
}
@Test
public void forwardedHeader() throws Exception {
void forwardedHeader() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add("Forwarded", "host=84.198.58.199;proto=https");
ServerHttpRequest request = this.requestMutator.apply(getRequest(headers));
@@ -80,7 +79,7 @@ public class ForwardedHeaderTransformerTests {
}
@Test
public void xForwardedPrefix() throws Exception {
void xForwardedPrefix() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add("X-Forwarded-Prefix", "/prefix");
ServerHttpRequest request = this.requestMutator.apply(getRequest(headers));
@@ -91,7 +90,7 @@ public class ForwardedHeaderTransformerTests {
}
@Test // gh-23305
public void xForwardedPrefixShouldNotLeadToDecodedPath() throws Exception {
void xForwardedPrefixShouldNotLeadToDecodedPath() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add("X-Forwarded-Prefix", "/prefix");
ServerHttpRequest request = MockServerHttpRequest
@@ -107,7 +106,7 @@ public class ForwardedHeaderTransformerTests {
}
@Test
public void xForwardedPrefixTrailingSlash() throws Exception {
void xForwardedPrefixTrailingSlash() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add("X-Forwarded-Prefix", "/prefix////");
ServerHttpRequest request = this.requestMutator.apply(getRequest(headers));
@@ -118,7 +117,7 @@ public class ForwardedHeaderTransformerTests {
}
@Test // SPR-17525
public void shouldNotDoubleEncode() throws Exception {
void shouldNotDoubleEncode() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add("Forwarded", "host=84.198.58.199;proto=https");
@@ -133,6 +132,28 @@ public class ForwardedHeaderTransformerTests {
assertForwardedHeadersRemoved(request);
}
@Test
void shouldConcatenatePrefixes() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add("X-Forwarded-Prefix", "/first,/second");
ServerHttpRequest request = this.requestMutator.apply(getRequest(headers));
assertThat(request.getURI()).isEqualTo(new URI("https://example.com/first/second/path"));
assertThat(request.getPath().value()).isEqualTo("/first/second/path");
assertForwardedHeadersRemoved(request);
}
@Test
void shouldConcatenatePrefixesWithTrailingSlashes() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add("X-Forwarded-Prefix", "/first/,/second//");
ServerHttpRequest request = this.requestMutator.apply(getRequest(headers));
assertThat(request.getURI()).isEqualTo(new URI("https://example.com/first/second/path"));
assertThat(request.getPath().value()).isEqualTo("/first/second/path");
assertForwardedHeadersRemoved(request);
}
private MockServerHttpRequest getRequest(HttpHeaders headers) {
return MockServerHttpRequest.get(BASE_URL).headers(headers).build();