diff --git a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java index 9909e3b9..87381573 100644 --- a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java +++ b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java @@ -18,8 +18,12 @@ package org.springframework.cloud.gateway.mvc.config; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.Collections; +import java.util.Enumeration; import java.util.Set; +import javax.servlet.http.HttpServletRequest; + import org.springframework.cloud.gateway.mvc.ProxyExchange; import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; @@ -31,7 +35,7 @@ import org.springframework.web.method.support.ModelAndViewContainer; /** * @author Dave Syer - * + * @author Tim Ysewyn */ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResolver { @@ -39,6 +43,8 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol private HttpHeaders headers; + private Set autoForwardedHeaders; + private Set sensitive; public ProxyExchangeArgumentResolver(RestTemplate builder) { @@ -49,6 +55,10 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol this.headers = headers; } + public void setAutoForwardedHeaders(Set autoForwardedHeaders) { + this.autoForwardedHeaders = autoForwardedHeaders; + } + public void setSensitive(Set sensitive) { this.sensitive = sensitive; } @@ -65,6 +75,9 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol ProxyExchange proxy = new ProxyExchange<>(rest, webRequest, mavContainer, binderFactory, type(parameter)); proxy.headers(headers); + if (this.autoForwardedHeaders.size() > 0) { + proxy.headers(extractAutoForwardedHeaders(webRequest)); + } if (sensitive != null) { proxy.sensitive(sensitive.toArray(new String[0])); } @@ -80,4 +93,19 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol return type; } + private HttpHeaders extractAutoForwardedHeaders(NativeWebRequest webRequest) { + HttpServletRequest nativeRequest = webRequest + .getNativeRequest(HttpServletRequest.class); + Enumeration headerNames = nativeRequest.getHeaderNames(); + HttpHeaders headers = new HttpHeaders(); + while (headerNames.hasMoreElements()) { + String header = headerNames.nextElement(); + if (this.autoForwardedHeaders.contains(header)) { + headers.addAll(header, + Collections.list(nativeRequest.getHeaders(header))); + } + } + return headers; + } + } diff --git a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyProperties.java b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyProperties.java index 6773f07f..5ee4b605 100644 --- a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyProperties.java +++ b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyProperties.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.mvc.config; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -29,6 +30,7 @@ import org.springframework.http.HttpHeaders; * @RequestMapping methods. * * @author Dave Syer + * @author Tim Ysewyn * */ @ConfigurationProperties("spring.cloud.gateway.proxy") @@ -39,6 +41,11 @@ public class ProxyProperties { */ private Map headers = new LinkedHashMap<>(); + /** + * A set of header names that should be send downstream by default. + */ + private Set autoForward = new HashSet<>(); + /** * A set of sensitive header names that will not be sent downstream by default. */ @@ -52,6 +59,14 @@ public class ProxyProperties { this.headers = headers; } + public Set getAutoForward() { + return autoForward; + } + + public void setAutoForward(Set autoForward) { + this.autoForward = autoForward; + } + public Set getSensitive() { return sensitive; } diff --git a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyResponseAutoConfiguration.java b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyResponseAutoConfiguration.java index fc521fa2..08d48a53 100644 --- a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyResponseAutoConfiguration.java +++ b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyResponseAutoConfiguration.java @@ -43,6 +43,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; * @RequestMapping methods. * * @author Dave Syer + * @author Tim Ysewyn */ @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication @@ -69,6 +70,7 @@ public class ProxyResponseAutoConfiguration implements WebMvcConfigurer { ProxyExchangeArgumentResolver resolver = new ProxyExchangeArgumentResolver( template); resolver.setHeaders(proxy.convertHeaders()); + resolver.setAutoForwardedHeaders(proxy.getAutoForward()); resolver.setSensitive(proxy.getSensitive()); // can be null return resolver; } diff --git a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java index 073b4bb8..4e3f8ae7 100644 --- a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java +++ b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java @@ -55,7 +55,8 @@ import org.springframework.web.util.UriComponentsBuilder; import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@SpringBootTest(properties = { + "spring.cloud.gateway.proxy.auto-forward=baz" }, webEnvironment = WebEnvironment.RANDOM_PORT) @ContextConfiguration(classes = TestApplication.class) public class ProductionConfigurationTests { @@ -247,15 +248,21 @@ public class ProductionConfigurationTests { @Test @SuppressWarnings({ "Duplicates", "unchecked" }) public void headers() throws Exception { - Map> headers = rest.exchange(RequestEntity - .get(rest.getRestTemplate().getUriTemplateHandler() - .expand("/proxy/headers")) - .header("foo", "bar").header("abc", "xyz").build(), Map.class).getBody(); + Map> headers = rest + .exchange( + RequestEntity + .get(rest.getRestTemplate().getUriTemplateHandler() + .expand("/proxy/headers")) + .header("foo", "bar").header("abc", "xyz") + .header("baz", "fob").build(), + Map.class) + .getBody(); assertThat(headers).doesNotContainKey("foo").doesNotContainKey("hello") .containsKeys("bar", "abc"); assertThat(headers.get("bar")).containsOnly("hello"); assertThat(headers.get("abc")).containsOnly("123"); + assertThat(headers.get("baz")).containsOnly("fob"); } @Test diff --git a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyExchangeArgumentResolver.java b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyExchangeArgumentResolver.java index fea92a1c..baecff03 100644 --- a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyExchangeArgumentResolver.java +++ b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyExchangeArgumentResolver.java @@ -34,7 +34,7 @@ import org.springframework.web.server.ServerWebExchange; /** * @author Dave Syer - * + * @author Tim Ysewyn */ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResolver { @@ -42,6 +42,8 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol private HttpHeaders headers; + private Set autoForwardedHeaders; + private Set sensitive; public ProxyExchangeArgumentResolver(WebClient builder) { @@ -52,6 +54,10 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol this.headers = headers; } + public void setAutoForwardedHeaders(Set autoForwardedHeaders) { + this.autoForwardedHeaders = autoForwardedHeaders; + } + public void setSensitive(Set sensitive) { this.sensitive = sensitive; } @@ -79,10 +85,23 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol ProxyExchange proxy = new ProxyExchange<>(rest, exchange, bindingContext, type(parameter)); proxy.headers(headers); + if (this.autoForwardedHeaders.size() > 0) { + proxy.headers(extractAutoForwardedHeaders(exchange)); + } if (sensitive != null) { proxy.sensitive(sensitive.toArray(new String[0])); } return Mono.just(proxy); } + private HttpHeaders extractAutoForwardedHeaders(ServerWebExchange exchange) { + HttpHeaders headers = new HttpHeaders(); + exchange.getRequest().getHeaders().forEach((header, values) -> { + if (this.autoForwardedHeaders.contains(header)) { + headers.addAll(header, values); + } + }); + return headers; + } + } diff --git a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyProperties.java b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyProperties.java index 100ec121..d7796a90 100644 --- a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyProperties.java +++ b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyProperties.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.webflux.config; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -29,6 +30,7 @@ import org.springframework.http.HttpHeaders; * @RequestMapping methods. * * @author Dave Syer + * @author Tim Ysewyn * */ @ConfigurationProperties("spring.cloud.gateway.proxy") @@ -39,6 +41,11 @@ public class ProxyProperties { */ private Map headers = new LinkedHashMap<>(); + /** + * A set of header names that should be send downstream by default. + */ + private Set autoForward = new HashSet<>(); + /** * A set of sensitive header names that will not be sent downstream by default. */ @@ -52,6 +59,14 @@ public class ProxyProperties { this.headers = headers; } + public Set getAutoForward() { + return autoForward; + } + + public void setAutoForward(Set autoForward) { + this.autoForward = autoForward; + } + public Set getSensitive() { return sensitive; } diff --git a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyResponseAutoConfiguration.java b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyResponseAutoConfiguration.java index 18722916..c5fd7837 100644 --- a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyResponseAutoConfiguration.java +++ b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyResponseAutoConfiguration.java @@ -37,6 +37,7 @@ import org.springframework.web.reactive.result.method.annotation.ArgumentResolve * @RequestMapping methods. * * @author Dave Syer + * @author Tim Ysewyn */ @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication @@ -56,6 +57,7 @@ public class ProxyResponseAutoConfiguration implements WebFluxConfigurer { ProxyExchangeArgumentResolver resolver = new ProxyExchangeArgumentResolver( template); resolver.setHeaders(proxy.convertHeaders()); + resolver.setAutoForwardedHeaders(proxy.getAutoForward()); resolver.setSensitive(proxy.getSensitive()); // can be null return resolver; } diff --git a/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ProductionConfigurationTests.java b/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ProductionConfigurationTests.java index dd0e568d..6e3ac0be 100644 --- a/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ProductionConfigurationTests.java +++ b/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ProductionConfigurationTests.java @@ -58,7 +58,8 @@ import org.springframework.web.util.UriComponentsBuilder; import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@SpringBootTest(properties = { + "spring.cloud.gateway.proxy.auto-forward=baz" }, webEnvironment = WebEnvironment.RANDOM_PORT) @ContextConfiguration(classes = TestApplication.class) @DirtiesContext public class ProductionConfigurationTests { @@ -193,15 +194,21 @@ public class ProductionConfigurationTests { @Test @SuppressWarnings({ "Duplicates", "unchecked" }) public void headers() throws Exception { - Map> headers = rest.exchange(RequestEntity - .get(rest.getRestTemplate().getUriTemplateHandler() - .expand("/proxy/headers")) - .header("foo", "bar").header("abc", "xyz").build(), Map.class).getBody(); + Map> headers = rest + .exchange( + RequestEntity + .get(rest.getRestTemplate().getUriTemplateHandler() + .expand("/proxy/headers")) + .header("foo", "bar").header("abc", "xyz") + .header("baz", "fob").build(), + Map.class) + .getBody(); assertThat(headers).doesNotContainKey("foo").doesNotContainKey("hello") .containsKeys("bar", "abc"); assertThat(headers.get("bar")).containsOnly("hello"); assertThat(headers.get("abc")).containsOnly("123"); + assertThat(headers.get("baz")).containsOnly("fob"); } @Test