From a93932c5e795d3e0ba66567d549874479ffcb3a0 Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Tue, 14 Feb 2017 21:14:51 -0700 Subject: [PATCH] Break out many tests from GatewayIntegrationTests --- .../DiscoveryClientRouteLocator.java | 15 +- .../route/RemoveRequestHeaderRouteFilter.java | 2 +- ...uestHeaderRouteFilterIntegrationTests.java | 49 ++ ...tParameterRouteFilterIntegrationTests.java | 58 +++ .../HystrixRouteFilterIntegrationTests.java | 64 +++ ...RedirectToRouteFilterIntegrationTests.java | 50 +++ ...oxyHeadersRouteFilterIntegrationTests.java | 56 +++ ...uestHeaderRouteFilterIntegrationTests.java | 50 +++ ...onseHeaderRouteFilterIntegrationTests.java | 46 ++ ...ewritePathRouteFilterIntegrationTests.java | 47 ++ ...ureHeadersRouteFilterIntegrationTests.java | 66 +++ .../SetPathRouteFilterIntegrationTests.java | 45 ++ ...etResponseRouteFilterIntegrationTests.java | 47 ++ .../SetStatusRouteFilterIntegrationTests.java | 56 +++ .../HostRoutePredicateIntegrationTests.java | 55 +++ .../UrlRoutePredicateIntegrationTests.java | 54 +++ .../cloud/gateway/test/AdhocTestSuite.java | 53 +++ .../gateway/test/BaseWebClientTests.java | 67 +++ .../gateway/test/FormIntegrationTests.java | 87 ++++ .../gateway/test/GatewayIntegrationTests.java | 418 +----------------- .../cloud/gateway/test/TestUtils.java | 23 + .../application-removenonproxyheaders.yml | 16 + 22 files changed, 1008 insertions(+), 416 deletions(-) create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/AddRequestHeaderRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/AddRequestParameterRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/HystrixRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/RedirectToRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/RemoveNonProxyHeadersRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/RemoveRequestHeaderRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/RemoveResponseHeaderRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/RewritePathRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/SecureHeadersRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/SetPathRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/SetResponseRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/filter/route/SetStatusRouteFilterIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/handler/predicate/UrlRoutePredicateIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java create mode 100644 src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/test/FormIntegrationTests.java create mode 100644 src/test/java/org/springframework/cloud/gateway/test/TestUtils.java create mode 100644 src/test/resources/application-removenonproxyheaders.yml diff --git a/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteLocator.java b/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteLocator.java index fbaa9e442..56d5b005b 100644 --- a/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteLocator.java +++ b/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteLocator.java @@ -1,14 +1,19 @@ package org.springframework.cloud.gateway.discovery; +import java.net.URI; + import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.gateway.api.RouteLocator; import org.springframework.cloud.gateway.api.FilterDefinition; import org.springframework.cloud.gateway.api.PredicateDefinition; import org.springframework.cloud.gateway.api.Route; +import org.springframework.cloud.gateway.api.RouteLocator; +import org.springframework.cloud.gateway.filter.route.RewritePathRouteFilter; +import org.springframework.cloud.gateway.handler.predicate.UrlRoutePredicate; -import reactor.core.publisher.Flux; +import static org.springframework.cloud.gateway.support.NameUtils.normalizeFilterName; +import static org.springframework.cloud.gateway.support.NameUtils.normalizePredicateName; -import java.net.URI; +import reactor.core.publisher.Flux; /** * TODO: developer configuration, in zuul, this was opt out, should be opt in @@ -34,7 +39,7 @@ public class DiscoveryClientRouteLocator implements RouteLocator { // add a predicate that matches the url at /serviceId/** PredicateDefinition predicate = new PredicateDefinition(); - predicate.setName("Url"); + predicate.setName(normalizePredicateName(UrlRoutePredicate.class)); predicate.setArgs("/" + serviceId + "/**"); route.getPredicates().add(predicate); @@ -42,7 +47,7 @@ public class DiscoveryClientRouteLocator implements RouteLocator { // add a filter that removes /serviceId by default FilterDefinition filter = new FilterDefinition(); - filter.setName("RewritePath"); + filter.setName(normalizeFilterName(RewritePathRouteFilter.class)); String regex = "/" + serviceId + "/(?.*)"; String replacement = "/${remaining}"; filter.setArgs(regex, replacement); diff --git a/src/main/java/org/springframework/cloud/gateway/filter/route/RemoveRequestHeaderRouteFilter.java b/src/main/java/org/springframework/cloud/gateway/filter/route/RemoveRequestHeaderRouteFilter.java index 69e845855..131e3cd71 100644 --- a/src/main/java/org/springframework/cloud/gateway/filter/route/RemoveRequestHeaderRouteFilter.java +++ b/src/main/java/org/springframework/cloud/gateway/filter/route/RemoveRequestHeaderRouteFilter.java @@ -8,7 +8,7 @@ import org.springframework.http.server.reactive.ServerHttpRequest; */ public class RemoveRequestHeaderRouteFilter implements RouteFilter { - public static final String FAKE_HEADER = "_______force_______"; + private static final String FAKE_HEADER = "_______force_______"; @Override public WebFilter apply(String... args) { diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/AddRequestHeaderRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/AddRequestHeaderRouteFilterIntegrationTests.java new file mode 100644 index 000000000..b03c46249 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/AddRequestHeaderRouteFilterIntegrationTests.java @@ -0,0 +1,49 @@ +package org.springframework.cloud.gateway.filter.route; + +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.getMap; +import static org.springframework.web.reactive.function.BodyExtractors.toMono; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class AddRequestHeaderRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void addRequestHeaderFilterWorks() { + Mono result = webClient.get() + .uri("/headers") + .header("Host", "www.addrequestheader.org") + .exchange() + .then(response -> response.body(toMono(Map.class))); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + Map headers = getMap(response, "headers"); + assertThat(headers).containsEntry("X-Request-Foo", "Bar"); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/AddRequestParameterRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/AddRequestParameterRouteFilterIntegrationTests.java new file mode 100644 index 000000000..981a24b50 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/AddRequestParameterRouteFilterIntegrationTests.java @@ -0,0 +1,58 @@ +package org.springframework.cloud.gateway.filter.route; + +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.getMap; +import static org.springframework.web.reactive.function.BodyExtractors.toMono; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class AddRequestParameterRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void addRequestParameterFilterWorksBlankQuery() { + testRequestParameterFilter(""); + } + + @Test + public void addRequestParameterFilterWorksNonBlankQuery() { + testRequestParameterFilter("?baz=bam"); + } + + private void testRequestParameterFilter(String query) { + Mono result = webClient.get() + .uri("/get" + query) + .header("Host", "www.addrequestparameter.org") + .exchange() + .then(response -> response.body(toMono(Map.class))); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + Map args = getMap(response, "args"); + assertThat(args).containsEntry("foo", "bar"); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/HystrixRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/HystrixRouteFilterIntegrationTests.java new file mode 100644 index 000000000..269128abe --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/HystrixRouteFilterIntegrationTests.java @@ -0,0 +1,64 @@ +package org.springframework.cloud.gateway.filter.route; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class HystrixRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void hystrixFilterWorks() { + Mono result = webClient.get() + .uri("/get") + .header("Host", "www.hystrixsuccess.org") + .exchange(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + assertStatus(response, HttpStatus.OK); + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER)) + .isEqualTo("hystrix_success_test"); + }) + .expectComplete() + .verify(DURATION); + } + + @Test + public void hystrixFilterTimesout() { + Mono result = webClient.get() + .uri("/delay/3") + .header("Host", "www.hystrixfailure.org") + .exchange(); + + StepVerifier.create(result) + .expectError() //TODO: can we get more specific as to the error? + .verify(); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + @Import(DefaultTestConfig.class) + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/RedirectToRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/RedirectToRouteFilterIntegrationTests.java new file mode 100644 index 000000000..b8a0332e1 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/RedirectToRouteFilterIntegrationTests.java @@ -0,0 +1,50 @@ +package org.springframework.cloud.gateway.filter.route; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class RedirectToRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void redirectToFilterWorks() { + Mono result = webClient.get() + .uri("/") + .header("Host", "www.redirectto.org") + .exchange(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + assertStatus(response, HttpStatus.FOUND); + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + assertThat(httpHeaders.getFirst(HttpHeaders.LOCATION)) + .isEqualTo("http://example.org"); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/RemoveNonProxyHeadersRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/RemoveNonProxyHeadersRouteFilterIntegrationTests.java new file mode 100644 index 000000000..620a8096b --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/RemoveNonProxyHeadersRouteFilterIntegrationTests.java @@ -0,0 +1,56 @@ +package org.springframework.cloud.gateway.filter.route; + +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.filter.route.RemoveNonProxyHeadersRouteFilter.DEFAULT_HEADERS_TO_REMOVE; +import static org.springframework.cloud.gateway.test.TestUtils.getMap; +import static org.springframework.web.reactive.function.BodyExtractors.toMono; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +//TODO: why does this break other tests if not in a profile? +@ActiveProfiles("removenonproxyheaders") +@DirtiesContext +public class RemoveNonProxyHeadersRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void removeNonProxyHeadersFilterWorks() { + Mono result = webClient.get() + .uri("/headers") + .header("Host", "www.removerequestheader.org") + .header("Proxy-Authorization", "myauth") + .exchange() + .then(response -> response.body(toMono(Map.class))); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + Map headers = getMap(response, "headers"); + for (String header : DEFAULT_HEADERS_TO_REMOVE) { + assertThat(headers).doesNotContainKey(header); + } + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/RemoveRequestHeaderRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/RemoveRequestHeaderRouteFilterIntegrationTests.java new file mode 100644 index 000000000..65d450c1a --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/RemoveRequestHeaderRouteFilterIntegrationTests.java @@ -0,0 +1,50 @@ +package org.springframework.cloud.gateway.filter.route; + +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.getMap; +import static org.springframework.web.reactive.function.BodyExtractors.toMono; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class RemoveRequestHeaderRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void removeRequestHeaderFilterWorks() { + Mono result = webClient.get() + .uri("/headers") + .header("Host", "www.removerequestheader.org") + .header("X-Request-Foo", "Bar") + .exchange() + .then(response -> response.body(toMono(Map.class))); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + Map headers = getMap(response, "headers"); + assertThat(headers).doesNotContainKey("X-Request-Foo"); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/RemoveResponseHeaderRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/RemoveResponseHeaderRouteFilterIntegrationTests.java new file mode 100644 index 000000000..7e106b511 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/RemoveResponseHeaderRouteFilterIntegrationTests.java @@ -0,0 +1,46 @@ +package org.springframework.cloud.gateway.filter.route; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.http.HttpHeaders; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class RemoveResponseHeaderRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void removeResponseHeaderFilterWorks() { + Mono result = webClient.get() + .uri("/headers") + .header("Host", "www.removereresponseheader.org") + .exchange(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + assertThat(httpHeaders).doesNotContainKey("X-Request-Foo"); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/RewritePathRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/RewritePathRouteFilterIntegrationTests.java new file mode 100644 index 000000000..d2f03795b --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/RewritePathRouteFilterIntegrationTests.java @@ -0,0 +1,47 @@ +package org.springframework.cloud.gateway.filter.route; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class RewritePathRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void rewritePathFilterWorks() { + Mono result = webClient.get() + .uri("/foo/get") + .header("Host", "www.baz.org") + .exchange(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + assertStatus(response, HttpStatus.OK); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/SecureHeadersRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/SecureHeadersRouteFilterIntegrationTests.java new file mode 100644 index 000000000..c689477d0 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/SecureHeadersRouteFilterIntegrationTests.java @@ -0,0 +1,66 @@ +package org.springframework.cloud.gateway.filter.route; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.CONTENT_SECURITY_POLICY_HEADER; +import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.REFERRER_POLICY_HEADER; +import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.STRICT_TRANSPORT_SECURITY_HEADER; +import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.X_CONTENT_TYPE_OPTIONS_HEADER; +import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.X_DOWNLOAD_OPTIONS_HEADER; +import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.X_FRAME_OPTIONS_HEADER; +import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER; +import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.X_XSS_PROTECTION_HEADER; +import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class SecureHeadersRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void secureHeadersFilterWorks() { + Mono result = webClient.get() + .uri("/headers") + .header("Host", "www.secureheaders.org") + .exchange(); + + SecureHeadersProperties defaults = new SecureHeadersProperties(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + assertStatus(response, HttpStatus.OK); + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + assertThat(httpHeaders.getFirst(X_XSS_PROTECTION_HEADER)).isEqualTo(defaults.getXssProtectionHeader()); + assertThat(httpHeaders.getFirst(STRICT_TRANSPORT_SECURITY_HEADER)).isEqualTo(defaults.getStrictTransportSecurity()); + assertThat(httpHeaders.getFirst(X_FRAME_OPTIONS_HEADER)).isEqualTo(defaults.getFrameOptions()); + assertThat(httpHeaders.getFirst(X_CONTENT_TYPE_OPTIONS_HEADER)).isEqualTo(defaults.getContentTypeOptions()); + assertThat(httpHeaders.getFirst(REFERRER_POLICY_HEADER)).isEqualTo(defaults.getReferrerPolicy()); + assertThat(httpHeaders.getFirst(CONTENT_SECURITY_POLICY_HEADER)).isEqualTo(defaults.getContentSecurityPolicy()); + assertThat(httpHeaders.getFirst(X_DOWNLOAD_OPTIONS_HEADER)).isEqualTo(defaults.getDownloadOptions()); + assertThat(httpHeaders.getFirst(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER)).isEqualTo(defaults.getPermittedCrossDomainPolicies()); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/SetPathRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/SetPathRouteFilterIntegrationTests.java new file mode 100644 index 000000000..c3ec77271 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/SetPathRouteFilterIntegrationTests.java @@ -0,0 +1,45 @@ +package org.springframework.cloud.gateway.filter.route; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.http.HttpStatus; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class SetPathRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void setPathFilterDefaultValuesWork() { + Mono result = webClient.get() + .uri("/foo/get") + .header("Host", "www.setpath.org") + .exchange(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + assertStatus(response, HttpStatus.OK); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/SetResponseRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/SetResponseRouteFilterIntegrationTests.java new file mode 100644 index 000000000..41b35f3dc --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/SetResponseRouteFilterIntegrationTests.java @@ -0,0 +1,47 @@ +package org.springframework.cloud.gateway.filter.route; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.http.HttpHeaders; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class SetResponseRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void setResponseHeaderFilterWorks() { + Mono result = webClient.get() + .uri("/headers") + .header("Host", "www.setreresponseheader.org") + .exchange(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + assertThat(httpHeaders).containsKey("X-Request-Foo"); + assertThat(httpHeaders.get("X-Request-Foo")).containsExactly("Bar"); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/filter/route/SetStatusRouteFilterIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/filter/route/SetStatusRouteFilterIntegrationTests.java new file mode 100644 index 000000000..4492ead67 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/filter/route/SetStatusRouteFilterIntegrationTests.java @@ -0,0 +1,56 @@ +package org.springframework.cloud.gateway.filter.route; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class SetStatusRouteFilterIntegrationTests extends BaseWebClientTests { + + @Test + public void setStatusIntWorks() { + setStatusStringTest("www.setstatusint.org", HttpStatus.UNAUTHORIZED); + } + + @Test + public void setStatusStringWorks() { + setStatusStringTest("www.setstatusstring.org", HttpStatus.BAD_REQUEST); + } + + private void setStatusStringTest(String host, HttpStatus status) { + Mono result = webClient.get() + .uri("/headers") + .header("Host", host) + .exchange(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + assertStatus(response, status); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateIntegrationTests.java new file mode 100644 index 000000000..522f014e8 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateIntegrationTests.java @@ -0,0 +1,55 @@ +package org.springframework.cloud.gateway.handler.predicate; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class HostRoutePredicateIntegrationTests extends BaseWebClientTests { + + @Test + public void hostRouteWorks() { + Mono result = webClient.get() + .uri("/get") + .header("Host", "www.example.org") + .exchange(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + assertStatus(response, HttpStatus.OK); + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER)) + .isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName()); + assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER)) + .isEqualTo("host_example_to_httpbin"); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + @Import(DefaultTestConfig.class) + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/handler/predicate/UrlRoutePredicateIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/handler/predicate/UrlRoutePredicateIntegrationTests.java new file mode 100644 index 000000000..e0b7a71d1 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/handler/predicate/UrlRoutePredicateIntegrationTests.java @@ -0,0 +1,54 @@ +package org.springframework.cloud.gateway.handler.predicate; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class UrlRoutePredicateIntegrationTests extends BaseWebClientTests { + + @Test + public void urlRouteWorks() { + Mono result = webClient.get() + .uri("/get") + .exchange(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + assertStatus(response, HttpStatus.OK); + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER)) + .isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName()); + assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER)) + .isEqualTo("default_path_to_httpbin"); + }) + .expectComplete() + .verify(DURATION); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + @Import(DefaultTestConfig.class) + public static class TestConfig { } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java b/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java new file mode 100644 index 000000000..ae00e5aa3 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java @@ -0,0 +1,53 @@ +package org.springframework.cloud.gateway.test; + +import org.junit.Ignore; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; +import org.springframework.cloud.gateway.filter.route.AddRequestHeaderRouteFilterIntegrationTests; +import org.springframework.cloud.gateway.filter.route.AddRequestParameterRouteFilterIntegrationTests; +import org.springframework.cloud.gateway.filter.route.HystrixRouteFilterIntegrationTests; +import org.springframework.cloud.gateway.filter.route.RedirectToRouteFilterIntegrationTests; +import org.springframework.cloud.gateway.filter.route.RemoveNonProxyHeadersRouteFilterIntegrationTests; +import org.springframework.cloud.gateway.filter.route.RemoveRequestHeaderRouteFilterIntegrationTests; +import org.springframework.cloud.gateway.filter.route.RewritePathRouteFilterTests; +import org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilterIntegrationTests; +import org.springframework.cloud.gateway.filter.route.SetPathRouteFilterIntegrationTests; +import org.springframework.cloud.gateway.filter.route.SetPathRouteFilterTests; +import org.springframework.cloud.gateway.filter.route.SetResponseRouteFilterIntegrationTests; +import org.springframework.cloud.gateway.filter.route.SetStatusRouteFilterIntegrationTests; +import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateTests; +import org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateTests; +import org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateTests; +import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateIntegrationTests; +import org.springframework.cloud.gateway.handler.predicate.UrlRoutePredicateIntegrationTests; + +/** + * @author Spencer Gibb + */ +@Ignore +@RunWith(Suite.class) +@SuiteClasses({GatewayIntegrationTests.class, + FormIntegrationTests.class, + // route filter tests + AddRequestHeaderRouteFilterIntegrationTests.class, + AddRequestParameterRouteFilterIntegrationTests.class, + HystrixRouteFilterIntegrationTests.class, + RedirectToRouteFilterIntegrationTests.class, + RemoveNonProxyHeadersRouteFilterIntegrationTests.class, + RemoveRequestHeaderRouteFilterIntegrationTests.class, + SecureHeadersRouteFilterIntegrationTests.class, + SetPathRouteFilterIntegrationTests.class, + SetPathRouteFilterTests.class, + SetResponseRouteFilterIntegrationTests.class, + SetStatusRouteFilterIntegrationTests.class, + RewritePathRouteFilterTests.class, + // route predicate tests + AfterRoutePredicateTests.class, + BeforeRoutePredicateTests.class, + BetweenRoutePredicateTests.class, + HostRoutePredicateIntegrationTests.class, + UrlRoutePredicateIntegrationTests.class, +}) +public class AdhocTestSuite { +} diff --git a/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java b/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java new file mode 100644 index 000000000..c6d93eb51 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java @@ -0,0 +1,67 @@ +package org.springframework.cloud.gateway.test; + +import java.time.Duration; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Before; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.cloud.gateway.api.Route; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.web.reactive.function.client.WebClient; + +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR; + +/** + * @author Spencer Gibb + */ +public class BaseWebClientTests { + + protected static final String HANDLER_MAPPER_HEADER = "X-Gateway-Handler-Mapper-Class"; + protected static final String ROUTE_ID_HEADER = "X-Gateway-Route-Id"; + protected static final Duration DURATION = Duration.ofSeconds(5); + + static { + //TODO: wait for option in boot 2.0 + System.setProperty("java.net.preferIPv4Stack", "true"); + } + + @LocalServerPort + protected int port = 0; + + protected WebClient webClient; + protected String baseUri; + + @Before + public void setup() { + //TODO: how to set new ReactorClientHttpConnector() + baseUri = "http://localhost:" + port; + this.webClient = WebClient.create(baseUri); + } + + @Configuration + protected static class DefaultTestConfig { + + private static final Log log = LogFactory.getLog(DefaultTestConfig.class); + + @Bean + @Order(500) + public GlobalFilter modifyResponseFilter() { + return (exchange, chain) -> { + log.info("modifyResponseFilter start"); + String value = (String) exchange.getAttribute(GATEWAY_HANDLER_MAPPER_ATTR).orElse("N/A"); + exchange.getResponse().getHeaders().add(HANDLER_MAPPER_HEADER, value); + Route route = (Route) exchange.getAttribute(GATEWAY_ROUTE_ATTR).orElse(null); + if (route != null) { + exchange.getResponse().getHeaders().add(ROUTE_ID_HEADER, route.getId()); + } + return chain.filter(exchange); + }; + } + } + +} diff --git a/src/test/java/org/springframework/cloud/gateway/test/FormIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/test/FormIntegrationTests.java new file mode 100644 index 000000000..8060afa91 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/test/FormIntegrationTests.java @@ -0,0 +1,87 @@ +package org.springframework.cloud.gateway.test; + +import java.nio.charset.Charset; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.BodyInserters; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.getMap; +import static org.springframework.web.reactive.function.BodyExtractors.toMono; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +@SuppressWarnings("unchecked") +public class FormIntegrationTests extends BaseWebClientTests { + + @Test + public void formUrlencodedWorks() { + LinkedMultiValueMap formData = new LinkedMultiValueMap<>(); + formData.add("foo", "bar"); + formData.add("baz", "bam"); + + MediaType contentType = new MediaType(MediaType.APPLICATION_FORM_URLENCODED, Charset.forName("UTF-8")); + Mono result = webClient.post() + .uri("/post") + .contentType(contentType) + .exchange(BodyInserters.fromFormData(formData)) + .then(response -> response.body(toMono(Map.class))); + + StepVerifier.create(result) + .consumeNextWith(map -> { + Map form = getMap(map, "form"); + assertThat(form).containsEntry("foo", "bar"); + assertThat(form).containsEntry("baz", "bam"); + }) + .expectComplete() + .verify(DURATION); + } + + @Test + public void multipartFormDataWorks() { + MultiValueMap form = new LinkedMultiValueMap<>(); + form.add("file", new ClassPathResource("1x1.png")); + + RestTemplate restTemplate = new RestTemplate(); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + HttpEntity> entity = new HttpEntity<>(form, headers); + + ResponseEntity response = restTemplate.exchange(baseUri + "/post", HttpMethod.POST, entity, Map.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + Map files = getMap(response.getBody(), "files"); + assertThat(files).containsKey("file"); + assertThat((String)files.get("file")).startsWith("data:application/octet-stream;base64,"); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + //@Import(DefaultTestConfig.class) + public static class TestConfig { } + +} 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 0354b59a1..968f612b0 100644 --- a/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java +++ b/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java @@ -1,153 +1,48 @@ package org.springframework.cloud.gateway.test; -import java.nio.charset.Charset; -import java.time.Duration; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.gateway.api.Route; import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.filter.GlobalFilter; -import org.springframework.cloud.gateway.filter.route.SecureHeadersProperties; import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; import org.springframework.core.annotation.Order; -import org.springframework.core.io.ClassPathResource; -import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.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.cloud.gateway.filter.route.SecureHeadersRouteFilter.CONTENT_SECURITY_POLICY_HEADER; -import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.REFERRER_POLICY_HEADER; -import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.STRICT_TRANSPORT_SECURITY_HEADER; -import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.X_CONTENT_TYPE_OPTIONS_HEADER; -import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.X_DOWNLOAD_OPTIONS_HEADER; -import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.X_FRAME_OPTIONS_HEADER; -import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER; -import static org.springframework.cloud.gateway.filter.route.SecureHeadersRouteFilter.X_XSS_PROTECTION_HEADER; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR; +import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; +import static org.springframework.cloud.gateway.test.TestUtils.getMap; import static org.springframework.web.reactive.function.BodyExtractors.toMono; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = RANDOM_PORT) -@DirtiesContext//(methodMode = DirtiesContext.MethodMode.AFTER_METHOD) +@SpringBootTest(webEnvironment = RANDOM_PORT/*, + properties = "spring.cloud.gateway.default-filters:AddResponseHeader=\"*/) +@DirtiesContext @SuppressWarnings("unchecked") -public class GatewayIntegrationTests { - - private static final String HANDLER_MAPPER_HEADER = "X-Gateway-Handler-Mapper-Class"; - private static final String ROUTE_ID_HEADER = "X-Gateway-Route-Id"; - private static final Duration DURATION = Duration.ofSeconds(5); - - static { - System.setProperty("java.net.preferIPv4Stack", "true"); - } - - @LocalServerPort - private int port = 0; - - private WebClient webClient; - private String baseUri; +public class GatewayIntegrationTests extends BaseWebClientTests { @Autowired private GatewayProperties properties; - @Before - public void setup() { - //TODO: how to set new ReactorClientHttpConnector() - baseUri = "http://localhost:" + port; - this.webClient = WebClient.create(baseUri); - } - - @Test - public void addRequestHeaderFilterWorks() { - Mono result = webClient.get() - .uri("/headers") - .header("Host", "www.addrequestheader.org") - .exchange() - .then(response -> response.body(toMono(Map.class))); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - Map headers = getMap(response, "headers"); - assertThat(headers).containsEntry("X-Request-Foo", "Bar"); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void addRequestParameterFilterWorksBlankQuery() { - testRequestParameterFilter(""); - } - - @Test - public void addRequestParameterFilterWorksNonBlankQuery() { - testRequestParameterFilter("?baz=bam"); - } - - private void testRequestParameterFilter(String query) { - Mono result = webClient.get() - .uri("/get" + query) - .header("Host", "www.addrequestparameter.org") - .exchange() - .then(response -> response.body(toMono(Map.class))); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - Map args = getMap(response, "args"); - assertThat(args).containsEntry("foo", "bar"); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void addResponseHeaderFilterWorks() { - Mono result = webClient.get() - .uri("/headers") - .header("Host", "www.addresponseheader.org") - .exchange(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - HttpHeaders httpHeaders = response.headers().asHttpHeaders(); - assertThat(httpHeaders.getFirst("X-Request-Foo")) - .isEqualTo("Bar"); - }) - .expectComplete() - .verify(DURATION); - } - @Test public void complexContentTypeWorks() { Mono result = webClient.get() @@ -213,58 +108,6 @@ public class GatewayIntegrationTests { .verify(DURATION); } - @Test - public void hostRouteWorks() { - Mono result = webClient.get() - .uri("/get") - .header("Host", "www.example.org") - .exchange(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - assertStatus(response, HttpStatus.OK); - HttpHeaders httpHeaders = response.headers().asHttpHeaders(); - assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER)) - .isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName()); - assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER)) - .isEqualTo("host_example_to_httpbin"); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void hystrixFilterWorks() { - Mono result = webClient.get() - .uri("/get") - .header("Host", "www.hystrixsuccess.org") - .exchange(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - assertStatus(response, HttpStatus.OK); - HttpHeaders httpHeaders = response.headers().asHttpHeaders(); - assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER)) - .isEqualTo("hystrix_success_test"); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void hystrixFilterTimesout() { - Mono result = webClient.get() - .uri("/delay/3") - .header("Host", "www.hystrixfailure.org") - .exchange(); - - StepVerifier.create(result) - .expectError() //TODO: can we get more specific as to the error? - .verify(); - } - @Test public void loadBalancerFilterWorks() { Mono result = webClient.get() @@ -284,49 +127,6 @@ public class GatewayIntegrationTests { .verify(DURATION); } - @Test - public void formUrlencodedWorks() { - LinkedMultiValueMap formData = new LinkedMultiValueMap<>(); - formData.add("foo", "bar"); - formData.add("baz", "bam"); - - MediaType contentType = new MediaType(MediaType.APPLICATION_FORM_URLENCODED, Charset.forName("UTF-8")); - Mono result = webClient.post() - .uri("/post") - .contentType(contentType) - .exchange(BodyInserters.fromFormData(formData)) - .then(response -> response.body(toMono(Map.class))); - - StepVerifier.create(result) - .consumeNextWith(map -> { - Map form = getMap(map, "form"); - assertThat(form).containsEntry("foo", "bar"); - assertThat(form).containsEntry("baz", "bam"); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void multipartFormDataWorks() { - MultiValueMap form = new LinkedMultiValueMap<>(); - form.add("file", new ClassPathResource("1x1.png")); - - RestTemplate restTemplate = new RestTemplate(); - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); - - HttpEntity> entity = new HttpEntity<>(form, headers); - - ResponseEntity response = restTemplate.exchange(baseUri + "/post", HttpMethod.POST, entity, Map.class); - - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - Map files = getMap(response.getBody(), "files"); - assertThat(files).containsKey("file"); - assertThat((String)files.get("file")).startsWith("data:application/octet-stream;base64,"); - } - @Test public void postWorks() { Mono result = webClient.post() @@ -341,215 +141,13 @@ public class GatewayIntegrationTests { .verify(DURATION); } - @Test - public void redirectToFilterWorks() { - Mono result = webClient.get() - .uri("/") - .header("Host", "www.redirectto.org") - .exchange(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - assertStatus(response, HttpStatus.FOUND); - HttpHeaders httpHeaders = response.headers().asHttpHeaders(); - assertThat(httpHeaders.getFirst(HttpHeaders.LOCATION)) - .isEqualTo("http://example.org"); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - @SuppressWarnings("unchecked") - public void removeRequestHeaderFilterWorks() { - Mono result = webClient.get() - .uri("/headers") - .header("Host", "www.removerequestheader.org") - .header("X-Request-Foo", "Bar") - .exchange() - .then(response -> response.body(toMono(Map.class))); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - Map headers = getMap(response, "headers"); - assertThat(headers).doesNotContainKey("X-Request-Foo"); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void removeResponseHeaderFilterWorks() { - Mono result = webClient.get() - .uri("/headers") - .header("Host", "www.removereresponseheader.org") - .exchange(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - HttpHeaders httpHeaders = response.headers().asHttpHeaders(); - assertThat(httpHeaders).doesNotContainKey("X-Request-Foo"); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void rewritePathFilterWorks() { - Mono result = webClient.get() - .uri("/foo/get") - .header("Host", "www.baz.org") - .exchange(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - assertStatus(response, HttpStatus.OK); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void secureHeadersFilterWorks() { - Mono result = webClient.get() - .uri("/headers") - .header("Host", "www.secureheaders.org") - .exchange(); - - SecureHeadersProperties defaults = new SecureHeadersProperties(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - assertStatus(response, HttpStatus.OK); - HttpHeaders httpHeaders = response.headers().asHttpHeaders(); - assertThat(httpHeaders.getFirst(X_XSS_PROTECTION_HEADER)).isEqualTo(defaults.getXssProtectionHeader()); - assertThat(httpHeaders.getFirst(STRICT_TRANSPORT_SECURITY_HEADER)).isEqualTo(defaults.getStrictTransportSecurity()); - assertThat(httpHeaders.getFirst(X_FRAME_OPTIONS_HEADER)).isEqualTo(defaults.getFrameOptions()); - assertThat(httpHeaders.getFirst(X_CONTENT_TYPE_OPTIONS_HEADER)).isEqualTo(defaults.getContentTypeOptions()); - assertThat(httpHeaders.getFirst(REFERRER_POLICY_HEADER)).isEqualTo(defaults.getReferrerPolicy()); - assertThat(httpHeaders.getFirst(CONTENT_SECURITY_POLICY_HEADER)).isEqualTo(defaults.getContentSecurityPolicy()); - assertThat(httpHeaders.getFirst(X_DOWNLOAD_OPTIONS_HEADER)).isEqualTo(defaults.getDownloadOptions()); - assertThat(httpHeaders.getFirst(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER)).isEqualTo(defaults.getPermittedCrossDomainPolicies()); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void setPathFilterDefaultValuesWork() { - Mono result = webClient.get() - .uri("/foo/get") - .header("Host", "www.setpath.org") - .exchange(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - assertStatus(response, HttpStatus.OK); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void setResponseHeaderFilterWorks() { - Mono result = webClient.get() - .uri("/headers") - .header("Host", "www.setreresponseheader.org") - .exchange(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - HttpHeaders httpHeaders = response.headers().asHttpHeaders(); - assertThat(httpHeaders).containsKey("X-Request-Foo"); - assertThat(httpHeaders.get("X-Request-Foo")).containsExactly("Bar"); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void setStatusIntWorks() { - setStatusStringTest("www.setstatusint.org", HttpStatus.UNAUTHORIZED); - } - - @Test - public void setStatusStringWorks() { - setStatusStringTest("www.setstatusstring.org", HttpStatus.BAD_REQUEST); - } - - private void setStatusStringTest(String host, HttpStatus status) { - Mono result = webClient.get() - .uri("/headers") - .header("Host", host) - .exchange(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - assertStatus(response, status); - }) - .expectComplete() - .verify(DURATION); - } - - @Test - public void urlRouteWorks() { - Mono result = webClient.get() - .uri("/get") - .exchange(); - - StepVerifier.create(result) - .consumeNextWith( - response -> { - assertStatus(response, HttpStatus.OK); - HttpHeaders httpHeaders = response.headers().asHttpHeaders(); - assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER)) - .isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName()); - assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER)) - .isEqualTo("default_path_to_httpbin"); - }) - .expectComplete() - .verify(DURATION); - } - - private Map getMap(Map response, String key) { - assertThat(response).containsKey(key).isInstanceOf(Map.class); - return (Map) response.get(key); - } - - private void assertStatus(ClientResponse response, HttpStatus status) { - HttpStatus statusCode = response.statusCode(); - assertThat(statusCode).isEqualTo(status); - } - @EnableAutoConfiguration @SpringBootConfiguration + @Import(DefaultTestConfig.class) public static class TestConfig { private static final Log log = LogFactory.getLog(TestConfig.class); - @Bean - @Order(500) - public GlobalFilter modifyResponseFilter() { - return (exchange, chain) -> { - log.info("modifyResponseFilter start"); - String value = (String) exchange.getAttribute(GATEWAY_HANDLER_MAPPER_ATTR).orElse("N/A"); - exchange.getResponse().getHeaders().add(HANDLER_MAPPER_HEADER, value); - Route route = (Route) exchange.getAttribute(GATEWAY_ROUTE_ATTR).orElse(null); - if (route != null) { - exchange.getResponse().getHeaders().add(ROUTE_ID_HEADER, route.getId()); - } - return chain.filter(exchange); - }; - } - @Bean @Order(-1) public GlobalFilter postFilter() { diff --git a/src/test/java/org/springframework/cloud/gateway/test/TestUtils.java b/src/test/java/org/springframework/cloud/gateway/test/TestUtils.java new file mode 100644 index 000000000..60a1d2135 --- /dev/null +++ b/src/test/java/org/springframework/cloud/gateway/test/TestUtils.java @@ -0,0 +1,23 @@ +package org.springframework.cloud.gateway.test; + +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.ClientResponse; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Spencer Gibb + */ +public class TestUtils { + public static Map getMap(Map response, String key) { + assertThat(response).containsKey(key).isInstanceOf(Map.class); + return (Map) response.get(key); + } + + public static void assertStatus(ClientResponse response, HttpStatus status) { + HttpStatus statusCode = response.statusCode(); + assertThat(statusCode).isEqualTo(status); + } +} diff --git a/src/test/resources/application-removenonproxyheaders.yml b/src/test/resources/application-removenonproxyheaders.yml new file mode 100644 index 000000000..2d737550d --- /dev/null +++ b/src/test/resources/application-removenonproxyheaders.yml @@ -0,0 +1,16 @@ + +spring: + cloud: + gateway: + default-filters: + - RemoveNonProxyHeaders + routes: + # ===================================== + - id: remove_request_header_test + uri: http://httpbin.org:80 + predicates: + - Host=**.removerequestheader.org + - Url=/headers + filters: + - RemoveRequestHeader=X-Request-Foo +