diff --git a/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index 84bffcc0..b76f6970 100644 --- a/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -15,6 +15,7 @@ import org.springframework.cloud.gateway.filter.WriteResponseFilter; import org.springframework.cloud.gateway.filter.route.AddRequestHeaderRouteFilter; import org.springframework.cloud.gateway.filter.route.AddRequestParameterRouteFilter; import org.springframework.cloud.gateway.filter.route.AddResponseHeaderRouteFilter; +import org.springframework.cloud.gateway.filter.route.RedirectToRouteFilter; import org.springframework.cloud.gateway.filter.route.RemoveRequestHeaderRouteFilter; import org.springframework.cloud.gateway.filter.route.RemoveResponseHeaderRouteFilter; import org.springframework.cloud.gateway.filter.route.RewritePathRouteFilter; @@ -141,6 +142,11 @@ public class GatewayAutoConfiguration { return new AddResponseHeaderRouteFilter(); } + @Bean(name = "RedirectToRouteFilter") + public RedirectToRouteFilter redirectToRouteFilter() { + return new RedirectToRouteFilter(); + } + @Bean(name = "RemoveRequestHeaderRouteFilter") public RemoveRequestHeaderRouteFilter removeRequestHeaderRouteFilter() { return new RemoveRequestHeaderRouteFilter(); diff --git a/src/main/java/org/springframework/cloud/gateway/filter/route/RedirectToRouteFilter.java b/src/main/java/org/springframework/cloud/gateway/filter/route/RedirectToRouteFilter.java new file mode 100644 index 00000000..ea26cce5 --- /dev/null +++ b/src/main/java/org/springframework/cloud/gateway/filter/route/RedirectToRouteFilter.java @@ -0,0 +1,48 @@ +package org.springframework.cloud.gateway.filter.route; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.WebFilter; + +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.isResponseCommitted; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.parse; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus; + +import reactor.core.publisher.Mono; + +/** + * @author Spencer Gibb + */ +public class RedirectToRouteFilter implements RouteFilter { + @Override + public WebFilter apply(String statusString, String[] args) { + validate(args, 1); + + final HttpStatus httpStatus = parse(statusString); + final URL url; + try { + url = URI.create(args[0]).toURL(); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Invalid url " + args[0], e); + } + + //TODO: caching can happen here + return (exchange, chain) -> + chain.filter(exchange).then(() -> { + if (!isResponseCommitted(exchange)) { + setResponseStatus(exchange, httpStatus); + + final ServerHttpResponse response = exchange.getResponse(); + response.getHeaders().set(HttpHeaders.LOCATION, url.toString()); + return response.setComplete(); + } + return Mono.empty(); + }); + } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java index d7ec2dbe..d90956fb 100644 --- a/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java +++ b/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java @@ -36,6 +36,7 @@ import static org.springframework.web.reactive.function.client.ClientRequest.GET import static org.springframework.web.reactive.function.client.ClientRequest.POST; import reactor.core.publisher.Mono; +import reactor.ipc.netty.resources.PoolResources; import reactor.test.StepVerifier; @RunWith(SpringRunner.class) @@ -194,18 +195,18 @@ public class GatewayIntegrationTests { ); verify( () -> - StepVerifier.create(result) - .consumeNextWith( - response -> { - HttpHeaders httpHeaders = response.headers().asHttpHeaders(); - HttpStatus statusCode = response.statusCode(); - assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER)) - .isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName()); - assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER)) - .isEqualTo("host_example_to_httpbin"); - assertThat(statusCode).isEqualTo(HttpStatus.OK); - }) - .expectComplete() + StepVerifier.create(result) + .consumeNextWith( + response -> { + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + HttpStatus statusCode = response.statusCode(); + assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER)) + .isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName()); + assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER)) + .isEqualTo("host_example_to_httpbin"); + assertThat(statusCode).isEqualTo(HttpStatus.OK); + }) + .expectComplete() .verify(DURATION) ); } @@ -227,6 +228,29 @@ public class GatewayIntegrationTests { ); } + @Test + public void redirectToFilterWorks() { + Mono result = webClient.exchange( + GET("http://localhost:" + port) + .header("Host", "www.redirectto.org") + .build() + ); + + verify( () -> + StepVerifier.create(result) + .consumeNextWith( + response -> { + HttpStatus statusCode = response.statusCode(); + assertThat(statusCode).isEqualTo(HttpStatus.FOUND); + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + assertThat(httpHeaders.getFirst(HttpHeaders.LOCATION)) + .isEqualTo("http://example.org"); + }) + .expectComplete() + .verify() + ); + } + @Test @SuppressWarnings("unchecked") public void removeRequestHeaderFilterWorks() { diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index bae7ca76..36e83740 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -47,6 +47,14 @@ spring: filters: - AddResponseHeader=X-Request-Foo, Bar + # ===================================== + - id: redirect_to_test + uri: http://httpbin.org:80 + predicates: + - Host=**.redirectto.org + filters: + - RedirectTo=302, http://example.org + # ===================================== - id: remove_request_header_test uri: http://httpbin.org:80 @@ -125,6 +133,7 @@ logging: level: org.springframework.cloud.gateway: TRACE org.springframework.http.server.reactive: DEBUG + reactor.ipc.netty: DEBUG management: context-path: /admin