From 24d75f49b017b220e9f603746bdc7482f987d039 Mon Sep 17 00:00:00 2001 From: Stefan_Stus Date: Fri, 29 Mar 2019 12:01:22 +0200 Subject: [PATCH] Sets original http status in http header in SetStatus filter fixes gh-990 --- .../main/asciidoc/spring-cloud-gateway.adoc | 12 +++++++ .../SetStatusGatewayFilterFactory.java | 24 +++++++++++++- .../SetStatusGatewayFilterFactoryTests.java | 33 +++++++++++++++---- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-gateway.adoc b/docs/src/main/asciidoc/spring-cloud-gateway.adoc index b21a7357..d0b3a132 100644 --- a/docs/src/main/asciidoc/spring-cloud-gateway.adoc +++ b/docs/src/main/asciidoc/spring-cloud-gateway.adoc @@ -1022,6 +1022,18 @@ spring: In either case, the HTTP status of the response will be set to 401. +The SetStatus GatewayFilter can be configured to return the original HTTP status code from the proxied request in a header in the response. Header will be added to the response if configured using following property. + +.application.yml +[source,yaml] +---- +spring: + cloud: + gateway: + set-status: + original-status-header-name: original-http-status +---- + === StripPrefix GatewayFilter Factory The StripPrefix GatewayFilter Factory takes one parameter, `parts`. The `parts` parameter indicated the number of parts in the path to strip from the request before sending it downstream. diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetStatusGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetStatusGatewayFilterFactory.java index f5ac35b6..95b75bd1 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetStatusGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetStatusGatewayFilterFactory.java @@ -21,17 +21,21 @@ import java.util.List; import reactor.core.publisher.Mono; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.support.HttpStatusHolder; +import org.springframework.http.HttpStatus; import org.springframework.web.server.ServerWebExchange; +import static java.util.Collections.singletonList; import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus; /** * @author Spencer Gibb */ +@ConfigurationProperties("spring.cloud.gateway.set-status") public class SetStatusGatewayFilterFactory extends AbstractGatewayFilterFactory { @@ -40,6 +44,11 @@ public class SetStatusGatewayFilterFactory */ public static final String STATUS_KEY = "status"; + /** + * The name of the header which contains http code of the proxied request. + */ + private String originalStatusHeaderName; + public SetStatusGatewayFilterFactory() { super(Config.class); } @@ -68,7 +77,12 @@ public class SetStatusGatewayFilterFactory return chain.filter(exchange).then(Mono.fromRunnable(() -> { // check not really needed, since it is guarded in setStatusCode, // but it's a good example - setResponseStatus(exchange, statusHolder); + HttpStatus statusCode = exchange.getResponse().getStatusCode(); + boolean isStatusCodeUpdated = setResponseStatus(exchange, statusHolder); + if (isStatusCodeUpdated && originalStatusHeaderName != null) { + exchange.getResponse().getHeaders().set(originalStatusHeaderName, + singletonList(statusCode.value()).toString()); + } })); } @@ -80,6 +94,14 @@ public class SetStatusGatewayFilterFactory }; } + public String getOriginalStatusHeaderName() { + return originalStatusHeaderName; + } + + public void setOriginalStatusHeaderName(String originalStatusHeaderName) { + this.originalStatusHeaderName = originalStatusHeaderName; + } + public static class Config { // TODO: relaxed HttpStatus converter diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetStatusGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetStatusGatewayFilterFactoryTests.java index 77406d0e..c786e097 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetStatusGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetStatusGatewayFilterFactoryTests.java @@ -16,9 +16,11 @@ package org.springframework.cloud.gateway.filter.factory; +import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -38,6 +40,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.reactive.server.WebTestClient; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -47,6 +50,9 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @DirtiesContext public class SetStatusGatewayFilterFactoryTests extends BaseWebClientTests { + @Autowired + private SetStatusGatewayFilterFactory filterFactory; + @Test public void setStatusIntWorks() { setStatusStringTest("www.setstatusint.org", HttpStatus.UNAUTHORIZED); @@ -57,11 +63,6 @@ public class SetStatusGatewayFilterFactoryTests extends BaseWebClientTests { setStatusStringTest("www.setstatusstring.org", HttpStatus.BAD_REQUEST); } - private void setStatusStringTest(String host, HttpStatus status) { - testClient.get().uri("/headers").header("Host", host).exchange().expectStatus() - .isEqualTo(status); - } - @Test public void nonStandardCodeWorks() { HttpHeaders headers = new HttpHeaders(); @@ -78,6 +79,15 @@ public class SetStatusGatewayFilterFactoryTests extends BaseWebClientTests { */ } + @Test + public void shouldSetStatusIntAndAddOriginalHeader() { + String headerName = "original-http-status"; + filterFactory.setOriginalStatusHeaderName(headerName); + setStatusStringTest("www.setstatusint.org", HttpStatus.UNAUTHORIZED) + .expectHeader().value(headerName, Matchers.is("[200]")); + + } + @Test public void toStringFormat() { Config config = new Config(); @@ -86,6 +96,11 @@ public class SetStatusGatewayFilterFactoryTests extends BaseWebClientTests { assertThat(filter.toString()).contains("401"); } + private WebTestClient.ResponseSpec setStatusStringTest(String host, HttpStatus status) { + return testClient.get().uri("/headers").header("Host", host).exchange() + .expectStatus().isEqualTo(status); + } + @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) @@ -96,10 +111,14 @@ public class SetStatusGatewayFilterFactoryTests extends BaseWebClientTests { @Bean public RouteLocator myRouteLocator(RouteLocatorBuilder builder) { + // @formatter:off return builder.routes() - .route("test_custom_http_status", r -> r.host("*.setcustomstatus.org") - .filters(f -> f.setStatus(432)).uri(uri)) + .route("test_custom_http_status", + r -> r.host("*.setcustomstatus.org") + .filters(f -> f.setStatus(432)) + .uri(uri)) .build(); + // @formatter:on } }