diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderGatewayFilterFactory.java index 2362c9e8..699a6f7f 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderGatewayFilterFactory.java @@ -21,6 +21,7 @@ import reactor.core.publisher.Mono; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.http.HttpHeaders; import org.springframework.web.server.ServerWebExchange; import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator; @@ -35,10 +36,7 @@ public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGate return new GatewayFilter() { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - String value = ServerWebExchangeUtils.expand(exchange, config.getValue()); - exchange.getResponse().getHeaders().add(config.getName(), value); - - return chain.filter(exchange); + return chain.filter(exchange).then(Mono.fromRunnable(() -> addHeader(exchange, config))); } @Override @@ -49,4 +47,13 @@ public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGate }; } + void addHeader(ServerWebExchange exchange, NameValueConfig config) { + final String value = ServerWebExchangeUtils.expand(exchange, config.getValue()); + HttpHeaders headers = exchange.getResponse().getHeaders(); + // if response has been commited, no more response headers will bee added. + if (!exchange.getResponse().isCommitted()) { + headers.add(config.getName(), value); + } + } + } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterIntegrationTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterIntegrationTests.java index 5164949b..9571c92c 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterIntegrationTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterIntegrationTests.java @@ -139,7 +139,7 @@ public class NettyRoutingFilterIntegrationTests extends BaseWebClientTests { String upstreamHeaderValue = "value-from-upstream"; testClient.post().uri("/responseheaders/200").header("Host", "www.mergeresponseheader.org") .header(header, upstreamHeaderValue).exchange().expectHeader() - .valueEquals(header, gatewayHeaderValue, upstreamHeaderValue); + .valueEquals(header, upstreamHeaderValue, gatewayHeaderValue); } @EnableAutoConfiguration diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderGatewayFilterFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderGatewayFilterFactoryTests.java index 8f72d577..7bfe96f9 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderGatewayFilterFactoryTests.java @@ -69,7 +69,7 @@ public class SetResponseHeaderGatewayFilterFactoryTests extends BaseWebClientTes public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { return builder.routes().route("test_set_response_header_dsl", r -> r.order(-1).host("{sub}.setresponseheaderdsl.org").filters(f -> f.prefixPath("/httpbin") - .addResponseHeader("X-Res-Foo", "First").setResponseHeader("X-Res-Foo", "Second-{sub}")) + .setResponseHeader("X-Res-Foo", "Second-{sub}").addResponseHeader("X-Res-Foo", "First")) .uri(uri)) .build(); } diff --git a/spring-cloud-gateway-server/src/test/resources/application.yml b/spring-cloud-gateway-server/src/test/resources/application.yml index 463b767e..da205542 100644 --- a/spring-cloud-gateway-server/src/test/resources/application.yml +++ b/spring-cloud-gateway-server/src/test/resources/application.yml @@ -175,19 +175,19 @@ spring: - Host=**.deduperesponseheader.org - Path=/headers filters: - - AddResponseHeader=Access-Control-Allow-Credentials, true - - AddResponseHeader=Access-Control-Allow-Credentials, false - - AddResponseHeader=Access-Control-Allow-Origin, https://musk.mars - - AddResponseHeader=Access-Control-Allow-Origin, * - - AddResponseHeader=Scout-Cookie, Thin Mints - - AddResponseHeader=Scout-Cookie, S'mores - - AddResponseHeader=Next-Week-Lottery-Numbers, 4 - - AddResponseHeader=Next-Week-Lottery-Numbers, 2 - - AddResponseHeader=Next-Week-Lottery-Numbers, 2 - - AddResponseHeader=Next-Week-Lottery-Numbers, 42 - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_FIRST - DedupeResponseHeader=Scout-Cookie, RETAIN_LAST - DedupeResponseHeader=Next-Week-Lottery-Numbers, RETAIN_UNIQUE + - AddResponseHeader=Access-Control-Allow-Credentials, false + - AddResponseHeader=Access-Control-Allow-Credentials, true + - AddResponseHeader=Access-Control-Allow-Origin, * + - AddResponseHeader=Access-Control-Allow-Origin, https://musk.mars + - AddResponseHeader=Scout-Cookie, S'mores + - AddResponseHeader=Scout-Cookie, Thin Mints + - AddResponseHeader=Next-Week-Lottery-Numbers, 42 + - AddResponseHeader=Next-Week-Lottery-Numbers, 2 + - AddResponseHeader=Next-Week-Lottery-Numbers, 2 + - AddResponseHeader=Next-Week-Lottery-Numbers, 4 # ===================================== - id: forward_test @@ -307,8 +307,8 @@ spring: - Host=**.removereresponseheader.org - Path=/headers filters: - - AddResponseHeader=X-Request-Foo, Bar - RemoveResponseHeader=X-Request-Foo + - AddResponseHeader=X-Request-Foo, Bar # ===================================== - id: request_header_size_test @@ -325,8 +325,8 @@ spring: - Host=**.rewritelocationresponseheader.org - Path=/headers filters: - - AddResponseHeader=Location, https://backend.org:443/v1/some/object/id - RewriteLocationResponseHeader + - AddResponseHeader=Location, https://backend.org:443/v1/some/object/id # ===================================== - id: rewrite_response_header_test @@ -335,8 +335,8 @@ spring: - Host=**.rewriteresponseheader.org - Path=/headers filters: - - AddResponseHeader=X-Request-Foo, /42?user=ford&password=omg!what&flag=true - RewriteResponseHeader=X-Request-Foo, password=[^&]+, password=*** + - AddResponseHeader=X-Request-Foo, /42?user=ford&password=omg!what&flag=true # ===================================== - id: sccb_failure_test @@ -404,9 +404,9 @@ spring: - Host=**.setreresponseheader.org - Path=/headers filters: - - AddResponseHeader=X-Response-Foo, Bar1 - - AddResponseHeader=X-Response-Foo, Bar2 - SetResponseHeader=X-Response-Foo, Bar + - AddResponseHeader=X-Response-Foo, Bar2 + - AddResponseHeader=X-Response-Foo, Bar1 # ===================================== - id: set_status_int_test