diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/webserver.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/webserver.adoc
index e61358d17c2..4536cd27766 100644
--- a/documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/webserver.adoc
+++ b/documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/webserver.adoc
@@ -517,8 +517,8 @@ If your web server of choice supports the set of HTTP headers you need, setting
| xref:how-to:webserver.adoc#howto.webserver.use-behind-a-proxy-server.tomcat[]
| Jetty
-| `"X-Forwarded-*"`
-|
+| `"X-Forwarded-*"`, `"Forwarded"`
+| xref:how-to:webserver.adoc#howto.webserver.use-behind-a-proxy-server.jetty[]
| Reactor Netty
| `"X-Forwarded-*"`
@@ -569,6 +569,24 @@ You can take complete control of the configuration of Tomcat's javadoc:org.apach
+[[howto.webserver.use-behind-a-proxy-server.jetty]]
+=== Customize Jetty's Proxy Configuration
+
+If you use Jetty, you can additionally configure the type of the headers used to carry "`forwarded`" information.
+By default, the `"X-Forwarded-*"` format is used, but you can opt into the standard RFC variant:
+
+[configprops,yaml]
+----
+server:
+ jetty:
+ forwarded-headers:
+ header-format: "standard"
+----
+
+For more specific options, you can switch off the forwarded configuration (using `server.forward-headers-strategy=NONE`) and directly use Jetty's javadoc:org.eclipse.jetty.server.ForwardedRequestCustomizer[] to change the HTTP configuration.
+
+
+
[[howto.webserver.enable-multiple-connectors]]
== Enable Multiple Connectors
diff --git a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/ConfigurableJettyWebServerFactory.java b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/ConfigurableJettyWebServerFactory.java
index 4e794b0ed94..804f9fe0276 100644
--- a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/ConfigurableJettyWebServerFactory.java
+++ b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/ConfigurableJettyWebServerFactory.java
@@ -56,6 +56,15 @@ public interface ConfigurableJettyWebServerFactory extends ConfigurableWebServer
*/
void setUseForwardHeaders(boolean useForwardHeaders);
+ /**
+ * Set if the RFC forwarded header should be processed.
+ *
+ * {@link #setUseForwardHeaders(boolean)} will take precedence if enabled.
+ * @param useRfcForwardHeader if forwarded header should be used
+ * @since 4.2.0
+ */
+ void setUseRfcForwardHeader(boolean useRfcForwardHeader);
+
/**
* Add {@link JettyServerCustomizer}s that will be applied to the {@link Server}
* before it is started.
diff --git a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/ForwardHeadersCustomizer.java b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/ForwardHeadersCustomizer.java
index c1394ddcd06..2016719d028 100644
--- a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/ForwardHeadersCustomizer.java
+++ b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/ForwardHeadersCustomizer.java
@@ -30,9 +30,36 @@ import org.eclipse.jetty.server.Server;
*/
public class ForwardHeadersCustomizer implements JettyServerCustomizer {
+ private final boolean useXForwarded;
+
+ /**
+ * Create a new customizer instance.
+ * @param useXForwarded "X-Forwarded-*" headers are used if {@code true}, otherwise
+ * standard "Forwarded" are used instead.
+ * @since 4.2.0
+ */
+ public ForwardHeadersCustomizer(boolean useXForwarded) {
+ this.useXForwarded = useXForwarded;
+ }
+
+ /**
+ * Create a new customizer instance with "X-Forwarded-*" support.
+ */
+ public ForwardHeadersCustomizer() {
+ this(true);
+ }
+
@Override
public void customize(Server server) {
ForwardedRequestCustomizer customizer = new ForwardedRequestCustomizer();
+ if (this.useXForwarded) {
+ // disable "Forwarded" support
+ customizer.setForwardedHeader(null);
+ }
+ else {
+ // disable "X-Forwarded-*" support
+ customizer.setForwardedOnly(true);
+ }
for (Connector connector : server.getConnectors()) {
for (ConnectionFactory connectionFactory : connector.getConnectionFactories()) {
if (connectionFactory instanceof HttpConfiguration.ConnectionFactory jettyConnectionFactory) {
diff --git a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/JettyWebServerFactory.java b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/JettyWebServerFactory.java
index 13c569f995d..37829f4afc8 100644
--- a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/JettyWebServerFactory.java
+++ b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/JettyWebServerFactory.java
@@ -64,6 +64,8 @@ public class JettyWebServerFactory extends AbstractConfigurableWebServerFactory
private boolean useForwardHeaders;
+ private boolean useRfcForwardHeader;
+
private Set jettyServerCustomizers = new LinkedHashSet<>();
private int maxConnections = -1;
@@ -180,6 +182,15 @@ public class JettyWebServerFactory extends AbstractConfigurableWebServerFactory
this.useForwardHeaders = useForwardHeaders;
}
+ public boolean isUseRfcForwardHeader() {
+ return this.useRfcForwardHeader;
+ }
+
+ @Override
+ public void setUseRfcForwardHeader(boolean useRfcForwardHeader) {
+ this.useRfcForwardHeader = useRfcForwardHeader;
+ }
+
protected AbstractConnector createConnector(InetSocketAddress address, Server server) {
return this.createConnector(address, server, null, null, null);
}
diff --git a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/autoconfigure/JettyServerProperties.java b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/autoconfigure/JettyServerProperties.java
index cf6ab922c91..dba86721121 100644
--- a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/autoconfigure/JettyServerProperties.java
+++ b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/autoconfigure/JettyServerProperties.java
@@ -86,6 +86,11 @@ public class JettyServerProperties {
*/
private final Accesslog accesslog = new Accesslog();
+ /**
+ * Forwarded headers configuration.
+ */
+ private final Forwardedheaders forwardedHeaders = new Forwardedheaders();
+
/**
* Thread related configuration.
*/
@@ -95,6 +100,10 @@ public class JettyServerProperties {
return this.accesslog;
}
+ public Forwardedheaders getForwardedHeaders() {
+ return this.forwardedHeaders;
+ }
+
public Threads getThreads() {
return this.threads;
}
@@ -268,6 +277,43 @@ public class JettyServerProperties {
}
+ /**
+ * Forwarded headers.
+ */
+ public static class Forwardedheaders {
+
+ /**
+ * Format of the forwarded headers to support.
+ */
+ private HeaderFormat headerFormat = HeaderFormat.X_FORWARDED;
+
+ public HeaderFormat getHeaderFormat() {
+ return this.headerFormat;
+ }
+
+ public void setHeaderFormat(HeaderFormat headerFormat) {
+ this.headerFormat = headerFormat;
+ }
+
+ /**
+ * Formats of forwarded headers supported by {@link Forwardedheaders}.
+ */
+ public enum HeaderFormat {
+
+ /**
+ * Use the standard "Forwarded" header, as defined by RFC 7239.
+ */
+ STANDARD,
+
+ /**
+ * Use the non-standard "X-Forwarded-*" headers.
+ */
+ X_FORWARDED
+
+ }
+
+ }
+
/**
* Jetty thread properties.
*/
diff --git a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/autoconfigure/JettyWebServerFactoryCustomizer.java b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/autoconfigure/JettyWebServerFactoryCustomizer.java
index 8088cd35481..7e4e6127ca9 100644
--- a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/autoconfigure/JettyWebServerFactoryCustomizer.java
+++ b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/autoconfigure/JettyWebServerFactoryCustomizer.java
@@ -79,7 +79,9 @@ public class JettyWebServerFactoryCustomizer
@Override
public void customize(ConfigurableJettyWebServerFactory factory) {
- factory.setUseForwardHeaders(getOrDeduceUseForwardHeaders());
+ if (getOrDeduceUseForwardHeaders()) {
+ configureForwardedSupport(factory);
+ }
JettyServerProperties.Threads threadProperties = this.jettyProperties.getThreads();
factory.setThreadPool(JettyThreadPool.create(this.jettyProperties.getThreads()));
PropertyMapper map = PropertyMapper.get();
@@ -121,6 +123,13 @@ public class JettyWebServerFactoryCustomizer
return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE);
}
+ private void configureForwardedSupport(ConfigurableJettyWebServerFactory factory) {
+ switch (this.jettyProperties.getForwardedHeaders().getHeaderFormat()) {
+ case X_FORWARDED -> factory.setUseForwardHeaders(true);
+ case STANDARD -> factory.setUseRfcForwardHeader(true);
+ }
+ }
+
private Consumer customizeHttpConfigurations(ConfigurableJettyWebServerFactory factory,
BiConsumer action) {
return customizeConnectionFactories(factory, HttpConfiguration.ConnectionFactory.class,
diff --git a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/reactive/JettyReactiveWebServerFactory.java b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/reactive/JettyReactiveWebServerFactory.java
index 82ee17f02c3..5018d4e7d7f 100644
--- a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/reactive/JettyReactiveWebServerFactory.java
+++ b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/reactive/JettyReactiveWebServerFactory.java
@@ -118,7 +118,10 @@ public class JettyReactiveWebServerFactory extends JettyWebServerFactory
customizer.customize(server);
}
if (this.isUseForwardHeaders()) {
- new ForwardHeadersCustomizer().customize(server);
+ new ForwardHeadersCustomizer(true).customize(server);
+ }
+ else if (this.isUseRfcForwardHeader()) {
+ new ForwardHeadersCustomizer(false).customize(server);
}
if (getShutdown() == Shutdown.GRACEFUL) {
GracefulHandler gracefulHandler = new GracefulHandler();
diff --git a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/servlet/JettyServletWebServerFactory.java b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/servlet/JettyServletWebServerFactory.java
index 0dd9605395e..4a06baccb96 100644
--- a/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/servlet/JettyServletWebServerFactory.java
+++ b/module/spring-boot-jetty/src/main/java/org/springframework/boot/jetty/servlet/JettyServletWebServerFactory.java
@@ -177,7 +177,10 @@ public class JettyServletWebServerFactory extends JettyWebServerFactory
customizer.customize(server);
}
if (this.isUseForwardHeaders()) {
- new ForwardHeadersCustomizer().customize(server);
+ new ForwardHeadersCustomizer(true).customize(server);
+ }
+ else if (this.isUseRfcForwardHeader()) {
+ new ForwardHeadersCustomizer(false).customize(server);
}
if (getShutdown() == Shutdown.GRACEFUL) {
GracefulHandler gracefulHandler = new GracefulHandler();
diff --git a/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/ForwardHeadersCustomizerTests.java b/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/ForwardHeadersCustomizerTests.java
new file mode 100644
index 00000000000..4438eff2412
--- /dev/null
+++ b/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/ForwardHeadersCustomizerTests.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012-present 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.jetty;
+
+import org.eclipse.jetty.server.ForwardedRequestCustomizer;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link ForwardHeadersCustomizer}.
+ *
+ * @author Brian Clozel
+ */
+class ForwardHeadersCustomizerTests {
+
+ @Test
+ void defaultConstructorUsesXForwardedHeaders() {
+ ForwardedRequestCustomizer customizer = customize(new ForwardHeadersCustomizer());
+ assertThat(customizer.getForwardedHeader()).isNull();
+ assertThat(customizer.getForwardedForHeader()).isNotNull();
+ }
+
+ @Test
+ void useXForwardedTrueEnablesXForwardedAndDisablesForwarded() {
+ ForwardedRequestCustomizer customizer = customize(new ForwardHeadersCustomizer(true));
+ assertThat(customizer.getForwardedHeader()).isNull();
+ assertThat(customizer.getForwardedForHeader()).isNotNull();
+ }
+
+ @Test
+ void useXForwardedFalseEnablesForwardedAndDisablesXForwarded() {
+ ForwardedRequestCustomizer customizer = customize(new ForwardHeadersCustomizer(false));
+ assertThat(customizer.getForwardedHeader()).isNotNull();
+ assertThat(customizer.getForwardedForHeader()).isNull();
+ }
+
+ private ForwardedRequestCustomizer customize(ForwardHeadersCustomizer customizer) {
+ Server server = new Server(0);
+ customizer.customize(server);
+ HttpConfiguration httpConfiguration = ((HttpConfiguration.ConnectionFactory) server.getConnectors()[0]
+ .getConnectionFactories()
+ .stream()
+ .filter(HttpConfiguration.ConnectionFactory.class::isInstance)
+ .findFirst()
+ .orElseThrow()).getHttpConfiguration();
+ return httpConfiguration.getCustomizer(ForwardedRequestCustomizer.class);
+ }
+
+}
diff --git a/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/autoconfigure/JettyServerPropertiesTests.java b/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/autoconfigure/JettyServerPropertiesTests.java
index 4793e758588..dccbdb36f08 100644
--- a/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/autoconfigure/JettyServerPropertiesTests.java
+++ b/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/autoconfigure/JettyServerPropertiesTests.java
@@ -100,6 +100,19 @@ class JettyServerPropertiesTests {
assertThat(this.properties.getAccesslog().getIgnorePaths()).containsExactly("/a/path", "/b/path");
}
+ @Test
+ void forwardedHeadersDefaultToXForwarded() {
+ assertThat(this.properties.getForwardedHeaders().getHeaderFormat())
+ .isEqualTo(JettyServerProperties.Forwardedheaders.HeaderFormat.X_FORWARDED);
+ }
+
+ @Test
+ void testCustomizeJettyForwardedHeadersFormat() {
+ bind("server.jetty.forwarded-headers.header-format", "standard");
+ assertThat(this.properties.getForwardedHeaders().getHeaderFormat())
+ .isEqualTo(JettyServerProperties.Forwardedheaders.HeaderFormat.STANDARD);
+ }
+
@Test
void jettyThreadPoolPropertyDefaultsShouldMatchServerDefault() {
JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0);
diff --git a/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/autoconfigure/JettyWebServerFactoryCustomizerTests.java b/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/autoconfigure/JettyWebServerFactoryCustomizerTests.java
index 14a9c85a614..79fbb7d5f60 100644
--- a/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/autoconfigure/JettyWebServerFactoryCustomizerTests.java
+++ b/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/autoconfigure/JettyWebServerFactoryCustomizerTests.java
@@ -54,8 +54,10 @@ import org.springframework.test.context.support.TestPropertySourceUtils;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
/**
* Tests for {@link JettyWebServerFactoryCustomizer}.
@@ -94,7 +96,8 @@ class JettyWebServerFactoryCustomizerTests {
void defaultUseForwardHeaders() {
ConfigurableJettyWebServerFactory factory = mock(ConfigurableJettyWebServerFactory.class);
this.customizer.customize(factory);
- then(factory).should().setUseForwardHeaders(false);
+ then(factory).should(never()).setUseForwardHeaders(anyBoolean());
+ then(factory).should(never()).setUseRfcForwardHeader(anyBoolean());
}
@Test
@@ -111,7 +114,28 @@ class JettyWebServerFactoryCustomizerTests {
this.serverProperties.setForwardHeadersStrategy(ServerProperties.ForwardHeadersStrategy.NONE);
ConfigurableJettyWebServerFactory factory = mock(ConfigurableJettyWebServerFactory.class);
this.customizer.customize(factory);
- then(factory).should().setUseForwardHeaders(false);
+ then(factory).should(never()).setUseForwardHeaders(anyBoolean());
+ then(factory).should(never()).setUseRfcForwardHeader(anyBoolean());
+ }
+
+ @Test
+ void forwardedHeadersFormatXForwardedConfiguresUseForwardHeaders() {
+ this.environment.setProperty("DYNO", "-");
+ bind("server.jetty.forwarded-headers.header-format=x_forwarded");
+ ConfigurableJettyWebServerFactory factory = mock(ConfigurableJettyWebServerFactory.class);
+ this.customizer.customize(factory);
+ then(factory).should().setUseForwardHeaders(true);
+ then(factory).should(never()).setUseRfcForwardHeader(anyBoolean());
+ }
+
+ @Test
+ void forwardedHeadersFormatStandardConfiguresUseRfcForwardHeader() {
+ this.environment.setProperty("DYNO", "-");
+ bind("server.jetty.forwarded-headers.header-format=standard");
+ ConfigurableJettyWebServerFactory factory = mock(ConfigurableJettyWebServerFactory.class);
+ this.customizer.customize(factory);
+ then(factory).should().setUseRfcForwardHeader(true);
+ then(factory).should(never()).setUseForwardHeaders(anyBoolean());
}
@Test
diff --git a/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/reactive/JettyReactiveWebServerFactoryTests.java b/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/reactive/JettyReactiveWebServerFactoryTests.java
index 3948dc53dcd..ecc431ef87d 100644
--- a/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/reactive/JettyReactiveWebServerFactoryTests.java
+++ b/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/reactive/JettyReactiveWebServerFactoryTests.java
@@ -43,6 +43,7 @@ import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.ArgumentMatchers.any;
@@ -121,6 +122,22 @@ class JettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactor
assertForwardHeaderIsUsed(factory);
}
+ @Test
+ void useRfcForwardHeader() {
+ JettyReactiveWebServerFactory factory = getFactory();
+ factory.setUseRfcForwardHeader(true);
+ assertRfcForwardHeaderIsUsed(factory);
+ }
+
+ @Test
+ void useForwardHeadersTakesPrecedenceOverUseRfcForwardHeader() {
+ JettyReactiveWebServerFactory factory = getFactory();
+ factory.setUseForwardHeaders(true);
+ factory.setUseRfcForwardHeader(true);
+ assertForwardHeaderIsUsed(factory);
+ assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertRfcForwardHeaderIsUsed(factory));
+ }
+
@Test
void whenServerIsShuttingDownGracefullyThenNewConnectionsCannotBeMade() {
JettyReactiveWebServerFactory factory = getFactory();
diff --git a/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/servlet/JettyServletWebServerFactoryTests.java b/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/servlet/JettyServletWebServerFactoryTests.java
index 7fe513d3253..57a3ff7f76b 100644
--- a/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/servlet/JettyServletWebServerFactoryTests.java
+++ b/module/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/servlet/JettyServletWebServerFactoryTests.java
@@ -442,6 +442,22 @@ class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryT
assertForwardHeaderIsUsed(factory);
}
+ @Test
+ void useRfcForwardHeader() throws Exception {
+ JettyServletWebServerFactory factory = getFactory();
+ factory.setUseRfcForwardHeader(true);
+ assertRfcForwardHeaderIsUsed(factory);
+ }
+
+ @Test
+ void useForwardHeadersTakesPrecedenceOverUseRfcForwardHeader() throws Exception {
+ JettyServletWebServerFactory factory = getFactory();
+ factory.setUseForwardHeaders(true);
+ factory.setUseRfcForwardHeader(true);
+ assertForwardHeaderIsUsed(factory);
+ assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> assertRfcForwardHeaderIsUsed(factory));
+ }
+
@Test
void defaultThreadPool() {
JettyServletWebServerFactory factory = getFactory();
diff --git a/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/reactive/AbstractReactiveWebServerFactoryTests.java b/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/reactive/AbstractReactiveWebServerFactoryTests.java
index ff6a636ce8b..47ed49cd5b8 100644
--- a/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/reactive/AbstractReactiveWebServerFactoryTests.java
+++ b/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/reactive/AbstractReactiveWebServerFactoryTests.java
@@ -677,6 +677,18 @@ public abstract class AbstractReactiveWebServerFactoryTests {
assertThat(body).isEqualTo("https");
}
+ protected void assertRfcForwardHeaderIsUsed(ConfigurableReactiveWebServerFactory factory) {
+ this.webServer = factory.getWebServer(new XForwardedHandler());
+ this.webServer.start();
+ String body = getWebClient(this.webServer.getPort()).build()
+ .get()
+ .header("Forwarded", "proto=https")
+ .retrieve()
+ .bodyToMono(String.class)
+ .block(Duration.ofSeconds(30));
+ assertThat(body).isEqualTo("https");
+ }
+
private T doWithRetry(Callable action) throws Exception {
Exception lastFailure = null;
for (int i = 0; i < 10; i++) {
diff --git a/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/servlet/AbstractServletWebServerFactoryTests.java b/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/servlet/AbstractServletWebServerFactoryTests.java
index c841c7de887..78d6f0f8f5e 100644
--- a/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/servlet/AbstractServletWebServerFactoryTests.java
+++ b/module/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/server/servlet/AbstractServletWebServerFactoryTests.java
@@ -1617,6 +1617,14 @@ public abstract class AbstractServletWebServerFactoryTests {
.contains("remoteaddr=140.211.11.130");
}
+ protected void assertRfcForwardHeaderIsUsed(ServletWebServerFactory factory)
+ throws IOException, URISyntaxException {
+ this.webServer = factory.getWebServer(new ServletRegistrationBean<>(new ExampleServlet(true, false), "/hello"));
+ this.webServer.start();
+ assertThat(getResponse(getLocalUrl("/hello"), "Forwarded:for=140.211.11.130"))
+ .contains("remoteaddr=140.211.11.130");
+ }
+
protected abstract ConfigurableServletWebServerFactory getFactory();
protected abstract org.apache.jasper.servlet.@Nullable JspServlet getJspServlet() throws Exception;