diff --git a/pom.xml b/pom.xml index e5df2f0b..75cd33a2 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,12 @@ spring-boot-starter-test test + + io.projectreactor.addons + reactor-test + ${reactor.version} + test + diff --git a/src/main/java/org/springframework/cloud/gateway/GatewayConfiguration.java b/src/main/java/org/springframework/cloud/gateway/GatewayAutoConfiguration.java similarity index 97% rename from src/main/java/org/springframework/cloud/gateway/GatewayConfiguration.java rename to src/main/java/org/springframework/cloud/gateway/GatewayAutoConfiguration.java index 807e5459..5227494b 100644 --- a/src/main/java/org/springframework/cloud/gateway/GatewayConfiguration.java +++ b/src/main/java/org/springframework/cloud/gateway/GatewayAutoConfiguration.java @@ -13,7 +13,7 @@ import org.springframework.web.client.reactive.WebClient; */ @Configuration @EnableConfigurationProperties -public class GatewayConfiguration { +public class GatewayAutoConfiguration { @Bean @ConditionalOnMissingBean diff --git a/src/main/java/org/springframework/cloud/gateway/GatewayFilter.java b/src/main/java/org/springframework/cloud/gateway/GatewayFilter.java index ed04cdfb..0b93f83f 100644 --- a/src/main/java/org/springframework/cloud/gateway/GatewayFilter.java +++ b/src/main/java/org/springframework/cloud/gateway/GatewayFilter.java @@ -33,6 +33,7 @@ public interface GatewayFilter { String GATEWAY_ROUTE_ATTR = "gatewayRoute"; String GATEWAY_REQUEST_URL_ATTR = "requestUrl"; + String GATEWAY_HANDLER_MAPPER_ATTR = "gatewayHandlerMapper"; /** * Process the Web request and (optionally) delegate to the next diff --git a/src/main/java/org/springframework/cloud/gateway/GatewayHostHandlerMapping.java b/src/main/java/org/springframework/cloud/gateway/GatewayHostHandlerMapping.java index 0f9a6e2b..e6f7aac4 100644 --- a/src/main/java/org/springframework/cloud/gateway/GatewayHostHandlerMapping.java +++ b/src/main/java/org/springframework/cloud/gateway/GatewayHostHandlerMapping.java @@ -45,6 +45,7 @@ public class GatewayHostHandlerMapping extends AbstractHandlerMapping { @Override protected Mono getHandlerInternal(ServerWebExchange exchange) { + exchange.getAttributes().put(GatewayFilter.GATEWAY_HANDLER_MAPPER_ATTR, getClass().getSimpleName()); String host = exchange.getRequest().getHeaders().getFirst("Host"); Object handler; try { diff --git a/src/main/java/org/springframework/cloud/gateway/GatewayUrlHandlerMapping.java b/src/main/java/org/springframework/cloud/gateway/GatewayUrlHandlerMapping.java index e06f493e..df288515 100644 --- a/src/main/java/org/springframework/cloud/gateway/GatewayUrlHandlerMapping.java +++ b/src/main/java/org/springframework/cloud/gateway/GatewayUrlHandlerMapping.java @@ -35,6 +35,8 @@ public class GatewayUrlHandlerMapping extends AbstractUrlHandlerMapping { return super.getHandler(exchange).map(o -> { if (o instanceof RouteHolder) { RouteHolder holder = (RouteHolder) o; + exchange.getAttributes().put(GatewayFilter.GATEWAY_HANDLER_MAPPER_ATTR, + GatewayUrlHandlerMapping.this.getClass().getSimpleName()); exchange.getAttributes().put(GatewayFilter.GATEWAY_ROUTE_ATTR, holder.route); return holder.webHandler; } diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..a1d57170 --- /dev/null +++ b/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.cloud.gateway.GatewayAutoConfiguration diff --git a/src/test/java/org/springframework/cloud/gateway/GatewayApplication.java b/src/test/java/org/springframework/cloud/gateway/GatewayApplication.java index 8adcaeb3..d473c787 100644 --- a/src/test/java/org/springframework/cloud/gateway/GatewayApplication.java +++ b/src/test/java/org/springframework/cloud/gateway/GatewayApplication.java @@ -1,50 +1,13 @@ package org.springframework.cloud.gateway; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; -import org.springframework.core.annotation.Order; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -//TODO: move to autoconfig -@Import(GatewayConfiguration.class) @SpringBootConfiguration @EnableAutoConfiguration public class GatewayApplication { - private static final Log log = LogFactory.getLog(GatewayApplication.class); - - @Bean - @Order(501) - public GatewayFilter modifyResponseFilter() { - return (exchange, chain) -> { - log.info("modifyResponseFilter start"); - exchange.getResponse().getHeaders().add("X-My-Custom", "MyCustomValue"); - return chain.filter(exchange); - }; - } - - @Bean - @Order(502) - public GatewayFilter postFilter() { - return (exchange, chain) -> { - log.info("postFilter start"); - return chain.filter(exchange).then(postFilterWork(exchange)); - }; - } - - private static Mono postFilterWork(ServerWebExchange exchange) { - log.info("postFilterWork"); - exchange.getResponse().getHeaders().add("X-Post-Header", "AddedAfterRoute"); - return Mono.empty(); - } - public static void main(String[] args) { new SpringApplicationBuilder() .sources(GatewayApplication.class) diff --git a/src/test/java/org/springframework/cloud/gateway/GatewayApplicationTests.java b/src/test/java/org/springframework/cloud/gateway/GatewayApplicationTests.java index 2bbc595b..d235103e 100644 --- a/src/test/java/org/springframework/cloud/gateway/GatewayApplicationTests.java +++ b/src/test/java/org/springframework/cloud/gateway/GatewayApplicationTests.java @@ -1,16 +1,109 @@ package org.springframework.cloud.gateway; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.reactive.ClientResponse; +import org.springframework.web.client.reactive.WebClient; +import org.springframework.web.server.ServerWebExchange; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.web.client.reactive.ClientRequest.GET; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; @RunWith(SpringRunner.class) -@SpringBootTest +@SpringBootTest(webEnvironment = RANDOM_PORT) public class GatewayApplicationTests { + public static final String HANDLER_MAPPER_HEADER = "X-Gateway-Handler-Mapper-Class"; + @LocalServerPort + private int port; + + private WebClient webClient = WebClient.builder(new ReactorClientHttpConnector()).build(); + @Test - public void contextLoads() { + public void urlRouteWorks() { + Mono result = webClient.exchange( + GET("http://localhost:" + port + "/get").build() + ); + + StepVerifier + .create(result.map(response -> response.headers().asHttpHeaders())) + .consumeNextWith( + httpHeaders -> { + assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER)) + .isEqualTo(GatewayUrlHandlerMapping.class.getSimpleName()); + }) + .expectComplete() + .verify(); + } + + @Test + public void hostRouteWorks() { + Mono result = webClient.exchange( + GET("http://localhost:" + port + "/get") + .header("Host", "www.example.org") + .build() + ); + + StepVerifier + .create(result) + .consumeNextWith( + response -> { + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + HttpStatus statusCode = response.statusCode(); + assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER)) + .isEqualTo(GatewayHostHandlerMapping.class.getSimpleName()); + assertThat(statusCode).isEqualTo(HttpStatus.OK); + }) + .expectComplete() + .verify(); + } + + @SpringBootApplication + public static class TestConfig { + + private static final Log log = LogFactory.getLog(TestConfig.class); + + @Bean + @Order(501) + public GatewayFilter modifyResponseFilter() { + return (exchange, chain) -> { + log.info("modifyResponseFilter start"); + String value = (String) exchange.getAttribute(GatewayFilter.GATEWAY_HANDLER_MAPPER_ATTR).orElse("N/A"); + exchange.getResponse().getHeaders().add(HANDLER_MAPPER_HEADER, value); + return chain.filter(exchange); + }; + } + + @Bean + @Order(502) + public GatewayFilter postFilter() { + return (exchange, chain) -> { + log.info("postFilter start"); + return chain.filter(exchange).then(postFilterWork(exchange)); + }; + } + + private static Mono postFilterWork(ServerWebExchange exchange) { + log.info("postFilterWork"); + exchange.getResponse().getHeaders().add("X-Post-Header", "AddedAfterRoute"); + return Mono.empty(); + } + } }