From a6f48b0f65047a35c42876c51a1d4c45df90109b Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Tue, 12 Feb 2019 14:48:31 +0100 Subject: [PATCH] Checkstyle (#833) Fixes gh-832 * Add formatting and checkstyle fixes. * Fix checkstyle setup. Add more checkstyle fixes and suppressions. * Fix license dates. --- docs/pom.xml | 3 +- pom.xml | 34 +- spring-cloud-gateway-core/pom.xml | 19 +- .../actuate/GatewayControllerEndpoint.java | 67 +- .../config/GatewayAutoConfiguration.java | 366 ++++----- ...ewayClassPathWarningAutoConfiguration.java | 32 +- .../GatewayEnvironmentPostProcessor.java | 15 +- ...ayLoadBalancerClientAutoConfiguration.java | 11 +- .../GatewayMetricsAutoConfiguration.java | 7 +- ...NoLoadBalancerClientAutoConfiguration.java | 12 +- .../gateway/config/GatewayProperties.java | 22 +- .../config/GatewayRedisAutoConfiguration.java | 41 +- .../gateway/config/GlobalCorsProperties.java | 3 +- .../gateway/config/HttpClientProperties.java | 138 ++-- .../config/LoadBalancerProperties.java | 6 +- .../PropertiesRouteDefinitionLocator.java | 8 +- ...DiscoveryClientRouteDefinitionLocator.java | 76 +- .../discovery/DiscoveryLocatorProperties.java | 32 +- ...tewayDiscoveryClientAutoConfiguration.java | 43 +- .../cloud/gateway/event/FilterArgsEvent.java | 8 +- .../gateway/event/PredicateArgsEvent.java | 8 +- .../gateway/event/RefreshRoutesEvent.java | 19 +- .../gateway/event/WeightDefinedEvent.java | 5 +- .../filter/AdaptCachedBodyGlobalFilter.java | 18 +- .../gateway/filter/FilterDefinition.java | 21 +- .../gateway/filter/ForwardPathFilter.java | 16 +- .../gateway/filter/ForwardRoutingFilter.java | 27 +- .../cloud/gateway/filter/GatewayFilter.java | 28 +- .../gateway/filter/GatewayFilterChain.java | 19 +- .../gateway/filter/GatewayMetricsFilter.java | 2 +- .../cloud/gateway/filter/GlobalFilter.java | 18 +- .../filter/LoadBalancerClientFilter.java | 29 +- .../gateway/filter/NettyRoutingFilter.java | 85 ++- .../filter/NettyWriteResponseFilter.java | 33 +- .../gateway/filter/OrderedGatewayFilter.java | 9 +- .../filter/RouteToRequestUrlFilter.java | 41 +- .../filter/WebClientHttpRoutingFilter.java | 34 +- .../filter/WebClientWriteResponseFilter.java | 17 +- .../filter/WebsocketRoutingFilter.java | 93 ++- .../filter/WeightCalculatorWebFilter.java | 85 ++- ...tChangeRequestUriGatewayFilterFactory.java | 5 +- .../factory/AbstractGatewayFilterFactory.java | 13 +- ...AbstractNameValueGatewayFilterFactory.java | 19 +- .../AddRequestHeaderGatewayFilterFactory.java | 11 +- ...dRequestParameterGatewayFilterFactory.java | 21 +- ...AddResponseHeaderGatewayFilterFactory.java | 7 +- .../FallbackHeadersGatewayFilterFactory.java | 61 +- .../filter/factory/GatewayFilterFactory.java | 13 +- .../factory/HystrixGatewayFilterFactory.java | 175 +++-- .../PrefixPathGatewayFilterFactory.java | 28 +- ...reserveHostHeaderGatewayFilterFactory.java | 9 +- .../RedirectToGatewayFilterFactory.java | 42 +- ...moveRequestHeaderGatewayFilterFactory.java | 10 +- ...oveResponseHeaderGatewayFilterFactory.java | 11 +- ...eaderToRequestUriGatewayFilterFactory.java | 21 +- ...equestRateLimiterGatewayFilterFactory.java | 39 +- .../RequestSizeGatewayFilterFactory.java | 62 +- .../factory/RetryGatewayFilterFactory.java | 149 ++-- .../RewritePathGatewayFilterFactory.java | 21 +- ...iteResponseHeaderGatewayFilterFactory.java | 29 +- .../SaveSessionGatewayFilterFactory.java | 19 +- .../SecureHeadersGatewayFilterFactory.java | 54 +- .../factory/SecureHeadersProperties.java | 70 +- .../factory/SetPathGatewayFilterFactory.java | 16 +- .../SetRequestHeaderGatewayFilterFactory.java | 7 +- ...SetResponseHeaderGatewayFilterFactory.java | 11 +- .../SetStatusGatewayFilterFactory.java | 23 +- .../StripPrefixGatewayFilterFactory.java | 26 +- .../rewrite/HttpMessageWriterResponse.java | 48 +- ...ModifyRequestBodyGatewayFilterFactory.java | 49 +- ...odifyResponseBodyGatewayFilterFactory.java | 242 +++--- .../factory/rewrite/RewriteFunction.java | 13 +- .../headers/ForwardedHeadersFilter.java | 123 +-- .../filter/headers/HttpHeadersFilter.java | 36 +- .../headers/RemoveHopByHopHeadersFilter.java | 38 +- .../headers/XForwardedHeadersFilter.java | 97 +-- .../filter/ratelimit/AbstractRateLimiter.java | 18 +- .../gateway/filter/ratelimit/KeyResolver.java | 21 +- .../ratelimit/PrincipalNameKeyResolver.java | 30 +- .../gateway/filter/ratelimit/RateLimiter.java | 31 +- .../filter/ratelimit/RedisRateLimiter.java | 124 ++- .../cloud/gateway/handler/AsyncPredicate.java | 3 +- .../gateway/handler/FilteringWebHandler.java | 44 +- .../handler/RoutePredicateHandlerMapping.java | 79 +- .../AbstractRoutePredicateFactory.java | 3 +- .../predicate/AfterRoutePredicateFactory.java | 15 +- .../BeforeRoutePredicateFactory.java | 12 +- .../BetweenRoutePredicateFactory.java | 21 +- ...ndryRouteServiceRoutePredicateFactory.java | 43 +- .../CookieRoutePredicateFactory.java | 19 +- .../HeaderRoutePredicateFactory.java | 20 +- .../predicate/HostRoutePredicateFactory.java | 19 +- .../MethodRoutePredicateFactory.java | 12 +- .../predicate/PathRoutePredicateFactory.java | 47 +- .../predicate/PredicateDefinition.java | 25 +- .../predicate/QueryRoutePredicateFactory.java | 20 +- .../predicate/ReadBodyPredicateFactory.java | 83 +- .../RemoteAddrRoutePredicateFactory.java | 60 +- .../predicate/RoutePredicateFactory.java | 10 +- .../WeightRoutePredicateFactory.java | 35 +- .../route/CachingRouteDefinitionLocator.java | 19 +- .../gateway/route/CachingRouteLocator.java | 16 +- .../CompositeRouteDefinitionLocator.java | 4 +- .../gateway/route/CompositeRouteLocator.java | 4 +- .../InMemoryRouteDefinitionRepository.java | 16 +- .../cloud/gateway/route/Route.java | 146 ++-- .../cloud/gateway/route/RouteDefinition.java | 58 +- .../gateway/route/RouteDefinitionLocator.java | 4 +- .../route/RouteDefinitionRepository.java | 7 +- .../route/RouteDefinitionRouteLocator.java | 181 +++-- .../gateway/route/RouteDefinitionWriter.java | 4 +- .../cloud/gateway/route/RouteLocator.java | 6 +- .../gateway/route/RouteRefreshListener.java | 9 +- .../gateway/route/builder/BooleanSpec.java | 38 +- .../route/builder/GatewayFilterSpec.java | 312 ++++---- .../gateway/route/builder/PredicateSpec.java | 109 +-- .../route/builder/RouteLocatorBuilder.java | 21 +- .../cloud/gateway/route/builder/UriSpec.java | 10 +- .../gateway/support/AbstractConfigurable.java | 9 +- .../support/AbstractStatefulConfigurable.java | 14 +- .../gateway/support/BodyInserterContext.java | 21 +- .../support/CachedBodyOutputMessage.java | 31 +- .../cloud/gateway/support/Configurable.java | 6 +- .../gateway/support/ConfigurationUtils.java | 18 +- .../support/DefaultClientResponse.java | 45 +- .../gateway/support/DefaultServerRequest.java | 60 +- .../support/DefaultServerResponse.java | 18 +- .../gateway/support/HttpStatusHolder.java | 44 +- .../cloud/gateway/support/NameUtils.java | 26 +- .../gateway/support/NotFoundException.java | 13 +- .../support/ServerWebExchangeUtils.java | 165 +++- .../gateway/support/ShortcutConfigurable.java | 180 +++-- .../gateway/support/StatefulConfigurable.java | 4 +- .../StringToZonedDateTimeConverter.java | 12 +- .../gateway/support/TimeoutException.java | 8 +- .../cloud/gateway/support/WeightConfig.java | 72 +- .../ipresolver/RemoteAddressResolver.java | 17 + .../XForwardedRemoteAddressResolver.java | 45 +- .../cloud/gateway/route/builder/RouteDsl.kt | 78 +- .../main/resources/META-INF/spring.factories | 1 - .../GatewayControllerEndpointTests.java | 21 +- .../config/GatewayAutoConfigurationTests.java | 84 ++- ...dBalancerClientAutoConfigurationTests.java | 17 +- .../cloud/gateway/cors/CorsTests.java | 50 +- ...outeDefinitionLocatorIntegrationTests.java | 34 +- ...veryClientRouteDefinitionLocatorTests.java | 69 +- ...DiscoveryClientAutoConfigurationTests.java | 21 +- .../filter/ForwardRoutingFilterTests.java | 31 +- .../filter/GatewayMetricFilterTests.java | 84 ++- .../filter/LoadBalancerClientFilterTests.java | 155 ++-- .../NettyRoutingFilterIntegrationTests.java | 16 +- .../filter/RouteToRequestUrlFilterTests.java | 87 +-- .../filter/WebsocketRoutingFilterTests.java | 4 +- .../WeightCalculatorWebFilterTests.java | 45 +- ...equestHeaderGatewayFilterFactoryTests.java | 43 +- ...estParameterGatewayFilterFactoryTests.java | 63 +- ...nseParameterGatewayFilterFactoryTests.java | 26 +- .../HystrixGatewayFilterFactoryTests.java | 93 +-- .../filter/factory/HystrixTestConfig.java | 47 +- .../PrefixPathGatewayFilterFactoryTest.java | 25 +- ...veHostHeaderGatewayFilterFactoryTests.java | 27 +- .../RedirectToGatewayFilterFactoryTests.java | 27 +- ...equestHeaderGatewayFilterFactoryTests.java | 19 +- ...sponseHeaderGatewayFilterFactoryTests.java | 17 +- ...iGatewayFilterFactoryIntegrationTests.java | 26 +- ...ToRequestUriGatewayFilterFactoryTests.java | 23 +- ...tRateLimiterGatewayFilterFactoryTests.java | 68 +- .../RequestSizeGatewayFilterFactoryTest.java | 42 +- ...yGatewayFilterFactoryIntegrationTests.java | 87 +-- ...hGatewayFilterFactoryIntegrationTests.java | 13 +- .../RewritePathGatewayFilterFactoryTests.java | 35 +- ...sponseHeaderGatewayFilterFactoryTests.java | 17 +- ...seHeaderGatewayFilterFactoryUnitTests.java | 14 +- .../SaveSessionGatewayFilterFactoryTests.java | 22 +- ...ecureHeadersGatewayFilterFactoryTests.java | 55 +- ...hGatewayFilterFactoryIntegrationTests.java | 26 +- .../SetPathGatewayFilterFactoryTests.java | 27 +- ...equestHeaderGatewayFilterFactoryTests.java | 28 +- ...sponseHeaderGatewayFilterFactoryTests.java | 17 +- .../SetStatusGatewayFilterFactoryTests.java | 28 +- ...xGatewayFilterFactoryIntegrationTests.java | 18 +- .../StripPrefixGatewayFilterFactoryTests.java | 33 +- .../headers/ForwardedHeadersFilterTests.java | 100 +-- .../HttpHeadersFilterMixedTypeTests.java | 19 +- .../headers/HttpHeadersFilterTests.java | 4 +- ...ttpStatusInResponseHeadersFilterTests.java | 11 +- .../NonStandardHeadersInResponseTests.java | 59 +- .../RemoveHopByHopHeadersFilterTests.java | 13 +- .../headers/XForwardedHeadersFilterTests.java | 107 +-- ...ncipalNameKeyResolverIntegrationTests.java | 50 +- .../RedisRateLimiterConfigTests.java | 46 +- ...isRateLimiterDefaultFilterConfigTests.java | 17 +- .../ratelimit/RedisRateLimiterTests.java | 57 +- ...edicateHandlerMappingIntegrationTests.java | 13 +- .../RoutePredicateHandlerMappingTests.java | 115 +-- .../AfterRoutePredicateFactoryTests.java | 8 +- .../BeforeRoutePredicateFactoryTests.java | 12 +- ...RoutePredicateFactoryIntegrationTests.java | 34 +- .../BetweenRoutePredicateFactoryTests.java | 79 +- ...RoutePredicateFactoryIntegrationTests.java | 26 +- ...RouteServiceRoutePredicateFactoryTest.java | 24 +- .../CookieRoutePredicateFactoryTests.java | 3 +- .../HeaderRoutePredicateFactoryTests.java | 41 +- .../HostRoutePredicateFactoryTests.java | 24 +- .../MethodRoutePredicateFactoryTests.java | 18 +- .../PathRoutePredicateFactoryTests.java | 37 +- .../QueryRoutePredicateFactoryTests.java | 37 +- .../ReadBodyPredicateFactoryTest.java | 63 +- .../RemoteAddrRoutePredicateFactoryTests.java | 33 +- ...RoutePredicateFactoryIntegrationTests.java | 45 +- ...ightRoutePredicateFactoryYaml404Tests.java | 22 +- .../CachingRouteDefinitionLocatorTests.java | 37 +- .../route/CachingRouteLocatorTests.java | 14 +- .../RouteDefinitionRouteLocatorTests.java | 22 +- .../route/RouteRefreshListenerTests.java | 4 +- .../cloud/gateway/route/RouteTests.java | 38 +- .../route/builder/GatewayFilterSpecTests.java | 42 +- .../route/builder/RouteBuilderTests.java | 13 +- .../support/ShortcutConfigurableTests.java | 34 +- .../XForwardedRemoteAddressResolverTest.java | 24 +- .../cloud/gateway/test/AdhocTestSuite.java | 12 +- .../gateway/test/BaseWebClientTests.java | 36 +- .../gateway/test/FormIntegrationTests.java | 43 +- .../cloud/gateway/test/ForwardTests.java | 30 +- .../gateway/test/GatewayIntegrationTests.java | 140 ++-- .../gateway/test/GatewayTestApplication.java | 21 +- .../test/HttpBinCompatibleController.java | 51 +- .../cloud/gateway/test/HttpStatusTests.java | 53 +- .../test/PermitAllSecurityConfiguration.java | 26 +- .../cloud/gateway/test/PostTests.java | 25 +- .../cloud/gateway/test/TestUtils.java | 4 +- .../gateway/test/WebfluxNotIncludedTests.java | 11 +- .../gateway/test/sse/SseIntegrationTests.java | 166 ++-- .../gateway/test/ssl/MultiCertSSLTests.java | 9 +- .../SSLHandshakeTimeoutDeprecatedTests.java | 10 +- .../test/ssl/SSLHandshakeTimeoutTests.java | 15 +- .../gateway/test/ssl/SingleCertSSLTests.java | 9 +- .../test/support/AbstractHttpServer.java | 37 +- .../gateway/test/support/HttpServer.java | 7 +- .../test/support/ReactorHttpServer.java | 10 +- .../gateway/test/support/redis/RedisRule.java | 80 +- .../websocket/WebSocketIntegrationTests.java | 267 ++++--- .../gateway/route/builder/RouteDslTests.kt | 150 ++-- .../application-redis-rate-limiter-config.yml | 6 +- ...tion-redis-rate-limiter-default-config.yml | 12 +- .../application-removenonproxyheaders.yml | 3 +- .../resources/application-weights-404.yml | 12 +- .../src/test/resources/application.yml | 20 +- spring-cloud-gateway-dependencies/pom.xml | 3 +- spring-cloud-gateway-mvc/pom.xml | 5 +- .../cloud/gateway/mvc/ProxyExchange.java | 63 +- .../config/ProxyExchangeArgumentResolver.java | 74 +- .../gateway/mvc/config/ProxyProperties.java | 57 +- .../ProxyResponseAutoConfiguration.java | 3 +- .../mvc/ProductionConfigurationTests.java | 708 +++++++++--------- spring-cloud-gateway-sample/pom.xml | 6 +- .../sample/GatewaySampleApplication.java | 23 +- .../gateway/sample/ThrottleGatewayFilter.java | 42 +- .../cloud/gateway/sample/AdditionalRoutes.kt | 42 +- .../src/main/resources/application.yml | 6 +- .../sample/GatewaySampleApplicationTests.java | 115 ++- ...ySampleApplicationWithoutMetricsTests.java | 8 +- spring-cloud-gateway-webflux/pom.xml | 5 +- .../cloud/gateway/webflux/ProxyExchange.java | 65 +- .../config/ProxyExchangeArgumentResolver.java | 6 +- .../webflux/config/ProxyProperties.java | 57 +- .../ProxyResponseAutoConfiguration.java | 1 + .../webflux/ProductionConfigurationTests.java | 96 +-- .../cloud/gateway/webflux/ReactiveTests.java | 22 +- .../src/test/resources/application.properties | 2 +- spring-cloud-starter-gateway/pom.xml | 3 +- src/checkstyle/checkstyle-suppressions.xml | 29 + 272 files changed, 6505 insertions(+), 5043 deletions(-) create mode 100644 src/checkstyle/checkstyle-suppressions.xml diff --git a/docs/pom.xml b/docs/pom.xml index ef905509..bde3fffe 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -1,5 +1,6 @@ - 4.0.0 diff --git a/pom.xml b/pom.xml index 602100bf..2b4a0c7c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - 4.0.0 @@ -19,8 +20,11 @@ https://github.com/spring-cloud-incubator/spring-cloud-gateway - scm:git:git://github.com/spring-cloud-incubator/spring-cloud-gateway.git - scm:git:ssh://git@github.com/spring-cloud-incubator/spring-cloud-gateway.git + + scm:git:git://github.com/spring-cloud-incubator/spring-cloud-gateway.git + + + scm:git:ssh://git@github.com/spring-cloud-incubator/spring-cloud-gateway.git HEAD @@ -51,6 +55,11 @@ 2.1.1.BUILD-SNAPSHOT 2.1.1.BUILD-SNAPSHOT 0.6 + true + + true + true + @@ -130,6 +139,19 @@ docs + + + + io.spring.javaformat + spring-javaformat-maven-plugin + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + spring @@ -207,7 +229,8 @@ surefireArgLine - ${project.build.directory}/jacoco.exec + ${project.build.directory}/jacoco.exec + @@ -218,7 +241,8 @@ - ${project.build.directory}/jacoco.exec + ${project.build.directory}/jacoco.exec + diff --git a/spring-cloud-gateway-core/pom.xml b/spring-cloud-gateway-core/pom.xml index 71020e0e..9e3daa28 100644 --- a/spring-cloud-gateway-core/pom.xml +++ b/spring-cloud-gateway-core/pom.xml @@ -1,5 +1,6 @@ - 4.0.0 @@ -134,7 +135,9 @@ compile - compile + + compile + ${project.basedir}/src/main/kotlin @@ -144,7 +147,9 @@ test-compile - test-compile + + test-compile + ${project.basedir}/src/test/kotlin @@ -176,15 +181,15 @@ java-compile compile - - compile + + compile java-test-compile test-compile - - testCompile + + testCompile diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpoint.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpoint.java index b27cfd63..f17b9f64 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpoint.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.actuate; @@ -25,11 +24,14 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint; +import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory; -import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.route.Route; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionLocator; @@ -46,9 +48,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - /** * @author Spencer Gibb */ @@ -58,15 +57,20 @@ public class GatewayControllerEndpoint implements ApplicationEventPublisherAware private static final Log log = LogFactory.getLog(GatewayControllerEndpoint.class); private RouteDefinitionLocator routeDefinitionLocator; + private List globalFilters; + private List GatewayFilters; + private RouteDefinitionWriter routeDefinitionWriter; + private RouteLocator routeLocator; + private ApplicationEventPublisher publisher; - public GatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator, List globalFilters, - List GatewayFilters, RouteDefinitionWriter routeDefinitionWriter, - RouteLocator routeLocator) { + public GatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator, + List globalFilters, List GatewayFilters, + RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) { this.routeDefinitionLocator = routeDefinitionLocator; this.globalFilters = globalFilters; this.GatewayFilters = GatewayFilters; @@ -83,7 +87,7 @@ public class GatewayControllerEndpoint implements ApplicationEventPublisherAware @PostMapping("/refresh") public Mono refresh() { - this.publisher.publishEvent(new RefreshRoutesEvent(this)); + this.publisher.publishEvent(new RefreshRoutesEvent(this)); return Mono.empty(); } @@ -104,9 +108,9 @@ public class GatewayControllerEndpoint implements ApplicationEventPublisherAware private HashMap putItem(HashMap map, Object o) { Integer order = null; if (o instanceof Ordered) { - order = ((Ordered)o).getOrder(); + order = ((Ordered) o).getOrder(); } - //filters.put(o.getClass().getName(), order); + // filters.put(o.getClass().getName(), order); map.put(o.toString(), order); return map; } @@ -114,8 +118,8 @@ public class GatewayControllerEndpoint implements ApplicationEventPublisherAware // TODO: Flush out routes without a definition @GetMapping("/routes") public Mono>> routes() { - Mono> routeDefs = this.routeDefinitionLocator.getRouteDefinitions() - .collectMap(RouteDefinition::getId); + Mono> routeDefs = this.routeDefinitionLocator + .getRouteDefinitions().collectMap(RouteDefinition::getId); Mono> routes = this.routeLocator.getRoutes().collectList(); return Mono.zip(routeDefs, routes).map(tuple -> { Map defs = tuple.getT1(); @@ -129,7 +133,8 @@ public class GatewayControllerEndpoint implements ApplicationEventPublisherAware if (defs.containsKey(route.getId())) { r.put("route_definition", defs.get(route.getId())); - } else { + } + else { HashMap obj = new HashMap<>(); obj.put("predicate", route.getPredicate().toString()); @@ -154,43 +159,45 @@ public class GatewayControllerEndpoint implements ApplicationEventPublisherAware }); } -/* -http POST :8080/admin/gateway/routes/apiaddreqhead uri=http://httpbin.org:80 predicates:='["Host=**.apiaddrequestheader.org", "Path=/headers"]' filters:='["AddRequestHeader=X-Request-ApiFoo, ApiBar"]' -*/ + /* + * http POST :8080/admin/gateway/routes/apiaddreqhead uri=http://httpbin.org:80 + * predicates:='["Host=**.apiaddrequestheader.org", "Path=/headers"]' + * filters:='["AddRequestHeader=X-Request-ApiFoo, ApiBar"]' + */ @PostMapping("/routes/{id}") @SuppressWarnings("unchecked") - public Mono> save(@PathVariable String id, @RequestBody Mono route) { - return this.routeDefinitionWriter.save(route.map(r -> { + public Mono> save(@PathVariable String id, + @RequestBody Mono route) { + return this.routeDefinitionWriter.save(route.map(r -> { r.setId(id); log.debug("Saving route: " + route); return r; - })).then(Mono.defer(() -> - Mono.just(ResponseEntity.created(URI.create("/routes/"+id)).build()) - )); + })).then(Mono.defer(() -> Mono + .just(ResponseEntity.created(URI.create("/routes/" + id)).build()))); } @DeleteMapping("/routes/{id}") public Mono> delete(@PathVariable String id) { return this.routeDefinitionWriter.delete(Mono.just(id)) .then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build()))) - .onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build())); + .onErrorResume(t -> t instanceof NotFoundException, + t -> Mono.just(ResponseEntity.notFound().build())); } @GetMapping("/routes/{id}") public Mono> route(@PathVariable String id) { - //TODO: missing RouteLocator + // TODO: missing RouteLocator return this.routeDefinitionLocator.getRouteDefinitions() - .filter(route -> route.getId().equals(id)) - .singleOrEmpty() + .filter(route -> route.getId().equals(id)).singleOrEmpty() .map(ResponseEntity::ok) .switchIfEmpty(Mono.just(ResponseEntity.notFound().build())); } @GetMapping("/routes/{id}/combinedfilters") public Mono> combinedfilters(@PathVariable String id) { - //TODO: missing global filters - return this.routeLocator.getRoutes() - .filter(route -> route.getId().equals(id)) + // TODO: missing global filters + return this.routeLocator.getRoutes().filter(route -> route.getId().equals(id)) .reduce(new HashMap<>(), this::putItem); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index 4a958bd1..40e18765 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; @@ -25,7 +24,6 @@ import io.netty.channel.ChannelOption; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import reactor.netty.http.client.HttpClient; import reactor.netty.resources.ConnectionProvider; import reactor.netty.tcp.ProxyProvider; @@ -128,14 +126,11 @@ import org.springframework.core.env.Environment; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.util.StringUtils; import org.springframework.validation.Validator; -import org.springframework.web.filter.reactive.HiddenHttpMethodFilter; import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient; import org.springframework.web.reactive.socket.client.WebSocketClient; import org.springframework.web.reactive.socket.server.WebSocketService; import org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilterChain; import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool.PoolType.DISABLED; import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool.PoolType.FIXED; @@ -146,130 +141,28 @@ import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool @Configuration @ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true) @EnableConfigurationProperties -@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class }) +@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class, + WebFluxAutoConfiguration.class }) @AutoConfigureAfter({ GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class }) @ConditionalOnClass(DispatcherHandler.class) public class GatewayAutoConfiguration { - @Configuration - @ConditionalOnClass(HttpClient.class) - protected static class NettyConfiguration { - @Bean - @ConditionalOnMissingBean - public HttpClient httpClient(HttpClientProperties properties) { - - // configure pool resources - HttpClientProperties.Pool pool = properties.getPool(); - - ConnectionProvider connectionProvider; - if (pool.getType() == DISABLED) { - connectionProvider = ConnectionProvider.newConnection(); - } else if (pool.getType() == FIXED) { - connectionProvider = ConnectionProvider.fixed(pool.getName(), - pool.getMaxConnections(), pool.getAcquireTimeout()); - } else { - connectionProvider = ConnectionProvider.elastic(pool.getName()); - } - - HttpClient httpClient = HttpClient.create(connectionProvider) - .tcpConfiguration(tcpClient -> { - - if (properties.getConnectTimeout() != null) { - tcpClient = tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, properties.getConnectTimeout()); - } - - // configure proxy if proxy host is set. - HttpClientProperties.Proxy proxy = properties.getProxy(); - - if (StringUtils.hasText(proxy.getHost())) { - - tcpClient = tcpClient.proxy(proxySpec -> { - ProxyProvider.Builder builder = proxySpec - .type(ProxyProvider.Proxy.HTTP) - .host(proxy.getHost()); - - PropertyMapper map = PropertyMapper.get(); - - map.from(proxy::getPort) - .whenNonNull() - .to(builder::port); - map.from(proxy::getUsername) - .whenHasText() - .to(builder::username); - map.from(proxy::getPassword) - .whenHasText() - .to(password -> builder.password(s -> password)); - map.from(proxy::getNonProxyHostsPattern) - .whenHasText() - .to(builder::nonProxyHosts); - }); - } - return tcpClient; - }); - - HttpClientProperties.Ssl ssl = properties.getSsl(); - if (ssl.getTrustedX509CertificatesForTrustManager().length > 0 - || ssl.isUseInsecureTrustManager()) { - httpClient = httpClient.secure(sslContextSpec -> { - // configure ssl - SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); - - X509Certificate[] trustedX509Certificates = ssl - .getTrustedX509CertificatesForTrustManager(); - if (trustedX509Certificates.length > 0) { - sslContextBuilder.trustManager(trustedX509Certificates); - } else if (ssl.isUseInsecureTrustManager()) { - sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE); - } - - sslContextSpec.sslContext(sslContextBuilder) - .defaultConfiguration(ssl.getDefaultConfigurationType()) - .handshakeTimeout(ssl.getHandshakeTimeout()) - .closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout()) - .closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout()); - }); - } - - return httpClient; - } - - @Bean - public HttpClientProperties httpClientProperties() { - return new HttpClientProperties(); - } - - @Bean - public NettyRoutingFilter routingFilter(HttpClient httpClient, - ObjectProvider> headersFilters, - HttpClientProperties properties) { - return new NettyRoutingFilter(httpClient, headersFilters, properties); - } - - @Bean - public NettyWriteResponseFilter nettyWriteResponseFilter(GatewayProperties properties) { - return new NettyWriteResponseFilter(properties.getStreamingMediaTypes()); - } - - @Bean - public ReactorNettyWebSocketClient reactorNettyWebSocketClient(HttpClient httpClient) { - return new ReactorNettyWebSocketClient(httpClient); - } - } - @Bean public StringToZonedDateTimeConverter stringToZonedDateTimeConverter() { return new StringToZonedDateTimeConverter(); } @Bean - public RouteLocatorBuilder routeLocatorBuilder(ConfigurableApplicationContext context) { + public RouteLocatorBuilder routeLocatorBuilder( + ConfigurableApplicationContext context) { return new RouteLocatorBuilder(context); } @Bean @ConditionalOnMissingBean - public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) { + public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator( + GatewayProperties properties) { return new PropertiesRouteDefinitionLocator(properties); } @@ -281,30 +174,33 @@ public class GatewayAutoConfiguration { @Bean @Primary - public RouteDefinitionLocator routeDefinitionLocator(List routeDefinitionLocators) { - return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators)); + public RouteDefinitionLocator routeDefinitionLocator( + List routeDefinitionLocators) { + return new CompositeRouteDefinitionLocator( + Flux.fromIterable(routeDefinitionLocators)); } @Bean public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, - List GatewayFilters, - List predicates, - RouteDefinitionLocator routeDefinitionLocator, - @Qualifier("webFluxConversionService") - ConversionService conversionService) { - return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, - properties, conversionService); + List GatewayFilters, + List predicates, + RouteDefinitionLocator routeDefinitionLocator, + @Qualifier("webFluxConversionService") ConversionService conversionService) { + return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, + GatewayFilters, properties, conversionService); } @Bean @Primary - //TODO: property to disable composite? + // TODO: property to disable composite? public RouteLocator cachedCompositeRouteLocator(List routeLocators) { - return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators))); + return new CachingRouteLocator( + new CompositeRouteLocator(Flux.fromIterable(routeLocators))); } @Bean - public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) { + public RouteRefreshListener routeRefreshListener( + ApplicationEventPublisher publisher) { return new RouteRefreshListener(publisher); } @@ -317,7 +213,7 @@ public class GatewayAutoConfiguration { public GlobalCorsProperties globalCorsProperties() { return new GlobalCorsProperties(); } - + @Bean public RoutePredicateHandlerMapping routePredicateHandlerMapping( FilteringWebHandler webHandler, RouteLocator routeLocator, @@ -326,26 +222,26 @@ public class GatewayAutoConfiguration { globalCorsProperties, environment); } - // ConfigurationProperty beans - @Bean public GatewayProperties gatewayProperties() { return new GatewayProperties(); } + // ConfigurationProperty beans + @Bean public SecureHeadersProperties secureHeadersProperties() { return new SecureHeadersProperties(); } - // HttpHeaderFilter beans - @Bean @ConditionalOnProperty(name = "spring.cloud.gateway.forwarded.enabled", matchIfMissing = true) public ForwardedHeadersFilter forwardedHeadersFilter() { return new ForwardedHeadersFilter(); } + // HttpHeaderFilter beans + @Bean public RemoveHopByHopHeadersFilter removeHopByHopHeadersFilter() { return new RemoveHopByHopHeadersFilter(); @@ -358,7 +254,7 @@ public class GatewayAutoConfiguration { } // GlobalFilter beans - + @Bean public AdaptCachedBodyGlobalFilter adaptCachedBodyGlobalFilter() { return new AdaptCachedBodyGlobalFilter(); @@ -370,7 +266,8 @@ public class GatewayAutoConfiguration { } @Bean - public ForwardRoutingFilter forwardRoutingFilter(ObjectProvider dispatcherHandler) { + public ForwardRoutingFilter forwardRoutingFilter( + ObjectProvider dispatcherHandler) { return new ForwardRoutingFilter(dispatcherHandler); } @@ -386,35 +283,34 @@ public class GatewayAutoConfiguration { @Bean public WebsocketRoutingFilter websocketRoutingFilter(WebSocketClient webSocketClient, - WebSocketService webSocketService, - ObjectProvider> headersFilters) { - return new WebsocketRoutingFilter(webSocketClient, webSocketService, headersFilters); + WebSocketService webSocketService, + ObjectProvider> headersFilters) { + return new WebsocketRoutingFilter(webSocketClient, webSocketService, + headersFilters); } @Bean - public WeightCalculatorWebFilter weightCalculatorWebFilter(Validator validator, ObjectProvider routeLocator) { + public WeightCalculatorWebFilter weightCalculatorWebFilter(Validator validator, + ObjectProvider routeLocator) { return new WeightCalculatorWebFilter(validator, routeLocator); } - /*@Bean - //TODO: default over netty? configurable - public WebClientHttpRoutingFilter webClientHttpRoutingFilter() { - //TODO: WebClient bean - return new WebClientHttpRoutingFilter(WebClient.routes().build()); - } - - @Bean - public WebClientWriteResponseFilter webClientWriteResponseFilter() { - return new WebClientWriteResponseFilter(); - }*/ - - // Predicate Factory beans - @Bean public AfterRoutePredicateFactory afterRoutePredicateFactory() { return new AfterRoutePredicateFactory(); } + /* + * @Bean //TODO: default over netty? configurable public WebClientHttpRoutingFilter + * webClientHttpRoutingFilter() { //TODO: WebClient bean return new + * WebClientHttpRoutingFilter(WebClient.routes().build()); } + * + * @Bean public WebClientWriteResponseFilter webClientWriteResponseFilter() { return + * new WebClientWriteResponseFilter(); } + */ + + // Predicate Factory beans + @Bean public BeforeRoutePredicateFactory beforeRoutePredicateFactory() { return new BeforeRoutePredicateFactory(); @@ -493,27 +389,15 @@ public class GatewayAutoConfiguration { return new AddResponseHeaderGatewayFilterFactory(); } - @Configuration - @ConditionalOnClass({HystrixObservableCommand.class, RxReactiveStreams.class}) - protected static class HystrixConfiguration { - @Bean - public HystrixGatewayFilterFactory hystrixGatewayFilterFactory(ObjectProvider dispatcherHandler) { - return new HystrixGatewayFilterFactory(dispatcherHandler); - } - - @Bean - public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() { - return new FallbackHeadersGatewayFilterFactory(); - } - } - @Bean - public ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory(ServerCodecConfigurer codecConfigurer) { + public ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory( + ServerCodecConfigurer codecConfigurer) { return new ModifyRequestBodyGatewayFilterFactory(codecConfigurer); } @Bean - public ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory(ServerCodecConfigurer codecConfigurer) { + public ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory( + ServerCodecConfigurer codecConfigurer) { return new ModifyResponseBodyGatewayFilterFactory(codecConfigurer); } @@ -550,8 +434,9 @@ public class GatewayAutoConfiguration { } @Bean - @ConditionalOnBean({RateLimiter.class, KeyResolver.class}) - public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory(RateLimiter rateLimiter, KeyResolver resolver) { + @ConditionalOnBean({ RateLimiter.class, KeyResolver.class }) + public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory( + RateLimiter rateLimiter, KeyResolver resolver) { return new RequestRateLimiterGatewayFilterFactory(rateLimiter, resolver); } @@ -571,7 +456,8 @@ public class GatewayAutoConfiguration { } @Bean - public SecureHeadersGatewayFilterFactory secureHeadersGatewayFilterFactory(SecureHeadersProperties properties) { + public SecureHeadersGatewayFilterFactory secureHeadersGatewayFilterFactory( + SecureHeadersProperties properties) { return new SecureHeadersGatewayFilterFactory(properties); } @@ -615,18 +501,148 @@ public class GatewayAutoConfiguration { return new RequestSizeGatewayFilterFactory(); } + @Configuration + @ConditionalOnClass(HttpClient.class) + protected static class NettyConfiguration { + + @Bean + @ConditionalOnMissingBean + public HttpClient httpClient(HttpClientProperties properties) { + + // configure pool resources + HttpClientProperties.Pool pool = properties.getPool(); + + ConnectionProvider connectionProvider; + if (pool.getType() == DISABLED) { + connectionProvider = ConnectionProvider.newConnection(); + } + else if (pool.getType() == FIXED) { + connectionProvider = ConnectionProvider.fixed(pool.getName(), + pool.getMaxConnections(), pool.getAcquireTimeout()); + } + else { + connectionProvider = ConnectionProvider.elastic(pool.getName()); + } + + HttpClient httpClient = HttpClient.create(connectionProvider) + .tcpConfiguration(tcpClient -> { + + if (properties.getConnectTimeout() != null) { + tcpClient = tcpClient.option( + ChannelOption.CONNECT_TIMEOUT_MILLIS, + properties.getConnectTimeout()); + } + + // configure proxy if proxy host is set. + HttpClientProperties.Proxy proxy = properties.getProxy(); + + if (StringUtils.hasText(proxy.getHost())) { + + tcpClient = tcpClient.proxy(proxySpec -> { + ProxyProvider.Builder builder = proxySpec + .type(ProxyProvider.Proxy.HTTP) + .host(proxy.getHost()); + + PropertyMapper map = PropertyMapper.get(); + + map.from(proxy::getPort).whenNonNull().to(builder::port); + map.from(proxy::getUsername).whenHasText() + .to(builder::username); + map.from(proxy::getPassword).whenHasText() + .to(password -> builder.password(s -> password)); + map.from(proxy::getNonProxyHostsPattern).whenHasText() + .to(builder::nonProxyHosts); + }); + } + return tcpClient; + }); + + HttpClientProperties.Ssl ssl = properties.getSsl(); + if (ssl.getTrustedX509CertificatesForTrustManager().length > 0 + || ssl.isUseInsecureTrustManager()) { + httpClient = httpClient.secure(sslContextSpec -> { + // configure ssl + SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); + + X509Certificate[] trustedX509Certificates = ssl + .getTrustedX509CertificatesForTrustManager(); + if (trustedX509Certificates.length > 0) { + sslContextBuilder.trustManager(trustedX509Certificates); + } + else if (ssl.isUseInsecureTrustManager()) { + sslContextBuilder + .trustManager(InsecureTrustManagerFactory.INSTANCE); + } + + sslContextSpec.sslContext(sslContextBuilder) + .defaultConfiguration(ssl.getDefaultConfigurationType()) + .handshakeTimeout(ssl.getHandshakeTimeout()) + .closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout()) + .closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout()); + }); + } + + return httpClient; + } + + @Bean + public HttpClientProperties httpClientProperties() { + return new HttpClientProperties(); + } + + @Bean + public NettyRoutingFilter routingFilter(HttpClient httpClient, + ObjectProvider> headersFilters, + HttpClientProperties properties) { + return new NettyRoutingFilter(httpClient, headersFilters, properties); + } + + @Bean + public NettyWriteResponseFilter nettyWriteResponseFilter( + GatewayProperties properties) { + return new NettyWriteResponseFilter(properties.getStreamingMediaTypes()); + } + + @Bean + public ReactorNettyWebSocketClient reactorNettyWebSocketClient( + HttpClient httpClient) { + return new ReactorNettyWebSocketClient(httpClient); + } + + } + + @Configuration + @ConditionalOnClass({ HystrixObservableCommand.class, RxReactiveStreams.class }) + protected static class HystrixConfiguration { + + @Bean + public HystrixGatewayFilterFactory hystrixGatewayFilterFactory( + ObjectProvider dispatcherHandler) { + return new HystrixGatewayFilterFactory(dispatcherHandler); + } + + @Bean + public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() { + return new FallbackHeadersGatewayFilterFactory(); + } + + } + @Configuration @ConditionalOnClass(Health.class) protected static class GatewayActuatorConfiguration { @Bean @ConditionalOnEnabledEndpoint - public GatewayControllerEndpoint gatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator, List globalFilters, - List GatewayFilters, RouteDefinitionWriter routeDefinitionWriter, - RouteLocator routeLocator) { - return new GatewayControllerEndpoint(routeDefinitionLocator, globalFilters, GatewayFilters, routeDefinitionWriter, routeLocator); + public GatewayControllerEndpoint gatewayControllerEndpoint( + RouteDefinitionLocator routeDefinitionLocator, + List globalFilters, + List GatewayFilters, + RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) { + return new GatewayControllerEndpoint(routeDefinitionLocator, globalFilters, + GatewayFilters, routeDefinitionWriter, routeLocator); } + } } - diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayClassPathWarningAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayClassPathWarningAutoConfiguration.java index 099faf9b..83cdd169 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayClassPathWarningAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayClassPathWarningAutoConfiguration.java @@ -1,7 +1,24 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.config; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; @@ -11,7 +28,9 @@ import org.springframework.context.annotation.Configuration; @AutoConfigureBefore(GatewayAutoConfiguration.class) public class GatewayClassPathWarningAutoConfiguration { - private static final Log log = LogFactory.getLog(GatewayClassPathWarningAutoConfiguration.class); + private static final Log log = LogFactory + .getLog(GatewayClassPathWarningAutoConfiguration.class); + private static final String BORDER = "\n\n**********************************************************\n\n"; @Configuration @@ -19,8 +38,9 @@ public class GatewayClassPathWarningAutoConfiguration { protected static class SpringMvcFoundOnClasspathConfiguration { public SpringMvcFoundOnClasspathConfiguration() { - log.warn(BORDER+"Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. "+ - "Please remove spring-boot-starter-web dependency."+BORDER); + log.warn(BORDER + + "Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. " + + "Please remove spring-boot-starter-web dependency." + BORDER); } } @@ -30,9 +50,11 @@ public class GatewayClassPathWarningAutoConfiguration { protected static class WebfluxMissingFromClasspathConfiguration { public WebfluxMissingFromClasspathConfiguration() { - log.warn(BORDER+"Spring Webflux is missing from the classpath, which is required for Spring Cloud Gateway at this time. "+ - "Please add spring-boot-starter-webflux dependency."+BORDER); + log.warn(BORDER + "Spring Webflux is missing from the classpath, " + + "which is required for Spring Cloud Gateway at this time. " + + "Please add spring-boot-starter-webflux dependency." + BORDER); } } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayEnvironmentPostProcessor.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayEnvironmentPostProcessor.java index 40a94f05..cc8b4f2f 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayEnvironmentPostProcessor.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayEnvironmentPostProcessor.java @@ -12,22 +12,25 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; +import java.util.Collections; + import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; -import java.util.Collections; - public class GatewayEnvironmentPostProcessor implements EnvironmentPostProcessor { + @Override - public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) { - env.getPropertySources().addFirst(new MapPropertySource("gateway-properties", - Collections.singletonMap("spring.webflux.hiddenmethod.filter.enabled", "false"))); + public void postProcessEnvironment(ConfigurableEnvironment env, + SpringApplication application) { + env.getPropertySources().addFirst( + new MapPropertySource("gateway-properties", Collections.singletonMap( + "spring.webflux.hiddenmethod.filter.enabled", "false"))); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayLoadBalancerClientAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayLoadBalancerClientAutoConfiguration.java index 2455f765..aa631b86 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayLoadBalancerClientAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayLoadBalancerClientAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; @@ -23,7 +22,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; -import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; import org.springframework.context.annotation.Bean; @@ -34,7 +32,8 @@ import org.springframework.web.reactive.DispatcherHandler; * @author Spencer Gibb */ @Configuration -@ConditionalOnClass({LoadBalancerClient.class, RibbonAutoConfiguration.class, DispatcherHandler.class}) +@ConditionalOnClass({ LoadBalancerClient.class, RibbonAutoConfiguration.class, + DispatcherHandler.class }) @AutoConfigureAfter(RibbonAutoConfiguration.class) @EnableConfigurationProperties(LoadBalancerProperties.class) public class GatewayLoadBalancerClientAutoConfiguration { @@ -44,7 +43,9 @@ public class GatewayLoadBalancerClientAutoConfiguration { @Bean @ConditionalOnBean(LoadBalancerClient.class) @ConditionalOnMissingBean(LoadBalancerClientFilter.class) - public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client, LoadBalancerProperties properties) { + public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client, + LoadBalancerProperties properties) { return new LoadBalancerClientFilter(client, properties); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayMetricsAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayMetricsAutoConfiguration.java index a9107582..c02894d8 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayMetricsAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayMetricsAutoConfiguration.java @@ -12,12 +12,12 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; import io.micrometer.core.instrument.MeterRegistry; + import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -36,12 +36,15 @@ import org.springframework.web.reactive.DispatcherHandler; @AutoConfigureBefore(HttpHandlerAutoConfiguration.class) @AutoConfigureAfter({ MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class }) -@ConditionalOnClass({ DispatcherHandler.class, MeterRegistry.class, MetricsAutoConfiguration.class}) +@ConditionalOnClass({ DispatcherHandler.class, MeterRegistry.class, + MetricsAutoConfiguration.class }) public class GatewayMetricsAutoConfiguration { + @Bean @ConditionalOnBean(MeterRegistry.class) @ConditionalOnProperty(name = "spring.cloud.gateway.metrics.enabled", matchIfMissing = true) public GatewayMetricsFilter gatewayMetricFilter(MeterRegistry meterRegistry) { return new GatewayMetricsFilter(meterRegistry); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayNoLoadBalancerClientAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayNoLoadBalancerClientAutoConfiguration.java index f91b374c..e633c673 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayNoLoadBalancerClientAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayNoLoadBalancerClientAutoConfiguration.java @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; @@ -51,7 +50,8 @@ public class GatewayNoLoadBalancerClientAutoConfiguration { @Bean @ConditionalOnMissingBean(LoadBalancerClientFilter.class) - public NoLoadBalancerClientFilter noLoadBalancerClientFilter(LoadBalancerProperties properties) { + public NoLoadBalancerClientFilter noLoadBalancerClientFilter( + LoadBalancerProperties properties) { return new NoLoadBalancerClientFilter(properties.isUse404()); } @@ -73,11 +73,15 @@ public class GatewayNoLoadBalancerClientAutoConfiguration { public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR); - if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) { + if (url == null + || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) { return chain.filter(exchange); } - throw NotFoundException.create(use404, "Unable to find instance for " + url.getHost()); + throw NotFoundException.create(use404, + "Unable to find instance for " + url.getHost()); } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java index 93dcd1b9..38c06c9c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; @@ -26,6 +25,7 @@ import javax.validation.constraints.NotNull; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; @@ -40,8 +40,9 @@ import org.springframework.validation.annotation.Validated; public class GatewayProperties { private final Log logger = LogFactory.getLog(getClass()); + /** - * List of Routes + * List of Routes. */ @NotNull @Valid @@ -52,18 +53,17 @@ public class GatewayProperties { */ private List defaultFilters = new ArrayList<>(); - private List streamingMediaTypes = Arrays.asList(MediaType.TEXT_EVENT_STREAM, - MediaType.APPLICATION_STREAM_JSON); + private List streamingMediaTypes = Arrays + .asList(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_STREAM_JSON); public List getRoutes() { return routes; } - public void setRoutes(List routes) { this.routes = routes; if (routes != null && routes.size() > 0 && logger.isDebugEnabled()) { - logger.debug("Routes supplied from Gateway Properties: "+routes); + logger.debug("Routes supplied from Gateway Properties: " + routes); } } @@ -85,10 +85,8 @@ public class GatewayProperties { @Override public String toString() { - return "GatewayProperties{" + - "routes=" + routes + - ", defaultFilters=" + defaultFilters + - ", streamingMediaTypes=" + streamingMediaTypes + - '}'; + return "GatewayProperties{" + "routes=" + routes + ", defaultFilters=" + + defaultFilters + ", streamingMediaTypes=" + streamingMediaTypes + '}'; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayRedisAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayRedisAutoConfiguration.java index e1e31b24..3c6eca50 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayRedisAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayRedisAutoConfiguration.java @@ -1,3 +1,19 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.config; import java.util.List; @@ -29,39 +45,38 @@ import org.springframework.web.reactive.DispatcherHandler; @AutoConfigureAfter(RedisReactiveAutoConfiguration.class) @AutoConfigureBefore(GatewayAutoConfiguration.class) @ConditionalOnBean(ReactiveRedisTemplate.class) -@ConditionalOnClass({RedisTemplate.class, DispatcherHandler.class}) +@ConditionalOnClass({ RedisTemplate.class, DispatcherHandler.class }) class GatewayRedisAutoConfiguration { @Bean @SuppressWarnings("unchecked") public RedisScript redisRequestRateLimiterScript() { DefaultRedisScript redisScript = new DefaultRedisScript<>(); - redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/scripts/request_rate_limiter.lua"))); + redisScript.setScriptSource(new ResourceScriptSource( + new ClassPathResource("META-INF/scripts/request_rate_limiter.lua"))); redisScript.setResultType(List.class); return redisScript; } @Bean - //TODO: replace with ReactiveStringRedisTemplate in future + // TODO: replace with ReactiveStringRedisTemplate in future public ReactiveRedisTemplate stringReactiveRedisTemplate( ReactiveRedisConnectionFactory reactiveRedisConnectionFactory) { RedisSerializer serializer = new StringRedisSerializer(); - RedisSerializationContext serializationContext = RedisSerializationContext - .newSerializationContext() - .key(serializer) - .value(serializer) - .hashKey(serializer) - .hashValue(serializer) - .build(); + RedisSerializationContext serializationContext = RedisSerializationContext + .newSerializationContext().key(serializer) + .value(serializer).hashKey(serializer).hashValue(serializer).build(); return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory, serializationContext); } @Bean @ConditionalOnMissingBean - public RedisRateLimiter redisRateLimiter(ReactiveRedisTemplate redisTemplate, - @Qualifier(RedisRateLimiter.REDIS_SCRIPT_NAME) RedisScript> redisScript, - Validator validator) { + public RedisRateLimiter redisRateLimiter( + ReactiveRedisTemplate redisTemplate, + @Qualifier(RedisRateLimiter.REDIS_SCRIPT_NAME) RedisScript> redisScript, + Validator validator) { return new RedisRateLimiter(redisTemplate, redisScript, validator); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GlobalCorsProperties.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GlobalCorsProperties.java index 8675ac41..a75d2131 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GlobalCorsProperties.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GlobalCorsProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java index 7aeb684a..e7777d2b 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,20 +12,10 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; -import org.springframework.boot.web.server.WebServerException; -import org.springframework.core.style.ToStringCreator; -import org.springframework.util.ResourceUtils; -import reactor.netty.resources.ConnectionProvider; -import reactor.netty.tcp.SslProvider; - - import java.io.IOException; import java.net.URL; import java.security.cert.Certificate; @@ -37,8 +27,17 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import reactor.netty.resources.ConnectionProvider; +import reactor.netty.tcp.SslProvider; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; +import org.springframework.boot.web.server.WebServerException; +import org.springframework.core.style.ToStringCreator; +import org.springframework.util.ResourceUtils; + /** - * Configuration properties for the Netty {@link reactor.netty.http.client.HttpClient} + * Configuration properties for the Netty {@link reactor.netty.http.client.HttpClient}. */ @ConfigurationProperties("spring.cloud.gateway.httpclient") public class HttpClientProperties { @@ -49,19 +48,23 @@ public class HttpClientProperties { /** The response timeout. */ private Duration responseTimeout; - /** Pool configuration for Netty HttpClient */ + /** Pool configuration for Netty HttpClient. */ private Pool pool = new Pool(); - /** Proxy configuration for Netty HttpClient */ + /** Proxy configuration for Netty HttpClient. */ private Proxy proxy = new Proxy(); - /** SSL configuration for Netty HttpClient */ + /** SSL configuration for Netty HttpClient. */ private Ssl ssl = new Ssl(); public Integer getConnectTimeout() { return connectTimeout; } + public void setConnectTimeout(Integer connectTimeout) { + this.connectTimeout = connectTimeout; + } + public Duration getResponseTimeout() { return responseTimeout; } @@ -70,10 +73,6 @@ public class HttpClientProperties { this.responseTimeout = responseTimeout; } - public void setConnectTimeout(Integer connectTimeout) { - this.connectTimeout = connectTimeout; - } - public Pool getPool() { return pool; } @@ -98,9 +97,14 @@ public class HttpClientProperties { this.ssl = ssl; } - public static class Pool { + @Override + public String toString() { + return new ToStringCreator(this).append("connectTimeout", connectTimeout) + .append("responseTimeout", responseTimeout).append("pool", pool) + .append("proxy", proxy).append("ssl", ssl).toString(); + } - public enum PoolType { ELASTIC, FIXED, DISABLED } + public static class Pool { /** Type of pool for HttpClient to use, defaults to ELASTIC. */ private PoolType type = PoolType.ELASTIC; @@ -108,7 +112,10 @@ public class HttpClientProperties { /** The channel pool map name, defaults to proxy. */ private String name = "proxy"; - /** Only for type FIXED, the maximum number of connections before starting pending acquisition on existing ones. */ + /** + * Only for type FIXED, the maximum number of connections before starting pending + * acquisition on existing ones. + */ private Integer maxConnections = ConnectionProvider.DEFAULT_POOL_MAX_CONNECTIONS; /** Only for type FIXED, the maximum time in millis to wait for aquiring. */ @@ -148,26 +155,50 @@ public class HttpClientProperties { @Override public String toString() { - return "Pool{" + - "type=" + type + - ", name='" + name + '\'' + - ", maxConnections=" + maxConnections + - ", acquireTimeout=" + acquireTimeout + - '}'; + return "Pool{" + "type=" + type + ", name='" + name + '\'' + + ", maxConnections=" + maxConnections + ", acquireTimeout=" + + acquireTimeout + '}'; } + + public enum PoolType { + + /** + * Elastic pool type. + */ + ELASTIC, + + /** + * Fixed pool type. + */ + FIXED, + + /** + * Disabled pool type. + */ + DISABLED + + } + } public class Proxy { + /** Hostname for proxy configuration of Netty HttpClient. */ private String host; + /** Port for proxy configuration of Netty HttpClient. */ private Integer port; + /** Username for proxy configuration of Netty HttpClient. */ private String username; + /** Password for proxy configuration of Netty HttpClient. */ private String password; - /** Regular expression (Java) for a configured list of hosts - * that should be reached directly, bypassing the proxy */ + + /** + * Regular expression (Java) for a configured list of hosts. that should be + * reached directly, bypassing the proxy + */ private String nonProxyHostsPattern; public String getHost() { @@ -212,28 +243,31 @@ public class HttpClientProperties { @Override public String toString() { - return "Proxy{" + - "host='" + host + '\'' + - ", port=" + port + - ", username='" + username + '\'' + - ", password='" + password + '\'' + - ", nonProxyHostsPattern='" + nonProxyHostsPattern + '\'' + - '}'; + return "Proxy{" + "host='" + host + '\'' + ", port=" + port + ", username='" + + username + '\'' + ", password='" + password + '\'' + + ", nonProxyHostsPattern='" + nonProxyHostsPattern + '\'' + '}'; } + } public class Ssl { - /** Installs the netty InsecureTrustManagerFactory. This is insecure and not suitable for production. */ + + /** + * Installs the netty InsecureTrustManagerFactory. This is insecure and not + * suitable for production. + */ private boolean useInsecureTrustManager = false; /** Trusted certificates for verifying the remote endpoint's certificate. */ private List trustedX509Certificates = new ArrayList<>(); - + // use netty default SSL timeouts /** SSL handshake timeout. Default to 10000 ms */ private Duration handshakeTimeout = Duration.ofMillis(10000); + /** SSL close_notify flush timeout. Default to 3000 ms. */ private Duration closeNotifyFlushTimeout = Duration.ofMillis(3000); + /** SSL close_notify read timeout. Default to 0 ms. */ private Duration closeNotifyReadTimeout = Duration.ZERO; @@ -243,7 +277,11 @@ public class HttpClientProperties { public List getTrustedX509Certificates() { return trustedX509Certificates; } - + + public void setTrustedX509Certificates(List trustedX509) { + this.trustedX509Certificates = trustedX509; + } + public X509Certificate[] getTrustedX509CertificatesForTrustManager() { try { CertificateFactory certificateFactory = CertificateFactory @@ -269,11 +307,7 @@ public class HttpClientProperties { } } - public void setTrustedX509Certificates(List trustedX509) { - this.trustedX509Certificates = trustedX509; - } - - //TODO: support configuration of other trust manager factories + // TODO: support configuration of other trust manager factories public boolean isUseInsecureTrustManager() { return useInsecureTrustManager; @@ -344,7 +378,8 @@ public class HttpClientProperties { return defaultConfigurationType; } - public void setDefaultConfigurationType(SslProvider.DefaultConfigurationType defaultConfigurationType) { + public void setDefaultConfigurationType( + SslProvider.DefaultConfigurationType defaultConfigurationType) { this.defaultConfigurationType = defaultConfigurationType; } @@ -359,16 +394,7 @@ public class HttpClientProperties { .append("defaultConfigurationType", defaultConfigurationType) .toString(); } + } - @Override - public String toString() { - return new ToStringCreator(this) - .append("connectTimeout", connectTimeout) - .append("responseTimeout", responseTimeout) - .append("pool", pool) - .append("proxy", proxy) - .append("ssl", ssl) - .toString(); - } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/LoadBalancerProperties.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/LoadBalancerProperties.java index fa004212..1c88a392 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/LoadBalancerProperties.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/LoadBalancerProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,8 +12,8 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ + package org.springframework.cloud.gateway.config; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -23,6 +23,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; */ @ConfigurationProperties("spring.cloud.gateway.loadbalancer") public class LoadBalancerProperties { + private boolean use404; public boolean isUse404() { @@ -32,4 +33,5 @@ public class LoadBalancerProperties { public void setUse404(boolean use404) { this.use404 = use404; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/PropertiesRouteDefinitionLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/PropertiesRouteDefinitionLocator.java index b5266539..73d477c5 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/PropertiesRouteDefinitionLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/PropertiesRouteDefinitionLocator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,16 +12,15 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; +import reactor.core.publisher.Flux; + import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionLocator; -import reactor.core.publisher.Flux; - /** * @author Spencer Gibb */ @@ -37,4 +36,5 @@ public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator public Flux getRouteDefinitions() { return Flux.fromIterable(this.properties.getRoutes()); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java index 9647ee2e..8bfd74bb 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.discovery; @@ -41,27 +40,33 @@ import org.springframework.util.StringUtils; /** * TODO: change to RouteLocator? use java dsl + * * @author Spencer Gibb */ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator { - private static final Log log = LogFactory.getLog(DiscoveryClientRouteDefinitionLocator.class); + private static final Log log = LogFactory + .getLog(DiscoveryClientRouteDefinitionLocator.class); private final DiscoveryClient discoveryClient; + private final DiscoveryLocatorProperties properties; + private final String routeIdPrefix; + private final SimpleEvaluationContext evalCtxt; - public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) { + public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, + DiscoveryLocatorProperties properties) { this.discoveryClient = discoveryClient; this.properties = properties; if (StringUtils.hasText(properties.getRouteIdPrefix())) { this.routeIdPrefix = properties.getRouteIdPrefix(); - } else { + } + else { this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + "_"; } - evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding() - .withInstanceMethods() + evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods() .build(); } @@ -69,13 +74,16 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc public Flux getRouteDefinitions() { SpelExpressionParser parser = new SpelExpressionParser(); - Expression includeExpr = parser.parseExpression(properties.getIncludeExpression()); + Expression includeExpr = parser + .parseExpression(properties.getIncludeExpression()); Expression urlExpr = parser.parseExpression(properties.getUrlExpression()); Predicate includePredicate; - if (properties.getIncludeExpression() == null || "true".equalsIgnoreCase(properties.getIncludeExpression())) { + if (properties.getIncludeExpression() == null + || "true".equalsIgnoreCase(properties.getIncludeExpression())) { includePredicate = instance -> true; - } else { + } + else { includePredicate = instance -> { Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class); if (include == null) { @@ -88,47 +96,53 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc return Flux.fromIterable(discoveryClient.getServices()) .map(discoveryClient::getInstances) .filter(instances -> !instances.isEmpty()) - .map(instances -> instances.get(0)) - .filter(includePredicate) + .map(instances -> instances.get(0)).filter(includePredicate) .map(instance -> { String serviceId = instance.getServiceId(); - RouteDefinition routeDefinition = new RouteDefinition(); - routeDefinition.setId(this.routeIdPrefix + serviceId); + RouteDefinition routeDefinition = new RouteDefinition(); + routeDefinition.setId(this.routeIdPrefix + serviceId); String uri = urlExpr.getValue(evalCtxt, instance, String.class); routeDefinition.setUri(URI.create(uri)); - final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties); + final ServiceInstance instanceForEval = new DelegatingServiceInstance( + instance, properties); for (PredicateDefinition original : this.properties.getPredicates()) { PredicateDefinition predicate = new PredicateDefinition(); predicate.setName(original.getName()); - for (Map.Entry entry : original.getArgs().entrySet()) { - String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry); + for (Map.Entry entry : original.getArgs() + .entrySet()) { + String value = getValueFromExpr(evalCtxt, parser, + instanceForEval, entry); predicate.addArg(entry.getKey(), value); } routeDefinition.getPredicates().add(predicate); } - for (FilterDefinition original : this.properties.getFilters()) { - FilterDefinition filter = new FilterDefinition(); - filter.setName(original.getName()); - for (Map.Entry entry : original.getArgs().entrySet()) { - String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry); + for (FilterDefinition original : this.properties.getFilters()) { + FilterDefinition filter = new FilterDefinition(); + filter.setName(original.getName()); + for (Map.Entry entry : original.getArgs() + .entrySet()) { + String value = getValueFromExpr(evalCtxt, parser, + instanceForEval, entry); filter.addArg(entry.getKey(), value); } routeDefinition.getFilters().add(filter); } - return routeDefinition; + return routeDefinition; }); } - String getValueFromExpr(SimpleEvaluationContext evalCtxt, SpelExpressionParser parser, ServiceInstance instance, Map.Entry entry) { + String getValueFromExpr(SimpleEvaluationContext evalCtxt, SpelExpressionParser parser, + ServiceInstance instance, Map.Entry entry) { try { Expression valueExpr = parser.parseExpression(entry.getValue()); return valueExpr.getValue(evalCtxt, instance, String.class); - } catch (ParseException | EvaluationException e) { + } + catch (ParseException | EvaluationException e) { if (log.isDebugEnabled()) { log.debug("Unable to parse " + entry.getValue(), e); } @@ -139,9 +153,11 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc private static class DelegatingServiceInstance implements ServiceInstance { final ServiceInstance delegate; + private final DiscoveryLocatorProperties properties; - private DelegatingServiceInstance(ServiceInstance delegate, DiscoveryLocatorProperties properties) { + private DelegatingServiceInstance(ServiceInstance delegate, + DiscoveryLocatorProperties properties) { this.delegate = delegate; this.properties = properties; } @@ -186,10 +202,10 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc @Override public String toString() { - return new ToStringCreator(this) - .append("delegate", delegate) - .append("properties", properties) - .toString(); + return new ToStringCreator(this).append("delegate", delegate) + .append("properties", properties).toString(); } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryLocatorProperties.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryLocatorProperties.java index 287b65a1..15156b5e 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryLocatorProperties.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryLocatorProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,12 +12,10 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.discovery; - import java.util.ArrayList; import java.util.List; @@ -29,28 +27,30 @@ import org.springframework.core.style.ToStringCreator; @ConfigurationProperties("spring.cloud.gateway.discovery.locator") public class DiscoveryLocatorProperties { - /** Flag that enables DiscoveryClient gateway integration */ + /** Flag that enables DiscoveryClient gateway integration. */ private boolean enabled = false; /** - * The prefix for the routeId, defaults to discoveryClient.getClass().getSimpleName() + "_". - * Service Id will be appended to create the routeId. + * The prefix for the routeId, defaults to discoveryClient.getClass().getSimpleName() + * + "_". Service Id will be appended to create the routeId. */ private String routeIdPrefix; /** - * SpEL expression that will evaluate whether to include a service in gateway integration or not, - * defaults to: true + * SpEL expression that will evaluate whether to include a service in gateway + * integration or not, defaults to: true. */ private String includeExpression = "true"; - /** SpEL expression that create the uri for each route, defaults to: 'lb://'+serviceId */ + /** + * SpEL expression that create the uri for each route, defaults to: 'lb://'+serviceId. + */ private String urlExpression = "'lb://'+serviceId"; /** - * Option to lower case serviceId in predicates and filters, defaults to false. - * Useful with eureka when it automatically uppercases serviceId. - * so MYSERIVCE, would match /myservice/** + * Option to lower case serviceId in predicates and filters, defaults to false. Useful + * with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match + * /myservice/** */ private boolean lowerCaseServiceId = false; @@ -116,14 +116,12 @@ public class DiscoveryLocatorProperties { @Override public String toString() { - return new ToStringCreator(this) - .append("enabled", enabled) + return new ToStringCreator(this).append("enabled", enabled) .append("routeIdPrefix", routeIdPrefix) .append("includeExpression", includeExpression) .append("urlExpression", urlExpression) .append("lowerCaseServiceId", lowerCaseServiceId) - .append("predicates", predicates) - .append("filters", filters) - .toString(); + .append("predicates", predicates).append("filters", filters).toString(); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/GatewayDiscoveryClientAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/GatewayDiscoveryClientAutoConfiguration.java index f2ccfa17..3de32254 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/GatewayDiscoveryClientAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/GatewayDiscoveryClientAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,9 @@ package org.springframework.cloud.gateway.discovery; +import java.util.ArrayList; +import java.util.List; + import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -33,9 +36,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.DispatcherHandler; -import java.util.ArrayList; -import java.util.List; - import static org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory.REGEXP_KEY; import static org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory.REPLACEMENT_KEY; import static org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory.PATTERN_KEY; @@ -49,26 +49,10 @@ import static org.springframework.cloud.gateway.support.NameUtils.normalizeRoute @ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true) @AutoConfigureBefore(GatewayAutoConfiguration.class) @AutoConfigureAfter(CompositeDiscoveryClientAutoConfiguration.class) -@ConditionalOnClass({DispatcherHandler.class, DiscoveryClient.class}) +@ConditionalOnClass({ DispatcherHandler.class, DiscoveryClient.class }) @EnableConfigurationProperties public class GatewayDiscoveryClientAutoConfiguration { - @Bean - @ConditionalOnBean(DiscoveryClient.class) - @ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled") - public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator( - DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) { - return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties); - } - - @Bean - public DiscoveryLocatorProperties discoveryLocatorProperties() { - DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties(); - properties.setPredicates(initPredicates()); - properties.setFilters(initFilters()); - return properties; - } - public static List initPredicates() { ArrayList definitions = new ArrayList<>(); // TODO: add a predicate that matches the url at /serviceId? @@ -96,5 +80,20 @@ public class GatewayDiscoveryClientAutoConfiguration { return definitions; } -} + @Bean + @ConditionalOnBean(DiscoveryClient.class) + @ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled") + public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator( + DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) { + return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties); + } + @Bean + public DiscoveryLocatorProperties discoveryLocatorProperties() { + DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties(); + properties.setPredicates(initPredicates()); + properties.setFilters(initFilters()); + return properties; + } + +} diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/FilterArgsEvent.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/FilterArgsEvent.java index 6fe2edd2..e63e845e 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/FilterArgsEvent.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/FilterArgsEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.event; @@ -22,9 +21,11 @@ import java.util.Map; import org.springframework.context.ApplicationEvent; public class FilterArgsEvent extends ApplicationEvent { - private String routeId; + private final Map args; + private String routeId; + public FilterArgsEvent(Object source, String routeId, Map args) { super(source); this.routeId = routeId; @@ -38,4 +39,5 @@ public class FilterArgsEvent extends ApplicationEvent { public Map getArgs() { return args; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/PredicateArgsEvent.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/PredicateArgsEvent.java index 2859aba3..2065e96c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/PredicateArgsEvent.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/PredicateArgsEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.event; @@ -22,9 +21,11 @@ import java.util.Map; import org.springframework.context.ApplicationEvent; public class PredicateArgsEvent extends ApplicationEvent { - private String routeId; + private final Map args; + private String routeId; + public PredicateArgsEvent(Object source, String routeId, Map args) { super(source); this.routeId = routeId; @@ -38,4 +39,5 @@ public class PredicateArgsEvent extends ApplicationEvent { public Map getArgs() { return args; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/RefreshRoutesEvent.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/RefreshRoutesEvent.java index c23e6072..b2431190 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/RefreshRoutesEvent.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/RefreshRoutesEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.event; @@ -24,12 +23,12 @@ import org.springframework.context.ApplicationEvent; */ public class RefreshRoutesEvent extends ApplicationEvent { - /** - * Create a new ApplicationEvent. - * - * @param source the object on which the event initially occurred (never {@code null}) - */ - public RefreshRoutesEvent(Object source) { - super(source); - } + /** + * Create a new ApplicationEvent. + * @param source the object on which the event initially occurred (never {@code null}) + */ + public RefreshRoutesEvent(Object source) { + super(source); + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/WeightDefinedEvent.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/WeightDefinedEvent.java index b1c5cccb..21fb14fe 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/WeightDefinedEvent.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/event/WeightDefinedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.event; @@ -21,6 +20,7 @@ import org.springframework.cloud.gateway.support.WeightConfig; import org.springframework.context.ApplicationEvent; public class WeightDefinedEvent extends ApplicationEvent { + private final WeightConfig weightConfig; public WeightDefinedEvent(Object source, WeightConfig weightConfig) { @@ -31,4 +31,5 @@ public class WeightDefinedEvent extends ApplicationEvent { public WeightConfig getWeightConfig() { return weightConfig; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/AdaptCachedBodyGlobalFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/AdaptCachedBodyGlobalFilter.java index a7f758b2..23aa0841 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/AdaptCachedBodyGlobalFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/AdaptCachedBodyGlobalFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,28 +12,33 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; public class AdaptCachedBodyGlobalFilter implements GlobalFilter, Ordered { + /** + * Cached request body key. + */ public static final String CACHED_REQUEST_BODY_KEY = "cachedRequestBody"; @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - Flux body = exchange.getAttributeOrDefault(CACHED_REQUEST_BODY_KEY, null); + Flux body = exchange.getAttributeOrDefault(CACHED_REQUEST_BODY_KEY, + null); if (body != null) { - ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) { + ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator( + exchange.getRequest()) { @Override public Flux getBody() { return body; @@ -50,4 +55,5 @@ public class AdaptCachedBodyGlobalFilter implements GlobalFilter, Ordered { public int getOrder() { return Ordered.HIGHEST_PRECEDENCE + 1000; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/FilterDefinition.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/FilterDefinition.java index e3545ddf..91b211e7 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/FilterDefinition.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/FilterDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -33,8 +32,10 @@ import static org.springframework.util.StringUtils.tokenizeToStringArray; */ @Validated public class FilterDefinition { + @NotNull private String name; + private Map args = new LinkedHashMap<>(); public FilterDefinition() { @@ -48,9 +49,9 @@ public class FilterDefinition { } setName(text.substring(0, eqIdx)); - String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ","); + String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ","); - for (int i=0; i < args.length; i++) { + for (int i = 0; i < args.length; i++) { this.args.put(NameUtils.generateName(i), args[i]); } } @@ -77,11 +78,14 @@ public class FilterDefinition { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } FilterDefinition that = (FilterDefinition) o; - return Objects.equals(name, that.name) && - Objects.equals(args, that.args); + return Objects.equals(name, that.name) && Objects.equals(args, that.args); } @Override @@ -97,4 +101,5 @@ public class FilterDefinition { sb.append('}'); return sb.toString(); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ForwardPathFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ForwardPathFilter.java index ebea7f3a..8e9decc7 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ForwardPathFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ForwardPathFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,14 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ + package org.springframework.cloud.gateway.filter; +import java.net.URI; + import reactor.core.publisher.Mono; -import java.net.URI; import org.springframework.cloud.gateway.route.Route; import org.springframework.core.Ordered; import org.springframework.web.server.ServerWebExchange; @@ -29,9 +30,11 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.i /** * Filter to set the path in the request URI if the {@link Route} URI has the scheme * forward. + * * @author Ryan Baxter */ -public class ForwardPathFilter implements GlobalFilter, Ordered{ +public class ForwardPathFilter implements GlobalFilter, Ordered { + @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); @@ -40,8 +43,8 @@ public class ForwardPathFilter implements GlobalFilter, Ordered{ if (isAlreadyRouted(exchange) || !"forward".equals(scheme)) { return chain.filter(exchange); } - exchange = exchange.mutate().request( - exchange.getRequest().mutate().path(routeUri.getPath()).build()) + exchange = exchange.mutate() + .request(exchange.getRequest().mutate().path(routeUri.getPath()).build()) .build(); return chain.filter(exchange); } @@ -50,4 +53,5 @@ public class ForwardPathFilter implements GlobalFilter, Ordered{ public int getOrder() { return 0; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ForwardRoutingFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ForwardRoutingFilter.java index 89b8c238..e77e8d56 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ForwardRoutingFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ForwardRoutingFilter.java @@ -1,3 +1,19 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter; import java.net.URI; @@ -20,10 +36,12 @@ public class ForwardRoutingFilter implements GlobalFilter, Ordered { private static final Log log = LogFactory.getLog(ForwardRoutingFilter.class); private final ObjectProvider dispatcherHandlerProvider; - //do not use this dispatcherHandler directly, use getDispatcherHandler() instead. + + // do not use this dispatcherHandler directly, use getDispatcherHandler() instead. private volatile DispatcherHandler dispatcherHandler; - public ForwardRoutingFilter(ObjectProvider dispatcherHandlerProvider) { + public ForwardRoutingFilter( + ObjectProvider dispatcherHandlerProvider) { this.dispatcherHandlerProvider = dispatcherHandlerProvider; } @@ -50,12 +68,13 @@ public class ForwardRoutingFilter implements GlobalFilter, Ordered { } setAlreadyRouted(exchange); - //TODO: translate url? + // TODO: translate url? if (log.isTraceEnabled()) { - log.trace("Forwarding to URI: "+requestUrl); + log.trace("Forwarding to URI: " + requestUrl); } return this.getDispatcherHandler().handle(exchange); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilter.java index 87936593..a6a3f5ea 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilter.java @@ -1,7 +1,5 @@ -package org.springframework.cloud.gateway.filter; - /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +14,17 @@ package org.springframework.cloud.gateway.filter; * limitations under the License. */ -import org.springframework.cloud.gateway.support.ShortcutConfigurable; -import org.springframework.web.server.ServerWebExchange; +package org.springframework.cloud.gateway.filter; import reactor.core.publisher.Mono; +import org.springframework.cloud.gateway.support.ShortcutConfigurable; +import org.springframework.web.server.ServerWebExchange; + /** - * Contract for interception-style, chained processing of Web requests that may - * be used to implement cross-cutting, application-agnostic requirements such - * as security, timeouts, and others. Specific to a Gateway + * Contract for interception-style, chained processing of Web requests that may be used to + * implement cross-cutting, application-agnostic requirements such as security, timeouts, + * and others. Specific to a Gateway * * Copied from WebFilter * @@ -33,12 +33,19 @@ import reactor.core.publisher.Mono; */ public interface GatewayFilter extends ShortcutConfigurable { + /** + * Name key. + */ String NAME_KEY = "name"; + + /** + * Value key. + */ String VALUE_KEY = "value"; /** - * Process the Web request and (optionally) delegate to the next - * {@code WebFilter} through the given {@link GatewayFilterChain}. + * Process the Web request and (optionally) delegate to the next {@code WebFilter} + * through the given {@link GatewayFilterChain}. * @param exchange the current server exchange * @param chain provides a way to delegate to the next filter * @return {@code Mono} to indicate when request processing is complete @@ -46,4 +53,3 @@ public interface GatewayFilter extends ShortcutConfigurable { Mono filter(ServerWebExchange exchange, GatewayFilterChain chain); } - diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilterChain.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilterChain.java index 4f96ce0d..7e9325e7 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilterChain.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayFilterChain.java @@ -1,8 +1,25 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter; +import reactor.core.publisher.Mono; + import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; -import reactor.core.publisher.Mono; /** * Contract to allow a {@link WebFilter} to delegate to the next in the chain. diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayMetricsFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayMetricsFilter.java index 28c8ba53..ffc5dce3 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayMetricsFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GatewayMetricsFilter.java @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -102,4 +101,5 @@ public class GatewayMetricsFilter implements GlobalFilter, Ordered { } sample.stop(meterRegistry.timer("gateway.requests", tags)); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GlobalFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GlobalFilter.java index 6554bda2..09b987ee 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GlobalFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/GlobalFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,20 +12,18 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.cloud.gateway.filter.GatewayFilterChain; - import reactor.core.publisher.Mono; +import org.springframework.web.server.ServerWebExchange; + /** - * Contract for interception-style, chained processing of Web requests that may - * be used to implement cross-cutting, application-agnostic requirements such - * as security, timeouts, and others. + * Contract for interception-style, chained processing of Web requests that may be used to + * implement cross-cutting, application-agnostic requirements such as security, timeouts, + * and others. * * @author Rossen Stoyanchev * @since 5.0 @@ -33,8 +31,8 @@ import reactor.core.publisher.Mono; public interface GlobalFilter { /** - * Process the Web request and (optionally) delegate to the next - * {@code WebFilter} through the given {@link GatewayFilterChain}. + * Process the Web request and (optionally) delegate to the next {@code WebFilter} + * through the given {@link GatewayFilterChain}. * @param exchange the current server exchange * @param chain provides a way to delegate to the next filter * @return {@code Mono} to indicate when request processing is complete diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java index fac7ed24..d6cc3afd 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -41,14 +40,19 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.a */ public class LoadBalancerClientFilter implements GlobalFilter, Ordered { - private static final Log log = LogFactory.getLog(LoadBalancerClientFilter.class); + /** + * Filter order for {@link LoadBalancerClientFilter}. + */ public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10100; + private static final Log log = LogFactory.getLog(LoadBalancerClientFilter.class); + protected final LoadBalancerClient loadBalancer; private LoadBalancerProperties properties; - public LoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) { + public LoadBalancerClientFilter(LoadBalancerClient loadBalancer, + LoadBalancerProperties properties) { this.loadBalancer = loadBalancer; this.properties = properties; } @@ -63,10 +67,11 @@ public class LoadBalancerClientFilter implements GlobalFilter, Ordered { public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR); - if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) { + if (url == null + || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) { return chain.filter(exchange); } - //preserve the original url + // preserve the original url addOriginalRequestUrl(exchange, url); log.trace("LoadBalancerClientFilter url before: " + url); @@ -74,7 +79,8 @@ public class LoadBalancerClientFilter implements GlobalFilter, Ordered { final ServiceInstance instance = choose(exchange); if (instance == null) { - throw NotFoundException.create(properties.isUse404(), "Unable to find instance for " + url.getHost()); + throw NotFoundException.create(properties.isUse404(), + "Unable to find instance for " + url.getHost()); } URI uri = exchange.getRequest().getURI(); @@ -86,7 +92,8 @@ public class LoadBalancerClientFilter implements GlobalFilter, Ordered { overrideScheme = url.getScheme(); } - URI requestUrl = loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri); + URI requestUrl = loadBalancer.reconstructURI( + new DelegatingServiceInstance(instance, overrideScheme), uri); log.trace("LoadBalancerClientFilter url chosen: " + requestUrl); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl); @@ -94,11 +101,14 @@ public class LoadBalancerClientFilter implements GlobalFilter, Ordered { } protected ServiceInstance choose(ServerWebExchange exchange) { - return loadBalancer.choose(((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost()); + return loadBalancer.choose( + ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost()); } class DelegatingServiceInstance implements ServiceInstance { + final ServiceInstance delegate; + private String overrideScheme; DelegatingServiceInstance(ServiceInstance delegate, String overrideScheme) { @@ -146,4 +156,5 @@ public class LoadBalancerClientFilter implements GlobalFilter, Ordered { } } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java index e451f35f..ae2c5269 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -22,7 +21,6 @@ import java.util.List; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpMethod; -import org.springframework.web.server.ResponseStatusException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.netty.NettyPipeline; @@ -42,6 +40,7 @@ import org.springframework.http.server.reactive.AbstractServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.StringUtils; +import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import static org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter.filterRequest; @@ -61,14 +60,17 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.s public class NettyRoutingFilter implements GlobalFilter, Ordered { private final HttpClient httpClient; + private final ObjectProvider> headersFiltersProvider; + private final HttpClientProperties properties; - //do not use this headersFilters directly, use getHeadersFilters() instead. + + // do not use this headersFilters directly, use getHeadersFilters() instead. private volatile List headersFilters; public NettyRoutingFilter(HttpClient httpClient, - ObjectProvider> headersFiltersProvider, - HttpClientProperties properties) { + ObjectProvider> headersFiltersProvider, + HttpClientProperties properties) { this.httpClient = httpClient; this.headersFiltersProvider = headersFiltersProvider; this.properties = properties; @@ -92,7 +94,8 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered { URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); String scheme = requestUrl.getScheme(); - if (isAlreadyRouted(exchange) || (!"http".equals(scheme) && !"https".equals(scheme))) { + if (isAlreadyRouted(exchange) + || (!"http".equals(scheme) && !"https".equals(scheme))) { return chain.filter(exchange); } setAlreadyRouted(exchange); @@ -107,15 +110,15 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered { final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders(); filtered.forEach(httpHeaders::set); - String transferEncoding = request.getHeaders().getFirst(HttpHeaders.TRANSFER_ENCODING); + String transferEncoding = request.getHeaders() + .getFirst(HttpHeaders.TRANSFER_ENCODING); boolean chunkedTransfer = "chunked".equalsIgnoreCase(transferEncoding); - boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false); + boolean preserveHost = exchange + .getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false); Flux responseFlux = this.httpClient - .chunkedTransfer(chunkedTransfer) - .request(method) - .uri(url) + .chunkedTransfer(chunkedTransfer).request(method).uri(url) .send((req, nettyOutbound) -> { req.headers(httpHeaders); @@ -123,49 +126,64 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered { String host = request.getHeaders().getFirst(HttpHeaders.HOST); req.header(HttpHeaders.HOST, host); } - return nettyOutbound - .options(NettyPipeline.SendOptions::flushOnEach) - .send(request.getBody().map(dataBuffer -> - ((NettyDataBuffer) dataBuffer).getNativeBuffer())); + return nettyOutbound.options(NettyPipeline.SendOptions::flushOnEach) + .send(request.getBody() + .map(dataBuffer -> ((NettyDataBuffer) dataBuffer) + .getNativeBuffer())); }).responseConnection((res, connection) -> { ServerHttpResponse response = exchange.getResponse(); // put headers and status so filters can modify the response HttpHeaders headers = new HttpHeaders(); - res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue())); + res.responseHeaders().forEach( + entry -> headers.add(entry.getKey(), entry.getValue())); String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE); if (StringUtils.hasLength(contentTypeValue)) { - exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, contentTypeValue); + exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, + contentTypeValue); } HttpStatus status = HttpStatus.resolve(res.status().code()); if (status != null) { response.setStatusCode(status); - } else if (response instanceof AbstractServerHttpResponse) { + } + else if (response instanceof AbstractServerHttpResponse) { // https://jira.spring.io/browse/SPR-16748 - ((AbstractServerHttpResponse) response).setStatusCodeValue(res.status().code()); - } else { - throw new IllegalStateException("Unable to set status code on response: " + res.status().code() + ", " + response.getClass()); + ((AbstractServerHttpResponse) response) + .setStatusCodeValue(res.status().code()); + } + else { + throw new IllegalStateException( + "Unable to set status code on response: " + + res.status().code() + ", " + + response.getClass()); } - // make sure headers filters run after setting status so it is available in response + // make sure headers filters run after setting status so it is + // available in response HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter( - getHeadersFilters(), headers, exchange, Type.RESPONSE); + getHeadersFilters(), headers, exchange, Type.RESPONSE); - if(!filteredResponseHeaders.containsKey(HttpHeaders.TRANSFER_ENCODING) && - filteredResponseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH)) { - //It is not valid to have both the transfer-encoding header and the content-length header - //remove the transfer-encoding header in the response if the content-length header is presen + if (!filteredResponseHeaders + .containsKey(HttpHeaders.TRANSFER_ENCODING) + && filteredResponseHeaders + .containsKey(HttpHeaders.CONTENT_LENGTH)) { + // It is not valid to have both the transfer-encoding header and + // the content-length header + // remove the transfer-encoding header in the response if the + // content-length header is presen response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING); } - exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES, filteredResponseHeaders.keySet()); + exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES, + filteredResponseHeaders.keySet()); response.getHeaders().putAll(filteredResponseHeaders); // Defer committing the response until all route filters have run - // Put client response as ServerWebExchange attribute and write response later NettyWriteResponseFilter + // Put client response as ServerWebExchange attribute and write + // response later NettyWriteResponseFilter exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res); exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection); @@ -174,10 +192,11 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered { if (properties.getResponseTimeout() != null) { responseFlux = responseFlux.timeout(properties.getResponseTimeout(), - Mono.error(new TimeoutException("Response took longer than timeout: " + - properties.getResponseTimeout()))) + Mono.error(new TimeoutException("Response took longer than timeout: " + + properties.getResponseTimeout()))) .onErrorMap(TimeoutException.class, - th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th)); + th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, + th.getMessage(), th)); } return responseFlux.then(chain.filter(exchange)); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyWriteResponseFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyWriteResponseFilter.java index d00cb8f0..2bc1a9ca 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyWriteResponseFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/NettyWriteResponseFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -40,10 +39,13 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.C */ public class NettyWriteResponseFilter implements GlobalFilter, Ordered { - private static final Log log = LogFactory.getLog(NettyWriteResponseFilter.class); - + /** + * Order for write response filter. + */ public static final int WRITE_RESPONSE_FILTER_ORDER = -1; + private static final Log log = LogFactory.getLog(NettyWriteResponseFilter.class); + private final List streamingMediaTypes; public NettyWriteResponseFilter(List streamingMediaTypes) { @@ -68,29 +70,32 @@ public class NettyWriteResponseFilter implements GlobalFilter, Ordered { log.trace("NettyWriteResponseFilter start"); ServerHttpResponse response = exchange.getResponse(); - NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory(); - //TODO: what if it's not netty + NettyDataBufferFactory factory = (NettyDataBufferFactory) response + .bufferFactory(); + // TODO: what if it's not netty - final Flux body = connection.inbound().receive() - .retain() //TODO: needed? + final Flux body = connection.inbound().receive().retain() // TODO: + // needed? .map(factory::wrap); MediaType contentType = null; try { contentType = response.getHeaders().getContentType(); - } catch (Exception e) { + } + catch (Exception e) { log.trace("invalid media type", e); } - return (isStreamingMediaType(contentType) ? - response.writeAndFlushWith(body.map(Flux::just)) : response.writeWith(body)); + return (isStreamingMediaType(contentType) + ? response.writeAndFlushWith(body.map(Flux::just)) + : response.writeWith(body)); })); } - //TODO: use framework if possible - //TODO: port to WebClientWriteResponseFilter + // TODO: use framework if possible + // TODO: port to WebClientWriteResponseFilter private boolean isStreamingMediaType(@Nullable MediaType contentType) { return (contentType != null && this.streamingMediaTypes.stream() - .anyMatch(contentType::isCompatibleWith)); + .anyMatch(contentType::isCompatibleWith)); } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/OrderedGatewayFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/OrderedGatewayFilter.java index f9f21079..75766a5e 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/OrderedGatewayFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/OrderedGatewayFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,22 +12,22 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; +import reactor.core.publisher.Mono; + import org.springframework.core.Ordered; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - /** * @author Spencer Gibb */ public class OrderedGatewayFilter implements GatewayFilter, Ordered { private final GatewayFilter delegate; + private final int order; public OrderedGatewayFilter(GatewayFilter delegate, int order) { @@ -57,4 +57,5 @@ public class OrderedGatewayFilter implements GatewayFilter, Ordered { sb.append('}'); return sb.toString(); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java index e8d439f9..95a27463 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -22,6 +21,8 @@ import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.route.Route; import org.springframework.core.Ordered; import org.springframework.web.server.ServerWebExchange; @@ -32,19 +33,27 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts; -import reactor.core.publisher.Mono; - /** * @author Spencer Gibb */ public class RouteToRequestUrlFilter implements GlobalFilter, Ordered { + /** + * Order of Route to URL. + */ + public static final int ROUTE_TO_URL_FILTER_ORDER = 10000; + private static final Log log = LogFactory.getLog(RouteToRequestUrlFilter.class); - public static final int ROUTE_TO_URL_FILTER_ORDER = 10000; private static final String SCHEME_REGEX = "[a-zA-Z]([a-zA-Z]|\\d|\\+|\\.|-)*:.*"; static final Pattern schemePattern = Pattern.compile(SCHEME_REGEX); + /* for testing */ + static boolean hasAnotherScheme(URI uri) { + return schemePattern.matcher(uri.getSchemeSpecificPart()).matches() + && uri.getHost() == null && uri.getRawPath() == null; + } + @Override public int getOrder() { return ROUTE_TO_URL_FILTER_ORDER; @@ -64,29 +73,25 @@ public class RouteToRequestUrlFilter implements GlobalFilter, Ordered { if (hasAnotherScheme(routeUri)) { // this is a special url, save scheme to special attribute // replace routeUri with schemeSpecificPart - exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme()); + exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, + routeUri.getScheme()); routeUri = URI.create(routeUri.getSchemeSpecificPart()); } - if("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) { - //Load balanced URIs should always have a host. If the host is null it is most - //likely because the host name was invalid (for example included an underscore) + if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) { + // Load balanced URIs should always have a host. If the host is null it is + // most + // likely because the host name was invalid (for example included an + // underscore) throw new IllegalStateException("Invalid host: " + routeUri.toString()); } URI mergedUrl = UriComponentsBuilder.fromUri(uri) // .uri(routeUri) - .scheme(routeUri.getScheme()) - .host(routeUri.getHost()) - .port(routeUri.getPort()) - .build(encoded) - .toUri(); + .scheme(routeUri.getScheme()).host(routeUri.getHost()) + .port(routeUri.getPort()).build(encoded).toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl); return chain.filter(exchange); } - /* for testing */ static boolean hasAnotherScheme(URI uri) { - return schemePattern.matcher(uri.getSchemeSpecificPart()).matches() && uri.getHost() == null - && uri.getRawPath() == null; - } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientHttpRoutingFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientHttpRoutingFilter.java index ba7f49bf..883bf712 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientHttpRoutingFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientHttpRoutingFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,14 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; import java.net.URI; +import reactor.core.publisher.Mono; + import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -35,8 +36,6 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.isAlreadyRouted; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setAlreadyRouted; -import reactor.core.publisher.Mono; - /** * @author Spencer Gibb */ @@ -58,7 +57,8 @@ public class WebClientHttpRoutingFilter implements GlobalFilter, Ordered { URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); String scheme = requestUrl.getScheme(); - if (isAlreadyRouted(exchange) || (!"http".equals(scheme) && !"https".equals(scheme))) { + if (isAlreadyRouted(exchange) + || (!"http".equals(scheme) && !"https".equals(scheme))) { return chain.filter(exchange); } setAlreadyRouted(exchange); @@ -67,18 +67,18 @@ public class WebClientHttpRoutingFilter implements GlobalFilter, Ordered { HttpMethod method = request.getMethod(); - RequestBodySpec bodySpec = this.webClient.method(method) - .uri(requestUrl) + RequestBodySpec bodySpec = this.webClient.method(method).uri(requestUrl) .headers(httpHeaders -> { httpHeaders.addAll(request.getHeaders()); - //TODO: can this support preserviceHostHeader? + // TODO: can this support preserviceHostHeader? httpHeaders.remove(HttpHeaders.HOST); }); RequestHeadersSpec headersSpec; if (requiresBody(method)) { headersSpec = bodySpec.body(BodyInserters.fromDataBuffers(request.getBody())); - } else { + } + else { headersSpec = bodySpec; } @@ -89,7 +89,8 @@ public class WebClientHttpRoutingFilter implements GlobalFilter, Ordered { response.getHeaders().putAll(res.headers().asHttpHeaders()); response.setStatusCode(res.statusCode()); // Defer committing the response until all route filters have run - // Put client response as ServerWebExchange attribute and write response later NettyWriteResponseFilter + // Put client response as ServerWebExchange attribute and write + // response later NettyWriteResponseFilter exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res); return chain.filter(exchange); }); @@ -97,12 +98,13 @@ public class WebClientHttpRoutingFilter implements GlobalFilter, Ordered { private boolean requiresBody(HttpMethod method) { switch (method) { - case PUT: - case POST: - case PATCH: - return true; - default: - return false; + case PUT: + case POST: + case PATCH: + return true; + default: + return false; } } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientWriteResponseFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientWriteResponseFilter.java index 77cd6955..0f4733f0 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientWriteResponseFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebClientWriteResponseFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,14 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import reactor.core.publisher.Mono; + import org.springframework.core.Ordered; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.reactive.function.BodyExtractors; @@ -27,17 +28,18 @@ import org.springframework.web.server.ServerWebExchange; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR; -import reactor.core.publisher.Mono; - /** * @author Spencer Gibb */ public class WebClientWriteResponseFilter implements GlobalFilter, Ordered { - private static final Log log = LogFactory.getLog(WebClientWriteResponseFilter.class); - + /** + * Order of Write Response Filter. + */ public static final int WRITE_RESPONSE_FILTER_ORDER = -1; + private static final Log log = LogFactory.getLog(WebClientWriteResponseFilter.class); + @Override public int getOrder() { return WRITE_RESPONSE_FILTER_ORDER; @@ -55,7 +57,8 @@ public class WebClientWriteResponseFilter implements GlobalFilter, Ordered { log.trace("WebClientWriteResponseFilter start"); ServerHttpResponse response = exchange.getResponse(); - return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())).log("webClient response"); + return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())) + .log("webClient response"); })); } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilter.java index fdf8bdf5..13364308 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilter.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter; import java.net.URI; @@ -33,23 +49,37 @@ import static org.springframework.util.StringUtils.commaDelimitedListToStringArr * @author Spencer Gibb */ public class WebsocketRoutingFilter implements GlobalFilter, Ordered { - private static final Log log = LogFactory.getLog(WebsocketRoutingFilter.class); + + /** + * Sec-Websocket protocol. + */ public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol"; + private static final Log log = LogFactory.getLog(WebsocketRoutingFilter.class); + private final WebSocketClient webSocketClient; + private final WebSocketService webSocketService; + private final ObjectProvider> headersFiltersProvider; - //do not use this headersFilters directly, use getHeadersFilters() instead. + + // do not use this headersFilters directly, use getHeadersFilters() instead. private volatile List headersFilters; public WebsocketRoutingFilter(WebSocketClient webSocketClient, - WebSocketService webSocketService, - ObjectProvider> headersFiltersProvider) { + WebSocketService webSocketService, + ObjectProvider> headersFiltersProvider) { this.webSocketClient = webSocketClient; this.webSocketService = webSocketService; this.headersFiltersProvider = headersFiltersProvider; } + /* for testing */ + static String convertHttpToWs(String scheme) { + scheme = scheme.toLowerCase(); + return "http".equals(scheme) ? "ws" : "https".equals(scheme) ? "wss" : scheme; + } + @Override public int getOrder() { // Before NettyRoutingFilter since this routes certain http requests @@ -63,37 +93,38 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered { URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); String scheme = requestUrl.getScheme(); - if (isAlreadyRouted(exchange) || (!"ws".equals(scheme) && !"wss".equals(scheme))) { + if (isAlreadyRouted(exchange) + || (!"ws".equals(scheme) && !"wss".equals(scheme))) { return chain.filter(exchange); } setAlreadyRouted(exchange); - HttpHeaders headers = exchange.getRequest().getHeaders(); HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange); List protocols = headers.get(SEC_WEBSOCKET_PROTOCOL); if (protocols != null) { - protocols = headers.get(SEC_WEBSOCKET_PROTOCOL).stream() - .flatMap(header -> Arrays.stream(commaDelimitedListToStringArray(header))) - .map(String::trim) - .collect(Collectors.toList()); + protocols = headers.get(SEC_WEBSOCKET_PROTOCOL).stream().flatMap( + header -> Arrays.stream(commaDelimitedListToStringArray(header))) + .map(String::trim).collect(Collectors.toList()); } - return this.webSocketService.handleRequest(exchange, - new ProxyWebSocketHandler(requestUrl, this.webSocketClient, - filtered, protocols)); + return this.webSocketService.handleRequest(exchange, new ProxyWebSocketHandler( + requestUrl, this.webSocketClient, filtered, protocols)); } private List getHeadersFilters() { if (this.headersFilters == null) { - this.headersFilters = this.headersFiltersProvider.getIfAvailable(ArrayList::new); + this.headersFilters = this.headersFiltersProvider + .getIfAvailable(ArrayList::new); headersFilters.add((headers, exchange) -> { HttpHeaders filtered = new HttpHeaders(); headers.entrySet().stream() - .filter(entry -> !entry.getKey().toLowerCase().startsWith("sec-websocket")) - .forEach(header -> filtered.addAll(header.getKey(), header.getValue())); + .filter(entry -> !entry.getKey().toLowerCase() + .startsWith("sec-websocket")) + .forEach(header -> filtered.addAll(header.getKey(), + header.getValue())); return filtered; }); } @@ -107,9 +138,11 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered { String scheme = requestUrl.getScheme().toLowerCase(); String upgrade = exchange.getRequest().getHeaders().getUpgrade(); // change the scheme if the socket client send a "http" or "https" - if ("WebSocket".equalsIgnoreCase(upgrade) && ("http".equals(scheme) || "https".equals(scheme))) { + if ("WebSocket".equalsIgnoreCase(upgrade) + && ("http".equals(scheme) || "https".equals(scheme))) { String wsScheme = convertHttpToWs(scheme); - URI wsRequestUrl = UriComponentsBuilder.fromUri(requestUrl).scheme(wsScheme).build().toUri(); + URI wsRequestUrl = UriComponentsBuilder.fromUri(requestUrl).scheme(wsScheme) + .build().toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, wsRequestUrl); if (log.isTraceEnabled()) { log.trace("changeSchemeTo:[" + wsRequestUrl + "]"); @@ -117,25 +150,25 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered { } } - /* for testing */ static String convertHttpToWs(String scheme) { - scheme = scheme.toLowerCase(); - return "http".equals(scheme) ? "ws" : "https".equals(scheme) ? "wss" : scheme; - } - private static class ProxyWebSocketHandler implements WebSocketHandler { private final WebSocketClient client; + private final URI url; + private final HttpHeaders headers; + private final List subProtocols; - public ProxyWebSocketHandler(URI url, WebSocketClient client, HttpHeaders headers, List protocols) { + ProxyWebSocketHandler(URI url, WebSocketClient client, HttpHeaders headers, + List protocols) { this.client = client; this.url = url; this.headers = headers; if (protocols != null) { this.subProtocols = protocols; - } else { + } + else { this.subProtocols = Collections.emptyList(); } } @@ -154,10 +187,10 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered { // Use retain() for Reactor Netty Mono proxySessionSend = proxySession .send(session.receive().doOnNext(WebSocketMessage::retain)); - // .log("proxySessionSend", Level.FINE); - Mono serverSessionSend = session - .send(proxySession.receive().doOnNext(WebSocketMessage::retain)); - // .log("sessionSend", Level.FINE); + // .log("proxySessionSend", Level.FINE); + Mono serverSessionSend = session.send( + proxySession.receive().doOnNext(WebSocketMessage::retain)); + // .log("sessionSend", Level.FINE); return Mono.zip(proxySessionSend, serverSessionSend).then(); } @@ -171,5 +204,7 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered { } }); } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilter.java index a84cc862..d5cdef53 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -50,15 +49,22 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.W /** * @author Spencer Gibb */ -public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartApplicationListener { +public class WeightCalculatorWebFilter + implements WebFilter, Ordered, SmartApplicationListener { + + /** + * Order of Weight Calculator Web filter. + */ + public static final int WEIGHT_CALC_FILTER_ORDER = 10001; private static final Log log = LogFactory.getLog(WeightCalculatorWebFilter.class); - public static final int WEIGHT_CALC_FILTER_ORDER = 10001; - private final Validator validator; + private final ObjectProvider routeLocator; + private Random random = new Random(); + private int order = WEIGHT_CALC_FILTER_ORDER; private Map groupWeights = new ConcurrentHashMap<>(); @@ -72,11 +78,23 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli this(validator, null); } - public WeightCalculatorWebFilter(Validator validator, ObjectProvider routeLocator) { + public WeightCalculatorWebFilter(Validator validator, + ObjectProvider routeLocator) { this.validator = validator; this.routeLocator = routeLocator; } + /* for testing */ + static Map getWeights(ServerWebExchange exchange) { + Map weights = exchange.getAttribute(WEIGHT_ATTR); + + if (weights == null) { + weights = new ConcurrentHashMap<>(); + exchange.getAttributes().put(WEIGHT_ATTR, weights); + } + return weights; + } + @Override public int getOrder() { return order; @@ -93,8 +111,9 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli @Override public boolean supportsEventType(Class eventType) { return PredicateArgsEvent.class.isAssignableFrom(eventType) || // config file - WeightDefinedEvent.class.isAssignableFrom(eventType) || // java dsl - RefreshRoutesEvent.class.isAssignableFrom(eventType); // force initialization + WeightDefinedEvent.class.isAssignableFrom(eventType) || // java dsl + RefreshRoutesEvent.class.isAssignableFrom(eventType); // force + // initialization } @Override @@ -106,10 +125,13 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli public void onApplicationEvent(ApplicationEvent event) { if (event instanceof PredicateArgsEvent) { handle((PredicateArgsEvent) event); - } else if (event instanceof WeightDefinedEvent) { - addWeightConfig(((WeightDefinedEvent)event).getWeightConfig()); - } else if (event instanceof RefreshRoutesEvent && routeLocator != null) { - routeLocator.ifAvailable(locator -> locator.getRoutes().subscribe()); // forces initialization + } + else if (event instanceof WeightDefinedEvent) { + addWeightConfig(((WeightDefinedEvent) event).getWeightConfig()); + } + else if (event instanceof RefreshRoutesEvent && routeLocator != null) { + routeLocator.ifAvailable(locator -> locator.getRoutes().subscribe()); // forces + // initialization } } @@ -123,8 +145,8 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli WeightConfig config = new WeightConfig(event.getRouteId()); - ConfigurationUtils.bind(config, args, - WeightConfig.CONFIG_PREFIX, WeightConfig.CONFIG_PREFIX, validator); + ConfigurationUtils.bind(config, args, WeightConfig.CONFIG_PREFIX, + WeightConfig.CONFIG_PREFIX, validator); addWeightConfig(config); } @@ -144,10 +166,11 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli GroupWeightConfig config = c; config.weights.put(weightConfig.getRouteId(), weightConfig.getWeight()); - //recalculate + // recalculate // normalize weights - int weightsSum = config.weights.values().stream().mapToInt(Integer::intValue).sum(); + int weightsSum = config.weights.values().stream().mapToInt(Integer::intValue) + .sum(); final AtomicInteger index = new AtomicInteger(0); config.weights.forEach((routeId, weight) -> { @@ -158,7 +181,7 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli config.rangeIndexes.put(index.getAndIncrement(), routeId); }); - //TODO: calculate ranges + // TODO: calculate ranges config.ranges.clear(); config.ranges.add(0.0); @@ -172,7 +195,7 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli } if (log.isTraceEnabled()) { - log.trace("Recalculated group weight config "+ config); + log.trace("Recalculated group weight config " + config); } } @@ -199,11 +222,12 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli List ranges = config.ranges; if (log.isTraceEnabled()) { - log.trace("Weight for group: "+group +", ranges: "+ranges +", r: "+r); + log.trace("Weight for group: " + group + ", ranges: " + ranges + ", r: " + + r); } for (int i = 0; i < ranges.size() - 1; i++) { - if (r >= ranges.get(i) && r < ranges.get(i+1)) { + if (r >= ranges.get(i) && r < ranges.get(i + 1)) { String routeId = config.rangeIndexes.get(i); weights.put(group, routeId); break; @@ -212,23 +236,14 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli } if (log.isTraceEnabled()) { - log.trace("Weights attr: "+weights); + log.trace("Weights attr: " + weights); } return chain.filter(exchange); } - /* for testing */ static Map getWeights(ServerWebExchange exchange) { - Map weights = exchange.getAttribute(WEIGHT_ATTR); - - if (weights == null) { - weights = new ConcurrentHashMap<>(); - exchange.getAttributes().put(WEIGHT_ATTR, weights); - } - return weights; - } - /* for testing */ static class GroupWeightConfig { + String group; LinkedHashMap weights = new LinkedHashMap<>(); @@ -236,6 +251,7 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli LinkedHashMap normalizedWeights = new LinkedHashMap<>(); LinkedHashMap rangeIndexes = new LinkedHashMap<>(); + List ranges = new ArrayList<>(); GroupWeightConfig(String group) { @@ -244,13 +260,12 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli @Override public String toString() { - return new ToStringCreator(this) - .append("group", group) + return new ToStringCreator(this).append("group", group) .append("weights", weights) .append("normalizedWeights", normalizedWeights) - .append("rangeIndexes", rangeIndexes) - .toString(); + .append("rangeIndexes", rangeIndexes).toString(); } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractChangeRequestUriGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractChangeRequestUriGatewayFilterFactory.java index c612d11d..ea92de96 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractChangeRequestUriGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractChangeRequestUriGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -36,6 +35,7 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G */ public abstract class AbstractChangeRequestUriGatewayFilterFactory extends AbstractGatewayFilterFactory { + private final int order; public AbstractChangeRequestUriGatewayFilterFactory(Class clazz, int order) { @@ -60,4 +60,5 @@ public abstract class AbstractChangeRequestUriGatewayFilterFactory return chain.filter(exchange); }, this.order); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java index d0cb6da7..97ef2992 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -21,10 +20,11 @@ import org.springframework.cloud.gateway.support.AbstractConfigurable; /** * This class is BETA and may be subject to change in a future release. - * @param + * + * @param {@link AbstractConfigurable} subtype */ -public abstract class AbstractGatewayFilterFactory - extends AbstractConfigurable implements GatewayFilterFactory { +public abstract class AbstractGatewayFilterFactory extends AbstractConfigurable + implements GatewayFilterFactory { @SuppressWarnings("unchecked") public AbstractGatewayFilterFactory() { @@ -36,6 +36,7 @@ public abstract class AbstractGatewayFilterFactory } public static class NameConfig { + private String name; public String getName() { @@ -45,5 +46,7 @@ public abstract class AbstractGatewayFilterFactory public void setName(String name) { this.name = name; } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractNameValueGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractNameValueGatewayFilterFactory.java index 597f6b90..38c8c0b1 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractNameValueGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractNameValueGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -26,21 +25,23 @@ import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.core.style.ToStringCreator; import org.springframework.validation.annotation.Validated; -public abstract class AbstractNameValueGatewayFilterFactory extends AbstractGatewayFilterFactory { +public abstract class AbstractNameValueGatewayFilterFactory extends + AbstractGatewayFilterFactory { public AbstractNameValueGatewayFilterFactory() { super(NameValueConfig.class); } public List shortcutFieldOrder() { - return Arrays.asList(GatewayFilter.NAME_KEY, GatewayFilter.VALUE_KEY); - } - + return Arrays.asList(GatewayFilter.NAME_KEY, GatewayFilter.VALUE_KEY); + } @Validated public static class NameValueConfig { + @NotEmpty protected String name; + @NotEmpty protected String value; @@ -64,10 +65,10 @@ public abstract class AbstractNameValueGatewayFilterFactory extends AbstractGate @Override public String toString() { - return new ToStringCreator(this) - .append("name", name) - .append("value", value) + return new ToStringCreator(this).append("name", name).append("value", value) .toString(); } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderGatewayFilterFactory.java index 9315df1f..26ff5eaf 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -23,17 +22,17 @@ import org.springframework.http.server.reactive.ServerHttpRequest; /** * @author Spencer Gibb */ -public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory { +public class AddRequestHeaderGatewayFilterFactory + extends AbstractNameValueGatewayFilterFactory { @Override public GatewayFilter apply(NameValueConfig config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest().mutate() - .header(config.getName(), config.getValue()) - .build(); + .header(config.getName(), config.getValue()).build(); return chain.filter(exchange.mutate().request(request).build()); }; - } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterGatewayFilterFactory.java index 9aea7a62..99e7b6a4 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -27,7 +26,8 @@ import org.springframework.web.util.UriComponentsBuilder; /** * @author Spencer Gibb */ -public class AddRequestParameterGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory { +public class AddRequestParameterGatewayFilterFactory + extends AbstractNameValueGatewayFilterFactory { @Override public GatewayFilter apply(NameValueConfig config) { @@ -43,22 +43,23 @@ public class AddRequestParameterGatewayFilterFactory extends AbstractNameValueGa } } - //TODO urlencode? + // TODO urlencode? query.append(config.getName()); query.append('='); query.append(config.getValue()); try { URI newUri = UriComponentsBuilder.fromUri(uri) - .replaceQuery(query.toString()) - .build(true) - .toUri(); + .replaceQuery(query.toString()).build(true).toUri(); - ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build(); + ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri) + .build(); return chain.filter(exchange.mutate().request(request).build()); - } catch (RuntimeException ex) { - throw new IllegalStateException("Invalid URI query: \"" + query.toString() + "\""); + } + catch (RuntimeException ex) { + throw new IllegalStateException( + "Invalid URI query: \"" + query.toString() + "\""); } }; } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderGatewayFilterFactory.java index 5692bf5b..5cb93a0b 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -22,7 +21,8 @@ import org.springframework.cloud.gateway.filter.GatewayFilter; /** * @author Spencer Gibb */ -public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory { +public class AddResponseHeaderGatewayFilterFactory + extends AbstractNameValueGatewayFilterFactory { @Override public GatewayFilter apply(NameValueConfig config) { @@ -32,4 +32,5 @@ public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGate return chain.filter(exchange); }; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/FallbackHeadersGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/FallbackHeadersGatewayFilterFactory.java index e18111fa..8a15222c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/FallbackHeadersGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/FallbackHeadersGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -31,7 +30,8 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.H /** * @author Olga Maciaszek-Sharma */ -public class FallbackHeadersGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class FallbackHeadersGatewayFilterFactory + extends AbstractGatewayFilterFactory { public FallbackHeadersGatewayFilterFactory() { super(Config.class); @@ -45,18 +45,29 @@ public class FallbackHeadersGatewayFilterFactory extends AbstractGatewayFilterFa @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { - ServerWebExchange filteredExchange = ofNullable((Throwable) exchange - .getAttribute(HYSTRIX_EXECUTION_EXCEPTION_ATTR)) - .map(executionException -> { - ServerHttpRequest.Builder requestBuilder = exchange.getRequest().mutate(); - requestBuilder.header(config.executionExceptionTypeHeaderName, executionException.getClass().getName()); - requestBuilder.header(config.executionExceptionMessageHeaderName, executionException.getMessage()); - ofNullable(getRootCause(executionException)).ifPresent(rootCause -> { - requestBuilder.header(config.rootCauseExceptionTypeHeaderName, rootCause.getClass().getName()); - requestBuilder.header(config.rootCauseExceptionMessageHeaderName, rootCause.getMessage()); - }); - return exchange.mutate().request(requestBuilder.build()).build(); - }).orElse(exchange); + ServerWebExchange filteredExchange = ofNullable( + (Throwable) exchange.getAttribute(HYSTRIX_EXECUTION_EXCEPTION_ATTR)) + .map(executionException -> { + ServerHttpRequest.Builder requestBuilder = exchange + .getRequest().mutate(); + requestBuilder.header( + config.executionExceptionTypeHeaderName, + executionException.getClass().getName()); + requestBuilder.header( + config.executionExceptionMessageHeaderName, + executionException.getMessage()); + ofNullable(getRootCause(executionException)) + .ifPresent(rootCause -> { + requestBuilder.header( + config.rootCauseExceptionTypeHeaderName, + rootCause.getClass().getName()); + requestBuilder.header( + config.rootCauseExceptionMessageHeaderName, + rootCause.getMessage()); + }); + return exchange.mutate().request(requestBuilder.build()) + .build(); + }).orElse(exchange); return chain.filter(filteredExchange); }; } @@ -64,20 +75,27 @@ public class FallbackHeadersGatewayFilterFactory extends AbstractGatewayFilterFa public static class Config { private static final String EXECUTION_EXCEPTION_TYPE = "Execution-Exception-Type"; + private static final String EXECUTION_EXCEPTION_MESSAGE = "Execution-Exception-Message"; + private static final String ROOT_CAUSE_EXCEPTION_TYPE = "Root-Cause-Exception-Type"; + private static final String ROOT_CAUSE_EXCEPTION_MESSAGE = "Root-Cause-Exception-Message"; private String executionExceptionTypeHeaderName = EXECUTION_EXCEPTION_TYPE; + private String executionExceptionMessageHeaderName = EXECUTION_EXCEPTION_MESSAGE; + private String rootCauseExceptionTypeHeaderName = ROOT_CAUSE_EXCEPTION_TYPE; + private String rootCauseExceptionMessageHeaderName = ROOT_CAUSE_EXCEPTION_MESSAGE; public String getExecutionExceptionTypeHeaderName() { return executionExceptionTypeHeaderName; } - public void setExecutionExceptionTypeHeaderName(String executionExceptionTypeHeaderName) { + public void setExecutionExceptionTypeHeaderName( + String executionExceptionTypeHeaderName) { this.executionExceptionTypeHeaderName = executionExceptionTypeHeaderName; } @@ -85,7 +103,8 @@ public class FallbackHeadersGatewayFilterFactory extends AbstractGatewayFilterFa return executionExceptionMessageHeaderName; } - public void setExecutionExceptionMessageHeaderName(String executionExceptionMessageHeaderName) { + public void setExecutionExceptionMessageHeaderName( + String executionExceptionMessageHeaderName) { this.executionExceptionMessageHeaderName = executionExceptionMessageHeaderName; } @@ -93,7 +112,8 @@ public class FallbackHeadersGatewayFilterFactory extends AbstractGatewayFilterFa return rootCauseExceptionTypeHeaderName; } - public void setRootCauseExceptionTypeHeaderName(String rootCauseExceptionTypeHeaderName) { + public void setRootCauseExceptionTypeHeaderName( + String rootCauseExceptionTypeHeaderName) { this.rootCauseExceptionTypeHeaderName = rootCauseExceptionTypeHeaderName; } @@ -101,8 +121,11 @@ public class FallbackHeadersGatewayFilterFactory extends AbstractGatewayFilterFa return rootCauseExceptionMessageHeaderName; } - public void setCauseExceptionMessageHeaderName(String causeExceptionMessageHeaderName) { + public void setCauseExceptionMessageHeaderName( + String causeExceptionMessageHeaderName) { this.rootCauseExceptionMessageHeaderName = causeExceptionMessageHeaderName; } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/GatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/GatewayFilterFactory.java index 97b7a971..0896bf07 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/GatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/GatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -31,7 +30,14 @@ import org.springframework.http.server.reactive.ServerHttpRequest; @FunctionalInterface public interface GatewayFilterFactory extends ShortcutConfigurable, Configurable { + /** + * Name key. + */ String NAME_KEY = "name"; + + /** + * Value key. + */ String VALUE_KEY = "value"; // useful for javadsl @@ -53,7 +59,7 @@ public interface GatewayFilterFactory extends ShortcutConfigurable, Configura GatewayFilter apply(C config); default String name() { - //TODO: deal with proxys + // TODO: deal with proxys return NameUtils.normalizeFilterFactoryName(getClass()); } @@ -61,4 +67,5 @@ public interface GatewayFilterFactory extends ShortcutConfigurable, Configura default ServerHttpRequest.Builder mutate(ServerHttpRequest request) { return request.mutate(); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/HystrixGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/HystrixGatewayFilterFactory.java index 1ad515db..b3493d79 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/HystrixGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/HystrixGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -53,18 +52,23 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.H import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts; /** - * Depends on `spring-cloud-starter-netflix-hystrix`, {@see http://cloud.spring.io/spring-cloud-netflix/} + * Depends on `spring-cloud-starter-netflix-hystrix`, + * {@see http://cloud.spring.io/spring-cloud-netflix/}. + * * @author Spencer Gibb * @author Michele Mancioppi * @author Olga Maciaszek-Sharma */ -public class HystrixGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class HystrixGatewayFilterFactory + extends AbstractGatewayFilterFactory { private final ObjectProvider dispatcherHandlerProvider; - //do not use this dispatcherHandler directly, use getDispatcherHandler() instead. + + // do not use this dispatcherHandler directly, use getDispatcherHandler() instead. private volatile DispatcherHandler dispatcherHandler; - public HystrixGatewayFilterFactory(ObjectProvider dispatcherHandlerProvider) { + public HystrixGatewayFilterFactory( + ObjectProvider dispatcherHandlerProvider) { super(Config.class); this.dispatcherHandlerProvider = dispatcherHandlerProvider; } @@ -95,21 +99,25 @@ public class HystrixGatewayFilterFactory extends AbstractGatewayFilterFactory { - RouteHystrixCommand command = new RouteHystrixCommand(config.setter, config.fallbackUri, exchange, chain); + RouteHystrixCommand command = new RouteHystrixCommand(config.setter, + config.fallbackUri, exchange, chain); return Mono.create(s -> { - Subscription sub = command.toObservable().subscribe(s::success, s::error, s::success); + Subscription sub = command.toObservable().subscribe(s::success, s::error, + s::success); s.onCancel(sub::unsubscribe); }).onErrorResume((Function>) throwable -> { if (throwable instanceof HystrixRuntimeException) { @@ -117,21 +125,24 @@ public class HystrixGatewayFilterFactory extends AbstractGatewayFilterFactory { - - private final URI fallbackUri; - private final ServerWebExchange exchange; - private final GatewayFilterChain chain; - - RouteHystrixCommand(Setter setter, URI fallbackUri, ServerWebExchange exchange, GatewayFilterChain chain) { - super(setter); - this.fallbackUri = fallbackUri; - this.exchange = exchange; - this.chain = chain; - } - - @Override - protected Observable construct() { - return RxReactiveStreams.toObservable(this.chain.filter(exchange)); - } - - @Override - protected Observable resumeWithFallback() { - if (this.fallbackUri == null) { - return super.resumeWithFallback(); - } - - //TODO: copied from RouteToRequestUrlFilter - URI uri = exchange.getRequest().getURI(); - //TODO: assume always? - boolean encoded = containsEncodedParts(uri); - URI requestUrl = UriComponentsBuilder.fromUri(uri) - .host(null) - .port(null) - .uri(this.fallbackUri) - .build(encoded) - .toUri(); - exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl); - addExceptionDetails(); - - ServerHttpRequest request = this.exchange.getRequest().mutate().uri(requestUrl).build(); - ServerWebExchange mutated = exchange.mutate().request(request).build(); - return RxReactiveStreams.toObservable(getDispatcherHandler().handle(mutated)); - } - - private void addExceptionDetails() { - Throwable executionException = getExecutionException(); - ofNullable(executionException) - .ifPresent(exception -> exchange.getAttributes().put(HYSTRIX_EXECUTION_EXCEPTION_ATTR, exception)); - } - } - public static class Config { + private String name; + private Setter setter; + private URI fallbackUri; public String getName() { @@ -216,7 +180,9 @@ public class HystrixGatewayFilterFactory extends AbstractGatewayFilterFactory { + + private final URI fallbackUri; + + private final ServerWebExchange exchange; + + private final GatewayFilterChain chain; + + RouteHystrixCommand(Setter setter, URI fallbackUri, ServerWebExchange exchange, + GatewayFilterChain chain) { + super(setter); + this.fallbackUri = fallbackUri; + this.exchange = exchange; + this.chain = chain; + } + + @Override + protected Observable construct() { + return RxReactiveStreams.toObservable(this.chain.filter(exchange)); + } + + @Override + protected Observable resumeWithFallback() { + if (this.fallbackUri == null) { + return super.resumeWithFallback(); + } + + // TODO: copied from RouteToRequestUrlFilter + URI uri = exchange.getRequest().getURI(); + // TODO: assume always? + boolean encoded = containsEncodedParts(uri); + URI requestUrl = UriComponentsBuilder.fromUri(uri).host(null).port(null) + .uri(this.fallbackUri).build(encoded).toUri(); + exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl); + addExceptionDetails(); + + ServerHttpRequest request = this.exchange.getRequest().mutate() + .uri(requestUrl).build(); + ServerWebExchange mutated = exchange.mutate().request(request).build(); + return RxReactiveStreams.toObservable(getDispatcherHandler().handle(mutated)); + } + + private void addExceptionDetails() { + Throwable executionException = getExecutionException(); + ofNullable(executionException).ifPresent(exception -> exchange.getAttributes() + .put(HYSTRIX_EXECUTION_EXCEPTION_ATTR, exception)); + } + + } + +} diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactory.java index ff658ca1..2e18e545 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -22,6 +21,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -32,12 +32,17 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.a /** * @author Spencer Gibb */ -public class PrefixPathGatewayFilterFactory extends AbstractGatewayFilterFactory { - - private static final Log log = LogFactory.getLog(PrefixPathGatewayFilterFactory.class); +public class PrefixPathGatewayFilterFactory + extends AbstractGatewayFilterFactory { + /** + * Prefix key. + */ public static final String PREFIX_KEY = "prefix"; + private static final Log log = LogFactory + .getLog(PrefixPathGatewayFilterFactory.class); + public PrefixPathGatewayFilterFactory() { super(Config.class); } @@ -51,7 +56,8 @@ public class PrefixPathGatewayFilterFactory extends AbstractGatewayFilterFactory public GatewayFilter apply(Config config) { return (exchange, chain) -> { - boolean alreadyPrefixed = exchange.getAttributeOrDefault(GATEWAY_ALREADY_PREFIXED_ATTR, false); + boolean alreadyPrefixed = exchange + .getAttributeOrDefault(GATEWAY_ALREADY_PREFIXED_ATTR, false); if (alreadyPrefixed) { return chain.filter(exchange); } @@ -61,14 +67,13 @@ public class PrefixPathGatewayFilterFactory extends AbstractGatewayFilterFactory addOriginalRequestUrl(exchange, req.getURI()); String newPath = config.prefix + req.getURI().getRawPath(); - ServerHttpRequest request = req.mutate() - .path(newPath) - .build(); + ServerHttpRequest request = req.mutate().path(newPath).build(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI()); if (log.isTraceEnabled()) { - log.trace("Prefixed URI with: "+config.prefix+" -> "+request.getURI()); + log.trace("Prefixed URI with: " + config.prefix + " -> " + + request.getURI()); } return chain.filter(exchange.mutate().request(request).build()); @@ -76,6 +81,7 @@ public class PrefixPathGatewayFilterFactory extends AbstractGatewayFilterFactory } public static class Config { + private String prefix; public String getPrefix() { @@ -85,5 +91,7 @@ public class PrefixPathGatewayFilterFactory extends AbstractGatewayFilterFactory public void setPrefix(String prefix) { this.prefix = prefix; } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactory.java index 99b72423..57ce7593 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -27,13 +26,15 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.P public class PreserveHostHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory { public GatewayFilter apply() { - return apply(o -> {}); + return apply(o -> { + }); } - public GatewayFilter apply(Object config) { + public GatewayFilter apply(Object config) { return (exchange, chain) -> { exchange.getAttributes().put(PRESERVE_HOST_HEADER_ATTRIBUTE, true); return chain.filter(exchange); }; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactory.java index 5ac15183..c10a6437 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -21,6 +20,8 @@ import java.net.URI; import java.util.Arrays; import java.util.List; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.support.HttpStatusHolder; import org.springframework.http.HttpHeaders; @@ -30,14 +31,20 @@ import org.springframework.util.Assert; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus; -import reactor.core.publisher.Mono; - /** * @author Spencer Gibb */ -public class RedirectToGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class RedirectToGatewayFilterFactory + extends AbstractGatewayFilterFactory { + /** + * Status key. + */ public static final String STATUS_KEY = "status"; + + /** + * URL key. + */ public static final String URL_KEY = "url"; public RedirectToGatewayFilterFactory() { @@ -56,7 +63,8 @@ public class RedirectToGatewayFilterFactory extends AbstractGatewayFilterFactory public GatewayFilter apply(String statusString, String urlString) { HttpStatusHolder httpStatus = HttpStatusHolder.parse(statusString); - Assert.isTrue(httpStatus.is3xxRedirection(), "status must be a 3xx code, but was " + statusString); + Assert.isTrue(httpStatus.is3xxRedirection(), + "status must be a 3xx code, but was " + statusString); final URI url = URI.create(urlString); return apply(httpStatus, url); } @@ -66,21 +74,22 @@ public class RedirectToGatewayFilterFactory extends AbstractGatewayFilterFactory } public GatewayFilter apply(HttpStatusHolder httpStatus, URI uri) { - return (exchange, chain) -> - chain.filter(exchange).then(Mono.defer(() -> { - if (!exchange.getResponse().isCommitted()) { - setResponseStatus(exchange, httpStatus); + return (exchange, chain) -> chain.filter(exchange).then(Mono.defer(() -> { + if (!exchange.getResponse().isCommitted()) { + setResponseStatus(exchange, httpStatus); - final ServerHttpResponse response = exchange.getResponse(); - response.getHeaders().set(HttpHeaders.LOCATION, uri.toString()); - return response.setComplete(); - } - return Mono.empty(); - })); + final ServerHttpResponse response = exchange.getResponse(); + response.getHeaders().set(HttpHeaders.LOCATION, uri.toString()); + return response.setComplete(); + } + return Mono.empty(); + })); } public static class Config { + String status; + String url; public String getStatus() { @@ -98,6 +107,7 @@ public class RedirectToGatewayFilterFactory extends AbstractGatewayFilterFactory public void setUrl(String url) { this.url = url; } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderGatewayFilterFactory.java index 4e31d84e..02c9a70d 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -26,7 +25,8 @@ import org.springframework.http.server.reactive.ServerHttpRequest; /** * @author Spencer Gibb */ -public class RemoveRequestHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class RemoveRequestHeaderGatewayFilterFactory + extends AbstractGatewayFilterFactory { public RemoveRequestHeaderGatewayFilterFactory() { super(NameConfig.class); @@ -41,10 +41,10 @@ public class RemoveRequestHeaderGatewayFilterFactory extends AbstractGatewayFilt public GatewayFilter apply(NameConfig config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest().mutate() - .headers(httpHeaders -> httpHeaders.remove(config.getName())) - .build(); + .headers(httpHeaders -> httpHeaders.remove(config.getName())).build(); return chain.filter(exchange.mutate().request(request).build()); }; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderGatewayFilterFactory.java index e676c816..2d640b57 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -20,14 +19,15 @@ package org.springframework.cloud.gateway.filter.factory; import java.util.Arrays; import java.util.List; -import org.springframework.cloud.gateway.filter.GatewayFilter; - import reactor.core.publisher.Mono; +import org.springframework.cloud.gateway.filter.GatewayFilter; + /** * @author Spencer Gibb */ -public class RemoveResponseHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class RemoveResponseHeaderGatewayFilterFactory + extends AbstractGatewayFilterFactory { public RemoveResponseHeaderGatewayFilterFactory() { super(NameConfig.class); @@ -44,4 +44,5 @@ public class RemoveResponseHeaderGatewayFilterFactory extends AbstractGatewayFil exchange.getResponse().getHeaders().remove(config.getName()); })); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactory.java index d2f2d191..29724510 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactory.java @@ -1,3 +1,19 @@ +/* + * Copyright 2018-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter.factory; import java.net.MalformedURLException; @@ -10,15 +26,17 @@ import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.web.server.ServerWebExchange; /** - * This filter changes the request uri by a request header + * This filter changes the request uri by a request header. * * @author Toshiaki Maki */ public class RequestHeaderToRequestUriGatewayFilterFactory extends AbstractChangeRequestUriGatewayFilterFactory { + private final Logger log = LoggerFactory .getLogger(RequestHeaderToRequestUriGatewayFilterFactory.class); @@ -46,4 +64,5 @@ public class RequestHeaderToRequestUriGatewayFilterFactory extends } }); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestRateLimiterGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestRateLimiterGatewayFilterFactory.java index f2cd7ac2..c962e83f 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestRateLimiterGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestRateLimiterGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -34,22 +33,30 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.s * User Request Rate Limiter filter. See https://stripe.com/blog/rate-limiters and */ @ConfigurationProperties("spring.cloud.gateway.filter.request-rate-limiter") -public class RequestRateLimiterGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class RequestRateLimiterGatewayFilterFactory extends + AbstractGatewayFilterFactory { + /** + * Key-Resolver key. + */ public static final String KEY_RESOLVER_KEY = "keyResolver"; + private static final String EMPTY_KEY = "____EMPTY_KEY__"; private final RateLimiter defaultRateLimiter; + private final KeyResolver defaultKeyResolver; - /** Switch to deny requests if the Key Resolver returns an empty key, defaults to true. */ + /** + * Switch to deny requests if the Key Resolver returns an empty key, defaults to true. + */ private boolean denyEmptyKey = true; /** HttpStatus to return when denyEmptyKey is true, defaults to FORBIDDEN. */ private String emptyKeyStatusCode = HttpStatus.FORBIDDEN.name(); public RequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, - KeyResolver defaultKeyResolver) { + KeyResolver defaultKeyResolver) { super(Config.class); this.defaultRateLimiter = defaultRateLimiter; this.defaultKeyResolver = defaultKeyResolver; @@ -83,12 +90,15 @@ public class RequestRateLimiterGatewayFilterFactory extends AbstractGatewayFilte @Override public GatewayFilter apply(Config config) { KeyResolver resolver = getOrDefault(config.keyResolver, defaultKeyResolver); - RateLimiter limiter = getOrDefault(config.rateLimiter, defaultRateLimiter); + RateLimiter limiter = getOrDefault(config.rateLimiter, + defaultRateLimiter); boolean denyEmpty = getOrDefault(config.denyEmptyKey, this.denyEmptyKey); - HttpStatusHolder emptyKeyStatus = HttpStatusHolder.parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode)); + HttpStatusHolder emptyKeyStatus = HttpStatusHolder + .parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode)); return (exchange, chain) -> { - Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR); + Route route = exchange + .getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR); return resolver.resolve(exchange).defaultIfEmpty(EMPTY_KEY).flatMap(key -> { if (EMPTY_KEY.equals(key)) { @@ -100,8 +110,10 @@ public class RequestRateLimiterGatewayFilterFactory extends AbstractGatewayFilte } return limiter.isAllowed(route.getId(), key).flatMap(response -> { - for (Map.Entry header : response.getHeaders().entrySet()) { - exchange.getResponse().getHeaders().add(header.getKey(), header.getValue()); + for (Map.Entry header : response.getHeaders() + .entrySet()) { + exchange.getResponse().getHeaders().add(header.getKey(), + header.getValue()); } if (response.isAllowed()) { @@ -120,10 +132,15 @@ public class RequestRateLimiterGatewayFilterFactory extends AbstractGatewayFilte } public static class Config { + private KeyResolver keyResolver; + private RateLimiter rateLimiter; + private HttpStatus statusCode = HttpStatus.TOO_MANY_REQUESTS; + private Boolean denyEmptyKey; + private String emptyKeyStatus; public KeyResolver getKeyResolver() { @@ -134,6 +151,7 @@ public class RequestRateLimiterGatewayFilterFactory extends AbstractGatewayFilte this.keyResolver = keyResolver; return this; } + public RateLimiter getRateLimiter() { return rateLimiter; } @@ -169,6 +187,7 @@ public class RequestRateLimiterGatewayFilterFactory extends AbstractGatewayFilte this.emptyKeyStatus = emptyKeyStatus; return this; } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestSizeGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestSizeGatewayFilterFactory.java index add5844b..1b3ec879 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestSizeGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RequestSizeGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -24,13 +23,16 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * This filter blocks the request, if the request size is more than the permissible size.The default request size is 5 MB. + * This filter blocks the request, if the request size is more than the permissible size. + * The default request size is 5 MB. + * * @author Arpan */ -public class RequestSizeGatewayFilterFactory - extends AbstractGatewayFilterFactory { +public class RequestSizeGatewayFilterFactory extends + AbstractGatewayFilterFactory { private static String PREFIX = "kMGTPE"; + private static String ERROR = "Request size is larger than permissible limit." + " Request size is %s where permissible limit is %s"; @@ -38,8 +40,24 @@ public class RequestSizeGatewayFilterFactory super(RequestSizeGatewayFilterFactory.RequestSizeConfig.class); } + private static String getErrorMessage(Long currentRequestSize, Long maxSize) { + return String.format(ERROR, getReadableByteCount(currentRequestSize), + getReadableByteCount(maxSize)); + } + + private static String getReadableByteCount(long bytes) { + int unit = 1000; + if (bytes < unit) { + return bytes + " B"; + } + int exp = (int) (Math.log(bytes) / Math.log(unit)); + String pre = Character.toString(PREFIX.charAt(exp - 1)); + return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); + } + @Override - public GatewayFilter apply(RequestSizeGatewayFilterFactory.RequestSizeConfig requestSizeConfig) { + public GatewayFilter apply( + RequestSizeGatewayFilterFactory.RequestSizeConfig requestSizeConfig) { requestSizeConfig.validate(); return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); @@ -49,7 +67,8 @@ public class RequestSizeGatewayFilterFactory if (currentRequestSize > requestSizeConfig.getMaxSize()) { exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE); exchange.getResponse().getHeaders().add("errorMessage", - getErrorMessage(currentRequestSize, requestSizeConfig.getMaxSize())); + getErrorMessage(currentRequestSize, + requestSizeConfig.getMaxSize())); return exchange.getResponse().setComplete(); } } @@ -61,31 +80,22 @@ public class RequestSizeGatewayFilterFactory private Long maxSize = 5000000L; - public RequestSizeGatewayFilterFactory.RequestSizeConfig setMaxSize(Long maxSize) { - this.maxSize = maxSize; - return this; - } - public Long getMaxSize() { return maxSize; } + public RequestSizeGatewayFilterFactory.RequestSizeConfig setMaxSize( + Long maxSize) { + this.maxSize = maxSize; + return this; + } + public void validate() { - Assert.isTrue(this.maxSize != null && this.maxSize > 0, "maxSize must be greater than 0"); + Assert.isTrue(this.maxSize != null && this.maxSize > 0, + "maxSize must be greater than 0"); Assert.isInstanceOf(Long.class, maxSize, "maxSize must be a number"); } + } - private static String getErrorMessage(Long currentRequestSize, Long maxSize) { - return String.format(ERROR, getReadableByteCount(currentRequestSize), getReadableByteCount(maxSize)); - } - - private static String getReadableByteCount(long bytes) { - int unit = 1000; - if (bytes < unit) - return bytes + " B"; - int exp = (int) (Math.log(bytes) / Math.log(unit)); - String pre = Character.toString(PREFIX.charAt(exp - 1)); - return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); - } -} \ No newline at end of file +} diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java index 6005053f..0bbfedc5 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -28,8 +27,6 @@ import java.util.function.Predicate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.reactivestreams.Publisher; -import org.springframework.cloud.gateway.support.TimeoutException; -import org.springframework.http.HttpHeaders; import reactor.core.publisher.Mono; import reactor.retry.Repeat; import reactor.retry.RepeatContext; @@ -37,7 +34,7 @@ import reactor.retry.Retry; import reactor.retry.RetryContext; import org.springframework.cloud.gateway.filter.GatewayFilter; -import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.cloud.gateway.support.TimeoutException; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus.Series; @@ -47,15 +44,24 @@ import org.springframework.web.server.ServerWebExchange; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_HEADER_NAMES; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ALREADY_ROUTED_ATTR; -public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class RetryGatewayFilterFactory + extends AbstractGatewayFilterFactory { + /** + * Retry iteration key. + */ public static final String RETRY_ITERATION_KEY = "retry_iteration"; + private static final Log log = LogFactory.getLog(RetryGatewayFilterFactory.class); public RetryGatewayFilterFactory() { super(RetryConfig.class); } + private static List toList(T... items) { + return new ArrayList<>(Arrays.asList(items)); + } + @Override public GatewayFilter apply(RetryConfig retryConfig) { retryConfig.validate(); @@ -70,16 +76,20 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory statusCode.series().equals(series)); } trace("retryableStatusCode: %b, statusCode %s, configured statuses %s, configured series %s", - retryableStatusCode, statusCode, retryConfig.getStatuses(), retryConfig.getSeries()); + retryableStatusCode, statusCode, retryConfig.getStatuses(), + retryConfig.getSeries()); HttpMethod httpMethod = exchange.getRequest().getMethod(); boolean retryableMethod = retryConfig.getMethods().contains(httpMethod); @@ -93,7 +103,7 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory reset(context.applicationContext())); } - //TODO: support timeout, backoff, jitter, etc... in Builder + // TODO: support timeout, backoff, jitter, etc... in Builder Retry exceptionRetry = null; if (!retryConfig.getExceptions().isEmpty()) { @@ -105,12 +115,14 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory clazz : retryConfig.getExceptions()) { if (clazz.isInstance(context.exception())) { trace("exception is retryable %s, configured exceptions", - context.exception().getClass().getName(), retryConfig.getExceptions()); + context.exception().getClass().getName(), + retryConfig.getExceptions()); return true; } } trace("exception is not retryable %s, configured exceptions", - context.exception().getClass().getName(), retryConfig.getExceptions()); + context.exception().getClass().getName(), + retryConfig.getExceptions()); return false; }; exceptionRetry = Retry.onlyIf(retryContextPredicate) @@ -118,53 +130,59 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory= retryConfig.getRetries(); - trace("exceedsMaxIterations %b, iteration %d, configured retries %d", - exceeds, iteration, retryConfig.getRetries()); + trace("exceedsMaxIterations %b, iteration %d, configured retries %d", exceeds, + iteration, retryConfig.getRetries()); return exceeds; } public void reset(ServerWebExchange exchange) { - //TODO: what else to do to reset SWE? - Set addedHeaders = exchange.getAttributeOrDefault(CLIENT_RESPONSE_HEADER_NAMES, Collections.emptySet()); - addedHeaders.forEach(header -> exchange.getResponse().getHeaders().remove(header)); + // TODO: what else to do to reset SWE? + Set addedHeaders = exchange.getAttributeOrDefault( + CLIENT_RESPONSE_HEADER_NAMES, Collections.emptySet()); + addedHeaders + .forEach(header -> exchange.getResponse().getHeaders().remove(header)); exchange.getAttributes().remove(GATEWAY_ALREADY_ROUTED_ATTR); } - public GatewayFilter apply(Repeat repeat, Retry retry) { + public GatewayFilter apply(Repeat repeat, + Retry retry) { return (exchange, chain) -> { trace("Entering retry-filter"); // chain.filter returns a Mono - Publisher publisher = chain.filter(exchange) - //.log("retry-filter", Level.INFO) - .doOnSuccessOrError((aVoid, throwable) -> { - int iteration = exchange.getAttributeOrDefault(RETRY_ITERATION_KEY, -1); + Publisher publisher = chain.filter(exchange) + // .log("retry-filter", Level.INFO) + .doOnSuccessOrError((aVoid, throwable) -> { + int iteration = exchange + .getAttributeOrDefault(RETRY_ITERATION_KEY, -1); int newIteration = iteration + 1; trace("setting new iteration in attr %d", newIteration); exchange.getAttributes().put(RETRY_ITERATION_KEY, newIteration); - }); + }); - if (retry != null) { + if (retry != null) { // retryWhen returns a Mono // retry needs to go before repeat - publisher = ((Mono)publisher).retryWhen(retry.withApplicationContext(exchange)); + publisher = ((Mono) publisher) + .retryWhen(retry.withApplicationContext(exchange)); } if (repeat != null) { - // repeatWhen returns a Flux + // repeatWhen returns a Flux // so this needs to be last and the variable a Publisher - publisher = ((Mono)publisher).repeatWhen(repeat.withApplicationContext(exchange)); + publisher = ((Mono) publisher) + .repeatWhen(repeat.withApplicationContext(exchange)); } - return Mono.fromDirect(publisher); + return Mono.fromDirect(publisher); }; } @@ -174,54 +192,29 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory List toList(T... items) { - return new ArrayList<>(Arrays.asList(items)); - } - @SuppressWarnings("unchecked") public static class RetryConfig { + private int retries = 3; - + private List series = toList(Series.SERVER_ERROR); - + private List statuses = new ArrayList<>(); - + private List methods = toList(HttpMethod.GET); - private List> exceptions = toList(IOException.class, TimeoutException.class); - - public RetryConfig setRetries(int retries) { - this.retries = retries; - return this; - } - - public RetryConfig setSeries(Series... series) { - this.series = Arrays.asList(series); - return this; - } - - public RetryConfig setStatuses(HttpStatus... statuses) { - this.statuses = Arrays.asList(statuses); - return this; - } - - public RetryConfig setMethods(HttpMethod... methods) { - this.methods = Arrays.asList(methods); - return this; - } + private List> exceptions = toList(IOException.class, + TimeoutException.class); public RetryConfig allMethods() { return setMethods(HttpMethod.values()); } - public RetryConfig setExceptions(Class... exceptions) { - this.exceptions = Arrays.asList(exceptions); - return this; - } - public void validate() { Assert.isTrue(this.retries > 0, "retries must be greater than 0"); - Assert.isTrue(!this.series.isEmpty() || !this.statuses.isEmpty() || !this.exceptions.isEmpty(), + Assert.isTrue( + !this.series.isEmpty() || !this.statuses.isEmpty() + || !this.exceptions.isEmpty(), "series, status and exceptions may not all be empty"); Assert.notEmpty(this.methods, "methods may not be empty"); } @@ -230,21 +223,47 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory getSeries() { return series; } + public RetryConfig setSeries(Series... series) { + this.series = Arrays.asList(series); + return this; + } + public List getStatuses() { return statuses; } + public RetryConfig setStatuses(HttpStatus... statuses) { + this.statuses = Arrays.asList(statuses); + return this; + } + public List getMethods() { return methods; } + public RetryConfig setMethods(HttpMethod... methods) { + this.methods = Arrays.asList(methods); + return this; + } + public List> getExceptions() { return exceptions; } + public RetryConfig setExceptions(Class... exceptions) { + this.exceptions = Arrays.asList(exceptions); + return this; + } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactory.java index a26fbba5..f0446856 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -29,9 +28,17 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.a /** * @author Spencer Gibb */ -public class RewritePathGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class RewritePathGatewayFilterFactory + extends AbstractGatewayFilterFactory { + /** + * Regexp key. + */ public static final String REGEXP_KEY = "regexp"; + + /** + * Replacement key. + */ public static final String REPLACEMENT_KEY = "replacement"; public RewritePathGatewayFilterFactory() { @@ -52,9 +59,7 @@ public class RewritePathGatewayFilterFactory extends AbstractGatewayFilterFactor String path = req.getURI().getRawPath(); String newPath = path.replaceAll(config.regexp, replacement); - ServerHttpRequest request = req.mutate() - .path(newPath) - .build(); + ServerHttpRequest request = req.mutate().path(newPath).build(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI()); @@ -63,7 +68,9 @@ public class RewritePathGatewayFilterFactory extends AbstractGatewayFilterFactor } public static class Config { + private String regexp; + private String replacement; public String getRegexp() { @@ -83,5 +90,7 @@ public class RewritePathGatewayFilterFactory extends AbstractGatewayFilterFactor this.replacement = replacement; return this; } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactory.java index ec7d2d55..0a45baf6 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,24 +12,32 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; -import org.springframework.cloud.gateway.filter.GatewayFilter; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - import java.util.Arrays; import java.util.List; +import reactor.core.publisher.Mono; + +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.web.server.ServerWebExchange; + /** * @author Vitaliy Pavlyuk */ -public class RewriteResponseHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class RewriteResponseHeaderGatewayFilterFactory extends + AbstractGatewayFilterFactory { + /** + * Regexp key. + */ public static final String REGEXP_KEY = "regexp"; + + /** + * Replacement key. + */ public static final String REPLACEMENT_KEY = "replacement"; public RewriteResponseHeaderGatewayFilterFactory() { @@ -54,7 +62,8 @@ public class RewriteResponseHeaderGatewayFilterFactory extends AbstractGatewayFi if (value == null) { return; } - final String newValue = rewrite(value, config.getRegexp(), config.getReplacement()); + final String newValue = rewrite(value, config.getRegexp(), + config.getReplacement()); exchange.getResponse().getHeaders().set(name, newValue); } @@ -63,7 +72,9 @@ public class RewriteResponseHeaderGatewayFilterFactory extends AbstractGatewayFi } public static class Config extends AbstractGatewayFilterFactory.NameConfig { + private String regexp; + private String replacement; public String getRegexp() { @@ -83,5 +94,7 @@ public class RewriteResponseHeaderGatewayFilterFactory extends AbstractGatewayFi this.replacement = replacement; return this; } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactory.java index bda6731d..90fb37e3 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 the original author or authors. + * Copyright 2018-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,25 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.cloud.gateway.filter.factory; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.web.server.WebSession; /** - * Save the current {@link WebSession} before executing the rest of the {@link org.springframework.cloud.gateway.filter.GatewayFilterChain}. + * Save the current {@link WebSession} before executing the rest of the + * {@link org.springframework.cloud.gateway.filter.GatewayFilterChain}. + * + * Filter is very useful for situation where the WebSession is lazy (e.g. Spring Session + * MongoDB) and making a remote call requires that {@link WebSession#save()} be called + * before the remote call is made. * - * Filter is very useful for situation where the WebSession is lazy (e.g. Spring Session MongoDB) and making a remote call requires - * that {@link WebSession#save()} be called before the remote call is made. - * * @author Greg Turnquist */ public class SaveSessionGatewayFilterFactory extends AbstractGatewayFilterFactory { @Override public GatewayFilter apply(Object config) { - return (exchange, chain) -> exchange.getSession() - .map(WebSession::save) - .then(chain.filter(exchange)); + return (exchange, chain) -> exchange.getSession().map(WebSession::save) + .then(chain.filter(exchange)); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactory.java index dc26be77..8ff1b430 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -21,18 +20,50 @@ import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.http.HttpHeaders; /** - * https://blog.appcanary.com/2017/http-security-headers.html + * https://blog.appcanary.com/2017/http-security-headers.html. + * * @author Spencer Gibb */ public class SecureHeadersGatewayFilterFactory extends AbstractGatewayFilterFactory { + /** + * Xss-Protection header name. + */ public static final String X_XSS_PROTECTION_HEADER = "X-Xss-Protection"; + + /** + * Strict transport security header name. + */ public static final String STRICT_TRANSPORT_SECURITY_HEADER = "Strict-Transport-Security"; + + /** + * Frame options header name. + */ public static final String X_FRAME_OPTIONS_HEADER = "X-Frame-Options"; + + /** + * Content-Type Options header name. + */ public static final String X_CONTENT_TYPE_OPTIONS_HEADER = "X-Content-Type-Options"; + + /** + * Referrer Policy header name. + */ public static final String REFERRER_POLICY_HEADER = "Referrer-Policy"; + + /** + * Content-Security Policy header name. + */ public static final String CONTENT_SECURITY_POLICY_HEADER = "Content-Security-Policy"; + + /** + * Download Options header name. + */ public static final String X_DOWNLOAD_OPTIONS_HEADER = "X-Download-Options"; + + /** + * Permitted Cross-Domain Policies header name. + */ public static final String X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER = "X-Permitted-Cross-Domain-Policies"; private final SecureHeadersProperties properties; @@ -43,22 +74,27 @@ public class SecureHeadersGatewayFilterFactory extends AbstractGatewayFilterFact @Override public GatewayFilter apply(Object config) { - //TODO: allow args to override properties + // TODO: allow args to override properties return (exchange, chain) -> { HttpHeaders headers = exchange.getResponse().getHeaders(); - //TODO: allow header to be disabled + // TODO: allow header to be disabled headers.add(X_XSS_PROTECTION_HEADER, properties.getXssProtectionHeader()); - headers.add(STRICT_TRANSPORT_SECURITY_HEADER, properties.getStrictTransportSecurity()); + headers.add(STRICT_TRANSPORT_SECURITY_HEADER, + properties.getStrictTransportSecurity()); headers.add(X_FRAME_OPTIONS_HEADER, properties.getFrameOptions()); - headers.add(X_CONTENT_TYPE_OPTIONS_HEADER, properties.getContentTypeOptions()); + headers.add(X_CONTENT_TYPE_OPTIONS_HEADER, + properties.getContentTypeOptions()); headers.add(REFERRER_POLICY_HEADER, properties.getReferrerPolicy()); - headers.add(CONTENT_SECURITY_POLICY_HEADER, properties.getContentSecurityPolicy()); + headers.add(CONTENT_SECURITY_POLICY_HEADER, + properties.getContentSecurityPolicy()); headers.add(X_DOWNLOAD_OPTIONS_HEADER, properties.getDownloadOptions()); - headers.add(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER, properties.getPermittedCrossDomainPolicies()); + headers.add(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER, + properties.getPermittedCrossDomainPolicies()); return chain.filter(exchange); }; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersProperties.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersProperties.java index 71138683..ab6579f2 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersProperties.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -24,22 +23,76 @@ import org.springframework.boot.context.properties.ConfigurationProperties; */ @ConfigurationProperties("spring.cloud.gateway.filter.secure-headers") public class SecureHeadersProperties { + + /** + * Xss-Protection header default. + */ public static final String X_XSS_PROTECTION_HEADER_DEFAULT = "1 ; mode=block"; - public static final String STRICT_TRANSPORT_SECURITY_HEADER_DEFAULT = "max-age=631138519"; //; includeSubDomains preload") - public static final String X_FRAME_OPTIONS_HEADER_DEFAULT = "DENY"; //SAMEORIGIN = ALLOW-FROM + + /** + * Strict transport security header default. + */ + public static final String STRICT_TRANSPORT_SECURITY_HEADER_DEFAULT = "max-age=631138519"; // ; + // includeSubDomains + // preload") + + /** + * Frame Options header default. + */ + public static final String X_FRAME_OPTIONS_HEADER_DEFAULT = "DENY"; // SAMEORIGIN = + // ALLOW-FROM + + /** + * Content-Type Options header default. + */ public static final String X_CONTENT_TYPE_OPTIONS_HEADER_DEFAULT = "nosniff"; - public static final String REFERRER_POLICY_HEADER_DEFAULT = "no-referrer"; //no-referrer-when-downgrade = origin = origin-when-cross-origin = same-origin = strict-origin = strict-origin-when-cross-origin = unsafe-url + + /** + * Referrer Policy header default. + */ + public static final String REFERRER_POLICY_HEADER_DEFAULT = "no-referrer"; // no-referrer-when-downgrade + // = + // origin + // = + // origin-when-cross-origin + // = + // same-origin + // = + // strict-origin + // = + // strict-origin-when-cross-origin + // = + // unsafe-url + + /** + * Content-Security Policy header default. + */ public static final String CONTENT_SECURITY_POLICY_HEADER_DEFAULT = "default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'"; + + /** + * Download Options header default. + */ public static final String X_DOWNLOAD_OPTIONS_HEADER_DEFAULT = "noopen"; + + /** + * Permitted Cross-Domain Policies header default. + */ public static final String X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER_DEFAULT = "none"; private String xssProtectionHeader = X_XSS_PROTECTION_HEADER_DEFAULT; + private String strictTransportSecurity = STRICT_TRANSPORT_SECURITY_HEADER_DEFAULT; + private String frameOptions = X_FRAME_OPTIONS_HEADER_DEFAULT; + private String contentTypeOptions = X_CONTENT_TYPE_OPTIONS_HEADER_DEFAULT; + private String referrerPolicy = REFERRER_POLICY_HEADER_DEFAULT; + private String contentSecurityPolicy = CONTENT_SECURITY_POLICY_HEADER_DEFAULT; + private String downloadOptions = X_DOWNLOAD_OPTIONS_HEADER_DEFAULT; + private String permittedCrossDomainPolicies = X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER_DEFAULT; public String getXssProtectionHeader() { @@ -110,14 +163,17 @@ public class SecureHeadersProperties { public String toString() { final StringBuffer sb = new StringBuffer("SecureHeadersProperties{"); sb.append("xssProtectionHeader='").append(xssProtectionHeader).append('\''); - sb.append(", strictTransportSecurity='").append(strictTransportSecurity).append('\''); + sb.append(", strictTransportSecurity='").append(strictTransportSecurity) + .append('\''); sb.append(", frameOptions='").append(frameOptions).append('\''); sb.append(", contentTypeOptions='").append(contentTypeOptions).append('\''); sb.append(", referrerPolicy='").append(referrerPolicy).append('\''); sb.append(", contentSecurityPolicy='").append(contentSecurityPolicy).append('\''); sb.append(", downloadOptions='").append(downloadOptions).append('\''); - sb.append(", permittedCrossDomainPolicies='").append(permittedCrossDomainPolicies).append('\''); + sb.append(", permittedCrossDomainPolicies='").append(permittedCrossDomainPolicies) + .append('\''); sb.append('}'); return sb.toString(); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactory.java index facb9e26..dfab40a4 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -33,8 +32,12 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.g /** * @author Spencer Gibb */ -public class SetPathGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class SetPathGatewayFilterFactory + extends AbstractGatewayFilterFactory { + /** + * Template key. + */ public static final String TEMPLATE_KEY = "template"; public SetPathGatewayFilterFactory() { @@ -61,15 +64,14 @@ public class SetPathGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class SetStatusGatewayFilterFactory + extends AbstractGatewayFilterFactory { + /** + * Status key. + */ public static final String STATUS_KEY = "status"; public SetStatusGatewayFilterFactory() { @@ -49,11 +52,11 @@ public class SetStatusGatewayFilterFactory extends AbstractGatewayFilterFactory< return (exchange, chain) -> { // option 1 (runs in filter order) - /*exchange.getResponse().beforeCommit(() -> { - exchange.getResponse().setStatusCode(finalStatus); - return Mono.empty(); - }); - return chain.filter(exchange);*/ + /* + * exchange.getResponse().beforeCommit(() -> { + * exchange.getResponse().setStatusCode(finalStatus); return Mono.empty(); }); + * return chain.filter(exchange); + */ // option 2 (runs in reverse filter order) return chain.filter(exchange).then(Mono.fromRunnable(() -> { @@ -65,7 +68,8 @@ public class SetStatusGatewayFilterFactory extends AbstractGatewayFilterFactory< } public static class Config { - //TODO: relaxed HttpStatus converter + + // TODO: relaxed HttpStatus converter private String status; public String getStatus() { @@ -75,6 +79,7 @@ public class SetStatusGatewayFilterFactory extends AbstractGatewayFilterFactory< public void setStatus(String status) { this.status = status; } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactory.java index a97309a1..20d9846b 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,8 +12,8 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ + package org.springframework.cloud.gateway.filter.factory; import java.util.Arrays; @@ -29,11 +29,16 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.a /** * This filter removes the first part of the path, known as the prefix, from the request - * before sending it downstream + * before sending it downstream. + * * @author Ryan Baxter */ -public class StripPrefixGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class StripPrefixGatewayFilterFactory + extends AbstractGatewayFilterFactory { + /** + * Parts key. + */ public static final String PARTS_KEY = "parts"; public StripPrefixGatewayFilterFactory() { @@ -47,16 +52,15 @@ public class StripPrefixGatewayFilterFactory extends AbstractGatewayFilterFactor @Override public GatewayFilter apply(Config config) { - return (exchange, chain) -> { + return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); addOriginalRequestUrl(exchange, request.getURI()); String path = request.getURI().getRawPath(); - String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(path, "/")) - .skip(config.parts).collect(Collectors.joining("/")); + String newPath = "/" + + Arrays.stream(StringUtils.tokenizeToStringArray(path, "/")) + .skip(config.parts).collect(Collectors.joining("/")); newPath += (newPath.length() > 1 && path.endsWith("/") ? "/" : ""); - ServerHttpRequest newRequest = request.mutate() - .path(newPath) - .build(); + ServerHttpRequest newRequest = request.mutate().path(newPath).build(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI()); @@ -65,6 +69,7 @@ public class StripPrefixGatewayFilterFactory extends AbstractGatewayFilterFactor } public static class Config { + private int parts; public int getParts() { @@ -74,6 +79,7 @@ public class StripPrefixGatewayFilterFactory extends AbstractGatewayFilterFactor public void setParts(int parts) { this.parts = parts; } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/HttpMessageWriterResponse.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/HttpMessageWriterResponse.java index 960ba228..ecd65040 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/HttpMessageWriterResponse.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/HttpMessageWriterResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,12 +12,16 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory.rewrite; +import java.util.function.Supplier; + import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.http.HttpHeaders; @@ -25,24 +29,21 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.MultiValueMap; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.util.function.Supplier; /** - * This class is BETA and may be subject to change in a future release. - * Response who's job it is to gather the Publisher<DataBuffer> from the writeWith message - * during a call to HttpMessageWriter.write. Also gathers any headers set there. + * This class is BETA and may be subject to change in a future release. Response who's job + * it is to gather the Publisher<DataBuffer> from the writeWith message during a + * call to HttpMessageWriter.write. Also gathers any headers set there. */ public class HttpMessageWriterResponse implements ServerHttpResponse { private final HttpHeaders headers = new HttpHeaders(); + private final DataBufferFactory dataBufferFactory; private Publisher body; - public HttpMessageWriterResponse(DataBufferFactory dataBufferFactory) { + public HttpMessageWriterResponse(DataBufferFactory dataBufferFactory) { this.dataBufferFactory = dataBufferFactory; } @@ -52,21 +53,21 @@ public class HttpMessageWriterResponse implements ServerHttpResponse { } @Override - public Mono writeWith(Publisher body) { - this.body = body; - return Mono.empty(); - } + public Mono writeWith(Publisher body) { + this.body = body; + return Mono.empty(); + } - @Override - public Mono writeAndFlushWith(Publisher> body) { - //TODO: is this kosher? - return writeWith(Flux.from(body) - .flatMapSequential(p -> p)); - } + @Override + public Mono writeAndFlushWith( + Publisher> body) { + // TODO: is this kosher? + return writeWith(Flux.from(body).flatMapSequential(p -> p)); + } - public Publisher getBody() { - return body; - } + public Publisher getBody() { + return body; + } @Override public boolean setStatusCode(HttpStatus status) { @@ -107,4 +108,5 @@ public class HttpMessageWriterResponse implements ServerHttpResponse { public Mono setComplete() { return null; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactory.java index 99473150..fc58a8ea 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyRequestBodyGatewayFilterFactory.java @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory.rewrite; @@ -41,8 +40,8 @@ import org.springframework.web.reactive.function.server.ServerRequest; /** * This filter is BETA and may be subject to change in a future release. */ -public class ModifyRequestBodyGatewayFilterFactory - extends AbstractGatewayFilterFactory { +public class ModifyRequestBodyGatewayFilterFactory extends + AbstractGatewayFilterFactory { private final List> messageReaders; @@ -61,14 +60,16 @@ public class ModifyRequestBodyGatewayFilterFactory public GatewayFilter apply(Config config) { return (exchange, chain) -> { Class inClass = config.getInClass(); - ServerRequest serverRequest = new DefaultServerRequest(exchange, this.messageReaders); + ServerRequest serverRequest = new DefaultServerRequest(exchange, + this.messageReaders); - //TODO: flux or mono + // TODO: flux or mono Mono modifiedBody = serverRequest.bodyToMono(inClass) // .log("modify_request_mono", Level.INFO) .flatMap(o -> config.rewriteFunction.apply(exchange, o)); - BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, config.getOutClass()); + BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, + config.getOutClass()); HttpHeaders headers = new HttpHeaders(); headers.putAll(exchange.getRequest().getHeaders()); @@ -76,12 +77,14 @@ public class ModifyRequestBodyGatewayFilterFactory // and then set in the request decorator headers.remove(HttpHeaders.CONTENT_LENGTH); - // if the body is changing content types, set it here, to the bodyInserter will know about it + // if the body is changing content types, set it here, to the bodyInserter + // will know about it if (config.getContentType() != null) { headers.set(HttpHeaders.CONTENT_TYPE, config.getContentType()); } - CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers); - return bodyInserter.insert(outputMessage, new BodyInserterContext()) + CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, + headers); + return bodyInserter.insert(outputMessage, new BodyInserterContext()) // .log("modify_request", Level.INFO) .then(Mono.defer(() -> { ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator( @@ -93,9 +96,12 @@ public class ModifyRequestBodyGatewayFilterFactory httpHeaders.putAll(super.getHeaders()); if (contentLength > 0) { httpHeaders.setContentLength(contentLength); - } else { - // TODO: this causes a 'HTTP/1.1 411 Length Required' on httpbin.org - httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); + } + else { + // TODO: this causes a 'HTTP/1.1 411 Length Required' + // on httpbin.org + httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, + "chunked"); } return httpHeaders; } @@ -112,13 +118,16 @@ public class ModifyRequestBodyGatewayFilterFactory } public static class Config { + private Class inClass; + private Class outClass; private String contentType; @Deprecated private Map inHints; + @Deprecated private Map outHints; @@ -168,16 +177,16 @@ public class ModifyRequestBodyGatewayFilterFactory return rewriteFunction; } - public Config setRewriteFunction(Class inClass, Class outClass, - RewriteFunction rewriteFunction) { - setInClass(inClass); - setOutClass(outClass); - setRewriteFunction(rewriteFunction); + public Config setRewriteFunction(RewriteFunction rewriteFunction) { + this.rewriteFunction = rewriteFunction; return this; } - public Config setRewriteFunction(RewriteFunction rewriteFunction) { - this.rewriteFunction = rewriteFunction; + public Config setRewriteFunction(Class inClass, Class outClass, + RewriteFunction rewriteFunction) { + setInClass(inClass); + setOutClass(outClass); + setRewriteFunction(rewriteFunction); return this; } @@ -189,5 +198,7 @@ public class ModifyRequestBodyGatewayFilterFactory this.contentType = contentType; return this; } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java index 93447dca..9aa2a6f3 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory.rewrite; @@ -49,8 +48,8 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.O /** * This filter is BETA and may be subject to change in a future release. */ -public class ModifyResponseBodyGatewayFilterFactory - extends AbstractGatewayFilterFactory { +public class ModifyResponseBodyGatewayFilterFactory extends + AbstractGatewayFilterFactory { private final ServerCodecConfigurer codecConfigurer; @@ -64,113 +63,16 @@ public class ModifyResponseBodyGatewayFilterFactory return new ModifyResponseGatewayFilter(config); } - public class ModifyResponseGatewayFilter implements GatewayFilter, Ordered { - private final Config config; - - public ModifyResponseGatewayFilter(Config config) { - this.config = config; - } - - @Override - @SuppressWarnings("unchecked") - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - - ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) { - - @Override - public Mono writeWith(Publisher body) { - - Class inClass = config.getInClass(); - Class outClass = config.getOutClass(); - - String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR); - HttpHeaders httpHeaders = new HttpHeaders(); - //explicitly add it in this way instead of 'httpHeaders.setContentType(originalResponseContentType)' - //this will prevent exception in case of using non-standard media types like "Content-Type: image" - httpHeaders.add(HttpHeaders.CONTENT_TYPE, originalResponseContentType); - ResponseAdapter responseAdapter = new ResponseAdapter(body, httpHeaders); - DefaultClientResponse clientResponse = new DefaultClientResponse(responseAdapter, ExchangeStrategies.withDefaults()); - - //TODO: flux or mono - Mono modifiedBody = clientResponse.bodyToMono(inClass) - .flatMap(originalBody -> config.rewriteFunction.apply(exchange, originalBody)); - - BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass); - CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, exchange.getResponse().getHeaders()); - return bodyInserter.insert(outputMessage, new BodyInserterContext()) - .then(Mono.defer(() -> { - Flux messageBody = outputMessage.getBody(); - HttpHeaders headers = getDelegate().getHeaders(); - if (!headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) { - messageBody = messageBody.doOnNext(data -> headers.setContentLength(data.readableByteCount())); - } - //TODO: use isStreamingMediaType? - return getDelegate().writeWith(messageBody); - })); - } - - @Override - public Mono writeAndFlushWith(Publisher> body) { - return writeWith(Flux.from(body) - .flatMapSequential(p -> p)); - } - }; - - return chain.filter(exchange.mutate().response(responseDecorator).build()); - } - - @Override - public int getOrder() { - return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1; - } - - } - - public class ResponseAdapter implements ClientHttpResponse { - - private final Flux flux; - private final HttpHeaders headers; - - public ResponseAdapter(Publisher body, HttpHeaders headers) { - this.headers = headers; - if (body instanceof Flux) { - flux = (Flux) body; - } else { - flux = ((Mono)body).flux(); - } - } - - @Override - public Flux getBody() { - return flux; - } - - @Override - public HttpHeaders getHeaders() { - return headers; - } - - @Override - public HttpStatus getStatusCode() { - return null; - } - - @Override - public int getRawStatusCode() { - return 0; - } - - @Override - public MultiValueMap getCookies() { - return null; - } - } - public static class Config { + private Class inClass; + private Class outClass; + private Map inHints; + private Map outHints; + private String newContentType; private RewriteFunction rewriteFunction; @@ -224,6 +126,11 @@ public class ModifyResponseBodyGatewayFilterFactory return rewriteFunction; } + public Config setRewriteFunction(RewriteFunction rewriteFunction) { + this.rewriteFunction = rewriteFunction; + return this; + } + public Config setRewriteFunction(Class inClass, Class outClass, RewriteFunction rewriteFunction) { setInClass(inClass); @@ -232,9 +139,124 @@ public class ModifyResponseBodyGatewayFilterFactory return this; } - public Config setRewriteFunction(RewriteFunction rewriteFunction) { - this.rewriteFunction = rewriteFunction; - return this; - } } + + public class ModifyResponseGatewayFilter implements GatewayFilter, Ordered { + + private final Config config; + + public ModifyResponseGatewayFilter(Config config) { + this.config = config; + } + + @Override + @SuppressWarnings("unchecked") + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + + ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator( + exchange.getResponse()) { + + @Override + public Mono writeWith(Publisher body) { + + Class inClass = config.getInClass(); + Class outClass = config.getOutClass(); + + String originalResponseContentType = exchange + .getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR); + HttpHeaders httpHeaders = new HttpHeaders(); + // explicitly add it in this way instead of + // 'httpHeaders.setContentType(originalResponseContentType)' + // this will prevent exception in case of using non-standard media + // types like "Content-Type: image" + httpHeaders.add(HttpHeaders.CONTENT_TYPE, + originalResponseContentType); + ResponseAdapter responseAdapter = new ResponseAdapter(body, + httpHeaders); + DefaultClientResponse clientResponse = new DefaultClientResponse( + responseAdapter, ExchangeStrategies.withDefaults()); + + // TODO: flux or mono + Mono modifiedBody = clientResponse.bodyToMono(inClass) + .flatMap(originalBody -> config.rewriteFunction + .apply(exchange, originalBody)); + + BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, + outClass); + CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage( + exchange, exchange.getResponse().getHeaders()); + return bodyInserter.insert(outputMessage, new BodyInserterContext()) + .then(Mono.defer(() -> { + Flux messageBody = outputMessage.getBody(); + HttpHeaders headers = getDelegate().getHeaders(); + if (!headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) { + messageBody = messageBody.doOnNext(data -> headers + .setContentLength(data.readableByteCount())); + } + // TODO: use isStreamingMediaType? + return getDelegate().writeWith(messageBody); + })); + } + + @Override + public Mono writeAndFlushWith( + Publisher> body) { + return writeWith(Flux.from(body).flatMapSequential(p -> p)); + } + }; + + return chain.filter(exchange.mutate().response(responseDecorator).build()); + } + + @Override + public int getOrder() { + return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1; + } + + } + + public class ResponseAdapter implements ClientHttpResponse { + + private final Flux flux; + + private final HttpHeaders headers; + + public ResponseAdapter(Publisher body, + HttpHeaders headers) { + this.headers = headers; + if (body instanceof Flux) { + flux = (Flux) body; + } + else { + flux = ((Mono) body).flux(); + } + } + + @Override + public Flux getBody() { + return flux; + } + + @Override + public HttpHeaders getHeaders() { + return headers; + } + + @Override + public HttpStatus getStatusCode() { + return null; + } + + @Override + public int getRawStatusCode() { + return 0; + } + + @Override + public MultiValueMap getCookies() { + return null; + } + + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/RewriteFunction.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/RewriteFunction.java index bc951ff8..71ab0ab4 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/RewriteFunction.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite/RewriteFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory.rewrite; @@ -20,12 +19,16 @@ package org.springframework.cloud.gateway.filter.factory.rewrite; import java.util.function.BiFunction; import org.reactivestreams.Publisher; + import org.springframework.web.server.ServerWebExchange; /** * This interface is BETA and may be subject to change in a future release. - * @param - * @param + * + * @param the type of the first argument to the function + * @param the type of element signaled by the {@link Publisher} */ -public interface RewriteFunction extends BiFunction> { +public interface RewriteFunction + extends BiFunction> { + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java index d3941636..9096fffc 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; @@ -25,6 +24,7 @@ import java.util.List; import java.util.Map; import org.jetbrains.annotations.Nullable; + import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -36,8 +36,56 @@ import org.springframework.web.server.ServerWebExchange; public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { + /** + * Forwarded header. + */ public static final String FORWARDED_HEADER = "Forwarded"; + /* for testing */ + static List parse(List values) { + ArrayList forwardeds = new ArrayList<>(); + if (CollectionUtils.isEmpty(values)) { + return forwardeds; + } + for (String value : values) { + Forwarded forwarded = parse(value); + forwardeds.add(forwarded); + } + return forwardeds; + } + + /* for testing */ + static Forwarded parse(String value) { + String[] pairs = StringUtils.tokenizeToStringArray(value, ";"); + + LinkedCaseInsensitiveMap result = splitIntoCaseInsensitiveMap(pairs); + if (result == null) { + return null; + } + + Forwarded forwarded = new Forwarded(result); + + return forwarded; + } + + @Nullable + /* for testing */ static LinkedCaseInsensitiveMap splitIntoCaseInsensitiveMap( + String[] pairs) { + if (ObjectUtils.isEmpty(pairs)) { + return null; + } + + LinkedCaseInsensitiveMap result = new LinkedCaseInsensitiveMap<>(); + for (String element : pairs) { + String[] splittedElement = StringUtils.split(element, "="); + if (splittedElement == null) { + continue; + } + result.put(splittedElement[0].trim(), splittedElement[1].trim()); + } + return result; + } + @Override public int getOrder() { return 0; @@ -50,8 +98,8 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { HttpHeaders updated = new HttpHeaders(); // copy all headers except Forwarded - original.entrySet().stream() - .filter(entry -> !entry.getKey().toLowerCase().equalsIgnoreCase(FORWARDED_HEADER)) + original.entrySet().stream().filter( + entry -> !entry.getKey().toLowerCase().equalsIgnoreCase(FORWARDED_HEADER)) .forEach(entry -> updated.addAll(entry.getKey(), entry.getValue())); List forwardeds = parse(original.get(FORWARDED_HEADER)); @@ -60,12 +108,11 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { updated.add(FORWARDED_HEADER, f.toHeaderValue()); } - //TODO: add new forwarded + // TODO: add new forwarded URI uri = request.getURI(); String host = original.getFirst(HttpHeaders.HOST); - Forwarded forwarded = new Forwarded() - .put("host", host) - .put("proto", uri.getScheme()); + Forwarded forwarded = new Forwarded().put("host", host).put("proto", + uri.getScheme()); InetSocketAddress remoteAddress = request.getRemoteAddress(); if (remoteAddress != null) { @@ -83,59 +130,19 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { return updated; } - - /* for testing */ static List parse(List values) { - ArrayList forwardeds = new ArrayList<>(); - if (CollectionUtils.isEmpty(values)) { - return forwardeds; - } - for (String value : values) { - Forwarded forwarded = parse(value); - forwardeds.add(forwarded); - } - return forwardeds; - } - - /* for testing */ static Forwarded parse(String value) { - String[] pairs = StringUtils.tokenizeToStringArray(value, ";"); - - LinkedCaseInsensitiveMap result = splitIntoCaseInsensitiveMap(pairs); - if (result == null) return null; - - Forwarded forwarded = new Forwarded(result); - - return forwarded; - } - - @Nullable - /* for testing */ static LinkedCaseInsensitiveMap splitIntoCaseInsensitiveMap(String[] pairs) { - if (ObjectUtils.isEmpty(pairs)) { - return null; - } - - LinkedCaseInsensitiveMap result = new LinkedCaseInsensitiveMap<>(); - for (String element : pairs) { - String[] splittedElement = StringUtils.split(element, "="); - if (splittedElement == null) { - continue; - } - result.put(splittedElement[0].trim(), splittedElement[1].trim()); - } - return result; - } - /* for testing */ static class Forwarded { private static final char EQUALS = '='; + private static final char SEMICOLON = ';'; private final Map values; - public Forwarded() { + Forwarded() { this.values = new HashMap<>(); } - public Forwarded(Map values) { + Forwarded(Map values) { this.values = values; } @@ -144,10 +151,9 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { return this; } - private String quoteIfNeeded(String s) { - if (s != null && s.contains(":")) { //TODO: broaded quote - return "\""+s+"\""; + if (s != null && s.contains(":")) { // TODO: broaded quote + return "\"" + s + "\""; } return s; } @@ -162,9 +168,7 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { @Override public String toString() { - return "Forwarded{" + - "values=" + this.values + - '}'; + return "Forwarded{" + "values=" + this.values + '}'; } public String toHeaderValue() { @@ -173,12 +177,11 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { if (builder.length() > 0) { builder.append(SEMICOLON); } - builder.append(entry.getKey()) - .append(EQUALS) - .append(entry.getValue()); + builder.append(entry.getKey()).append(EQUALS).append(entry.getValue()); } return builder.toString(); } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilter.java index 0e5644a9..32e85c17 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 the original author or authors. + * Copyright 2017-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; @@ -24,21 +23,8 @@ import org.springframework.web.server.ServerWebExchange; public interface HttpHeadersFilter { - enum Type { - REQUEST, RESPONSE - } - - /** - * Filters a set of Http Headers - * - * @param input Http Headers - * @param exchange - * @return filtered Http Headers - */ - HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange); - static HttpHeaders filterRequest(List filters, - ServerWebExchange exchange) { + ServerWebExchange exchange) { HttpHeaders headers = exchange.getRequest().getHeaders(); return filter(filters, headers, exchange, Type.REQUEST); } @@ -48,8 +34,7 @@ public interface HttpHeadersFilter { HttpHeaders response = input; if (filters != null) { HttpHeaders reduce = filters.stream() - .filter(headersFilter -> headersFilter.supports(type)) - .reduce(input, + .filter(headersFilter -> headersFilter.supports(type)).reduce(input, (headers, filter) -> filter.filter(headers, exchange), (httpHeaders, httpHeaders2) -> { httpHeaders.addAll(httpHeaders2); @@ -61,7 +46,22 @@ public interface HttpHeadersFilter { return response; } + /** + * Filters a set of Http Headers. + * @param input Http Headers + * @param exchange a {@link ServerWebExchange} that should be filtered + * @return filtered Http Headers + */ + HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange); + default boolean supports(Type type) { return type.equals(Type.REQUEST); } + + enum Type { + + REQUEST, RESPONSE + + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilter.java index 5c74271e..1d1e71ba 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; @@ -29,21 +28,18 @@ import org.springframework.web.server.ServerWebExchange; @ConfigurationProperties("spring.cloud.gateway.filter.remove-hop-by-hop") public class RemoveHopByHopHeadersFilter implements HttpHeadersFilter, Ordered { - public static final Set HEADERS_REMOVED_ON_REQUEST = - new HashSet<>(Arrays.asList( - "connection", - "keep-alive", - "transfer-encoding", - "te", - "trailer", - "proxy-authorization", - "proxy-authenticate", - "x-application-context", - "upgrade" - // these two are not listed in https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3 - //"proxy-connection", - // "content-length", - )); + /** + * Headers to remove as the result of applying the filter. + */ + public static final Set HEADERS_REMOVED_ON_REQUEST = new HashSet<>( + Arrays.asList("connection", "keep-alive", "transfer-encoding", "te", + "trailer", "proxy-authorization", "proxy-authenticate", + "x-application-context", "upgrade" + // these two are not listed in + // https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3 + // "proxy-connection", + // "content-length", + )); private int order = Ordered.LOWEST_PRECEDENCE; @@ -69,7 +65,7 @@ public class RemoveHopByHopHeadersFilter implements HttpHeadersFilter, Ordered { @Override public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) { HttpHeaders filtered = new HttpHeaders(); - + input.entrySet().stream() .filter(entry -> !this.headers.contains(entry.getKey().toLowerCase())) .forEach(entry -> filtered.addAll(entry.getKey(), entry.getValue())); @@ -77,9 +73,9 @@ public class RemoveHopByHopHeadersFilter implements HttpHeadersFilter, Ordered { return filtered; } - @Override + @Override public boolean supports(Type type) { - return type.equals(Type.REQUEST) || - type.equals(Type.RESPONSE); + return type.equals(Type.REQUEST) || type.equals(Type.RESPONSE); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilter.java index a5ec3a5e..fe5cf3cd 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; @@ -20,6 +19,7 @@ package org.springframework.cloud.gateway.filter.headers; import java.net.URI; import java.util.LinkedHashSet; import java.util.List; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; @@ -32,34 +32,34 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G @ConfigurationProperties("spring.cloud.gateway.x-forwarded") public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered { - /** default http port */ + + /** Default http port. */ public static final int HTTP_PORT = 80; - /** default https port */ + /** Default https port. */ public static final int HTTPS_PORT = 443; - /** http url scheme */ + /** Http url scheme. */ public static final String HTTP_SCHEME = "http"; - /** https url scheme */ + /** Https url scheme. */ public static final String HTTPS_SCHEME = "https"; - /** X-Forwarded-For Header */ + /** X-Forwarded-For Header. */ public static final String X_FORWARDED_FOR_HEADER = "X-Forwarded-For"; - /** X-Forwarded-Host Header */ + /** X-Forwarded-Host Header. */ public static final String X_FORWARDED_HOST_HEADER = "X-Forwarded-Host"; - /** X-Forwarded-Port Header */ + /** X-Forwarded-Port Header. */ public static final String X_FORWARDED_PORT_HEADER = "X-Forwarded-Port"; - /** X-Forwarded-Proto Header */ + /** X-Forwarded-Proto Header. */ public static final String X_FORWARDED_PROTO_HEADER = "X-Forwarded-Proto"; - /** X-Forwarded-Prefix Header */ + /** X-Forwarded-Prefix Header. */ public static final String X_FORWARDED_PREFIX_HEADER = "X-Forwarded-Prefix"; - /** The order of the XForwardedHeadersFilter. */ private int order = 0; @@ -185,18 +185,17 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered { this.protoAppend = protoAppend; } - public void setPrefixAppend(boolean prefixAppend) { - this.prefixAppend = prefixAppend; - } - public boolean isPrefixAppend() { return prefixAppend; } + public void setPrefixAppend(boolean prefixAppend) { + this.prefixAppend = prefixAppend; + } + @Override public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) { - ServerHttpRequest request = exchange.getRequest(); HttpHeaders original = input; HttpHeaders updated = new HttpHeaders(); @@ -204,14 +203,13 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered { original.entrySet().stream() .forEach(entry -> updated.addAll(entry.getKey(), entry.getValue())); - if (isForEnabled() && - request.getRemoteAddress() != null && request.getRemoteAddress().getAddress() != null) - { + if (isForEnabled() && request.getRemoteAddress() != null + && request.getRemoteAddress().getAddress() != null) { String remoteAddr = request.getRemoteAddress().getAddress().getHostAddress(); List xforwarded = original.get(X_FORWARDED_FOR_HEADER); // prevent duplicates - if (remoteAddr != null && - (xforwarded == null || !xforwarded.contains(remoteAddr))) { + if (remoteAddr != null + && (xforwarded == null || !xforwarded.contains(remoteAddr))) { write(updated, X_FORWARDED_FOR_HEADER, remoteAddr, isForAppend()); } } @@ -221,32 +219,32 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered { write(updated, X_FORWARDED_PROTO_HEADER, proto, isProtoAppend()); } - if(isPrefixEnabled()) { - //if the path of the url that the gw is routing to is a subset (and ending part) of the url that it is routing from then the difference is the prefix - //e.g. if request original.com/prefix/get/ is routed to routedservice:8090/get then /prefix is the prefix - see XForwardedHeadersFilterTests - //so first get uris, then extract paths and remove one from another if it's the ending part + if (isPrefixEnabled()) { + // If the path of the url that the gw is routing to is a subset + // (and ending part) of the url that it is routing from then the difference + // is the prefix e.g. if request original.com/prefix/get/ is routed + // to routedservice:8090/get then /prefix is the prefix + // - see XForwardedHeadersFilterTests, so first get uris, then extract paths + // and remove one from another if it's the ending part. - LinkedHashSet originalUris = exchange.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + LinkedHashSet originalUris = exchange + .getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); URI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); - if(originalUris != null && requestUri != null) { + if (originalUris != null && requestUri != null) { originalUris.stream().forEach(originalUri -> { - if(originalUri!=null && originalUri.getPath()!=null) { + if (originalUri != null && originalUri.getPath() != null) { String prefix = originalUri.getPath(); - //strip trailing slashes before checking if request path is end of original path + // strip trailing slashes before checking if request path is end + // of original path String originalUriPath = stripTrailingSlash(originalUri); String requestUriPath = stripTrailingSlash(requestUri); - if(requestUriPath!=null && (originalUriPath.endsWith(requestUriPath))) { - prefix = originalUriPath.replace(requestUriPath, ""); - if (prefix != null && prefix.length() > 0 && - prefix.length() <= originalUri.getPath().length()) { - write(updated, X_FORWARDED_PREFIX_HEADER, prefix, isPrefixAppend()); - } - } + updateRequest(updated, originalUri, originalUriPath, + requestUriPath); } }); @@ -269,6 +267,18 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered { return updated; } + private void updateRequest(HttpHeaders updated, URI originalUri, + String originalUriPath, String requestUriPath) { + String prefix; + if (requestUriPath != null && (originalUriPath.endsWith(requestUriPath))) { + prefix = originalUriPath.replace(requestUriPath, ""); + if (prefix != null && prefix.length() > 0 + && prefix.length() <= originalUri.getPath().length()) { + write(updated, X_FORWARDED_PREFIX_HEADER, prefix, isPrefixAppend()); + } + } + } + private void write(HttpHeaders headers, String name, String value, boolean append) { if (append) { headers.add(name, value); @@ -276,7 +286,8 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered { List values = headers.get(name); String delimitedValue = StringUtils.collectionToCommaDelimitedString(values); headers.set(name, delimitedValue); - } else { + } + else { headers.set(name, value); } } @@ -287,11 +298,9 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered { private boolean hasHeader(ServerHttpRequest request, String name) { HttpHeaders headers = request.getHeaders(); - return headers.containsKey(name) && - StringUtils.hasLength(headers.getFirst(name)); + return headers.containsKey(name) && StringUtils.hasLength(headers.getFirst(name)); } - private String toHostHeader(ServerHttpRequest request) { int port = request.getURI().getPort(); String host = request.getURI().getHost(); @@ -308,8 +317,10 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered { private String stripTrailingSlash(URI uri) { if (uri.getPath().endsWith("/")) { return uri.getPath().substring(0, uri.getPath().length() - 1); - } else { + } + else { return uri.getPath(); } } -} \ No newline at end of file + +} diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/AbstractRateLimiter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/AbstractRateLimiter.java index 9ca41404..0b66bca4 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/AbstractRateLimiter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/AbstractRateLimiter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.ratelimit; @@ -26,11 +25,15 @@ import org.springframework.context.ApplicationListener; import org.springframework.core.style.ToStringCreator; import org.springframework.validation.Validator; -public abstract class AbstractRateLimiter extends AbstractStatefulConfigurable implements RateLimiter, ApplicationListener { +public abstract class AbstractRateLimiter extends AbstractStatefulConfigurable + implements RateLimiter, ApplicationListener { + private String configurationPropertyName; + private Validator validator; - protected AbstractRateLimiter(Class configClass, String configurationPropertyName, Validator validator) { + protected AbstractRateLimiter(Class configClass, String configurationPropertyName, + Validator validator) { super(configClass); this.configurationPropertyName = configurationPropertyName; this.validator = validator; @@ -58,8 +61,8 @@ public abstract class AbstractRateLimiter extends AbstractStatefulConfigurabl String routeId = event.getRouteId(); C routeConfig = newConfig(); - ConfigurationUtils.bind(routeConfig, args, - configurationPropertyName, configurationPropertyName, validator); + ConfigurationUtils.bind(routeConfig, args, configurationPropertyName, + configurationPropertyName, validator); getConfig().put(routeId, routeConfig); } @@ -72,8 +75,7 @@ public abstract class AbstractRateLimiter extends AbstractStatefulConfigurabl public String toString() { return new ToStringCreator(this) .append("configurationPropertyName", configurationPropertyName) - .append("config", getConfig()) - .append("configClass", getConfigClass()) + .append("config", getConfig()).append("configClass", getConfigClass()) .toString(); } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/KeyResolver.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/KeyResolver.java index 3f3e7e70..8f90da60 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/KeyResolver.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/KeyResolver.java @@ -1,11 +1,30 @@ +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter.ratelimit; -import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import org.springframework.web.server.ServerWebExchange; + /** * @author Spencer Gibb */ public interface KeyResolver { + Mono resolve(ServerWebExchange exchange); + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolver.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolver.java index 502e9e58..c48546a2 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolver.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolver.java @@ -1,16 +1,38 @@ -package org.springframework.cloud.gateway.filter.ratelimit; +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; +package org.springframework.cloud.gateway.filter.ratelimit; import java.security.Principal; +import reactor.core.publisher.Mono; + +import org.springframework.web.server.ServerWebExchange; + public class PrincipalNameKeyResolver implements KeyResolver { + /** + * {@link PrincipalNameKeyResolver} bean name. + */ public static final String BEAN_NAME = "principalNameKeyResolver"; @Override public Mono resolve(ServerWebExchange exchange) { - return exchange.getPrincipal().map(Principal::getName).switchIfEmpty(Mono.empty()); + return exchange.getPrincipal().map(Principal::getName) + .switchIfEmpty(Mono.empty()); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RateLimiter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RateLimiter.java index 5f9a4b1d..0ddd7ca2 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RateLimiter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RateLimiter.java @@ -1,13 +1,29 @@ +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter.ratelimit; -import org.springframework.cloud.gateway.support.StatefulConfigurable; - -import org.springframework.util.Assert; -import reactor.core.publisher.Mono; - import java.util.Collections; import java.util.Map; +import reactor.core.publisher.Mono; + +import org.springframework.cloud.gateway.support.StatefulConfigurable; +import org.springframework.util.Assert; + /** * @author Spencer Gibb */ @@ -16,8 +32,11 @@ public interface RateLimiter extends StatefulConfigurable { Mono isAllowed(String routeId, String id); class Response { + private final boolean allowed; + private final long tokensRemaining; + private final Map headers; public Response(boolean allowed, Map headers) { @@ -56,5 +75,7 @@ public interface RateLimiter extends StatefulConfigurable { sb.append('}'); return sb.toString(); } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java index 875d85ee..76c66fb8 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter.ratelimit; import java.time.Instant; @@ -12,12 +28,12 @@ import javax.validation.constraints.Min; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jetbrains.annotations.NotNull; -import org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.beans.BeansException; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.data.redis.core.ReactiveRedisTemplate; @@ -27,35 +43,72 @@ import org.springframework.validation.annotation.Validated; /** * See https://stripe.com/blog/rate-limiters and - * https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L11-L34 + * https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L11-L34. * * @author Spencer Gibb */ @ConfigurationProperties("spring.cloud.gateway.redis-rate-limiter") -public class RedisRateLimiter extends AbstractRateLimiter implements ApplicationContextAware { +public class RedisRateLimiter extends AbstractRateLimiter + implements ApplicationContextAware { + + /** + * @deprecated use {@link Config#replenishRate} + */ @Deprecated public static final String REPLENISH_RATE_KEY = "replenishRate"; + + /** + * @deprecated use {@link Config#burstCapacity} + */ @Deprecated public static final String BURST_CAPACITY_KEY = "burstCapacity"; + /** + * Redis Rate Limiter property name. + */ public static final String CONFIGURATION_PROPERTY_NAME = "redis-rate-limiter"; + + /** + * Redis Script name. + */ public static final String REDIS_SCRIPT_NAME = "redisRequestRateLimiterScript"; + + /** + * Remaining Rate Limit header name. + */ public static final String REMAINING_HEADER = "X-RateLimit-Remaining"; + + /** + * Replenish Rate Limit header name. + */ public static final String REPLENISH_RATE_HEADER = "X-RateLimit-Replenish-Rate"; + + /** + * Burst Capacity Header name. + */ public static final String BURST_CAPACITY_HEADER = "X-RateLimit-Burst-Capacity"; private Log log = LogFactory.getLog(getClass()); private ReactiveRedisTemplate redisTemplate; + private RedisScript> script; + private AtomicBoolean initialized = new AtomicBoolean(false); + private Config defaultConfig; // configuration properties - /** Whether or not to include headers containing rate limiter information, defaults to true. */ + /** + * Whether or not to include headers containing rate limiter information, defaults to + * true. + */ private boolean includeHeaders = true; - /** The name of the header that returns number of remaining requests during the current second. */ + /** + * The name of the header that returns number of remaining requests during the current + * second. + */ private String remainingHeader = REMAINING_HEADER; /** The name of the header that returns the replenish rate configuration. */ @@ -65,7 +118,7 @@ public class RedisRateLimiter extends AbstractRateLimiter redisTemplate, - RedisScript> script, Validator validator) { + RedisScript> script, Validator validator) { super(Config.class, CONFIGURATION_PROPERTY_NAME, validator); this.redisTemplate = redisTemplate; this.script = script; @@ -74,11 +127,23 @@ public class RedisRateLimiter extends AbstractRateLimiter getKeys(String id) { + // use `{}` around keys to use Redis Key hash tags + // this allows for using redis cluster + + // Make a unique key per user. + String prefix = "request_rate_limiter.{" + id; + + // You need two Redis keys for Token Bucket. + String tokenKey = prefix + "}.tokens"; + String timestampKey = prefix + "}.timestamp"; + return Arrays.asList(tokenKey, timestampKey); + } + public boolean isIncludeHeaders() { return includeHeaders; } @@ -115,7 +180,8 @@ public class RedisRateLimiter extends AbstractRateLimiter 0) { this.setValidator(context.getBean(Validator.class)); @@ -150,22 +216,23 @@ public class RedisRateLimiter extends AbstractRateLimiter keys = getKeys(id); - // The arguments to the LUA script. time() returns unixtime in seconds. - List scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", - Instant.now().getEpochSecond() + "", "1"); + List scriptArgs = Arrays.asList(replenishRate + "", + burstCapacity + "", Instant.now().getEpochSecond() + "", "1"); // allowed, tokens_left = redis.eval(SCRIPT, keys, args) - Flux> flux = this.redisTemplate.execute(this.script, keys, scriptArgs); - // .log("redisratelimiter", Level.FINER); + Flux> flux = this.redisTemplate.execute(this.script, keys, + scriptArgs); + // .log("redisratelimiter", Level.FINER); return flux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L))) .reduce(new ArrayList(), (longs, l) -> { longs.addAll(l); return longs; - }) .map(results -> { + }).map(results -> { boolean allowed = results.get(0) == 1L; Long tokensLeft = results.get(1); - Response response = new Response(allowed, getHeaders(routeConfig, tokensLeft)); + Response response = new Response(allowed, + getHeaders(routeConfig, tokensLeft)); if (log.isDebugEnabled()) { log.debug("response: " + response); @@ -192,7 +259,8 @@ public class RedisRateLimiter extends AbstractRateLimiter getKeys(String id) { - // use `{}` around keys to use Redis Key hash tags - // this allows for using redis cluster - - // Make a unique key per user. - String prefix = "request_rate_limiter.{" + id; - - // You need two Redis keys for Token Bucket. - String tokenKey = prefix + "}.tokens"; - String timestampKey = prefix + "}.timestamp"; - return Arrays.asList(tokenKey, timestampKey); - } - @Validated public static class Config { + @Min(1) private int replenishRate; @@ -247,10 +303,10 @@ public class RedisRateLimiter extends AbstractRateLimiter globalFilters; @@ -56,21 +56,20 @@ public class FilteringWebHandler implements WebHandler { } private static List loadFilters(List filters) { - return filters.stream() - .map(filter -> { - GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter); - if (filter instanceof Ordered) { - int order = ((Ordered) filter).getOrder(); - return new OrderedGatewayFilter(gatewayFilter, order); - } - return gatewayFilter; - }).collect(Collectors.toList()); + return filters.stream().map(filter -> { + GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter); + if (filter instanceof Ordered) { + int order = ((Ordered) filter).getOrder(); + return new OrderedGatewayFilter(gatewayFilter, order); + } + return gatewayFilter; + }).collect(Collectors.toList()); } - /* TODO: relocate @EventListener(RefreshRoutesEvent.class) - void handleRefresh() { - this.combinedFiltersForRoute.clear(); - }*/ + /* + * TODO: relocate @EventListener(RefreshRoutesEvent.class) void handleRefresh() { + * this.combinedFiltersForRoute.clear(); + */ @Override public Mono handle(ServerWebExchange exchange) { @@ -79,11 +78,11 @@ public class FilteringWebHandler implements WebHandler { List combined = new ArrayList<>(this.globalFilters); combined.addAll(gatewayFilters); - //TODO: needed or cached? + // TODO: needed or cached? AnnotationAwareOrderComparator.sort(combined); if (logger.isDebugEnabled()) { - logger.debug("Sorted gatewayFilterFactories: "+ combined); + logger.debug("Sorted gatewayFilterFactories: " + combined); } return new DefaultGatewayFilterChain(combined).filter(exchange); @@ -92,9 +91,10 @@ public class FilteringWebHandler implements WebHandler { private static class DefaultGatewayFilterChain implements GatewayFilterChain { private final int index; + private final List filters; - public DefaultGatewayFilterChain(List filters) { + DefaultGatewayFilterChain(List filters) { this.filters = filters; this.index = 0; } @@ -113,20 +113,23 @@ public class FilteringWebHandler implements WebHandler { return Mono.defer(() -> { if (this.index < filters.size()) { GatewayFilter filter = filters.get(this.index); - DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1); + DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, + this.index + 1); return filter.filter(exchange, chain); - } else { + } + else { return Mono.empty(); // complete } }); } + } private static class GatewayFilterAdapter implements GatewayFilter { private final GlobalFilter delegate; - public GatewayFilterAdapter(GlobalFilter delegate) { + GatewayFilterAdapter(GlobalFilter delegate) { this.delegate = delegate; } @@ -142,6 +145,7 @@ public class FilteringWebHandler implements WebHandler { sb.append('}'); return sb.toString(); } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java index 2b1d9ac0..52d2d93a 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler; @@ -39,26 +38,33 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { private final FilteringWebHandler webHandler; + private final RouteLocator routeLocator; + private final Integer managmentPort; - public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) { + public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, + RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, + Environment environment) { this.webHandler = webHandler; this.routeLocator = routeLocator; if (environment.containsProperty("management.server.port")) { - managmentPort = new Integer(environment.getProperty("management.server.port")); - } else { + managmentPort = new Integer( + environment.getProperty("management.server.port")); + } + else { managmentPort = null; } - setOrder(1); + setOrder(1); setCorsConfigurations(globalCorsProperties.getCorsConfigurations()); } @Override protected Mono getHandlerInternal(ServerWebExchange exchange) { // don't handle requests on the management port if set - if (managmentPort != null && exchange.getRequest().getURI().getPort() == managmentPort.intValue()) { + if (managmentPort != null + && exchange.getRequest().getURI().getPort() == managmentPort.intValue()) { return Mono.empty(); } exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName()); @@ -68,7 +74,8 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { .flatMap((Function>) r -> { exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); if (logger.isDebugEnabled()) { - logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r); + logger.debug( + "Mapping [" + getExchangeDesc(exchange) + "] to " + r); } exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); @@ -76,21 +83,23 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> { exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); if (logger.isTraceEnabled()) { - logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]"); + logger.trace("No RouteDefinition found for [" + + getExchangeDesc(exchange) + "]"); } }))); } @Override - protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) { + protected CorsConfiguration getCorsConfiguration(Object handler, + ServerWebExchange exchange) { // TODO: support cors configuration via properties on a route see gh-229 // see RequestMappingHandlerMapping.initCorsConfiguration() - // also see https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsWebFilterTests.java - + // also see + // https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsWebFilterTests.java return super.getCorsConfiguration(handler, exchange); } - //TODO: get desc from factory? + // TODO: get desc from factory? private String getExchangeDesc(ServerWebExchange exchange) { StringBuilder out = new StringBuilder(); out.append("Exchange: "); @@ -101,25 +110,25 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { } protected Mono lookupRoute(ServerWebExchange exchange) { - return this.routeLocator - .getRoutes() - //individually filter routes so that filterWhen error delaying is not a problem - .concatMap(route -> Mono - .just(route) - .filterWhen(r -> { - // add the current route we are testing - exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId()); - return r.getPredicate().apply(exchange); - }) - //instead of immediately stopping main flux due to error, log and swallow it - .doOnError(e -> logger.error("Error applying predicate for route: "+route.getId(), e)) - .onErrorResume(e -> Mono.empty()) - ) + return this.routeLocator.getRoutes() + // individually filter routes so that filterWhen error delaying is not a + // problem + .concatMap(route -> Mono.just(route).filterWhen(r -> { + // add the current route we are testing + exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId()); + return r.getPredicate().apply(exchange); + }) + // instead of immediately stopping main flux due to error, log and + // swallow it + .doOnError(e -> logger.error( + "Error applying predicate for route: " + route.getId(), + e)) + .onErrorResume(e -> Mono.empty())) // .defaultIfEmpty() put a static Route not found // or .switchIfEmpty() // .switchIfEmpty(Mono.empty().log("noroute")) .next() - //TODO: error handling + // TODO: error handling .map(route -> { if (logger.isDebugEnabled()) { logger.debug("Route matched: " + route.getId()); @@ -128,16 +137,17 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { return route; }); - /* TODO: trace logging - if (logger.isTraceEnabled()) { - logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); - }*/ + /* + * TODO: trace logging if (logger.isTraceEnabled()) { + * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); } + */ } /** * Validate the given handler against the current request. - *

The default implementation is empty. Can be overridden in subclasses, - * for example to enforce specific preconditions expressed in URL mappings. + *

+ * The default implementation is empty. Can be overridden in subclasses, for example + * to enforce specific preconditions expressed in URL mappings. * @param route the Route object to validate * @param exchange current exchange * @throws Exception if validation failed @@ -149,4 +159,5 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { protected String getSimpleName() { return "RoutePredicateHandlerMapping"; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/AbstractRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/AbstractRoutePredicateFactory.java index ca6add1f..ee32c6b8 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/AbstractRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/AbstractRoutePredicateFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactory.java index 25b6a6bc..efa81ce2 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -22,15 +21,19 @@ import java.util.Collections; import java.util.List; import java.util.function.Predicate; -import org.springframework.web.server.ServerWebExchange; - import javax.validation.constraints.NotNull; +import org.springframework.web.server.ServerWebExchange; + /** * @author Spencer Gibb */ -public class AfterRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class AfterRoutePredicateFactory + extends AbstractRoutePredicateFactory { + /** + * DateTime key. + */ public static final String DATETIME_KEY = "datetime"; public AfterRoutePredicateFactory() { @@ -52,6 +55,7 @@ public class AfterRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class BeforeRoutePredicateFactory + extends AbstractRoutePredicateFactory { + /** + * DateTime key. + */ public static final String DATETIME_KEY = "datetime"; public BeforeRoutePredicateFactory() { @@ -50,6 +53,7 @@ public class BeforeRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class BetweenRoutePredicateFactory + extends AbstractRoutePredicateFactory { + /** + * DateTime 1 key. + */ public static final String DATETIME1_KEY = "datetime1"; + + /** + * DateTime 2 key. + */ public static final String DATETIME2_KEY = "datetime2"; public BetweenRoutePredicateFactory() { @@ -52,8 +57,7 @@ public class BetweenRoutePredicateFactory extends AbstractRoutePredicateFactory< ZonedDateTime datetime1 = config.datetime1; ZonedDateTime datetime2 = config.datetime2; Assert.isTrue(datetime1.isBefore(datetime2), - config.datetime1 + - " must be before " + config.datetime2); + config.datetime1 + " must be before " + config.datetime2); return exchange -> { final ZonedDateTime now = ZonedDateTime.now(); @@ -63,8 +67,10 @@ public class BetweenRoutePredicateFactory extends AbstractRoutePredicateFactory< @Validated public static class Config { + @NotNull private ZonedDateTime datetime1; + @NotNull private ZonedDateTime datetime2; @@ -85,6 +91,7 @@ public class BetweenRoutePredicateFactory extends AbstractRoutePredicateFactory< this.datetime2 = datetime2; return this; } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactory.java index 89e90383..7994809c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactory.java @@ -1,3 +1,19 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.handler.predicate; import java.util.function.Predicate; @@ -5,16 +21,31 @@ import java.util.function.Predicate; import org.springframework.web.server.ServerWebExchange; /** - * Creates a predicate which indicates if the request is intended for a Cloud Foundry Route Service. - * @see Cloud Foundry Route Service documentation. + * Creates a predicate which indicates if the request is intended for a Cloud Foundry + * Route Service. + * * @author Andrew Fitzgerald + * @see Cloud Foundry + * Route Service documentation */ -public class CloudFoundryRouteServiceRoutePredicateFactory extends - AbstractRoutePredicateFactory { +public class CloudFoundryRouteServiceRoutePredicateFactory + extends AbstractRoutePredicateFactory { + /** + * Forwarded URL header name. + */ public static final String X_CF_FORWARDED_URL = "X-CF-Forwarded-Url"; + + /** + * Proxy signature header name. + */ public static final String X_CF_PROXY_SIGNATURE = "X-CF-Proxy-Signature"; + + /** + * Proxy metadata header name. + */ public static final String X_CF_PROXY_METADATA = "X-CF-Proxy-Metadata"; + private final HeaderRoutePredicateFactory factory = new HeaderRoutePredicateFactory(); public CloudFoundryRouteServiceRoutePredicateFactory() { @@ -22,8 +53,7 @@ public class CloudFoundryRouteServiceRoutePredicateFactory extends } @Override - public Predicate apply( - Object unused) { + public Predicate apply(Object unused) { return headerPredicate(X_CF_FORWARDED_URL) .and(headerPredicate(X_CF_PROXY_SIGNATURE)) .and(headerPredicate(X_CF_PROXY_METADATA)); @@ -35,4 +65,5 @@ public class CloudFoundryRouteServiceRoutePredicateFactory extends config.setRegexp(".*"); return factory.apply(config); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/CookieRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/CookieRoutePredicateFactory.java index 18ea6daa..4b490f94 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/CookieRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/CookieRoutePredicateFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -30,9 +29,17 @@ import org.springframework.web.server.ServerWebExchange; /** * @author Spencer Gibb */ -public class CookieRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class CookieRoutePredicateFactory + extends AbstractRoutePredicateFactory { + /** + * Name key. + */ public static final String NAME_KEY = "name"; + + /** + * Regexp key. + */ public static final String REGEXP_KEY = "regexp"; public CookieRoutePredicateFactory() { @@ -47,7 +54,8 @@ public class CookieRoutePredicateFactory extends AbstractRoutePredicateFactory apply(Config config) { return exchange -> { - List cookies = exchange.getRequest().getCookies().get(config.name); + List cookies = exchange.getRequest().getCookies() + .get(config.name); if (cookies == null) { return false; } @@ -65,6 +73,7 @@ public class CookieRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class HeaderRoutePredicateFactory + extends AbstractRoutePredicateFactory { + /** + * Header key. + */ public static final String HEADER_KEY = "header"; + + /** + * Regexp key. + */ public static final String REGEXP_KEY = "regexp"; public HeaderRoutePredicateFactory() { @@ -50,7 +57,8 @@ public class HeaderRoutePredicateFactory extends AbstractRoutePredicateFactory { - List values = exchange.getRequest().getHeaders().getOrDefault(config.header, Collections.emptyList()); + List values = exchange.getRequest().getHeaders() + .getOrDefault(config.header, Collections.emptyList()); if (values.isEmpty()) { return false; } @@ -67,8 +75,10 @@ public class HeaderRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class HostRoutePredicateFactory + extends AbstractRoutePredicateFactory { private PathMatcher pathMatcher = new AntPathMatcher("."); @@ -62,11 +62,11 @@ public class HostRoutePredicateFactory extends AbstractRoutePredicateFactory { String host = exchange.getRequest().getHeaders().getFirst("Host"); Optional optionalPattern = config.getPatterns().stream() - .filter(pattern -> this.pathMatcher.match(pattern, host)) - .findFirst(); + .filter(pattern -> this.pathMatcher.match(pattern, host)).findFirst(); if (optionalPattern.isPresent()) { - Map variables = this.pathMatcher.extractUriTemplateVariables(optionalPattern.get(), host); + Map variables = this.pathMatcher + .extractUriTemplateVariables(optionalPattern.get(), host); ServerWebExchangeUtils.putUriTemplateVariables(exchange, variables); return true; } @@ -77,6 +77,7 @@ public class HostRoutePredicateFactory extends AbstractRoutePredicateFactory patterns = new ArrayList<>(); @Deprecated @@ -104,9 +105,9 @@ public class HostRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class MethodRoutePredicateFactory + extends AbstractRoutePredicateFactory { + /** + * Method key. + */ public static final String METHOD_KEY = "method"; public MethodRoutePredicateFactory() { @@ -49,6 +52,7 @@ public class MethodRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class PathRoutePredicateFactory + extends AbstractRoutePredicateFactory { + private static final Log log = LogFactory.getLog(RoutePredicateFactory.class); + private static final String MATCH_OPTIONAL_TRAILING_SEPARATOR_KEY = "matchOptionalTrailingSeparator"; private PathPatternParser pathPatternParser = new PathPatternParser(); @@ -51,6 +53,15 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory apply(Config config) { final ArrayList pathPatterns = new ArrayList<>(); synchronized (this.pathPatternParser) { - pathPatternParser.setMatchOptionalTrailingSeparator(config.isMatchOptionalTrailingSeparator()); + pathPatternParser.setMatchOptionalTrailingSeparator( + config.isMatchOptionalTrailingSeparator()); config.getPatterns().forEach(pattern -> { PathPattern pathPattern = this.pathPatternParser.parse(pattern); pathPatterns.add(pathPattern); @@ -79,8 +91,7 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory optionalPathPattern = pathPatterns.stream() - .filter(pattern -> pattern.matches(path)) - .findFirst(); + .filter(pattern -> pattern.matches(path)).findFirst(); if (optionalPathPattern.isPresent()) { PathPattern pathPattern = optionalPathPattern.get(); @@ -88,24 +99,19 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory patterns = new ArrayList<>(); + private boolean matchOptionalTrailingSeparator = true; @Deprecated @@ -136,19 +142,20 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory args = new LinkedHashMap<>(); public PredicateDefinition() { @@ -44,14 +45,14 @@ public class PredicateDefinition { public PredicateDefinition(String text) { int eqIdx = text.indexOf('='); if (eqIdx <= 0) { - throw new ValidationException("Unable to parse PredicateDefinition text '" + text + "'" + - ", must be of the form name=value"); + throw new ValidationException("Unable to parse PredicateDefinition text '" + + text + "'" + ", must be of the form name=value"); } setName(text.substring(0, eqIdx)); - String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ","); + String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ","); - for (int i=0; i < args.length; i++) { + for (int i = 0; i < args.length; i++) { this.args.put(NameUtils.generateName(i), args[i]); } } @@ -78,11 +79,14 @@ public class PredicateDefinition { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } PredicateDefinition that = (PredicateDefinition) o; - return Objects.equals(name, that.name) && - Objects.equals(args, that.args); + return Objects.equals(name, that.name) && Objects.equals(args, that.args); } @Override @@ -98,4 +102,5 @@ public class PredicateDefinition { sb.append('}'); return sb.toString(); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java index c7e38ccb..c14062c2 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -30,9 +29,17 @@ import org.springframework.web.server.ServerWebExchange; /** * @author Spencer Gibb */ -public class QueryRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class QueryRoutePredicateFactory + extends AbstractRoutePredicateFactory { + /** + * Param key. + */ public static final String PARAM_KEY = "param"; + + /** + * Regexp key. + */ public static final String REGEXP_KEY = "regexp"; public QueryRoutePredicateFactory() { @@ -52,8 +59,8 @@ public class QueryRoutePredicateFactory extends AbstractRoutePredicateFactory values = exchange.getRequest().getQueryParams().get(config.param); + List values = exchange.getRequest().getQueryParams() + .get(config.param); if (values == null) { return false; } @@ -68,6 +75,7 @@ public class QueryRoutePredicateFactory extends AbstractRoutePredicateFactory { + protected static final Log LOGGER = LogFactory.getLog(ReadBodyPredicateFactory.class); private static final String TEST_ATTRIBUTE = "read_body_predicate_test_attribute"; + private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject"; - private static final List> messageReaders = HandlerStrategies.withDefaults().messageReaders(); + + private static final List> messageReaders = HandlerStrategies + .withDefaults().messageReaders(); public ReadBodyPredicateFactory() { super(Config.class); @@ -63,48 +64,61 @@ public class ReadBodyPredicateFactory Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY); Mono modifiedBody; - // We can only read the body from the request once, once that happens if we try to read the body again an - // exception will be thrown. The below if/else caches the body object as a request attribute in the ServerWebExchange - // so if this filter is run more than once (due to more than one route using it) we do not try to read the - // request body multiple times + // We can only read the body from the request once, once that happens if we + // try to read the body again an exception will be thrown. The below if/else + // caches the body object as a request attribute in the ServerWebExchange + // so if this filter is run more than once (due to more than one route + // using it) we do not try to read the request body multiple times if (cachedBody != null) { try { boolean test = config.predicate.test(cachedBody); exchange.getAttributes().put(TEST_ATTRIBUTE, test); return Mono.just(test); - } catch (ClassCastException e) { + } + catch (ClassCastException e) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Predicate test failed because class in predicate does not match the cached body object", - e); + LOGGER.debug("Predicate test failed because class in predicate " + + "does not match the cached body object", e); } } return Mono.just(false); - } else { - //Join all the DataBuffers so we have a single DataBuffer for the body + } + else { + // Join all the DataBuffers so we have a single DataBuffer for the body return DataBufferUtils.join(exchange.getRequest().getBody()) .flatMap(dataBuffer -> { - //Update the retain counts so we can read the body twice, once to parse into an object - //that we can test the predicate against and a second time when the HTTP client sends - //the request downstream - //Note: if we end up reading the body twice we will run into a problem, but as of right - //now there is no good use case for doing this + // Update the retain counts so we can read the body twice, + // once to parse into an object + // that we can test the predicate against and a second time + // when the HTTP client sends + // the request downstream + // Note: if we end up reading the body twice we will run into + // a problem, but as of right + // now there is no good use case for doing this DataBufferUtils.retain(dataBuffer); - //Make a slice for each read so each read has its own read/write indexes - Flux cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount()))); + // Make a slice for each read so each read has its own + // read/write indexes + Flux cachedFlux = Flux.defer(() -> Flux.just( + dataBuffer.slice(0, dataBuffer.readableByteCount()))); - ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) { + ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator( + exchange.getRequest()) { @Override public Flux getBody() { return cachedFlux; } }; - return ServerRequest.create(exchange.mutate().request(mutatedRequest).build(), messageReaders) - .bodyToMono(inClass) - .doOnNext(objectValue -> { - exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue); - exchange.getAttributes().put(CACHED_REQUEST_BODY_KEY, cachedFlux); - }) - .map(objectValue -> config.predicate.test(objectValue)); + return ServerRequest + .create(exchange.mutate().request(mutatedRequest) + .build(), messageReaders) + .bodyToMono(inClass).doOnNext(objectValue -> { + exchange.getAttributes().put( + CACHE_REQUEST_BODY_OBJECT_KEY, + objectValue); + exchange.getAttributes() + .put(CACHED_REQUEST_BODY_KEY, cachedFlux); + }).map(objectValue -> config.predicate + .test(objectValue)); }); } @@ -119,8 +133,11 @@ public class ReadBodyPredicateFactory } public static class Config { + private Class inClass; + private Predicate predicate; + private Map hints; public Class getInClass() { @@ -136,13 +153,13 @@ public class ReadBodyPredicateFactory return predicate; } - public Config setPredicate(Class inClass, Predicate predicate) { - setInClass(inClass); + public Config setPredicate(Predicate predicate) { this.predicate = predicate; return this; } - public Config setPredicate(Predicate predicate) { + public Config setPredicate(Class inClass, Predicate predicate) { + setInClass(inClass); this.predicate = predicate; return this; } @@ -155,5 +172,7 @@ public class ReadBodyPredicateFactory this.hints = hints; return this; } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicateFactory.java index 22dcc96b..2df50502 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicateFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,21 +12,10 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; -import io.netty.handler.ipfilter.IpFilterRuleType; -import io.netty.handler.ipfilter.IpSubnetFilterRule; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.cloud.gateway.support.ipresolver.RemoteAddressResolver; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.server.ServerWebExchange; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; @@ -34,14 +23,28 @@ import java.util.Collections; import java.util.List; import java.util.function.Predicate; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +import io.netty.handler.ipfilter.IpFilterRuleType; +import io.netty.handler.ipfilter.IpSubnetFilterRule; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.gateway.support.ipresolver.RemoteAddressResolver; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.server.ServerWebExchange; + import static org.springframework.cloud.gateway.support.ShortcutConfigurable.ShortcutType.GATHER_LIST; /** * @author Spencer Gibb */ -public class RemoteAddrRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class RemoteAddrRoutePredicateFactory + extends AbstractRoutePredicateFactory { - private static final Log log = LogFactory.getLog(RemoteAddrRoutePredicateFactory.class); + private static final Log log = LogFactory + .getLog(RemoteAddrRoutePredicateFactory.class); public RemoteAddrRoutePredicateFactory() { super(Config.class); @@ -60,24 +63,26 @@ public class RemoteAddrRoutePredicateFactory extends AbstractRoutePredicateFacto @NotNull private List convert(List values) { List sources = new ArrayList<>(); - for (String arg : values) { - addSource(sources, arg); - } + for (String arg : values) { + addSource(sources, arg); + } return sources; } @Override public Predicate apply(Config config) { - List sources = convert(config.sources); + List sources = convert(config.sources); return exchange -> { - InetSocketAddress remoteAddress = config.remoteAddressResolver.resolve(exchange); + InetSocketAddress remoteAddress = config.remoteAddressResolver + .resolve(exchange); if (remoteAddress != null && remoteAddress.getAddress() != null) { String hostAddress = remoteAddress.getAddress().getHostAddress(); String host = exchange.getRequest().getURI().getHost(); if (log.isDebugEnabled() && !hostAddress.equals(host)) { - log.debug("Remote addresses didn't match " + hostAddress + " != " + host); + log.debug("Remote addresses didn't match " + hostAddress + " != " + + host); } for (IpSubnetFilterRule source : sources) { @@ -96,20 +101,23 @@ public class RemoteAddrRoutePredicateFactory extends AbstractRoutePredicateFacto source = source + "/32"; } - String[] ipAddressCidrPrefix = source.split("/",2); + String[] ipAddressCidrPrefix = source.split("/", 2); String ipAddress = ipAddressCidrPrefix[0]; int cidrPrefix = Integer.parseInt(ipAddressCidrPrefix[1]); - sources.add(new IpSubnetFilterRule(ipAddress, cidrPrefix, IpFilterRuleType.ACCEPT)); + sources.add( + new IpSubnetFilterRule(ipAddress, cidrPrefix, IpFilterRuleType.ACCEPT)); } @Validated public static class Config { + @NotEmpty private List sources = new ArrayList<>(); @NotNull - private RemoteAddressResolver remoteAddressResolver = new RemoteAddressResolver(){}; + private RemoteAddressResolver remoteAddressResolver = new RemoteAddressResolver() { + }; public List getSources() { return sources; @@ -125,10 +133,12 @@ public class RemoteAddrRoutePredicateFactory extends AbstractRoutePredicateFacto return this; } - - public Config setRemoteAddressResolver(RemoteAddressResolver remoteAddressResolver) { + public Config setRemoteAddressResolver( + RemoteAddressResolver remoteAddressResolver) { this.remoteAddressResolver = remoteAddressResolver; return this; } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RoutePredicateFactory.java index 6c511337..df47f2d6 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RoutePredicateFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -33,6 +32,10 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.t */ @FunctionalInterface public interface RoutePredicateFactory extends ShortcutConfigurable, Configurable { + + /** + * Pattern key. + */ String PATTERN_KEY = "pattern"; // useful for javadsl @@ -59,7 +62,8 @@ public interface RoutePredicateFactory extends ShortcutConfigurable, Configur throw new UnsupportedOperationException("newConfig() not implemented"); } - default void beforeApply(C config) {} + default void beforeApply(C config) { + } Predicate apply(C config); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactory.java index 4ccd3a48..60fcd449 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -25,6 +24,7 @@ import java.util.function.Predicate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.cloud.gateway.event.WeightDefinedEvent; import org.springframework.cloud.gateway.support.WeightConfig; import org.springframework.context.ApplicationEventPublisher; @@ -37,14 +37,23 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.W /** * @author Spencer Gibb */ -//TODO: make this a generic Choose out of group predicate? -public class WeightRoutePredicateFactory extends AbstractRoutePredicateFactory implements ApplicationEventPublisherAware { +// TODO: make this a generic Choose out of group predicate? +public class WeightRoutePredicateFactory + extends AbstractRoutePredicateFactory + implements ApplicationEventPublisherAware { + + /** + * Weight config group key. + */ + public static final String GROUP_KEY = WeightConfig.CONFIG_PREFIX + ".group"; + + /** + * Weight config weight key. + */ + public static final String WEIGHT_KEY = WeightConfig.CONFIG_PREFIX + ".weight"; private static final Log log = LogFactory.getLog(WeightRoutePredicateFactory.class); - public static final String GROUP_KEY = WeightConfig.CONFIG_PREFIX + ".group"; - public static final String WEIGHT_KEY = WeightConfig.CONFIG_PREFIX + ".weight"; - private ApplicationEventPublisher publisher; public WeightRoutePredicateFactory() { @@ -63,7 +72,7 @@ public class WeightRoutePredicateFactory extends AbstractRoutePredicateFactory { +public class CachingRouteDefinitionLocator + implements RouteDefinitionLocator, ApplicationListener { private final RouteDefinitionLocator delegate; + private final Flux routeDefinitions; + private final Map cache = new HashMap<>(); public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) { @@ -49,7 +51,7 @@ public class CachingRouteDefinitionLocator implements RouteDefinitionLocator, Ap } /** - * Clears the cache of routeDefinisions + * Clears the cache of routeDefinitions. * @return routeDefinitions flux */ public Flux refresh() { @@ -63,7 +65,8 @@ public class CachingRouteDefinitionLocator implements RouteDefinitionLocator, Ap } @Deprecated - /* for testing */ void handleRefresh() { - refresh(); - } + /* for testing */ void handleRefresh() { + refresh(); + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/CachingRouteLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/CachingRouteLocator.java index a7eda34f..a109ccd1 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/CachingRouteLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/CachingRouteLocator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -21,26 +20,30 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.springframework.context.ApplicationListener; import reactor.cache.CacheFlux; import reactor.core.publisher.Flux; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; +import org.springframework.context.ApplicationListener; import org.springframework.core.annotation.AnnotationAwareOrderComparator; /** * @author Spencer Gibb */ -public class CachingRouteLocator implements RouteLocator, ApplicationListener { +public class CachingRouteLocator + implements RouteLocator, ApplicationListener { private final RouteLocator delegate; + private final Flux routes; + private final Map cache = new HashMap<>(); public CachingRouteLocator(RouteLocator delegate) { this.delegate = delegate; routes = CacheFlux.lookup(cache, "routes", Route.class) - .onCacheMissResume(() -> this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE)); + .onCacheMissResume(() -> this.delegate.getRoutes() + .sort(AnnotationAwareOrderComparator.INSTANCE)); } @Override @@ -49,7 +52,7 @@ public class CachingRouteLocator implements RouteLocator, ApplicationListener refresh() { @@ -66,4 +69,5 @@ public class CachingRouteLocator implements RouteLocator, ApplicationListener getRouteDefinitions() { return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/CompositeRouteLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/CompositeRouteLocator.java index c7aef785..36d13c60 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/CompositeRouteLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/CompositeRouteLocator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -34,4 +33,5 @@ public class CompositeRouteLocator implements RouteLocator { public Flux getRoutes() { return this.delegates.flatMap(RouteLocator::getRoutes); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/InMemoryRouteDefinitionRepository.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/InMemoryRouteDefinitionRepository.java index a2e35ee4..1e86d515 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/InMemoryRouteDefinitionRepository.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/InMemoryRouteDefinitionRepository.java @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -20,23 +19,24 @@ package org.springframework.cloud.gateway.route; import java.util.LinkedHashMap; import java.util.Map; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.support.NotFoundException; import static java.util.Collections.synchronizedMap; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - /** * @author Spencer Gibb */ public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository { - private final Map routes = synchronizedMap(new LinkedHashMap()); + private final Map routes = synchronizedMap( + new LinkedHashMap()); @Override public Mono save(Mono route) { - return route.flatMap( r -> { + return route.flatMap(r -> { routes.put(r.getId(), r); return Mono.empty(); }); @@ -49,7 +49,8 @@ public class InMemoryRouteDefinitionRepository implements RouteDefinitionReposit routes.remove(id); return Mono.empty(); } - return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: "+routeId))); + return Mono.defer(() -> Mono.error( + new NotFoundException("RouteDefinition not found: " + routeId))); }); } @@ -57,4 +58,5 @@ public class InMemoryRouteDefinitionRepository implements RouteDefinitionReposit public Flux getRouteDefinitions() { return Flux.fromIterable(routes.values()); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/Route.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/Route.java index 58835691..fca6fa45 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/Route.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/Route.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -51,14 +50,22 @@ public class Route implements Ordered { private final List gatewayFilters; + private Route(String id, URI uri, int order, + AsyncPredicate predicate, + List gatewayFilters) { + this.id = id; + this.uri = uri; + this.order = order; + this.predicate = predicate; + this.gatewayFilters = gatewayFilters; + } + public static Builder builder() { return new Builder(); } public static Builder builder(RouteDefinition routeDefinition) { - return new Builder() - .id(routeDefinition.getId()) - .uri(routeDefinition.getUri()) + return new Builder().id(routeDefinition.getId()).uri(routeDefinition.getUri()) .order(routeDefinition.getOrder()); } @@ -67,21 +74,64 @@ public class Route implements Ordered { } public static AsyncBuilder async(RouteDefinition routeDefinition) { - return new AsyncBuilder() - .id(routeDefinition.getId()) - .uri(routeDefinition.getUri()) - .order(routeDefinition.getOrder()); + return new AsyncBuilder().id(routeDefinition.getId()) + .uri(routeDefinition.getUri()).order(routeDefinition.getOrder()); } - private Route(String id, URI uri, int order, AsyncPredicate predicate, List gatewayFilters) { - this.id = id; - this.uri = uri; - this.order = order; - this.predicate = predicate; - this.gatewayFilters = gatewayFilters; + public String getId() { + return this.id; + } + + public URI getUri() { + return this.uri; + } + + public int getOrder() { + return order; + } + + public AsyncPredicate getPredicate() { + return this.predicate; + } + + public List getFilters() { + return Collections.unmodifiableList(this.gatewayFilters); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Route route = (Route) o; + return Objects.equals(id, route.id) && Objects.equals(uri, route.uri) + && Objects.equals(order, route.order) + && Objects.equals(predicate, route.predicate) + && Objects.equals(gatewayFilters, route.gatewayFilters); + } + + @Override + public int hashCode() { + return Objects.hash(id, uri, predicate, gatewayFilters); + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("Route{"); + sb.append("id='").append(id).append('\''); + sb.append(", uri=").append(uri); + sb.append(", order=").append(order); + sb.append(", predicate=").append(predicate); + sb.append(", gatewayFilters=").append(gatewayFilters); + sb.append('}'); + return sb.toString(); } public abstract static class AbstractBuilder> { + protected String id; protected URI uri; @@ -90,7 +140,8 @@ public class Route implements Ordered { protected List gatewayFilters = new ArrayList<>(); - protected AbstractBuilder() {} + protected AbstractBuilder() { + } protected abstract B getThis(); @@ -115,13 +166,12 @@ public class Route implements Ordered { public B uri(URI uri) { this.uri = uri; String scheme = this.uri.getScheme(); - Assert.hasText(scheme, "The parameter [" + this.uri + "] format is incorrect, scheme can not be empty"); + Assert.hasText(scheme, "The parameter [" + this.uri + + "] format is incorrect, scheme can not be empty"); if (this.uri.getPort() < 0 && scheme.startsWith("http")) { // default known http ports int port = this.uri.getScheme().equals("https") ? 443 : 80; - this.uri = UriComponentsBuilder.fromUri(this.uri) - .port(port) - .build(false) + this.uri = UriComponentsBuilder.fromUri(this.uri).port(port).build(false) .toUri(); } return getThis(); @@ -129,8 +179,6 @@ public class Route implements Ordered { public abstract AsyncPredicate getPredicate(); - - public B replaceFilters(List gatewayFilters) { this.gatewayFilters = gatewayFilters; return getThis(); @@ -156,8 +204,10 @@ public class Route implements Ordered { AsyncPredicate predicate = getPredicate(); Assert.notNull(predicate, "predicate can not be null"); - return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters); + return new Route(this.id, this.uri, this.order, predicate, + this.gatewayFilters); } + } public static class AsyncBuilder extends AbstractBuilder { @@ -200,9 +250,11 @@ public class Route implements Ordered { this.predicate = this.predicate.negate(); return this; } + } public static class Builder extends AbstractBuilder { + protected Predicate predicate; @Override @@ -235,52 +287,4 @@ public class Route implements Ordered { } - public String getId() { - return this.id; - } - - public URI getUri() { - return this.uri; - } - - public int getOrder() { - return order; - } - - public AsyncPredicate getPredicate() { - return this.predicate; - } - - public List getFilters() { - return Collections.unmodifiableList(this.gatewayFilters); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Route route = (Route) o; - return Objects.equals(id, route.id) && - Objects.equals(uri, route.uri) && - Objects.equals(order, route.order) && - Objects.equals(predicate, route.predicate) && - Objects.equals(gatewayFilters, route.gatewayFilters); - } - - @Override - public int hashCode() { - return Objects.hash(id, uri, predicate, gatewayFilters); - } - - @Override - public String toString() { - final StringBuffer sb = new StringBuffer("Route{"); - sb.append("id='").append(id).append('\''); - sb.append(", uri=").append(uri); - sb.append(", order=").append(order); - sb.append(", predicate=").append(predicate); - sb.append(", gatewayFilters=").append(gatewayFilters); - sb.append('}'); - return sb.toString(); - } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinition.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinition.java index f34c6b1a..a68a9b5f 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinition.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,25 +12,25 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; -import javax.validation.constraints.NotEmpty; -import org.springframework.cloud.gateway.filter.FilterDefinition; -import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; -import org.springframework.validation.annotation.Validated; - -import javax.validation.Valid; -import javax.validation.ValidationException; -import javax.validation.constraints.NotNull; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.UUID; +import javax.validation.Valid; +import javax.validation.ValidationException; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; +import org.springframework.validation.annotation.Validated; + import static org.springframework.util.StringUtils.tokenizeToStringArray; /** @@ -38,6 +38,7 @@ import static org.springframework.util.StringUtils.tokenizeToStringArray; */ @Validated public class RouteDefinition { + @NotEmpty private String id = UUID.randomUUID().toString(); @@ -53,22 +54,23 @@ public class RouteDefinition { private int order = 0; - public RouteDefinition() {} + public RouteDefinition() { + } public RouteDefinition(String text) { int eqIdx = text.indexOf('='); if (eqIdx <= 0) { - throw new ValidationException("Unable to parse RouteDefinition text '" + text + "'" + - ", must be of the form name=value"); + throw new ValidationException("Unable to parse RouteDefinition text '" + text + + "'" + ", must be of the form name=value"); } setId(text.substring(0, eqIdx)); - String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ","); + String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ","); setUri(URI.create(args[0])); - for (int i=1; i < args.length; i++) { + for (int i = 1; i < args.length; i++) { this.predicates.add(new PredicateDefinition(args[i])); } } @@ -115,13 +117,17 @@ public class RouteDefinition { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } RouteDefinition routeDefinition = (RouteDefinition) o; - return Objects.equals(id, routeDefinition.id) && - Objects.equals(predicates, routeDefinition.predicates) && - Objects.equals(order, routeDefinition.order) && - Objects.equals(uri, routeDefinition.uri); + return Objects.equals(id, routeDefinition.id) + && Objects.equals(predicates, routeDefinition.predicates) + && Objects.equals(order, routeDefinition.order) + && Objects.equals(uri, routeDefinition.uri); } @Override @@ -131,12 +137,8 @@ public class RouteDefinition { @Override public String toString() { - return "RouteDefinition{" + - "id='" + id + '\'' + - ", predicates=" + predicates + - ", filters=" + filters + - ", uri=" + uri + - ", order=" + order + - '}'; + return "RouteDefinition{" + "id='" + id + '\'' + ", predicates=" + predicates + + ", filters=" + filters + ", uri=" + uri + ", order=" + order + '}'; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionLocator.java index e27dacd6..64035a3c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionLocator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -25,4 +24,5 @@ import reactor.core.publisher.Flux; public interface RouteDefinitionLocator { Flux getRouteDefinitions(); + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionRepository.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionRepository.java index 16b6cb3c..57e8c5fc 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionRepository.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -20,5 +19,7 @@ package org.springframework.cloud.gateway.route; /** * @author Spencer Gibb */ -public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter { +public interface RouteDefinitionRepository + extends RouteDefinitionLocator, RouteDefinitionWriter { + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionRouteLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionRouteLocator.java index 368a9b4d..6a96183c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionRouteLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionRouteLocator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -26,6 +25,8 @@ import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import reactor.core.publisher.Flux; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -50,40 +51,52 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.validation.Validator; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Flux; - /** - * {@link RouteLocator} that loads routes from a {@link RouteDefinitionLocator} + * {@link RouteLocator} that loads routes from a {@link RouteDefinitionLocator}. + * * @author Spencer Gibb */ -public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware { +public class RouteDefinitionRouteLocator + implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware { + + /** + * Default filters name. + */ + public static final String DEFAULT_FILTERS = "defaultFilters"; + protected final Log logger = LogFactory.getLog(getClass()); - public static final String DEFAULT_FILTERS = "defaultFilters"; private final RouteDefinitionLocator routeDefinitionLocator; - private final ConversionService conversionService; - private final Map predicates = new LinkedHashMap<>(); - private final Map gatewayFilterFactories = new HashMap<>(); - private final GatewayProperties gatewayProperties; - private final SpelExpressionParser parser = new SpelExpressionParser(); - private BeanFactory beanFactory; - private ApplicationEventPublisher publisher; - public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, - List predicates, - List gatewayFilterFactories, - GatewayProperties gatewayProperties, - ConversionService conversionService) { - this.routeDefinitionLocator = routeDefinitionLocator; - this.conversionService = conversionService; - initFactories(predicates); - gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory)); - this.gatewayProperties = gatewayProperties; - } + private final ConversionService conversionService; + + private final Map predicates = new LinkedHashMap<>(); + + private final Map gatewayFilterFactories = new HashMap<>(); + + private final GatewayProperties gatewayProperties; + + private final SpelExpressionParser parser = new SpelExpressionParser(); + + private BeanFactory beanFactory; + + private ApplicationEventPublisher publisher; @Autowired private Validator validator; + public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, + List predicates, + List gatewayFilterFactories, + GatewayProperties gatewayProperties, ConversionService conversionService) { + this.routeDefinitionLocator = routeDefinitionLocator; + this.conversionService = conversionService; + initFactories(predicates); + gatewayFilterFactories.forEach( + factory -> this.gatewayFilterFactories.put(factory.name(), factory)); + this.gatewayProperties = gatewayProperties; + } + @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; @@ -98,7 +111,7 @@ public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAwa predicates.forEach(factory -> { String key = factory.name(); if (this.predicates.containsKey(key)) { - this.logger.warn("A RoutePredicateFactory named "+ key + this.logger.warn("A RoutePredicateFactory named " + key + " already exists, class: " + this.predicates.get(key) + ". It will be overwritten."); } @@ -111,9 +124,8 @@ public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAwa @Override public Flux getRoutes() { - return this.routeDefinitionLocator.getRouteDefinitions() - .map(this::convertToRoute) - //TODO: error handling + return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute) + // TODO: error handling .map(route -> { if (logger.isDebugEnabled()) { logger.debug("RouteDefinition matched: " + route.getId()); @@ -121,50 +133,52 @@ public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAwa return route; }); - - /* TODO: trace logging - if (logger.isTraceEnabled()) { - logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); - }*/ + /* + * TODO: trace logging if (logger.isTraceEnabled()) { + * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); } + */ } private Route convertToRoute(RouteDefinition routeDefinition) { AsyncPredicate predicate = combinePredicates(routeDefinition); List gatewayFilters = getFilters(routeDefinition); - return Route.async(routeDefinition) - .asyncPredicate(predicate) - .replaceFilters(gatewayFilters) - .build(); + return Route.async(routeDefinition).asyncPredicate(predicate) + .replaceFilters(gatewayFilters).build(); } @SuppressWarnings("unchecked") - private List loadGatewayFilters(String id, List filterDefinitions) { - List filters = filterDefinitions.stream() - .map(definition -> { - GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName()); - if (factory == null) { - throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName()); - } - Map args = definition.getArgs(); - if (logger.isDebugEnabled()) { - logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName()); - } + private List loadGatewayFilters(String id, + List filterDefinitions) { + List filters = filterDefinitions.stream().map(definition -> { + GatewayFilterFactory factory = this.gatewayFilterFactories + .get(definition.getName()); + if (factory == null) { + throw new IllegalArgumentException( + "Unable to find GatewayFilterFactory with name " + + definition.getName()); + } + Map args = definition.getArgs(); + if (logger.isDebugEnabled()) { + logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + + definition.getName()); + } - Map properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory); + Map properties = factory.shortcutType().normalize(args, + factory, this.parser, this.beanFactory); - Object configuration = factory.newConfig(); + Object configuration = factory.newConfig(); - ConfigurationUtils.bind(configuration, properties, factory.shortcutFieldPrefix(), - definition.getName(), validator, conversionService); + ConfigurationUtils.bind(configuration, properties, + factory.shortcutFieldPrefix(), definition.getName(), validator, + conversionService); - GatewayFilter gatewayFilter = factory.apply(configuration); - if (this.publisher != null) { - this.publisher.publishEvent(new FilterArgsEvent(this, id, properties)); - } - return gatewayFilter; - }) - .collect(Collectors.toList()); + GatewayFilter gatewayFilter = factory.apply(configuration); + if (this.publisher != null) { + this.publisher.publishEvent(new FilterArgsEvent(this, id, properties)); + } + return gatewayFilter; + }).collect(Collectors.toList()); ArrayList ordered = new ArrayList<>(filters.size()); for (int i = 0; i < filters.size(); i++) { @@ -183,26 +197,31 @@ public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAwa private List getFilters(RouteDefinition routeDefinition) { List filters = new ArrayList<>(); - //TODO: support option to apply defaults after route specific filters? + // TODO: support option to apply defaults after route specific filters? if (!this.gatewayProperties.getDefaultFilters().isEmpty()) { filters.addAll(loadGatewayFilters(DEFAULT_FILTERS, this.gatewayProperties.getDefaultFilters())); } if (!routeDefinition.getFilters().isEmpty()) { - filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters())); + filters.addAll(loadGatewayFilters(routeDefinition.getId(), + routeDefinition.getFilters())); } AnnotationAwareOrderComparator.sort(filters); return filters; } - private AsyncPredicate combinePredicates(RouteDefinition routeDefinition) { + private AsyncPredicate combinePredicates( + RouteDefinition routeDefinition) { List predicates = routeDefinition.getPredicates(); - AsyncPredicate predicate = lookup(routeDefinition, predicates.get(0)); + AsyncPredicate predicate = lookup(routeDefinition, + predicates.get(0)); - for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) { - AsyncPredicate found = lookup(routeDefinition, andPredicate); + for (PredicateDefinition andPredicate : predicates.subList(1, + predicates.size())) { + AsyncPredicate found = lookup(routeDefinition, + andPredicate); predicate = predicate.and(found); } @@ -210,24 +229,30 @@ public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAwa } @SuppressWarnings("unchecked") - private AsyncPredicate lookup(RouteDefinition route, PredicateDefinition predicate) { + private AsyncPredicate lookup(RouteDefinition route, + PredicateDefinition predicate) { RoutePredicateFactory factory = this.predicates.get(predicate.getName()); if (factory == null) { - throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName()); + throw new IllegalArgumentException( + "Unable to find RoutePredicateFactory with name " + + predicate.getName()); } Map args = predicate.getArgs(); if (logger.isDebugEnabled()) { - logger.debug("RouteDefinition " + route.getId() + " applying " - + args + " to " + predicate.getName()); + logger.debug("RouteDefinition " + route.getId() + " applying " + args + " to " + + predicate.getName()); } - Map properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory); - Object config = factory.newConfig(); - ConfigurationUtils.bind(config, properties, factory.shortcutFieldPrefix(), predicate.getName(), - validator, conversionService); - if (this.publisher != null) { - this.publisher.publishEvent(new PredicateArgsEvent(this, route.getId(), properties)); - } - return factory.applyAsync(config); + Map properties = factory.shortcutType().normalize(args, factory, + this.parser, this.beanFactory); + Object config = factory.newConfig(); + ConfigurationUtils.bind(config, properties, factory.shortcutFieldPrefix(), + predicate.getName(), validator, conversionService); + if (this.publisher != null) { + this.publisher.publishEvent( + new PredicateArgsEvent(this, route.getId(), properties)); + } + return factory.applyAsync(config); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionWriter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionWriter.java index 15134489..3df42c17 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionWriter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteDefinitionWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -27,4 +26,5 @@ public interface RouteDefinitionWriter { Mono save(Mono route); Mono delete(Mono routeId); + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteLocator.java index a17936c4..f11df6c1 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteLocator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -22,8 +21,9 @@ import reactor.core.publisher.Flux; /** * @author Spencer Gibb */ -//TODO: rename to Routes? +// TODO: rename to Routes? public interface RouteLocator { Flux getRoutes(); + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteRefreshListener.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteRefreshListener.java index 5877992d..cfddd83f 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteRefreshListener.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteRefreshListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -31,11 +30,11 @@ import org.springframework.util.Assert; // see ZuulDiscoveryRefreshListener // TODO: make abstract class in commons? -public class RouteRefreshListener - implements ApplicationListener { +public class RouteRefreshListener implements ApplicationListener { + + private final ApplicationEventPublisher publisher; private HeartbeatMonitor monitor = new HeartbeatMonitor(); - private final ApplicationEventPublisher publisher; public RouteRefreshListener(ApplicationEventPublisher publisher) { Assert.notNull(publisher, "publisher may not be null"); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/BooleanSpec.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/BooleanSpec.java index cdde8ce8..7766d0ba 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/BooleanSpec.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/BooleanSpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,11 +33,10 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.t */ public class BooleanSpec extends UriSpec { - enum Operator { AND, OR, NEGATE } - final AsyncPredicate predicate; - public BooleanSpec(Route.AsyncBuilder routeBuilder, RouteLocatorBuilder.Builder builder) { + public BooleanSpec(Route.AsyncBuilder routeBuilder, + RouteLocatorBuilder.Builder builder) { super(routeBuilder, builder); // save current predicate useful in kotlin dsl predicate = routeBuilder.getPredicate(); @@ -70,41 +69,50 @@ public class BooleanSpec extends UriSpec { /** * Add filters to the route definition. - * @param fn A {@link Function} that takes in a {@link GatewayFilterSpec} and returns a {@link UriSpec} + * @param fn A {@link Function} that takes in a {@link GatewayFilterSpec} and returns + * a {@link UriSpec} * @return a {@link UriSpec} */ public UriSpec filters(Function fn) { return fn.apply(new GatewayFilterSpec(routeBuilder, builder)); } + enum Operator { + + AND, OR, NEGATE + + } + public static class BooleanOpSpec extends PredicateSpec { private Operator operator; - BooleanOpSpec(Route.AsyncBuilder routeBuilder, RouteLocatorBuilder.Builder builder, Operator operator) { + BooleanOpSpec(Route.AsyncBuilder routeBuilder, + RouteLocatorBuilder.Builder builder, Operator operator) { super(routeBuilder, builder); Assert.notNull(operator, "operator may not be null"); this.operator = operator; } public BooleanSpec predicate(Predicate predicate) { - return asyncPredicate(toAsyncPredicate(predicate)); + return asyncPredicate(toAsyncPredicate(predicate)); } @Override public BooleanSpec asyncPredicate(AsyncPredicate predicate) { switch (this.operator) { - case AND: - this.routeBuilder.and(predicate); - break; - case OR: - this.routeBuilder.or(predicate); - break; - case NEGATE: - this.routeBuilder.negate(); + case AND: + this.routeBuilder.and(predicate); + break; + case OR: + this.routeBuilder.or(predicate); + break; + case NEGATE: + this.routeBuilder.negate(); } return new BooleanSpec(this.routeBuilder, this.builder); } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java index 7777c173..fa722d18 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,7 +75,8 @@ public class GatewayFilterSpec extends UriSpec { private static final Log log = LogFactory.getLog(GatewayFilterSpec.class); - public GatewayFilterSpec(Route.AsyncBuilder routeBuilder, RouteLocatorBuilder.Builder builder) { + public GatewayFilterSpec(Route.AsyncBuilder routeBuilder, + RouteLocatorBuilder.Builder builder) { super(routeBuilder, builder); } @@ -101,8 +102,8 @@ public class GatewayFilterSpec extends UriSpec { public GatewayFilterSpec filter(GatewayFilter gatewayFilter, int order) { if (gatewayFilter instanceof Ordered) { this.routeBuilder.filter(gatewayFilter); - log.warn("GatewayFilter already implements ordered "+gatewayFilter.getClass() - + "ignoring order parameter: "+order); + log.warn("GatewayFilter already implements ordered " + + gatewayFilter.getClass() + "ignoring order parameter: " + order); return this; } this.routeBuilder.filter(new OrderedGatewayFilter(gatewayFilter, order)); @@ -115,20 +116,21 @@ public class GatewayFilterSpec extends UriSpec { * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec filters(GatewayFilter... gatewayFilters) { - List filters = transformToOrderedFilters(Stream.of(gatewayFilters)); + List filters = transformToOrderedFilters( + Stream.of(gatewayFilters)); this.routeBuilder.filters(filters); return this; } public List transformToOrderedFilters(Stream stream) { - return stream - .map(filter -> { - if (filter instanceof Ordered) { - return filter; - } else { - return new OrderedGatewayFilter(filter, 0); - } - }).collect(Collectors.toList()); + return stream.map(filter -> { + if (filter instanceof Ordered) { + return filter; + } + else { + return new OrderedGatewayFilter(filter, 0); + } + }).collect(Collectors.toList()); } /** @@ -176,26 +178,29 @@ public class GatewayFilterSpec extends UriSpec { } /** - * Wraps the route in a Hystrix command. - * Depends on @{code org.springframework.cloud::spring-cloud-starter-netflix-hystrix} being on the classpath, - * {@see http://cloud.spring.io/spring-cloud-netflix/} - * @param configConsumer a {@link Consumer} which provides configuration for the Hystrix command + * Wraps the route in a Hystrix command. Depends on @{code + * org.springframework.cloud::spring-cloud-starter-netflix-hystrix} being on the + * classpath, {@see http://cloud.spring.io/spring-cloud-netflix/} + * @param configConsumer a {@link Consumer} which provides configuration for the + * Hystrix command * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - public GatewayFilterSpec hystrix(Consumer configConsumer) { + public GatewayFilterSpec hystrix( + Consumer configConsumer) { HystrixGatewayFilterFactory factory; try { factory = getBean(HystrixGatewayFilterFactory.class); } catch (NoSuchBeanDefinitionException e) { - throw new NoSuchBeanDefinitionException(HystrixGatewayFilterFactory.class, "This is probably because Hystrix is missing from the classpath, which can be resolved by adding dependency on 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'"); + throw new NoSuchBeanDefinitionException(HystrixGatewayFilterFactory.class, + "This is probably because Hystrix is missing from the classpath, which can be resolved by adding dependency on 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'"); } return filter(factory.apply(this.routeBuilder.getId(), configConsumer)); } /** - * A filter that can be used to modify the request body. - * This filter is BETA and may be subject to change in a future release. + * A filter that can be used to modify the request body. This filter is BETA and may + * be subject to change in a future release. * @param inClass the class to convert the incoming request body to * @param outClass the class the Gateway will add to the request before it is routed * @param rewriteFunction the {@link RewriteFunction} that transforms the request body @@ -203,15 +208,16 @@ public class GatewayFilterSpec extends UriSpec { * @param the new request body class * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - //TODO: setup custom spec - public GatewayFilterSpec modifyRequestBody(Class inClass, Class outClass, RewriteFunction rewriteFunction) { + // TODO: setup custom spec + public GatewayFilterSpec modifyRequestBody(Class inClass, Class outClass, + RewriteFunction rewriteFunction) { return filter(getBean(ModifyRequestBodyGatewayFilterFactory.class) .apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction))); } /** - * A filter that can be used to modify the request body. - * This filter is BETA and may be subject to change in a future release. + * A filter that can be used to modify the request body. This filter is BETA and may + * be subject to change in a future release. * @param inClass the class to convert the incoming request body to * @param outClass the class the Gateway will add to the request before it is routed * @param newContentType the new Content-Type header to be sent @@ -220,44 +226,56 @@ public class GatewayFilterSpec extends UriSpec { * @param the new request body class * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - public GatewayFilterSpec modifyRequestBody(Class inClass, Class outClass, String newContentType, RewriteFunction rewriteFunction) { + public GatewayFilterSpec modifyRequestBody(Class inClass, Class outClass, + String newContentType, RewriteFunction rewriteFunction) { return filter(getBean(ModifyRequestBodyGatewayFilterFactory.class) - .apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction).setContentType(newContentType))); + .apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction) + .setContentType(newContentType))); } + /** - * A filter that can be used to modify the response body - * This filter is BETA and may be subject to change in a future release. + * A filter that can be used to modify the response body This filter is BETA and may + * be subject to change in a future release. * @param inClass the class to conver the response body to - * @param outClass the class the Gateway will add to the response before it is returned to the client - * @param rewriteFunction the {@link RewriteFunction} that transforms the response body + * @param outClass the class the Gateway will add to the response before it is + * returned to the client + * @param rewriteFunction the {@link RewriteFunction} that transforms the response + * body * @param the original response body class * @param the new response body class * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - public GatewayFilterSpec modifyResponseBody(Class inClass, Class outClass, RewriteFunction rewriteFunction) { + public GatewayFilterSpec modifyResponseBody(Class inClass, + Class outClass, RewriteFunction rewriteFunction) { return filter(getBean(ModifyResponseBodyGatewayFilterFactory.class) .apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction))); } /** - * A filter that can be used to modify the response body - * This filter is BETA and may be subject to change in a future release. + * A filter that can be used to modify the response body This filter is BETA and may + * be subject to change in a future release. * @param inClass the class to conver the response body to - * @param outClass the class the Gateway will add to the response before it is returned to the client + * @param outClass the class the Gateway will add to the response before it is + * returned to the client * @param newContentType the new Content-Type header to be returned - * @param rewriteFunction the {@link RewriteFunction} that transforms the response body + * @param rewriteFunction the {@link RewriteFunction} that transforms the response + * body * @param the original response body class * @param the new response body class * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - //TODO: setup custom spec - public GatewayFilterSpec modifyResponseBody(Class inClass, Class outClass, String newContentType, RewriteFunction rewriteFunction) { + // TODO: setup custom spec + public GatewayFilterSpec modifyResponseBody(Class inClass, + Class outClass, String newContentType, + RewriteFunction rewriteFunction) { return filter(getBean(ModifyResponseBodyGatewayFilterFactory.class) - .apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction).setNewContentType(newContentType))); + .apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction) + .setNewContentType(newContentType))); } /** - * A filter that can be used to add a prefix to the path of a request before it is routed by the Gateway. + * A filter that can be used to add a prefix to the path of a request before it is + * routed by the Gateway. * @param prefix the prefix to add to the path * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ @@ -267,7 +285,8 @@ public class GatewayFilterSpec extends UriSpec { } /** - * A filter that will preserve the host header of the request on the outgoing request from the Gateway. + * A filter that will preserve the host header of the request on the outgoing request + * from the Gateway. * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec preserveHostHeader() { @@ -277,7 +296,8 @@ public class GatewayFilterSpec extends UriSpec { /** * A filter that will return a redirect response back to the client. * @param status an HTTP status code, should be a {@code 300} series redirect - * @param url the URL to redirect to. This URL will be set in the {@code location} header + * @param url the URL to redirect to. This URL will be set in the {@code location} + * header * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec redirect(int status, URI url) { @@ -287,7 +307,8 @@ public class GatewayFilterSpec extends UriSpec { /** * A filter that will return a redirect response back to the client. * @param status an HTTP status code, should be a {@code 300} series redirect - * @param url the URL to redirect to. This URL will be set in the {@code location} header + * @param url the URL to redirect to. This URL will be set in the {@code location} + * header * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec redirect(int status, String url) { @@ -297,7 +318,8 @@ public class GatewayFilterSpec extends UriSpec { /** * A filter that will return a redirect response back to the client. * @param status an HTTP status code, should be a {@code 300} series redirect - * @param url the URL to redirect to. This URL will be set in the {@code location} header + * @param url the URL to redirect to. This URL will be set in the {@code location} + * header * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec redirect(String status, URI url) { @@ -307,7 +329,8 @@ public class GatewayFilterSpec extends UriSpec { /** * A filter that will return a redirect response back to the client. * @param status an HTTP status code, should be a {@code 300} series redirect - * @param url the URL to redirect to. This URL will be set in the {@code location} header + * @param url the URL to redirect to. This URL will be set in the {@code location} + * header * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec redirect(String status, String url) { @@ -317,19 +340,23 @@ public class GatewayFilterSpec extends UriSpec { /** * A filter that will return a redirect response back to the client. * @param status an HTTP status code, should be a {@code 300} series redirect - * @param url the URL to redirect to. This URL will be set in the {@code location} header + * @param url the URL to redirect to. This URL will be set in the {@code location} + * header * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec redirect(HttpStatus status, URL url) { try { - return filter(getBean(RedirectToGatewayFilterFactory.class).apply(status, url.toURI())); - } catch (URISyntaxException e) { + return filter(getBean(RedirectToGatewayFilterFactory.class).apply(status, + url.toURI())); + } + catch (URISyntaxException e) { throw new IllegalArgumentException("Invalid URL", e); } } /** - * A filter that will remove a request header before the request is routed by the Gateway. + * A filter that will remove a request header before the request is routed by the + * Gateway. * @param headerName the name of the header to remove * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ @@ -339,7 +366,8 @@ public class GatewayFilterSpec extends UriSpec { } /** - * A filter that will remove a response header before the Gateway returns the response to the client. + * A filter that will remove a response header before the Gateway returns the response + * to the client. * @param headerName the name of the header to remove * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ @@ -350,47 +378,23 @@ public class GatewayFilterSpec extends UriSpec { /** * A filter that will set up a request rate limiter for a route. - * @param configConsumer a {@link Consumer} that will return configuration for the rate limiter + * @param configConsumer a {@link Consumer} that will return configuration for the + * rate limiter * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - public GatewayFilterSpec requestRateLimiter(Consumer configConsumer) { - return filter(getBean(RequestRateLimiterGatewayFilterFactory.class).apply(configConsumer)); + public GatewayFilterSpec requestRateLimiter( + Consumer configConsumer) { + return filter(getBean(RequestRateLimiterGatewayFilterFactory.class) + .apply(configConsumer)); } /** * A filter that will set up a request rate limiter for a route. * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - public RequestRateLimiterSpec requestRateLimiter() { - return new RequestRateLimiterSpec(getBean(RequestRateLimiterGatewayFilterFactory.class)); - } - - public class RequestRateLimiterSpec { - private final RequestRateLimiterGatewayFilterFactory filter; - - public RequestRateLimiterSpec(RequestRateLimiterGatewayFilterFactory filter) { - this.filter = filter; - } - - public > RequestRateLimiterSpec rateLimiter(Class rateLimiterType, - Consumer configConsumer) { - R rateLimiter = getBean(rateLimiterType); - C config = rateLimiter.newConfig(); - configConsumer.accept(config); - rateLimiter.getConfig().put(routeBuilder.getId(), config); - return this; - } - - public GatewayFilterSpec configure(Consumer configConsumer) { - filter(this.filter.apply(configConsumer)); - return GatewayFilterSpec.this; - } - - // useful when nothing to configure - public GatewayFilterSpec and() { - return configure(config -> {}); - } - + public RequestRateLimiterSpec requestRateLimiter() { + return new RequestRateLimiterSpec( + getBean(RequestRateLimiterGatewayFilterFactory.class)); } /** @@ -405,8 +409,8 @@ public class GatewayFilterSpec extends UriSpec { } /** - * A filter that will retry failed requests. - * By default {@code 5xx} errors and {@code GET}s are retryable. + * A filter that will retry failed requests. By default {@code 5xx} errors and + * {@code GET}s are retryable. * @param retries max number of retries * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ @@ -417,11 +421,13 @@ public class GatewayFilterSpec extends UriSpec { /** * A filter that will retry failed requests. - * @param retryConsumer a {@link Consumer} which returns a {@link org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory.RetryConfig} - * to configure the retry functionality + * @param retryConsumer a {@link Consumer} which returns a + * {@link org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory.RetryConfig} + * to configure the retry functionality * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - public GatewayFilterSpec retry(Consumer retryConsumer) { + public GatewayFilterSpec retry( + Consumer retryConsumer) { return filter(getBean(RetryGatewayFilterFactory.class).apply(retryConsumer)); } @@ -431,24 +437,27 @@ public class GatewayFilterSpec extends UriSpec { * @param retry a {@link Retry} * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - public GatewayFilterSpec retry(Repeat repeat, Retry retry) { + public GatewayFilterSpec retry(Repeat repeat, + Retry retry) { return filter(getBean(RetryGatewayFilterFactory.class).apply(repeat, retry)); } /** * A filter that adds a number of headers to the response at the reccomendation from - * this blog post. + * this blog + * post. * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ @SuppressWarnings("unchecked") public GatewayFilterSpec secureHeaders() { - return filter(getBean(SecureHeadersGatewayFilterFactory.class).apply(c -> {})); + return filter(getBean(SecureHeadersGatewayFilterFactory.class).apply(c -> { + })); } /** * A filter that sets the path of the request before it is routed by the Gateway. - * @param template the path to set on the request, allows multiple matching segments using URI templates from - * Spring Framework + * @param template the path to set on the request, allows multiple matching segments + * using URI templates from Spring Framework * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec setPath(String template) { @@ -468,7 +477,8 @@ public class GatewayFilterSpec extends UriSpec { } /** - * A filter that sets a header on the response before it is returned to the client by the Gateway. + * A filter that sets a header on the response before it is returned to the client by + * the Gateway. * @param headerName the header name * @param headerValue the value of the header * @return a {@link GatewayFilterSpec} that can be used to apply additional filters @@ -479,19 +489,22 @@ public class GatewayFilterSpec extends UriSpec { } /** - * A filter that rewrites a header value on the response before it is returned to the client by the Gateway. + * A filter that rewrites a header value on the response before it is returned to the + * client by the Gateway. * @param headerName the header name * @param regex a Java regular expression to match the path against * @param replacement the replacement for the path * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - public GatewayFilterSpec rewriteResponseHeader(String headerName, String regex, String replacement) { - return filter(getBean(RewriteResponseHeaderGatewayFilterFactory.class) - .apply(c -> c.setReplacement(replacement).setRegexp(regex).setName(headerName))); + public GatewayFilterSpec rewriteResponseHeader(String headerName, String regex, + String replacement) { + return filter(getBean(RewriteResponseHeaderGatewayFilterFactory.class).apply( + c -> c.setReplacement(replacement).setRegexp(regex).setName(headerName))); } /** - * A filter that sets the status on the response before it is returned to the client by the Gateway. + * A filter that sets the status on the response before it is returned to the client + * by the Gateway. * @param status the status to set on the response * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ @@ -500,7 +513,8 @@ public class GatewayFilterSpec extends UriSpec { } /** - * A filter that sets the status on the response before it is returned to the client by the Gateway. + * A filter that sets the status on the response before it is returned to the client + * by the Gateway. * @param status the status to set on the response * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ @@ -509,7 +523,8 @@ public class GatewayFilterSpec extends UriSpec { } /** - * A filter that sets the status on the response before it is returned to the client by the Gateway. + * A filter that sets the status on the response before it is returned to the client + * by the Gateway. * @param status the status to set on the response * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ @@ -519,16 +534,20 @@ public class GatewayFilterSpec extends UriSpec { } /** - * A filter which forces a {@code WebSession::save} operation before forwarding the call downstream. This is of - * particular use when using something like Spring Session - * with a lazy data store and need to ensure the session state has been saved before making the forwarded call. - * If you are integrating Spring Security with - * Spring Session, and want to ensure security details have been forwarded to the remote process, this is critical. + * A filter which forces a {@code WebSession::save} operation before forwarding the + * call downstream. This is of particular use when using something like + * Spring Session with a lazy + * data store and need to ensure the session state has been saved before making the + * forwarded call. If you are integrating + * Spring Security with + * Spring Session, and want to ensure security details have been forwarded to the + * remote process, this is critical. * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ @SuppressWarnings("unchecked") public GatewayFilterSpec saveSession() { - return filter(getBean(SaveSessionGatewayFilterFactory.class).apply(c -> {})); + return filter(getBean(SaveSessionGatewayFilterFactory.class).apply(c -> { + })); } /** @@ -542,8 +561,8 @@ public class GatewayFilterSpec extends UriSpec { } /** - * A filter which changes the URI the request will be routed to by the Gateway by pulling it from a header on the - * request. + * A filter which changes the URI the request will be routed to by the Gateway by + * pulling it from a header on the request. * @param headerName the header name containing the URI * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ @@ -554,8 +573,8 @@ public class GatewayFilterSpec extends UriSpec { /** * A filter which change the URI the request will be routed to by the Gateway. - * @param determineRequestUri a {@link Function} which takes a {@link ServerWebExchange} and returns a URI to - * route the request to + * @param determineRequestUri a {@link Function} which takes a + * {@link ServerWebExchange} and returns a URI to route the request to * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec changeRequestUri( @@ -571,40 +590,42 @@ public class GatewayFilterSpec extends UriSpec { })); } - /** * A filter that sets the maximum permissible size of a Request. * @param size the maximum size of a request * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ public GatewayFilterSpec setRequestSize(Long size) { - return filter(getBean(RequestSizeGatewayFilterFactory.class).apply(c -> c.setMaxSize(size))); + return filter(getBean(RequestSizeGatewayFilterFactory.class) + .apply(c -> c.setMaxSize(size))); } /** - * Adds hystrix execution exception headers to fallback request. - * Depends on @{code org.springframework.cloud::spring-cloud-starter-netflix-hystrix} being on the classpath, - * {@see http://cloud.spring.io/spring-cloud-netflix/} - * - * @param config a {@link FallbackHeadersGatewayFilterFactory.Config} which provides the header names. - * If header names arguments are not provided, default values are used. + * Adds hystrix execution exception headers to fallback request. Depends on @{code + * org.springframework.cloud::spring-cloud-starter-netflix-hystrix} being on the + * classpath, {@see http://cloud.spring.io/spring-cloud-netflix/} + * @param config a {@link FallbackHeadersGatewayFilterFactory.Config} which provides + * the header names. If header names arguments are not provided, default values are + * used. * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - public GatewayFilterSpec fallbackHeaders(FallbackHeadersGatewayFilterFactory.Config config) { + public GatewayFilterSpec fallbackHeaders( + FallbackHeadersGatewayFilterFactory.Config config) { FallbackHeadersGatewayFilterFactory factory = getFallbackHeadersGatewayFilterFactory(); return filter(factory.apply(config)); } /** - * Adds hystrix execution exception headers to fallback request. - * Depends on @{code org.springframework.cloud::spring-cloud-starter-netflix-hystrix} being on the classpath, - * {@see http://cloud.spring.io/spring-cloud-netflix/} - * - * @param configConsumer a {@link Consumer} which can be used to set up the names of the headers in the config. - * If header names arguments are not provided, default values are used. + * Adds hystrix execution exception headers to fallback request. Depends on @{code + * org.springframework.cloud::spring-cloud-starter-netflix-hystrix} being on the + * classpath, {@see http://cloud.spring.io/spring-cloud-netflix/} + * @param configConsumer a {@link Consumer} which can be used to set up the names of + * the headers in the config. If header names arguments are not provided, default + * values are used. * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ - public GatewayFilterSpec fallbackHeaders(Consumer configConsumer) { + public GatewayFilterSpec fallbackHeaders( + Consumer configConsumer) { FallbackHeadersGatewayFilterFactory factory = getFallbackHeadersGatewayFilterFactory(); return filter(factory.apply(configConsumer)); } @@ -613,11 +634,44 @@ public class GatewayFilterSpec extends UriSpec { FallbackHeadersGatewayFilterFactory factory; try { factory = getBean(FallbackHeadersGatewayFilterFactory.class); - } catch (NoSuchBeanDefinitionException e) { - throw new NoSuchBeanDefinitionException(FallbackHeadersGatewayFilterFactory.class, + } + catch (NoSuchBeanDefinitionException e) { + throw new NoSuchBeanDefinitionException( + FallbackHeadersGatewayFilterFactory.class, "This is probably because Hystrix is missing from the classpath, which can be resolved by adding dependency on 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'"); } return factory; } + public class RequestRateLimiterSpec { + + private final RequestRateLimiterGatewayFilterFactory filter; + + public RequestRateLimiterSpec(RequestRateLimiterGatewayFilterFactory filter) { + this.filter = filter; + } + + public > RequestRateLimiterSpec rateLimiter( + Class rateLimiterType, Consumer configConsumer) { + R rateLimiter = getBean(rateLimiterType); + C config = rateLimiter.newConfig(); + configConsumer.accept(config); + rateLimiter.getConfig().put(routeBuilder.getId(), config); + return this; + } + + public GatewayFilterSpec configure( + Consumer configConsumer) { + filter(this.filter.apply(configConsumer)); + return GatewayFilterSpec.this; + } + + // useful when nothing to configure + public GatewayFilterSpec and() { + return configure(config -> { + }); + } + + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java index 28dac2e5..fa88a529 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,7 +76,7 @@ public class PredicateSpec extends UriSpec { */ public BooleanSpec after(ZonedDateTime datetime) { return asyncPredicate(getBean(AfterRoutePredicateFactory.class) - .applyAsync(c-> c.setDatetime(datetime))); + .applyAsync(c -> c.setDatetime(datetime))); } /** @@ -85,7 +85,8 @@ public class PredicateSpec extends UriSpec { * @return a {@link BooleanSpec} to be used to add logical operators */ public BooleanSpec before(ZonedDateTime datetime) { - return asyncPredicate(getBean(BeforeRoutePredicateFactory.class).applyAsync(c -> c.setDatetime(datetime))); + return asyncPredicate(getBean(BeforeRoutePredicateFactory.class) + .applyAsync(c -> c.setDatetime(datetime))); } /** @@ -102,7 +103,8 @@ public class PredicateSpec extends UriSpec { /** * A predicate that checks if a cookie matches a given regular expression * @param name the name of the cookie - * @param regex the value of the cookies will be evaluated against this regular expression + * @param regex the value of the cookies will be evaluated against this regular + * expression * @return a {@link BooleanSpec} to be used to add logical operators */ public BooleanSpec cookie(String name, String regex) { @@ -121,7 +123,8 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate that checks if a given headers has a value which matches a regular expression + * A predicate that checks if a given headers has a value which matches a regular + * expression * @param header the header name to check * @param regex the regular expression to check against * @return a {@link BooleanSpec} to be used to add logical operators @@ -133,12 +136,13 @@ public class PredicateSpec extends UriSpec { /** * A predicate that checks if the {@code host} header matches a given pattern - * @param pattern the pattern to check against. The pattern is an Ant style pattern with {@code .} as a separator + * @param pattern the pattern to check against. The pattern is an Ant style pattern + * with {@code .} as a separator * @return a {@link BooleanSpec} to be used to add logical operators */ public BooleanSpec host(String... pattern) { return asyncPredicate(getBean(HostRoutePredicateFactory.class) - .applyAsync(c-> c.setPatterns(Arrays.asList(pattern)))); + .applyAsync(c -> c.setPatterns(Arrays.asList(pattern)))); } /** @@ -163,8 +167,8 @@ public class PredicateSpec extends UriSpec { /** * A predicate that checks if the path of the request matches the given pattern - * @param patterns the pattern to check the path against. - * The pattern is a {@link org.springframework.util.PathMatcher} pattern + * @param patterns the pattern to check the path against. The pattern is a + * {@link org.springframework.util.PathMatcher} pattern * @return a {@link BooleanSpec} to be used to add logical operators */ public BooleanSpec path(String... patterns) { @@ -174,36 +178,36 @@ public class PredicateSpec extends UriSpec { /** * A predicate that checks if the path of the request matches the given pattern - * @param pattern the pattern to check the path against. - * The pattern is a {@link org.springframework.util.PathMatcher} pattern - * @param matchOptionalTrailingSeparator set to false if you do not want this path to match - * when there is a trailing / + * @param pattern the pattern to check the path against. The pattern is a + * {@link org.springframework.util.PathMatcher} pattern + * @param matchOptionalTrailingSeparator set to false if you do not want this path to + * match when there is a trailing / * @return a {@link BooleanSpec} to be used to add logical operators */ @Deprecated public BooleanSpec path(String pattern, boolean matchOptionalTrailingSeparator) { - return asyncPredicate(getBean(PathRoutePredicateFactory.class) - .applyAsync(c -> c.setPatterns(Collections.singletonList(pattern)) - .setMatchOptionalTrailingSeparator(matchOptionalTrailingSeparator))); + return asyncPredicate(getBean(PathRoutePredicateFactory.class).applyAsync(c -> c + .setPatterns(Collections.singletonList(pattern)) + .setMatchOptionalTrailingSeparator(matchOptionalTrailingSeparator))); } /** * A predicate that checks if the path of the request matches the given pattern - * @param patterns the pattern to check the path against. - * The pattern is a {@link org.springframework.util.PathMatcher} pattern - * @param matchOptionalTrailingSeparator set to false if you do not want this path to match - * when there is a trailing / + * @param patterns the pattern to check the path against. The pattern is a + * {@link org.springframework.util.PathMatcher} pattern + * @param matchOptionalTrailingSeparator set to false if you do not want this path to + * match when there is a trailing / * @return a {@link BooleanSpec} to be used to add logical operators */ public BooleanSpec path(boolean matchOptionalTrailingSeparator, String... patterns) { - return asyncPredicate(getBean(PathRoutePredicateFactory.class) - .applyAsync(c -> c.setPatterns(Arrays.asList(patterns)) - .setMatchOptionalTrailingSeparator(matchOptionalTrailingSeparator))); + return asyncPredicate(getBean(PathRoutePredicateFactory.class).applyAsync(c -> c + .setPatterns(Arrays.asList(patterns)) + .setMatchOptionalTrailingSeparator(matchOptionalTrailingSeparator))); } /** - * This predicate is BETA and may be subject to change in a future release. - * A predicate that checks the contents of the request body + * This predicate is BETA and may be subject to change in a future release. A + * predicate that checks the contents of the request body * @param inClass the class to parse the body to * @param predicate a predicate to check the contents of the body * @param the type the body is parsed to @@ -236,13 +240,15 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate which checks the remote address of the request. - * By default the RemoteAddr Route Predicate Factory uses the remote address from the incoming request. - * This may not match the actual client IP address if Spring Cloud Gateway sits behind a proxy layer. - * Use {@link PredicateSpec#remoteAddr(RemoteAddressResolver, String...)} to customize the resolver. - * You can customize the way that the remote address is resolved by setting a custom RemoteAddressResolver. - - * @param addrs the remote address to verify. Should use CIDR-notation (IPv4 or IPv6) strings. + * A predicate which checks the remote address of the request. By default the + * RemoteAddr Route Predicate Factory uses the remote address from the incoming + * request. This may not match the actual client IP address if Spring Cloud Gateway + * sits behind a proxy layer. Use + * {@link PredicateSpec#remoteAddr(RemoteAddressResolver, String...)} to customize the + * resolver. You can customize the way that the remote address is resolved by setting + * a custom RemoteAddressResolver. + * @param addrs the remote address to verify. Should use CIDR-notation (IPv4 or IPv6) + * strings. * @return a {@link BooleanSpec} to be used to add logical operators */ public BooleanSpec remoteAddr(String... addrs) { @@ -250,34 +256,38 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate which checks the remote address of the request. Useful if Spring Cloud Gateway site behind a proxy - * layer. Spring Cloud Gateway comes with one non-default remote address resolver which is based off of the - * {@code X-Forwarded-For} header, {@link org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver}. - * See {@link org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver} for more information. - * @param resolver the {@link RemoteAddressResolver} to use to resolve the remote IP address against - * @param addrs the remote address to verify. Should use CIDR-notation (IPv4 or IPv6) strings. + * A predicate which checks the remote address of the request. Useful if Spring Cloud + * Gateway site behind a proxy layer. Spring Cloud Gateway comes with one non-default + * remote address resolver which is based off of the {@code X-Forwarded-For} header, + * {@link org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver}. + * See + * {@link org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver} + * for more information. + * @param resolver the {@link RemoteAddressResolver} to use to resolve the remote IP + * address against + * @param addrs the remote address to verify. Should use CIDR-notation (IPv4 or IPv6) + * strings. * @return a {@link BooleanSpec} to be used to add logical operators */ public BooleanSpec remoteAddr(RemoteAddressResolver resolver, String... addrs) { - return asyncPredicate(getBean(RemoteAddrRoutePredicateFactory.class).applyAsync(c -> { - c.setSources(addrs); - if (resolver != null) { - c.setRemoteAddressResolver(resolver); - } - })); + return asyncPredicate( + getBean(RemoteAddrRoutePredicateFactory.class).applyAsync(c -> { + c.setSources(addrs); + if (resolver != null) { + c.setRemoteAddressResolver(resolver); + } + })); } /** - * A predicate which will select a route based on its assigned weight. The + * A predicate which will select a route based on its assigned weight. The * @param group the group the route belongs to * @param weight the weight for the route * @return a {@link BooleanSpec} to be used to add logical operators */ public BooleanSpec weight(String group, int weight) { - return asyncPredicate(getBean(WeightRoutePredicateFactory.class) - .applyAsync(c -> c.setGroup(group) - .setRouteId(routeBuilder.getId()) - .setWeight(weight))); + return asyncPredicate(getBean(WeightRoutePredicateFactory.class).applyAsync(c -> c + .setGroup(group).setRouteId(routeBuilder.getId()).setWeight(weight))); } public BooleanSpec cloudFoundryRouteService() { @@ -293,4 +303,5 @@ public class PredicateSpec extends UriSpec { public BooleanSpec alwaysTrue() { return predicate(exchange -> true); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/RouteLocatorBuilder.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/RouteLocatorBuilder.java index 81d894c8..f083b2a3 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/RouteLocatorBuilder.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/RouteLocatorBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,12 +21,12 @@ import java.util.List; import java.util.UUID; import java.util.function.Function; +import reactor.core.publisher.Flux; + import org.springframework.cloud.gateway.route.Route; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.context.ConfigurableApplicationContext; -import reactor.core.publisher.Flux; - /** * Used to build a {@link RouteLocator} */ @@ -52,6 +52,7 @@ public class RouteLocatorBuilder { public static class Builder { private List routes = new ArrayList<>(); + private ConfigurableApplicationContext context; public Builder(ConfigurableApplicationContext context) { @@ -61,7 +62,8 @@ public class RouteLocatorBuilder { /** * Creates a new {@link Route} * @param id the unique id for the route - * @param fn a function which takes in a {@link PredicateSpec} and returns a {@link Route.AsyncBuilder} + * @param fn a function which takes in a {@link PredicateSpec} and returns a + * {@link Route.AsyncBuilder} * @return a {@link Builder} */ public Builder route(String id, Function fn) { @@ -72,7 +74,8 @@ public class RouteLocatorBuilder { /** * Creates a new {@link Route} - * @param fn a function which takes in a {@link PredicateSpec} and returns a {@link Route.AsyncBuilder} + * @param fn a function which takes in a {@link PredicateSpec} and returns a + * {@link Route.AsyncBuilder} * @return a {@link Builder} */ public Builder route(Function fn) { @@ -86,7 +89,8 @@ public class RouteLocatorBuilder { * @return a {@link RouteLocator} */ public RouteLocator build() { - return () -> Flux.fromIterable(this.routes).map(routeBuilder -> routeBuilder.build()); + return () -> Flux.fromIterable(this.routes) + .map(routeBuilder -> routeBuilder.build()); } ConfigurableApplicationContext getContext() { @@ -96,11 +100,13 @@ public class RouteLocatorBuilder { void add(Route.AsyncBuilder route) { routes.add(route); } + } - public static class RouteSpec { + private final Route.AsyncBuilder routeBuilder = Route.async(); + private final Builder builder; RouteSpec(Builder builder) { @@ -122,5 +128,4 @@ public class RouteLocatorBuilder { } - } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/UriSpec.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/UriSpec.java index 4a54db24..e4fe4e1f 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/UriSpec.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/builder/UriSpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +16,17 @@ package org.springframework.cloud.gateway.route.builder; -import org.springframework.cloud.gateway.route.Route; - import java.net.URI; +import org.springframework.cloud.gateway.route.Route; + /** * A specification to add a URI to a route. */ public class UriSpec { + final Route.AsyncBuilder routeBuilder; + final RouteLocatorBuilder.Builder builder; UriSpec(Route.AsyncBuilder routeBuilder, RouteLocatorBuilder.Builder builder) { @@ -50,8 +52,8 @@ public class UriSpec { return this.routeBuilder.uri(uri); } - T getBean(Class type) { return this.builder.getContext().getBean(type); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/AbstractConfigurable.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/AbstractConfigurable.java index 5a6b0c12..89b4042c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/AbstractConfigurable.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/AbstractConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -21,6 +20,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.core.style.ToStringCreator; public abstract class AbstractConfigurable implements Configurable { + private Class configClass; protected AbstractConfigurable(Class configClass) { @@ -38,8 +38,7 @@ public abstract class AbstractConfigurable implements Configurable { @Override public String toString() { - return new ToStringCreator(this) - .append("configClass", configClass) - .toString(); + return new ToStringCreator(this).append("configClass", configClass).toString(); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/AbstractStatefulConfigurable.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/AbstractStatefulConfigurable.java index 88c6348b..47abec2a 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/AbstractStatefulConfigurable.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/AbstractStatefulConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -22,7 +21,9 @@ import java.util.Map; import org.springframework.core.style.ToStringCreator; -public abstract class AbstractStatefulConfigurable extends AbstractConfigurable implements StatefulConfigurable { +public abstract class AbstractStatefulConfigurable extends AbstractConfigurable + implements StatefulConfigurable { + private Map config = new HashMap<>(); protected AbstractStatefulConfigurable(Class configClass) { @@ -36,9 +37,8 @@ public abstract class AbstractStatefulConfigurable extends AbstractConfigurab @Override public String toString() { - return new ToStringCreator(this) - .append("config", config) - .append("configClass", getConfigClass()) - .toString(); + return new ToStringCreator(this).append("config", config) + .append("configClass", getConfigClass()).toString(); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/BodyInserterContext.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/BodyInserterContext.java index ce14a310..ce5f6b39 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/BodyInserterContext.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/BodyInserterContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,22 +12,22 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; -import org.springframework.http.codec.HttpMessageWriter; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.web.reactive.function.BodyInserter; -import org.springframework.web.reactive.function.client.ExchangeStrategies; - import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; +import org.springframework.http.codec.HttpMessageWriter; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.reactive.function.BodyInserter; +import org.springframework.web.reactive.function.client.ExchangeStrategies; + public class BodyInserterContext implements BodyInserter.Context { + private final ExchangeStrategies exchangeStrategies; public BodyInserterContext() { @@ -35,19 +35,22 @@ public class BodyInserterContext implements BodyInserter.Context { } public BodyInserterContext(ExchangeStrategies exchangeStrategies) { - this.exchangeStrategies = exchangeStrategies; //TODO: support custom strategies + this.exchangeStrategies = exchangeStrategies; // TODO: support custom strategies } @Override public List> messageWriters() { return exchangeStrategies.messageWriters(); } + @Override public Optional serverRequest() { return Optional.empty(); } + @Override public Map hints() { - return Collections.emptyMap(); //TODO: support hints + return Collections.emptyMap(); // TODO: support hints } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/CachedBodyOutputMessage.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/CachedBodyOutputMessage.java index 172d6d18..f7e09e8a 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/CachedBodyOutputMessage.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/CachedBodyOutputMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -34,6 +33,7 @@ import org.springframework.web.server.ServerWebExchange; /** * Mock implementation of {@link ClientHttpRequest}. + * * @author Brian Clozel * @author Rossen Stoyanchev * @since 5.0 @@ -41,11 +41,12 @@ import org.springframework.web.server.ServerWebExchange; public class CachedBodyOutputMessage implements ReactiveHttpOutputMessage { private final DataBufferFactory bufferFactory; + private final HttpHeaders httpHeaders; - private Flux body = Flux.error( - new IllegalStateException("The body is not set. " + - "Did handling complete with success? Is a custom \"writeHandler\" configured?")); + private Flux body = Flux + .error(new IllegalStateException("The body is not set. " + + "Did handling complete with success? Is a custom \"writeHandler\" configured?")); private Function, Mono> writeHandler = initDefaultWriteHandler(); @@ -82,8 +83,9 @@ public class CachedBodyOutputMessage implements ReactiveHttpOutputMessage { } /** - * Return the request body, or an error stream if the body was never set - * or when {@link #setWriteHandler} is configured. + * Return the request body, or an error stream if the body was never set or when + * {@link #setWriteHandler} is configured. + * @return body as {@link Flux} */ public Flux getBody() { return this.body; @@ -92,12 +94,12 @@ public class CachedBodyOutputMessage implements ReactiveHttpOutputMessage { /** * Configure a custom handler for writing the request body. * - *

The default write handler consumes and caches the request body so it - * may be accessed subsequently, e.g. in test assertions. Use this property - * when the request body is an infinite stream. - * - * @param writeHandler the write handler to use returning {@code Mono} - * when the body has been "written" (i.e. consumed). + *

+ * The default write handler consumes and caches the request body so it may be + * accessed subsequently, e.g. in test assertions. Use this property when the request + * body is an infinite stream. + * @param writeHandler the write handler to use returning {@code Mono} when the + * body has been "written" (i.e. consumed). */ public void setWriteHandler(Function, Mono> writeHandler) { Assert.notNull(writeHandler, "'writeHandler' is required"); @@ -110,7 +112,8 @@ public class CachedBodyOutputMessage implements ReactiveHttpOutputMessage { } @Override - public Mono writeAndFlushWith(Publisher> body) { + public Mono writeAndFlushWith( + Publisher> body) { return writeWith(Flux.from(body).flatMap(p -> p)); } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/Configurable.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/Configurable.java index d4bf1eb9..9ef1278e 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/Configurable.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/Configurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,12 +12,14 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; public interface Configurable { + Class getConfigClass(); + C newConfig(); + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ConfigurationUtils.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ConfigurationUtils.java index 59f3c322..d196806b 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ConfigurationUtils.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ConfigurationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -33,16 +32,20 @@ import org.springframework.validation.Validator; public abstract class ConfigurationUtils { - public static void bind(Object o, Map properties, String configurationPropertyName, String bindingName, Validator validator) { + public static void bind(Object o, Map properties, + String configurationPropertyName, String bindingName, Validator validator) { bind(o, properties, configurationPropertyName, bindingName, validator, null); } - public static void bind(Object o, Map properties, String configurationPropertyName, String bindingName, - Validator validator, ConversionService conversionService) { + public static void bind(Object o, Map properties, + String configurationPropertyName, String bindingName, Validator validator, + ConversionService conversionService) { Object toBind = getTargetObject(o); - new Binder(Collections.singletonList(new MapConfigurationPropertySource(properties)), null, conversionService) - .bind(configurationPropertyName, Bindable.ofInstance(toBind)); + new Binder( + Collections.singletonList(new MapConfigurationPropertySource(properties)), + null, conversionService).bind(configurationPropertyName, + Bindable.ofInstance(toBind)); if (validator != null) { BindingResult errors = new BeanPropertyBindingResult(toBind, bindingName); @@ -65,4 +68,5 @@ public abstract class ConfigurationUtils { } return (T) candidate; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultClientResponse.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultClientResponse.java index c4197b40..074a0f51 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultClientResponse.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultClientResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -23,8 +22,6 @@ import java.util.Map; import java.util.Optional; import java.util.OptionalLong; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.ExchangeStrategies; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -41,6 +38,8 @@ import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyExtractor; import org.springframework.web.reactive.function.BodyExtractors; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.ExchangeStrategies; /** * Default implementation of {@link ClientResponse}. @@ -57,14 +56,13 @@ public class DefaultClientResponse implements ClientResponse { private final ExchangeStrategies strategies; - - public DefaultClientResponse(ClientHttpResponse response, ExchangeStrategies strategies) { + public DefaultClientResponse(ClientHttpResponse response, + ExchangeStrategies strategies) { this.response = response; this.strategies = strategies; this.headers = new DefaultHeaders(); } - @Override public ExchangeStrategies strategies() { return this.strategies; @@ -97,10 +95,12 @@ public class DefaultClientResponse implements ClientResponse { public List> messageReaders() { return strategies.messageReaders(); } + @Override public Optional serverResponse() { return Optional.empty(); } + @Override public Map hints() { return Collections.emptyMap(); @@ -120,13 +120,10 @@ public class DefaultClientResponse implements ClientResponse { @SuppressWarnings("unchecked") private Mono consumeAndCancel() { - return (Mono) this.response.getBody() - .map(buffer -> { - DataBufferUtils.release(buffer); - throw new ReadCancellationException(); - }) - .onErrorResume(ReadCancellationException.class, ex -> Mono.empty()) - .then(); + return (Mono) this.response.getBody().map(buffer -> { + DataBufferUtils.release(buffer); + throw new ReadCancellationException(); + }).onErrorResume(ReadCancellationException.class, ex -> Mono.empty()).then(); } @Override @@ -170,7 +167,8 @@ public class DefaultClientResponse implements ClientResponse { } @Override - public Mono> toEntity(ParameterizedTypeReference typeReference) { + public Mono> toEntity( + ParameterizedTypeReference typeReference) { if (Void.class.isAssignableFrom(typeReference.getType().getClass())) { return toEntityInternal(consumeAndCancel()); } @@ -182,8 +180,7 @@ public class DefaultClientResponse implements ClientResponse { private Mono> toEntityInternal(Mono bodyMono) { HttpHeaders headers = headers().asHttpHeaders(); HttpStatus statusCode = statusCode(); - return bodyMono - .map(body -> new ResponseEntity<>(body, headers, statusCode)) + return bodyMono.map(body -> new ResponseEntity<>(body, headers, statusCode)) .switchIfEmpty(Mono.defer( () -> Mono.just(new ResponseEntity<>(headers, statusCode)))); } @@ -194,18 +191,22 @@ public class DefaultClientResponse implements ClientResponse { } @Override - public Mono>> toEntityList(ParameterizedTypeReference typeReference) { + public Mono>> toEntityList( + ParameterizedTypeReference typeReference) { return toEntityListInternal(bodyToFlux(typeReference)); } private Mono>> toEntityListInternal(Flux bodyFlux) { HttpHeaders headers = headers().asHttpHeaders(); HttpStatus statusCode = statusCode(); - return bodyFlux - .collectList() + return bodyFlux.collectList() .map(body -> new ResponseEntity<>(body, headers, statusCode)); } + @SuppressWarnings("serial") + private static class ReadCancellationException extends RuntimeException { + + } private class DefaultHeaders implements Headers { @@ -237,11 +238,7 @@ public class DefaultClientResponse implements ClientResponse { private OptionalLong toOptionalLong(long value) { return (value != -1 ? OptionalLong.of(value) : OptionalLong.empty()); } - } - - @SuppressWarnings("serial") - private static class ReadCancellationException extends RuntimeException { } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerRequest.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerRequest.java index 45583f0c..b0f8f682 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerRequest.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -30,7 +29,6 @@ import java.util.Optional; import java.util.OptionalLong; import java.util.function.Function; -import org.springframework.web.reactive.function.server.HandlerStrategies; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -49,6 +47,7 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyExtractor; import org.springframework.web.reactive.function.BodyExtractors; import org.springframework.web.reactive.function.UnsupportedMediaTypeException; +import org.springframework.web.reactive.function.server.HandlerStrategies; import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.server.ServerWebExchange; @@ -65,11 +64,11 @@ import org.springframework.web.util.UriComponentsBuilder; */ public class DefaultServerRequest implements ServerRequest { - private static final Function ERROR_MAPPER = - ex -> (ex.getContentType() != null ? - new UnsupportedMediaTypeStatusException(ex.getContentType(), ex.getSupportedMediaTypes()) : - new UnsupportedMediaTypeStatusException(ex.getMessage())); - + private static final Function ERROR_MAPPER = ex -> (ex + .getContentType() != null + ? new UnsupportedMediaTypeStatusException(ex.getContentType(), + ex.getSupportedMediaTypes()) + : new UnsupportedMediaTypeStatusException(ex.getMessage())); private final ServerWebExchange exchange; @@ -81,13 +80,14 @@ public class DefaultServerRequest implements ServerRequest { this(exchange, HandlerStrategies.withDefaults().messageReaders()); } - public DefaultServerRequest(ServerWebExchange exchange, List> messageReaders) { + public DefaultServerRequest(ServerWebExchange exchange, + List> messageReaders) { this.exchange = exchange; - this.messageReaders = Collections.unmodifiableList(new ArrayList<>(messageReaders)); + this.messageReaders = Collections + .unmodifiableList(new ArrayList<>(messageReaders)); this.headers = new DefaultHeaders(); } - @Override public String methodName() { return request().getMethodValue(); @@ -134,22 +134,24 @@ public class DefaultServerRequest implements ServerRequest { } @Override - public T body(BodyExtractor extractor, Map hints) { - return extractor.extract(request(), - new BodyExtractor.Context() { - @Override - public List> messageReaders() { - return messageReaders; - } - @Override - public Optional serverResponse() { - return Optional.of(exchange().getResponse()); - } - @Override - public Map hints() { - return hints; - } - }); + public T body(BodyExtractor extractor, + Map hints) { + return extractor.extract(request(), new BodyExtractor.Context() { + @Override + public List> messageReaders() { + return messageReaders; + } + + @Override + public Optional serverResponse() { + return Optional.of(exchange().getResponse()); + } + + @Override + public Map hints() { + return hints; + } + }); } @Override @@ -225,7 +227,6 @@ public class DefaultServerRequest implements ServerRequest { return String.format("%s %s", method(), path()); } - private class DefaultHeaders implements Headers { private HttpHeaders delegate() { @@ -283,8 +284,8 @@ public class DefaultServerRequest implements ServerRequest { public String toString() { return delegate().toString(); } - } + } private final class ServerRequestAdapter implements HttpRequest { @@ -302,6 +303,7 @@ public class DefaultServerRequest implements ServerRequest { public HttpHeaders getHeaders() { return request().getHeaders(); } + } } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerResponse.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerResponse.java index 13d390c2..504b78d8 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerResponse.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/DefaultServerResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -21,8 +20,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import org.springframework.web.reactive.function.server.HandlerStrategies; -import org.springframework.web.reactive.result.view.ViewResolver; import reactor.core.publisher.Mono; import org.springframework.http.HttpHeaders; @@ -34,12 +31,13 @@ import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; +import org.springframework.web.reactive.function.server.HandlerStrategies; import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.reactive.result.view.ViewResolver; import org.springframework.web.server.ServerWebExchange; public class DefaultServerResponse implements ServerResponse { - private final ServerWebExchange exchange; private final BodyInserter inserter; @@ -61,7 +59,7 @@ public class DefaultServerResponse implements ServerResponse { @Override public final HttpStatus statusCode() { - //TODO: non standard status code + // TODO: non standard status code return HttpStatus.valueOf(response().getStatusCode().value()); } @@ -82,10 +80,12 @@ public class DefaultServerResponse implements ServerResponse { public List> messageWriters() { return context.messageWriters(); } + @Override public Optional serverRequest() { return Optional.of(exchange.getRequest()); } + @Override public Map hints() { return hints; @@ -93,8 +93,8 @@ public class DefaultServerResponse implements ServerResponse { }); } - - public static class HandlerStrategiesResponseContext implements ServerResponse.Context { + public static class HandlerStrategiesResponseContext + implements ServerResponse.Context { private HandlerStrategies strategies = HandlerStrategies.withDefaults(); @@ -114,5 +114,7 @@ public class DefaultServerResponse implements ServerResponse { public List viewResolvers() { return this.strategies.viewResolvers(); } + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/HttpStatusHolder.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/HttpStatusHolder.java index 51035033..bd61d9a1 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/HttpStatusHolder.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/HttpStatusHolder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -24,20 +23,9 @@ import org.springframework.util.Assert; public class HttpStatusHolder { private final HttpStatus httpStatus; + private final Integer status; - public static HttpStatusHolder parse(String status) { - final HttpStatus httpStatus = ServerWebExchangeUtils.parse(status); - final Integer intStatus; - if (httpStatus == null) { - intStatus = Integer.parseInt(status); - } else { - intStatus = null; - } - - return new HttpStatusHolder(httpStatus, intStatus); - } - public HttpStatusHolder(HttpStatus httpStatus, Integer status) { Assert.isTrue(httpStatus != null || status != null, "httpStatus and status may not both be null"); @@ -45,6 +33,19 @@ public class HttpStatusHolder { this.status = status; } + public static HttpStatusHolder parse(String status) { + final HttpStatus httpStatus = ServerWebExchangeUtils.parse(status); + final Integer intStatus; + if (httpStatus == null) { + intStatus = Integer.parseInt(status); + } + else { + intStatus = null; + } + + return new HttpStatusHolder(httpStatus, intStatus); + } + public HttpStatus getHttpStatus() { return httpStatus; } @@ -56,6 +57,7 @@ public class HttpStatusHolder { /** * Whether this status code is in the HTTP series * {@link org.springframework.http.HttpStatus.Series#INFORMATIONAL}. + * @return true if status code is in the INFORMATIONAL http series */ public boolean is1xxInformational() { return HttpStatus.Series.INFORMATIONAL.equals(getSeries()); @@ -64,6 +66,7 @@ public class HttpStatusHolder { /** * Whether this status code is in the HTTP series * {@link org.springframework.http.HttpStatus.Series#SUCCESSFUL}. + * @return true if status code is in the SUCCESSFUL http series */ public boolean is2xxSuccessful() { return HttpStatus.Series.SUCCESSFUL.equals(getSeries()); @@ -72,15 +75,16 @@ public class HttpStatusHolder { /** * Whether this status code is in the HTTP series * {@link org.springframework.http.HttpStatus.Series#REDIRECTION}. + * @return true if status code is in the REDIRECTION http series */ public boolean is3xxRedirection() { return HttpStatus.Series.REDIRECTION.equals(getSeries()); } - /** * Whether this status code is in the HTTP series * {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR}. + * @return true if status code is in the CLIENT_ERROR http series */ public boolean is4xxClientError() { return HttpStatus.Series.CLIENT_ERROR.equals(getSeries()); @@ -89,6 +93,7 @@ public class HttpStatusHolder { /** * Whether this status code is in the HTTP series * {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR}. + * @return true if status code is in the SERVER_ERROR http series */ public boolean is5xxServerError() { return HttpStatus.Series.SERVER_ERROR.equals(getSeries()); @@ -104,11 +109,11 @@ public class HttpStatusHolder { return null; } - /** * Whether this status code is in the HTTP series * {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR} or * {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR}. + * @return true if is either CLIENT_ERROR or SERVER_ERROR */ public boolean isError() { return is4xxClientError() || is5xxServerError(); @@ -116,9 +121,8 @@ public class HttpStatusHolder { @Override public String toString() { - return new ToStringCreator(this) - .append("httpStatus", httpStatus) - .append("status", status) - .toString(); + return new ToStringCreator(this).append("httpStatus", httpStatus) + .append("status", status).toString(); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java index 9d34aa60..75208cd6 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -23,19 +22,31 @@ import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory /** * @author Spencer Gibb */ -public class NameUtils { +public final class NameUtils { + + private NameUtils() { + throw new AssertionError("Must not instantiate utility class."); + } + + /** + * Generated name prefix. + */ public static final String GENERATED_NAME_PREFIX = "_genkey_"; public static String generateName(int i) { return GENERATED_NAME_PREFIX + i; } - public static String normalizeRoutePredicateName(Class clazz) { - return removeGarbage(clazz.getSimpleName().replace(RoutePredicateFactory.class.getSimpleName(), "")); + public static String normalizeRoutePredicateName( + Class clazz) { + return removeGarbage(clazz.getSimpleName() + .replace(RoutePredicateFactory.class.getSimpleName(), "")); } - public static String normalizeFilterFactoryName(Class clazz) { - return removeGarbage(clazz.getSimpleName().replace(GatewayFilterFactory.class.getSimpleName(), "")); + public static String normalizeFilterFactoryName( + Class clazz) { + return removeGarbage(clazz.getSimpleName() + .replace(GatewayFilterFactory.class.getSimpleName(), "")); } private static String removeGarbage(String s) { @@ -46,4 +57,5 @@ public class NameUtils { return s; } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/NotFoundException.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/NotFoundException.java index 0486b4d1..145d1138 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/NotFoundException.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/NotFoundException.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -24,6 +23,7 @@ import org.springframework.web.server.ResponseStatusException; * @author Spencer Gibb */ public class NotFoundException extends ResponseStatusException { + public NotFoundException(String message) { this(HttpStatus.SERVICE_UNAVAILABLE, message); } @@ -41,12 +41,15 @@ public class NotFoundException extends ResponseStatusException { } public static NotFoundException create(boolean with404, String message) { - HttpStatus httpStatus = with404? HttpStatus.NOT_FOUND: HttpStatus.SERVICE_UNAVAILABLE; + HttpStatus httpStatus = with404 ? HttpStatus.NOT_FOUND + : HttpStatus.SERVICE_UNAVAILABLE; return new NotFoundException(httpStatus, message); } - public static NotFoundException create(boolean with404, String message, Throwable cause) { - HttpStatus httpStatus = with404? HttpStatus.NOT_FOUND: HttpStatus.SERVICE_UNAVAILABLE; + public static NotFoundException create(boolean with404, String message, + Throwable cause) { + HttpStatus httpStatus = with404 ? HttpStatus.NOT_FOUND + : HttpStatus.SERVICE_UNAVAILABLE; return new NotFoundException(httpStatus, message, cause); } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java index 17e16036..2af08ec9 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -36,33 +35,105 @@ import org.springframework.web.server.ServerWebExchange; /** * @author Spencer Gibb */ -public class ServerWebExchangeUtils { +public final class ServerWebExchangeUtils { + + /** + * Preserve-Host header attribute name. + */ + public static final String PRESERVE_HOST_HEADER_ATTRIBUTE = qualify( + "preserveHostHeader"); + + /** + * URI template variables attribute name. + */ + public static final String URI_TEMPLATE_VARIABLES_ATTRIBUTE = qualify( + "uriTemplateVariables"); + + /** + * Client response attribute name. + */ + public static final String CLIENT_RESPONSE_ATTR = qualify("gatewayClientResponse"); + + /** + * Client response connection attribute name. + */ + public static final String CLIENT_RESPONSE_CONN_ATTR = qualify( + "gatewayClientResponseConnection"); + + /** + * Client response header names attribute name. + */ + public static final String CLIENT_RESPONSE_HEADER_NAMES = qualify( + "gatewayClientResponseHeaderNames"); + + /** + * Gateway route attribute name. + */ + public static final String GATEWAY_ROUTE_ATTR = qualify("gatewayRoute"); + + /** + * Gateway request URL attribute name. + */ + public static final String GATEWAY_REQUEST_URL_ATTR = qualify("gatewayRequestUrl"); + + /** + * Gateway original request URL attribute name. + */ + public static final String GATEWAY_ORIGINAL_REQUEST_URL_ATTR = qualify( + "gatewayOriginalRequestUrl"); + + /** + * Gateway handler mapper attribute name. + */ + public static final String GATEWAY_HANDLER_MAPPER_ATTR = qualify( + "gatewayHandlerMapper"); + + /** + * Gateway scheme prefix attribute name. + */ + public static final String GATEWAY_SCHEME_PREFIX_ATTR = qualify( + "gatewaySchemePrefix"); + + /** + * Gateway predicate route attribute name. + */ + public static final String GATEWAY_PREDICATE_ROUTE_ATTR = qualify( + "gatewayPredicateRouteAttr"); + + /** + * Weight attribute name. + */ + public static final String WEIGHT_ATTR = qualify("routeWeight"); + + /** + * Original response Content-Type attribute name. + */ + public static final String ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR = "original_response_content_type"; + + /** + * Hystrix execution exception attribute name. + */ + public static final String HYSTRIX_EXECUTION_EXCEPTION_ATTR = qualify( + "hystrixExecutionException"); + + /** + * Used when a routing filter has been successfully called. Allows users to write + * custom routing filters that disable built in routing filters. + */ + public static final String GATEWAY_ALREADY_ROUTED_ATTR = qualify( + "gatewayAlreadyRouted"); + + /** + * Gateway already prefixed attribute name. + */ + public static final String GATEWAY_ALREADY_PREFIXED_ATTR = qualify( + "gatewayAlreadyPrefixed"); private static final Log logger = LogFactory.getLog(ServerWebExchangeUtils.class); - public static final String PRESERVE_HOST_HEADER_ATTRIBUTE = qualify("preserveHostHeader"); - public static final String URI_TEMPLATE_VARIABLES_ATTRIBUTE = qualify("uriTemplateVariables"); - - public static final String CLIENT_RESPONSE_ATTR = qualify("gatewayClientResponse"); - public static final String CLIENT_RESPONSE_CONN_ATTR = qualify("gatewayClientResponseConnection"); - public static final String CLIENT_RESPONSE_HEADER_NAMES = qualify("gatewayClientResponseHeaderNames"); - public static final String GATEWAY_ROUTE_ATTR = qualify("gatewayRoute"); - public static final String GATEWAY_REQUEST_URL_ATTR = qualify("gatewayRequestUrl"); - public static final String GATEWAY_ORIGINAL_REQUEST_URL_ATTR = qualify("gatewayOriginalRequestUrl"); - public static final String GATEWAY_HANDLER_MAPPER_ATTR = qualify("gatewayHandlerMapper"); - public static final String GATEWAY_SCHEME_PREFIX_ATTR = qualify("gatewaySchemePrefix"); - public static final String GATEWAY_PREDICATE_ROUTE_ATTR = qualify("gatewayPredicateRouteAttr"); - public static final String WEIGHT_ATTR = qualify("routeWeight"); - public static final String ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR = "original_response_content_type"; - public static final String HYSTRIX_EXECUTION_EXCEPTION_ATTR = qualify("hystrixExecutionException"); - - /** - * Used when a routing filter has been successfully call. Allows users to write custom - * routing filters that disable built in routing filters. - */ - public static final String GATEWAY_ALREADY_ROUTED_ATTR = qualify("gatewayAlreadyRouted"); - - public static final String GATEWAY_ALREADY_PREFIXED_ATTR = qualify("gatewayAlreadyPrefixed"); + private ServerWebExchangeUtils() { + throw new AssertionError("Must not instantiate utility class."); + } private static String qualify(String attr) { return ServerWebExchangeUtils.class.getName() + "." + attr; @@ -76,15 +147,18 @@ public class ServerWebExchangeUtils { return exchange.getAttributeOrDefault(GATEWAY_ALREADY_ROUTED_ATTR, false); } - public static boolean setResponseStatus(ServerWebExchange exchange, HttpStatus httpStatus) { + public static boolean setResponseStatus(ServerWebExchange exchange, + HttpStatus httpStatus) { boolean response = exchange.getResponse().setStatusCode(httpStatus); if (!response && logger.isWarnEnabled()) { - logger.warn("Unable to set status code to "+ httpStatus + ". Response already committed."); + logger.warn("Unable to set status code to " + httpStatus + + ". Response already committed."); } return response; } - public static boolean setResponseStatus(ServerWebExchange exchange, HttpStatusHolder statusHolder) { + public static boolean setResponseStatus(ServerWebExchange exchange, + HttpStatusHolder statusHolder) { if (exchange.getResponse().isCommitted()) { return false; } @@ -92,8 +166,9 @@ public class ServerWebExchangeUtils { return setResponseStatus(exchange, statusHolder.getHttpStatus()); } if (statusHolder.getStatus() != null - && exchange.getResponse() instanceof AbstractServerHttpResponse) { //non-standard - ((AbstractServerHttpResponse)exchange.getResponse()).setStatusCodeValue(statusHolder.getStatus()); + && exchange.getResponse() instanceof AbstractServerHttpResponse) { // non-standard + ((AbstractServerHttpResponse) exchange.getResponse()) + .setStatusCodeValue(statusHolder.getStatus()); return true; } return false; @@ -111,7 +186,8 @@ public class ServerWebExchangeUtils { try { int status = Integer.parseInt(statusString); httpStatus = HttpStatus.resolve(status); - } catch (NumberFormatException e) { + } + catch (NumberFormatException e) { // try the enum string httpStatus = HttpStatus.valueOf(statusString.toUpperCase()); } @@ -119,30 +195,39 @@ public class ServerWebExchangeUtils { } public static void addOriginalRequestUrl(ServerWebExchange exchange, URI url) { - exchange.getAttributes().computeIfAbsent(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, s -> new LinkedHashSet<>()); - LinkedHashSet uris = exchange.getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + exchange.getAttributes().computeIfAbsent(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, + s -> new LinkedHashSet<>()); + LinkedHashSet uris = exchange + .getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); uris.add(url); } - public static AsyncPredicate toAsyncPredicate(Predicate predicate) { + public static AsyncPredicate toAsyncPredicate( + Predicate predicate) { Assert.notNull(predicate, "predicate must not be null"); return t -> Mono.just(predicate.test(t)); } @SuppressWarnings("unchecked") - public static void putUriTemplateVariables(ServerWebExchange exchange, Map uriVariables) { + public static void putUriTemplateVariables(ServerWebExchange exchange, + Map uriVariables) { if (exchange.getAttributes().containsKey(URI_TEMPLATE_VARIABLES_ATTRIBUTE)) { - Map existingVariables = (Map) exchange.getAttributes().get(URI_TEMPLATE_VARIABLES_ATTRIBUTE); + Map existingVariables = (Map) exchange + .getAttributes().get(URI_TEMPLATE_VARIABLES_ATTRIBUTE); HashMap newVariables = new HashMap<>(); newVariables.putAll(existingVariables); newVariables.putAll(uriVariables); exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, newVariables); - } else { + } + else { exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriVariables); } } - public static Map getUriTemplateVariables(ServerWebExchange exchange) { - return exchange.getAttributeOrDefault(URI_TEMPLATE_VARIABLES_ATTRIBUTE, new HashMap<>()); + public static Map getUriTemplateVariables( + ServerWebExchange exchange) { + return exchange.getAttributeOrDefault(URI_TEMPLATE_VARIABLES_ATTRIBUTE, + new HashMap<>()); } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java index 43d09a5f..6a317c50 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,11 +12,17 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + import org.springframework.beans.factory.BeanFactory; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.expression.Expression; @@ -25,96 +31,25 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.util.Assert; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - /** * @author Spencer Gibb */ public interface ShortcutConfigurable { - enum ShortcutType { - DEFAULT { - @Override - public Map normalize(Map args, ShortcutConfigurable shortcutConf, SpelExpressionParser parser, BeanFactory beanFactory) { - Map map = new HashMap<>(); - int entryIdx = 0; - for (Map.Entry entry : args.entrySet()) { - String key = normalizeKey(entry.getKey(), entryIdx, shortcutConf, args); - Object value = getValue(parser, beanFactory, entry.getValue()); - - map.put(key, value); - entryIdx++; - } - return map; - } - }, - - GATHER_LIST { - @Override - public Map normalize(Map args, ShortcutConfigurable shortcutConf, SpelExpressionParser parser, BeanFactory beanFactory) { - Map map = new HashMap<>(); - // field order should be of size 1 - List fieldOrder = shortcutConf.shortcutFieldOrder(); - Assert.isTrue(fieldOrder != null - && fieldOrder.size() == 1, - "Shortcut Configuration Type GATHER_LIST must have shortcutFieldOrder of size 1"); - String fieldName = fieldOrder.get(0); - map.put(fieldName, args.values().stream() - .map(value -> getValue(parser, beanFactory, value)) - .collect(Collectors.toList())); - return map; - } - }, - - // list is all elements except last which is a boolean flag - GATHER_LIST_TAIL_FLAG { - @Override - public Map normalize(Map args, ShortcutConfigurable shortcutConf, SpelExpressionParser parser, BeanFactory beanFactory) { - Map map = new HashMap<>(); - // field order should be of size 1 - List fieldOrder = shortcutConf.shortcutFieldOrder(); - Assert.isTrue(fieldOrder != null - && fieldOrder.size() == 2, - "Shortcut Configuration Type GATHER_LIST_HEAD must have shortcutFieldOrder of size 2"); - List values = new ArrayList<>(args.values()); - if (!values.isEmpty()) { - // strip boolean flag if last entry is true or false - int lastIdx = values.size() - 1; - String lastValue = values.get(lastIdx); - if (lastValue.equalsIgnoreCase("true") - || lastValue.equalsIgnoreCase("false")) { - values = values.subList(0, lastIdx); - map.put(fieldOrder.get(1), getValue(parser, beanFactory, lastValue)); - } - } - String fieldName = fieldOrder.get(0); - map.put(fieldName, values.stream() - .map(value -> getValue(parser, beanFactory, value)) - .collect(Collectors.toList())); - return map; - } - }; - - public abstract Map normalize(Map args, ShortcutConfigurable shortcutConf, - SpelExpressionParser parser, BeanFactory beanFactory); - } - - static String normalizeKey(String key, int entryIdx, ShortcutConfigurable argHints, Map args) { + static String normalizeKey(String key, int entryIdx, ShortcutConfigurable argHints, + Map args) { // RoutePredicateFactory has name hints and this has a fake key name // replace with the matching key hint - if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && !argHints.shortcutFieldOrder().isEmpty() - && entryIdx < args.size() && entryIdx < argHints.shortcutFieldOrder().size()) { + if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) + && !argHints.shortcutFieldOrder().isEmpty() && entryIdx < args.size() + && entryIdx < argHints.shortcutFieldOrder().size()) { key = argHints.shortcutFieldOrder().get(entryIdx); } return key; } - static Object getValue(SpelExpressionParser parser, BeanFactory beanFactory, String entryValue) { + static Object getValue(SpelExpressionParser parser, BeanFactory beanFactory, + String entryValue) { Object value; String rawValue = entryValue; if (rawValue != null) { @@ -124,9 +59,11 @@ public interface ShortcutConfigurable { // assume it's spel StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new BeanFactoryResolver(beanFactory)); - Expression expression = parser.parseExpression(entryValue, new TemplateParserContext()); + Expression expression = parser.parseExpression(entryValue, + new TemplateParserContext()); value = expression.getValue(context); - } else { + } + else { value = entryValue; } return value; @@ -138,7 +75,7 @@ public interface ShortcutConfigurable { /** * Returns hints about the number of args and the order for shortcut parsing. - * @return + * @return the list of hints */ default List shortcutFieldOrder() { return Collections.emptyList(); @@ -148,4 +85,81 @@ public interface ShortcutConfigurable { return ""; } + enum ShortcutType { + + DEFAULT { + @Override + public Map normalize(Map args, + ShortcutConfigurable shortcutConf, SpelExpressionParser parser, + BeanFactory beanFactory) { + Map map = new HashMap<>(); + int entryIdx = 0; + for (Map.Entry entry : args.entrySet()) { + String key = normalizeKey(entry.getKey(), entryIdx, shortcutConf, + args); + Object value = getValue(parser, beanFactory, entry.getValue()); + + map.put(key, value); + entryIdx++; + } + return map; + } + }, + + GATHER_LIST { + @Override + public Map normalize(Map args, + ShortcutConfigurable shortcutConf, SpelExpressionParser parser, + BeanFactory beanFactory) { + Map map = new HashMap<>(); + // field order should be of size 1 + List fieldOrder = shortcutConf.shortcutFieldOrder(); + Assert.isTrue(fieldOrder != null && fieldOrder.size() == 1, + "Shortcut Configuration Type GATHER_LIST must have shortcutFieldOrder of size 1"); + String fieldName = fieldOrder.get(0); + map.put(fieldName, + args.values().stream() + .map(value -> getValue(parser, beanFactory, value)) + .collect(Collectors.toList())); + return map; + } + }, + + // list is all elements except last which is a boolean flag + GATHER_LIST_TAIL_FLAG { + @Override + public Map normalize(Map args, + ShortcutConfigurable shortcutConf, SpelExpressionParser parser, + BeanFactory beanFactory) { + Map map = new HashMap<>(); + // field order should be of size 1 + List fieldOrder = shortcutConf.shortcutFieldOrder(); + Assert.isTrue(fieldOrder != null && fieldOrder.size() == 2, + "Shortcut Configuration Type GATHER_LIST_HEAD must have shortcutFieldOrder of size 2"); + List values = new ArrayList<>(args.values()); + if (!values.isEmpty()) { + // strip boolean flag if last entry is true or false + int lastIdx = values.size() - 1; + String lastValue = values.get(lastIdx); + if (lastValue.equalsIgnoreCase("true") + || lastValue.equalsIgnoreCase("false")) { + values = values.subList(0, lastIdx); + map.put(fieldOrder.get(1), + getValue(parser, beanFactory, lastValue)); + } + } + String fieldName = fieldOrder.get(0); + map.put(fieldName, + values.stream().map(value -> getValue(parser, beanFactory, value)) + .collect(Collectors.toList())); + return map; + } + }; + + public abstract Map normalize(Map args, + ShortcutConfigurable shortcutConf, SpelExpressionParser parser, + BeanFactory beanFactory); + + } + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/StatefulConfigurable.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/StatefulConfigurable.java index 4d1d0cca..d86905d2 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/StatefulConfigurable.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/StatefulConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -22,4 +21,5 @@ import java.util.Map; public interface StatefulConfigurable extends Configurable { Map getConfig(); + } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/StringToZonedDateTimeConverter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/StringToZonedDateTimeConverter.java index 2c5d1229..8d5dc9c1 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/StringToZonedDateTimeConverter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/StringToZonedDateTimeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,18 +12,18 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; -import org.springframework.core.convert.converter.Converter; - import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import org.springframework.core.convert.converter.Converter; + public class StringToZonedDateTimeConverter implements Converter { + @Override public ZonedDateTime convert(String source) { ZonedDateTime dateTime; @@ -32,11 +32,13 @@ public class StringToZonedDateTimeConverter implements ConverterX-Forwarded-For * reference - * @author Andrew Fitzgerald */ public class XForwardedRemoteAddressResolver implements RemoteAddressResolver { + /** + * Forwarded-For header name. + */ public static final String X_FORWARDED_FOR = "X-Forwarded-For"; + private static final Logger log = LoggerFactory .getLogger(XForwardedRemoteAddressResolver.class); - private final RemoteAddressResolver defaultRemoteIpResolver = new RemoteAddressResolver() {}; + + private final RemoteAddressResolver defaultRemoteIpResolver = new RemoteAddressResolver() { + }; private final int maxTrustedIndex; @@ -39,18 +62,16 @@ public class XForwardedRemoteAddressResolver implements RemoteAddressResolver { /** * @return a {@link XForwardedRemoteAddressResolver} which always extracts the first * IP address found in the X-Forwarded-For header (when present). Equivalent to - * calling {@link #maxTrustedIndex(int)} with a - * {@link #maxTrustedIndex} of {@link Integer#MAX_VALUE}. This configuration is - * vulnerable to spoofing via manually setting the X-Forwarded-For header. If the - * resulting IP address is used for security purposes, use - * {@link #maxTrustedIndex(int)} instead. + * calling {@link #maxTrustedIndex(int)} with a {@link #maxTrustedIndex} of + * {@link Integer#MAX_VALUE}. This configuration is vulnerable to spoofing via + * manually setting the X-Forwarded-For header. If the resulting IP address is used + * for security purposes, use {@link #maxTrustedIndex(int)} instead. */ public static XForwardedRemoteAddressResolver trustAll() { return new XForwardedRemoteAddressResolver(Integer.MAX_VALUE); } /** - * @return a {@link XForwardedRemoteAddressResolver} which extracts the last * trusted IP address found in the X-Forwarded-For header (when present). * This configuration exists to prevent a malicious actor from spoofing the value of * the X-Forwarded-For header. If you know that your gateway application is only @@ -60,7 +81,7 @@ public class XForwardedRemoteAddressResolver implements RemoteAddressResolver { * * * Given the X-Forwarded-For value of [0.0.0.1, 0.0.0.2, 0.0.0.3]: - * + * *

 	 * maxTrustedIndex -> result
 	 *
@@ -70,12 +91,11 @@ public class XForwardedRemoteAddressResolver implements RemoteAddressResolver {
 	 * 3 -> 0.0.0.1
 	 * [4, MAX_VALUE] -> 0.0.0.1
 	 * 
- * * @param maxTrustedIndex correlates to the number of trusted proxies expected in * front of Spring Cloud Gateway (index starts at 1). + * @return a {@link XForwardedRemoteAddressResolver} which extracts the last */ - public static XForwardedRemoteAddressResolver maxTrustedIndex( - int maxTrustedIndex) { + public static XForwardedRemoteAddressResolver maxTrustedIndex(int maxTrustedIndex) { Assert.isTrue(maxTrustedIndex > 0, "An index greater than 0 is required"); return new XForwardedRemoteAddressResolver(maxTrustedIndex); } @@ -114,4 +134,5 @@ public class XForwardedRemoteAddressResolver implements RemoteAddressResolver { } return values; } + } diff --git a/spring-cloud-gateway-core/src/main/kotlin/org/springframework/cloud/gateway/route/builder/RouteDsl.kt b/spring-cloud-gateway-core/src/main/kotlin/org/springframework/cloud/gateway/route/builder/RouteDsl.kt index 6f0973dc..4cf7e08c 100644 --- a/spring-cloud-gateway-core/src/main/kotlin/org/springframework/cloud/gateway/route/builder/RouteDsl.kt +++ b/spring-cloud-gateway-core/src/main/kotlin/org/springframework/cloud/gateway/route/builder/RouteDsl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ import java.util.function.Predicate * @author Spencer Gibb */ fun RouteLocatorBuilder.routes(routeLocator: RouteLocatorDsl.() -> Unit): RouteLocator { - return RouteLocatorDsl(this).apply(routeLocator).build() + return RouteLocatorDsl(this).apply(routeLocator).build() } @@ -52,45 +52,45 @@ fun RouteLocatorBuilder.routes(routeLocator: RouteLocatorDsl.() -> Unit): RouteL * Provider for [RouteLocator] DSL functionality */ class RouteLocatorDsl(val builder: RouteLocatorBuilder) { - private val routes = builder.routes() + private val routes = builder.routes() - /** - * DSL to add a route to the [RouteLocator] - * - * @see [Route.Builder] - */ - fun route(id: String? = null, order: Int = 0, uri: String? = null, init: PredicateSpec.() -> Unit) { - val predicateSpec = if (id == null) { - RouteSpec(routes).randomId() - } else { - RouteSpec(routes).id(id) - } - predicateSpec.order(order) - if (uri != null) { - predicateSpec.uri(uri) - } - - predicateSpec.apply(init) - - val route: Route.AsyncBuilder = predicateSpec.routeBuilder - routes.add(route) - } + /** + * DSL to add a route to the [RouteLocator] + * + * @see [Route.Builder] + */ + fun route(id: String? = null, order: Int = 0, uri: String? = null, init: PredicateSpec.() -> Unit) { + val predicateSpec = if (id == null) { + RouteSpec(routes).randomId() + } else { + RouteSpec(routes).id(id) + } + predicateSpec.order(order) + if (uri != null) { + predicateSpec.uri(uri) + } - fun build(): RouteLocator { - return routes.build() - } + predicateSpec.apply(init) - /** - * A helper to return a composed [Predicate] that tests against this [Predicate] AND the [other] predicate - */ - infix fun BooleanSpec.and(other: BooleanSpec) = - this.routeBuilder.asyncPredicate(this.predicate.and(other.predicate)) + val route: Route.AsyncBuilder = predicateSpec.routeBuilder + routes.add(route) + } - /** - * A helper to return a composed [Predicate] that tests against this [Predicate] OR the [other] predicate - */ - infix fun BooleanSpec.or(other: BooleanSpec) = - this.routeBuilder.asyncPredicate(this.predicate.or(other.predicate)) + fun build(): RouteLocator { + return routes.build() + } + + /** + * A helper to return a composed [Predicate] that tests against this [Predicate] AND the [other] predicate + */ + infix fun BooleanSpec.and(other: BooleanSpec) = + this.routeBuilder.asyncPredicate(this.predicate.and(other.predicate)) + + /** + * A helper to return a composed [Predicate] that tests against this [Predicate] OR the [other] predicate + */ + infix fun BooleanSpec.or(other: BooleanSpec) = + this.routeBuilder.asyncPredicate(this.predicate.or(other.predicate)) } @@ -99,8 +99,8 @@ class RouteLocatorDsl(val builder: RouteLocatorBuilder) { * Extension method to add filters {} block to dsl */ fun PredicateSpec.filters(init: GatewayFilterSpec.() -> Unit) { - val spec = createGatewayFilterSpec() - spec.apply(init) + val spec = createGatewayFilterSpec() + spec.apply(init) } diff --git a/spring-cloud-gateway-core/src/main/resources/META-INF/spring.factories b/spring-cloud-gateway-core/src/main/resources/META-INF/spring.factories index 349084ac..733183a5 100644 --- a/spring-cloud-gateway-core/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-gateway-core/src/main/resources/META-INF/spring.factories @@ -7,6 +7,5 @@ org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfigur org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,\ org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,\ org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration - org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.cloud.gateway.config.GatewayEnvironmentPostProcessor \ No newline at end of file diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java index b12276b8..5ab43e4a 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.actuate; @@ -48,19 +47,14 @@ public class GatewayControllerEndpointTests { @Test public void testRefresh() { - testClient.post() - .uri("http://localhost:"+port+"/actuator/gateway/refresh") - .exchange() - .expectStatus().isOk(); + testClient.post().uri("http://localhost:" + port + "/actuator/gateway/refresh") + .exchange().expectStatus().isOk(); } @Test public void testRoutes() { - testClient.get() - .uri("http://localhost:"+port+"/actuator/gateway/routes") - .exchange() - .expectStatus().isOk() - .expectBodyList(Map.class) + testClient.get().uri("http://localhost:" + port + "/actuator/gateway/routes") + .exchange().expectStatus().isOk().expectBodyList(Map.class) .consumeWith(result -> { List responseBody = result.getResponseBody(); assertThat(responseBody).isNotEmpty(); @@ -70,5 +64,8 @@ public class GatewayControllerEndpointTests { @SpringBootConfiguration @EnableAutoConfiguration @Import(PermitAllSecurityConfiguration.class) - static class TestConfig{} + static class TestConfig { + + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java index a6c62345..b792a509 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; @@ -37,18 +36,15 @@ public class GatewayAutoConfigurationTests { @Test public void noHiddenHttpMethodFilter() { - try (ConfigurableApplicationContext ctx = - SpringApplication.run(NoHiddenHttpMethodFilterConfig.class, "--spring.jmx.enabled=false")) { - assertThat(ctx.getEnvironment().getProperty("spring.webflux.hiddenmethod.filter.enabled")) - .isEqualTo("false"); + try (ConfigurableApplicationContext ctx = SpringApplication.run( + NoHiddenHttpMethodFilterConfig.class, "--spring.jmx.enabled=false")) { + assertThat(ctx.getEnvironment() + .getProperty("spring.webflux.hiddenmethod.filter.enabled")) + .isEqualTo("false"); assertThat(ctx.getBeanNamesForType(HiddenHttpMethodFilter.class)).isEmpty(); } } - @EnableAutoConfiguration - @SpringBootConfiguration - protected static class NoHiddenHttpMethodFilterConfig {} - @Test public void nettyHttpClientDefaults() { new ReactiveWebApplicationContextRunner() @@ -56,22 +52,22 @@ public class GatewayAutoConfigurationTests { MetricsAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class)) - .withPropertyValues("debug=true") - .run(context -> { + .withPropertyValues("debug=true").run(context -> { assertThat(context).hasSingleBean(HttpClient.class); HttpClient httpClient = context.getBean(HttpClient.class); - /*FIXME: 2.1.0 - HttpClientOptions options = httpClient.options(); - - PoolResources poolResources = options.getPoolResources(); - assertThat(poolResources).isNotNull(); - //TODO: howto test PoolResources - - ClientProxyOptions proxyOptions = options.getProxyOptions(); - assertThat(proxyOptions).isNull(); - - SslContext sslContext = options.sslContext(); - assertThat(sslContext).isNull();*/ + /* + * FIXME: 2.1.0 HttpClientOptions options = httpClient.options(); + * + * PoolResources poolResources = options.getPoolResources(); + * assertThat(poolResources).isNotNull(); //TODO: howto test + * PoolResources + * + * ClientProxyOptions proxyOptions = options.getProxyOptions(); + * assertThat(proxyOptions).isNull(); + * + * SslContext sslContext = options.sslContext(); + * assertThat(sslContext).isNull(); + */ }); } @@ -82,7 +78,8 @@ public class GatewayAutoConfigurationTests { MetricsAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class)) - .withPropertyValues("spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager=true", + .withPropertyValues( + "spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager=true", "spring.cloud.gateway.httpclient.connect-timeout=10", "spring.cloud.gateway.httpclient.response-timeout=10s", "spring.cloud.gateway.httpclient.pool.type=fixed", @@ -90,20 +87,29 @@ public class GatewayAutoConfigurationTests { .run(context -> { assertThat(context).hasSingleBean(HttpClient.class); HttpClient httpClient = context.getBean(HttpClient.class); - /* FIXME: 2.1.0 - HttpClientOptions options = httpClient.options(); - - PoolResources poolResources = options.getPoolResources(); - assertThat(poolResources).isNotNull(); - //TODO: howto test PoolResources - - ClientProxyOptions proxyOptions = options.getProxyOptions(); - assertThat(proxyOptions).isNotNull(); - assertThat(proxyOptions.getAddress().get().getHostName()).isEqualTo("myhost"); - - SslContext sslContext = options.sslContext(); - assertThat(sslContext).isNotNull();*/ - //TODO: howto test SslContext + /* + * FIXME: 2.1.0 HttpClientOptions options = httpClient.options(); + * + * PoolResources poolResources = options.getPoolResources(); + * assertThat(poolResources).isNotNull(); //TODO: howto test + * PoolResources + * + * ClientProxyOptions proxyOptions = options.getProxyOptions(); + * assertThat(proxyOptions).isNotNull(); + * assertThat(proxyOptions.getAddress().get().getHostName()).isEqualTo + * ("myhost"); + * + * SslContext sslContext = options.sslContext(); + * assertThat(sslContext).isNotNull(); + */ + // TODO: howto test SslContext }); } + + @EnableAutoConfiguration + @SpringBootConfiguration + protected static class NoHiddenHttpMethodFilterConfig { + + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayNoLoadBalancerClientAutoConfigurationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayNoLoadBalancerClientAutoConfigurationTests.java index 2ca9df93..30cbec38 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayNoLoadBalancerClientAutoConfigurationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/config/GatewayNoLoadBalancerClientAutoConfigurationTests.java @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.config; @@ -38,7 +37,7 @@ import org.springframework.util.SocketUtils; import org.springframework.web.bind.annotation.RestController; @RunWith(ModifiedClassPathRunner.class) -@ClassPathExclusions({"spring-cloud-netflix-ribbon-*.jar"}) +@ClassPathExclusions({ "spring-cloud-netflix-ribbon-*.jar" }) public class GatewayNoLoadBalancerClientAutoConfigurationTests { private static int port; @@ -51,11 +50,10 @@ public class GatewayNoLoadBalancerClientAutoConfigurationTests { @Test public void noLoadBalancerClientReportsError() { try (ConfigurableApplicationContext context = new SpringApplication(Config.class) - .run("--server.port="+port, "--spring.jmx.enabled=false")) { - WebTestClient client = WebTestClient.bindToServer().baseUrl("http://localhost:" + port).build(); - client.get() - .header(HttpHeaders.HOST, "www.lbfail.org") - .exchange() + .run("--server.port=" + port, "--spring.jmx.enabled=false")) { + WebTestClient client = WebTestClient.bindToServer() + .baseUrl("http://localhost:" + port).build(); + client.get().header(HttpHeaders.HOST, "www.lbfail.org").exchange() .expectStatus().is5xxServerError(); } } @@ -69,9 +67,10 @@ public class GatewayNoLoadBalancerClientAutoConfigurationTests { @Bean public RouteLocator hystrixRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route("lb_fail", r -> r.host("**.lbfail.org") - .uri("lb://fail")) + .route("lb_fail", r -> r.host("**.lbfail.org").uri("lb://fail")) .build(); } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/cors/CorsTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/cors/CorsTests.java index 4032ccad..93a374d7 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/cors/CorsTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/cors/CorsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,20 +12,16 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.cors; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - import java.util.Arrays; import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; + import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -38,7 +34,8 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.reactive.function.client.ClientResponse; -import reactor.core.publisher.Mono; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT) @@ -53,18 +50,17 @@ public class CorsTests extends BaseWebClientTests { HttpHeaders asHttpHeaders = clientResponse.headers().asHttpHeaders(); Mono bodyToMono = clientResponse.bodyToMono(String.class); // pre-flight request shouldn't return the response body - assertNull(bodyToMono.block()); - assertEquals( - "Missing header value in response: " - + HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, - "*", asHttpHeaders.getAccessControlAllowOrigin()); - assertEquals( - "Missing header value in response: " - + HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, - Arrays.asList(new HttpMethod[] { HttpMethod.GET }), - asHttpHeaders.getAccessControlAllowMethods()); - assertEquals("Pre Flight call failed.", HttpStatus.OK, - clientResponse.statusCode()); + assertThat(bodyToMono.block()).isNull(); + assertThat(asHttpHeaders.getAccessControlAllowOrigin()) + .as("Missing header value in response: " + + HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) + .isEqualTo("*"); + assertThat(asHttpHeaders.getAccessControlAllowMethods()) + .as("Missing header value in response: " + + HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS) + .isEqualTo(Arrays.asList(new HttpMethod[] { HttpMethod.GET })); + assertThat(clientResponse.statusCode()).as("Pre Flight call failed.") + .isEqualTo(HttpStatus.OK); } @Test @@ -74,13 +70,13 @@ public class CorsTests extends BaseWebClientTests { .exchange().block(); HttpHeaders asHttpHeaders = clientResponse.headers().asHttpHeaders(); Mono bodyToMono = clientResponse.bodyToMono(String.class); - assertNotNull(bodyToMono.block()); - assertEquals( - "Missing header value in response: " - + HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, - "*", asHttpHeaders.getAccessControlAllowOrigin()); - assertEquals("CORS request failed.", HttpStatus.OK, - clientResponse.statusCode()); + assertThat(bodyToMono.block()).isNotNull(); + assertThat(asHttpHeaders.getAccessControlAllowOrigin()) + .as("Missing header value in response: " + + HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) + .isEqualTo("*"); + assertThat(clientResponse.statusCode()).as("CORS request failed.") + .isEqualTo(HttpStatus.OK); } @EnableAutoConfiguration diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorIntegrationTests.java index 4a46f4de..d041f7f0 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.discovery; @@ -42,25 +41,25 @@ import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) -@SpringBootTest(classes = DiscoveryClientRouteDefinitionLocatorIntegrationTests.Config.class, - properties = {"spring.cloud.gateway.discovery.locator.enabled=true", - "spring.cloud.gateway.discovery.locator.route-id-prefix=test__", }) +@SpringBootTest(classes = DiscoveryClientRouteDefinitionLocatorIntegrationTests.Config.class, properties = { + "spring.cloud.gateway.discovery.locator.enabled=true", + "spring.cloud.gateway.discovery.locator.route-id-prefix=test__" }) public class DiscoveryClientRouteDefinitionLocatorIntegrationTests { - @Autowired - private RouteLocator routeLocator; + @Autowired + private RouteLocator routeLocator; - @Autowired + @Autowired private ApplicationEventPublisher publisher; - @Autowired + @Autowired private TestDiscoveryClient discoveryClient; - @Test - public void newServiceAddsRoute() { + @Test + public void newServiceAddsRoute() { List routes = routeLocator.getRoutes() - .filter(route -> route.getId().startsWith("test__")) - .collectList().block(); + .filter(route -> route.getId().startsWith("test__")).collectList() + .block(); assertThat(routes).hasSize(1); discoveryClient.multiple(); @@ -68,8 +67,8 @@ public class DiscoveryClientRouteDefinitionLocatorIntegrationTests { publisher.publishEvent(new HeartbeatEvent(this, 1L)); routes = routeLocator.getRoutes() - .filter(route -> route.getId().startsWith("test__")) - .collectList().block(); + .filter(route -> route.getId().startsWith("test__")).collectList() + .block(); assertThat(routes).hasSize(2); } @@ -81,13 +80,16 @@ public class DiscoveryClientRouteDefinitionLocatorIntegrationTests { TestDiscoveryClient discoveryClient() { return new TestDiscoveryClient(); } + } private static class TestDiscoveryClient implements DiscoveryClient { + AtomicBoolean single = new AtomicBoolean(true); DefaultServiceInstance instance1 = new DefaultServiceInstance("service1_1", "service1", "localhost", 8001, false); + DefaultServiceInstance instance2 = new DefaultServiceInstance("service2_1", "service2", "localhost", 8001, false); @@ -118,5 +120,7 @@ public class DiscoveryClientRouteDefinitionLocatorIntegrationTests { } return Arrays.asList("service1", "service2"); } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java index 3a3f0f13..f0b4fcaa 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.discovery; @@ -45,40 +44,43 @@ import static org.springframework.cloud.gateway.filter.factory.RewritePathGatewa import static org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory.PATTERN_KEY; @RunWith(SpringRunner.class) -@SpringBootTest(classes = DiscoveryClientRouteDefinitionLocatorTests.Config.class, - properties = {"spring.cloud.gateway.discovery.locator.enabled=true", - "spring.cloud.gateway.discovery.locator.route-id-prefix=testedge_", - "spring.cloud.gateway.discovery.locator.include-expression=metadata['edge'] == 'true'", - "spring.cloud.gateway.discovery.locator.lower-case-service-id=true", - /*"spring.cloud.gateway.discovery.locator.predicates[0].name=Path", - "spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]='/'+serviceId.toLowerCase()+'/**'", - "spring.cloud.gateway.discovery.locator.filters[0].name=RewritePath", - "spring.cloud.gateway.discovery.locator.filters[0].args[regexp]='/' + serviceId.toLowerCase() + '/(?.*)'", - "spring.cloud.gateway.discovery.locator.filters[0].args[replacement]='/$\\\\{remaining}'",*/ - }) +@SpringBootTest(classes = DiscoveryClientRouteDefinitionLocatorTests.Config.class, properties = { + "spring.cloud.gateway.discovery.locator.enabled=true", + "spring.cloud.gateway.discovery.locator.route-id-prefix=testedge_", + "spring.cloud.gateway.discovery.locator.include-expression=metadata['edge'] == 'true'", + "spring.cloud.gateway.discovery.locator.lower-case-service-id=true" + /* + * "spring.cloud.gateway.discovery.locator.predicates[0].name=Path", + * "spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]='/'+serviceId.toLowerCase()+'/**'", + * "spring.cloud.gateway.discovery.locator.filters[0].name=RewritePath", + * "spring.cloud.gateway.discovery.locator.filters[0].args[regexp]='/' + serviceId.toLowerCase() + '/(?.*)'" + * , + * "spring.cloud.gateway.discovery.locator.filters[0].args[replacement]='/$\\\\{remaining}'", + */ +}) public class DiscoveryClientRouteDefinitionLocatorTests { - @Autowired(required = false) - private DiscoveryClientRouteDefinitionLocator locator; + @Autowired(required = false) + private DiscoveryClientRouteDefinitionLocator locator; - @Test - public void includeExpressionWorks() { - assertThat(locator) - .as("DiscoveryClientRouteDefinitionLocator was null") - .isNotNull(); + @Test + public void includeExpressionWorks() { + assertThat(locator).as("DiscoveryClientRouteDefinitionLocator was null") + .isNotNull(); - List definitions = locator.getRouteDefinitions().collectList().block(); + List definitions = locator.getRouteDefinitions().collectList() + .block(); assertThat(definitions).hasSize(1); RouteDefinition definition = definitions.get(0); assertThat(definition.getId()).isEqualTo("testedge_SERVICE1"); - assertThat(definition.getUri()).hasScheme("lb") - .hasHost("SERVICE1"); + assertThat(definition.getUri()).hasScheme("lb").hasHost("SERVICE1"); assertThat(definition.getPredicates()).hasSize(1); PredicateDefinition predicate = definition.getPredicates().get(0); assertThat(predicate.getName()).isEqualTo("Path"); - assertThat(predicate.getArgs()).hasSize(1).containsEntry(PATTERN_KEY, "/service1/**"); + assertThat(predicate.getArgs()).hasSize(1).containsEntry(PATTERN_KEY, + "/service1/**"); assertThat(definition.getFilters()).hasSize(1); FilterDefinition filter = definition.getFilters().get(0); @@ -95,17 +97,22 @@ public class DiscoveryClientRouteDefinitionLocatorTests { @Bean DiscoveryClient discoveryClient() { DiscoveryClient discoveryClient = mock(DiscoveryClient.class); - when(discoveryClient.getServices()).thenReturn(Arrays.asList("SERVICE1", "Service2")); - whenInstance(discoveryClient, "SERVICE1", Collections.singletonMap("edge", "true")); + when(discoveryClient.getServices()) + .thenReturn(Arrays.asList("SERVICE1", "Service2")); + whenInstance(discoveryClient, "SERVICE1", + Collections.singletonMap("edge", "true")); whenInstance(discoveryClient, "Service2", Collections.emptyMap()); return discoveryClient; } - private void whenInstance(DiscoveryClient discoveryClient, String serviceId, Map metadata) { - DefaultServiceInstance instance1 = new DefaultServiceInstance(serviceId, "localhost", 8001, - false, metadata); - when(discoveryClient.getInstances(serviceId)). - thenReturn(Collections.singletonList(instance1)); + private void whenInstance(DiscoveryClient discoveryClient, String serviceId, + Map metadata) { + DefaultServiceInstance instance1 = new DefaultServiceInstance(serviceId, + "localhost", 8001, false, metadata); + when(discoveryClient.getInstances(serviceId)) + .thenReturn(Collections.singletonList(instance1)); } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/GatewayDiscoveryClientAutoConfigurationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/GatewayDiscoveryClientAutoConfigurationTests.java index 6ed1ff6d..63c6c0f5 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/GatewayDiscoveryClientAutoConfigurationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/discovery/GatewayDiscoveryClientAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.springframework.cloud.gateway.discovery; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -35,9 +36,9 @@ import static org.mockito.Mockito.mock; public class GatewayDiscoveryClientAutoConfigurationTests { @RunWith(SpringRunner.class) - @SpringBootTest(classes = Config.class, - properties = {"spring.cloud.gateway.discovery.locator.enabled=true", - "spring.cloud.gateway.loadbalancer.use404=true"}) + @SpringBootTest(classes = Config.class, properties = { + "spring.cloud.gateway.discovery.locator.enabled=true", + "spring.cloud.gateway.loadbalancer.use404=true" }) public static class EnabledByProperty { @Autowired(required = false) @@ -48,8 +49,7 @@ public class GatewayDiscoveryClientAutoConfigurationTests { @Test public void routeLocatorBeanExists() { - assertThat(locator) - .as("DiscoveryClientRouteDefinitionLocator was null") + assertThat(locator).as("DiscoveryClientRouteDefinitionLocator was null") .isNotNull(); } @@ -57,22 +57,23 @@ public class GatewayDiscoveryClientAutoConfigurationTests { public void use404() { assertThat(properties.isUse404()).isTrue(); } + } @RunWith(SpringRunner.class) @SpringBootTest(classes = Config.class) public static class DisabledByDefault { + @Autowired(required = false) private DiscoveryClientRouteDefinitionLocator locator; @Test public void routeLocatorBeanMissing() { - assertThat(locator) - .as("DiscoveryClientRouteDefinitionLocator exists") + assertThat(locator).as("DiscoveryClientRouteDefinitionLocator exists") .isNull(); } - } + } @SpringBootConfiguration @EnableAutoConfiguration @@ -82,5 +83,7 @@ public class GatewayDiscoveryClientAutoConfigurationTests { DiscoveryClient discoveryClient() { return mock(DiscoveryClient.class); } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ForwardRoutingFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ForwardRoutingFilterTests.java index fab6c6fc..0da4e0c1 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ForwardRoutingFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ForwardRoutingFilterTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter; import java.net.URI; @@ -49,7 +65,8 @@ public class ForwardRoutingFilterTests { @Before public void setup() { - exchange = MockServerWebExchange.from(MockServerHttpRequest.get("localendpoint").build()); + exchange = MockServerWebExchange + .from(MockServerHttpRequest.get("localendpoint").build()); when(objectProvider.getIfAvailable()).thenReturn(this.dispatcherHandler); } @@ -66,7 +83,8 @@ public class ForwardRoutingFilterTests { @Test public void shouldFilterWhenGatewayRequestUrlSchemeIsForward() { - URI uri = UriComponentsBuilder.fromUriString("forward://endpoint").build().toUri(); + URI uri = UriComponentsBuilder.fromUriString("forward://endpoint").build() + .toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri); assertThat(exchange.getAttributes().get(GATEWAY_ALREADY_ROUTED_ATTR)).isNull(); @@ -76,16 +94,19 @@ public class ForwardRoutingFilterTests { verifyNoMoreInteractions(chain); verify(dispatcherHandler).handle(exchange); - assertThat(exchange.getAttributes().get(GATEWAY_ALREADY_ROUTED_ATTR)).isEqualTo(true); + assertThat(exchange.getAttributes().get(GATEWAY_ALREADY_ROUTED_ATTR)) + .isEqualTo(true); } @Test public void shouldFilterAndKeepHostPathAsSpecified() { - URI uri = UriComponentsBuilder.fromUriString("forward://host/outage").build().toUri(); + URI uri = UriComponentsBuilder.fromUriString("forward://host/outage").build() + .toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri); - ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class); + ArgumentCaptor captor = ArgumentCaptor + .forClass(ServerWebExchange.class); forwardRoutingFilter.filter(exchange, chain); diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/GatewayMetricFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/GatewayMetricFilterTests.java index 9196fa92..ace42176 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/GatewayMetricFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/GatewayMetricFilterTests.java @@ -12,16 +12,18 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.search.MeterNotFoundException; -import io.micrometer.core.instrument.search.RequiredSearch; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,10 +48,6 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -69,28 +67,27 @@ public class GatewayMetricFilterTests extends BaseWebClientTests { @Test public void gatewayRequestsMeterFilterHasTags() { testClient.get().uri("/headers") - .header(HttpHeaders.HOST, "www.metricshappypath.org") - .exchange() - .expectStatus().isOk() - .returnResult(String.class).consumeWith(result -> { - assertMetricsContainsTag("outcome", HttpStatus.Series.SUCCESSFUL.name()); - assertMetricsContainsTag("status", HttpStatus.OK.name()); - assertMetricsContainsTag("routeId", "test_metrics_happy_path"); - assertMetricsContainsTag("routeUri", "lb://testservice"); - }); + .header(HttpHeaders.HOST, "www.metricshappypath.org").exchange() + .expectStatus().isOk().returnResult(String.class).consumeWith(result -> { + assertMetricsContainsTag("outcome", + HttpStatus.Series.SUCCESSFUL.name()); + assertMetricsContainsTag("status", HttpStatus.OK.name()); + assertMetricsContainsTag("routeId", "test_metrics_happy_path"); + assertMetricsContainsTag("routeUri", "lb://testservice"); + }); } @Test public void gatewayRequestsMeterFilterHasTagsForBadTargetUri() { - testClient.get().uri("/badtargeturi") - .exchange() - .expectStatus().is5xxServerError() + testClient.get().uri("/badtargeturi").exchange().expectStatus().is5xxServerError() .returnResult(String.class).consumeWith(result -> { - assertMetricsContainsTag("outcome", HttpStatus.Series.SERVER_ERROR.name()); - assertMetricsContainsTag("status", HttpStatus.INTERNAL_SERVER_ERROR.name()); - assertMetricsContainsTag("routeId", "default_path_to_httpbin"); - assertMetricsContainsTag("routeUri", testUri); - }); + assertMetricsContainsTag("outcome", + HttpStatus.Series.SERVER_ERROR.name()); + assertMetricsContainsTag("status", + HttpStatus.INTERNAL_SERVER_ERROR.name()); + assertMetricsContainsTag("routeId", "default_path_to_httpbin"); + assertMetricsContainsTag("routeUri", testUri); + }); } @Test @@ -111,18 +108,24 @@ public class GatewayMetricFilterTests extends BaseWebClientTests { private void assertMetricsContainsTag(String tagKey, String tagValue) { List meterIds = null; try { - meterIds = this.meterRegistry.getMeters().stream() - .map(Meter::getId) + meterIds = this.meterRegistry.getMeters().stream().map(Meter::getId) .collect(Collectors.toList()); - Collection timers = this.meterRegistry.get(REQUEST_METRICS_NAME).timers(); - System.err.println("Looking for gateway.requests: tag: " + tagKey + ", value: "+ tagValue); - timers.forEach(timer -> System.err.println(timer.getId()+timer.getClass().getSimpleName())); + Collection timers = this.meterRegistry.get(REQUEST_METRICS_NAME) + .timers(); + System.err.println("Looking for gateway.requests: tag: " + tagKey + + ", value: " + tagValue); + timers.forEach(timer -> System.err + .println(timer.getId() + timer.getClass().getSimpleName())); long count = getCount(tagKey, tagValue); assertThat(count).isEqualTo(1); - } catch (MeterNotFoundException e) { - System.err.println("\n\n\nError finding gatway.requests meter: tag: " + tagKey + ", value: "+ tagValue); - System.err.println("\n\n\nMeter ids prior to search: "+meterIds + "\n\n\n and after:"); - this.meterRegistry.forEachMeter(meter -> System.err.println(meter.getId() + meter.getClass().getSimpleName())); + } + catch (MeterNotFoundException e) { + System.err.println("\n\n\nError finding gatway.requests meter: tag: " + tagKey + + ", value: " + tagValue); + System.err.println( + "\n\n\nMeter ids prior to search: " + meterIds + "\n\n\n and after:"); + this.meterRegistry.forEachMeter(meter -> System.err + .println(meter.getId() + meter.getClass().getSimpleName())); // try again? long count = getCount(tagKey, tagValue); @@ -133,8 +136,8 @@ public class GatewayMetricFilterTests extends BaseWebClientTests { } private long getCount(String tagKey, String tagValue) { - return this.meterRegistry.get(REQUEST_METRICS_NAME).tag(tagKey, tagValue) - .timer().count(); + return this.meterRegistry.get(REQUEST_METRICS_NAME).tag(tagKey, tagValue).timer() + .count(); } @EnableAutoConfiguration @@ -142,17 +145,18 @@ public class GatewayMetricFilterTests extends BaseWebClientTests { @RestController @Import(DefaultTestConfig.class) public static class CustomConfig { + @Value("${test.uri}") protected String testUri; @Bean public RouteLocator myRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route("test_metrics_happy_path", r -> r.host("*.metricshappypath.org") - .uri(testUri)) - .route("test_custom_http_status_metrics", r -> r.host("*.setcustomstatusmetrics.org") - .filters(f -> f.setStatus(432)) - .uri(testUri)) + .route("test_metrics_happy_path", + r -> r.host("*.metricshappypath.org").uri(testUri)) + .route("test_custom_http_status_metrics", + r -> r.host("*.setcustomstatusmetrics.org") + .filters(f -> f.setStatus(432)).uri(testUri)) .build(); } @@ -160,5 +164,7 @@ public class GatewayMetricFilterTests extends BaseWebClientTests { public String exception() { throw new RuntimeException("an error"); } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilterTests.java index 8c7dadff..7e723929 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilterTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter; import java.net.URI; @@ -62,8 +78,10 @@ public class LoadBalancerClientFilterTests { @Before public void setup() { properties = new LoadBalancerProperties(); - loadBalancerClientFilter = new LoadBalancerClientFilter(loadBalancerClient, properties); - exchange = MockServerWebExchange.from(MockServerHttpRequest.get("loadbalancerclient.org").build()); + loadBalancerClientFilter = new LoadBalancerClientFilter(loadBalancerClient, + properties); + exchange = MockServerWebExchange + .from(MockServerHttpRequest.get("loadbalancerclient.org").build()); } @Test @@ -94,9 +112,11 @@ public class LoadBalancerClientFilterTests { try { loadBalancerClientFilter.filter(exchange, chain); - } catch (NotFoundException e) { + } + catch (NotFoundException e) { assertThat(e.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); - } catch (Exception e) { + } + catch (Exception e) { throw e; } } @@ -108,9 +128,11 @@ public class LoadBalancerClientFilterTests { properties.setUse404(true); try { loadBalancerClientFilter.filter(exchange, chain); - } catch (NotFoundException e) { + } + catch (NotFoundException e) { assertThat(e.getStatus()).isEqualTo(HttpStatus.NOT_FOUND); - } catch (Exception e) { + } + catch (Exception e) { throw e; } } @@ -120,15 +142,19 @@ public class LoadBalancerClientFilterTests { URI url = UriComponentsBuilder.fromUriString("lb://myservice").build().toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, url); - ServiceInstance serviceInstance = new DefaultServiceInstance("myservice", "localhost", 8080, true); + ServiceInstance serviceInstance = new DefaultServiceInstance("myservice", + "localhost", 8080, true); when(loadBalancerClient.choose("myservice")).thenReturn(serviceInstance); - URI requestUrl = UriComponentsBuilder.fromUriString("https://localhost:8080").build().toUri(); - when(loadBalancerClient.reconstructURI(any(ServiceInstance.class), any(URI.class))).thenReturn(requestUrl); + URI requestUrl = UriComponentsBuilder.fromUriString("https://localhost:8080") + .build().toUri(); + when(loadBalancerClient.reconstructURI(any(ServiceInstance.class), + any(URI.class))).thenReturn(requestUrl); loadBalancerClientFilter.filter(exchange, chain); - LinkedHashSet attribute = exchange.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + LinkedHashSet attribute = exchange + .getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); assertThat(attribute).contains(url); verify(loadBalancerClient).choose("myservice"); @@ -142,7 +168,8 @@ public class LoadBalancerClientFilterTests { verifyNoMoreInteractions(loadBalancerClient); - assertThat((URI)exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).isEqualTo(requestUrl); + assertThat((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)) + .isEqualTo(requestUrl); verify(chain).filter(exchange); verifyNoMoreInteractions(chain); @@ -150,8 +177,7 @@ public class LoadBalancerClientFilterTests { @Test public void instanceOverrideNonSecureScheme() { - MockServerHttpRequest request = MockServerHttpRequest - .get("https://localhost") + MockServerHttpRequest request = MockServerHttpRequest.get("https://localhost") .build(); URI lbUri = URI.create("lb://service1"); @@ -162,8 +188,7 @@ public class LoadBalancerClientFilterTests { @Test public void instanceOverrideSecureScheme() { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost") + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost") .build(); URI lbUri = URI.create("lb://service1"); @@ -174,8 +199,7 @@ public class LoadBalancerClientFilterTests { @Test public void instanceOverrideSecureSchemePrefix() { - MockServerHttpRequest request = MockServerHttpRequest - .get("https://localhost") + MockServerHttpRequest request = MockServerHttpRequest.get("https://localhost") .build(); URI lbUri = URI.create("http://service1"); @@ -188,8 +212,7 @@ public class LoadBalancerClientFilterTests { @Test public void instanceOverrideNonSecureSchemePrefix() { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost") + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost") .build(); URI lbUri = URI.create("https://service1"); @@ -200,24 +223,21 @@ public class LoadBalancerClientFilterTests { assertThat(uri).hasScheme("https").hasHost("service1-host1"); } - @Test public void happyPath() { MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/get?a=b") - .build(); + .get("http://localhost/get?a=b").build(); URI lbUri = URI.create("lb://service1?a=b"); ServerWebExchange webExchange = testFilter(request, lbUri); URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - assertThat(uri).hasScheme("http").hasHost("service1-host1") - .hasParameter("a", "b"); + assertThat(uri).hasScheme("http").hasHost("service1-host1").hasParameter("a", + "b"); } @Test public void noQueryParams() { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/get") + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/get") .build(); ServerWebExchange webExchange = testFilter(request, URI.create("lb://service1")); @@ -227,13 +247,14 @@ public class LoadBalancerClientFilterTests { @Test public void encodedParameters() { - URI url = UriComponentsBuilder.fromUriString("http://localhost/get?a=b&c=d[]").buildAndExpand().encode().toUri(); + URI url = UriComponentsBuilder.fromUriString("http://localhost/get?a=b&c=d[]") + .buildAndExpand().encode().toUri(); - MockServerHttpRequest request = MockServerHttpRequest - .method(HttpMethod.GET, url) + MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, url) .build(); - URI lbUrl = UriComponentsBuilder.fromUriString("lb://service1?a=b&c=d[]").buildAndExpand().encode().toUri(); + URI lbUrl = UriComponentsBuilder.fromUriString("lb://service1?a=b&c=d[]") + .buildAndExpand().encode().toUri(); // prove that it is encoded assertThat(lbUrl.getRawQuery()).isEqualTo("a=b&c=d%5B%5D"); @@ -242,8 +263,7 @@ public class LoadBalancerClientFilterTests { ServerWebExchange webExchange = testFilter(request, lbUrl); URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - assertThat(uri).hasScheme("http").hasHost("service1-host1") - .hasParameter("a", "b") + assertThat(uri).hasScheme("http").hasHost("service1-host1").hasParameter("a", "b") .hasParameter("c", "d[]"); // prove that it is not double encoded @@ -254,8 +274,7 @@ public class LoadBalancerClientFilterTests { public void unencodedParameters() { URI url = URI.create("http://localhost/get?a=b&c=d[]"); - MockServerHttpRequest request = MockServerHttpRequest - .method(HttpMethod.GET, url) + MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, url) .build(); URI lbUrl = URI.create("lb://service1?a=b&c=d[]"); @@ -266,8 +285,7 @@ public class LoadBalancerClientFilterTests { ServerWebExchange webExchange = testFilter(request, lbUrl); URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - assertThat(uri).hasScheme("http").hasHost("service1-host1") - .hasParameter("a", "b") + assertThat(uri).hasScheme("http").hasHost("service1-host1").hasParameter("a", "b") .hasParameter("c", "d[]"); // prove that it is NOT encoded @@ -277,8 +295,7 @@ public class LoadBalancerClientFilterTests { @Test public void happyPathWithAttributeRatherThanScheme() { MockServerHttpRequest request = MockServerHttpRequest - .get("ws://localhost/get?a=b") - .build(); + .get("ws://localhost/get?a=b").build(); URI lbUri = URI.create("ws://service1?a=b"); @@ -287,8 +304,7 @@ public class LoadBalancerClientFilterTests { ServerWebExchange webExchange = testFilter(exchange, lbUri, 8081); URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - assertThat(uri).hasScheme("ws").hasHost("service1-host1") - .hasParameter("a", "b"); + assertThat(uri).hasScheme("ws").hasHost("service1-host1").hasParameter("a", "b"); } @Test @@ -306,76 +322,97 @@ public class LoadBalancerClientFilterTests { @Test public void shouldSelectSpecifiedServer() { - URI uri1 = UriComponentsBuilder.fromUriString("lb://myservice").port(11111).build().toUri(); - URI uri2 = UriComponentsBuilder.fromUriString("lb://myservice").port(22222).build().toUri(); + URI uri1 = UriComponentsBuilder.fromUriString("lb://myservice").port(11111) + .build().toUri(); + URI uri2 = UriComponentsBuilder.fromUriString("lb://myservice").port(22222) + .build().toUri(); SpringClientFactory clientFactory = mock(SpringClientFactory.class); ILoadBalancer loadBalancer = mock(ILoadBalancer.class); - when(clientFactory.getLoadBalancerContext("myservice")).thenReturn(new RibbonLoadBalancerContext(loadBalancer)); + when(clientFactory.getLoadBalancerContext("myservice")) + .thenReturn(new RibbonLoadBalancerContext(loadBalancer)); when(clientFactory.getLoadBalancer("myservice")).thenReturn(loadBalancer); - when(loadBalancer.chooseServer("11111")).thenReturn(new Server("myservice-host1", 8081)); - when(loadBalancer.chooseServer("22222")).thenReturn(new Server("myservice-host2", 8081)); + when(loadBalancer.chooseServer("11111")) + .thenReturn(new Server("myservice-host1", 8081)); + when(loadBalancer.chooseServer("22222")) + .thenReturn(new Server("myservice-host2", 8081)); - LoadBalancerClient loadBalancerClient = new RibbonLoadBalancerClient(clientFactory) { + LoadBalancerClient loadBalancerClient = new RibbonLoadBalancerClient( + clientFactory) { private String loadBalancerKey; + public ServiceInstance choose(String serviceId) { String[] strings = serviceId.split("<<>>"); loadBalancerKey = strings[1]; return super.choose(strings[0], loadBalancerKey); } + protected Server getServer(ILoadBalancer loadBalancer) { - return loadBalancer == null ? null : loadBalancer.chooseServer(StringUtils.isEmpty(loadBalancerKey) ? "default" : loadBalancerKey); + return loadBalancer == null ? null + : loadBalancer.chooseServer(StringUtils.isEmpty(loadBalancerKey) + ? "default" : loadBalancerKey); } }; - LoadBalancerClientFilter loadBalancerClientFilter = new LoadBalancerClientFilter(loadBalancerClient, properties) { + LoadBalancerClientFilter loadBalancerClientFilter = new LoadBalancerClientFilter( + loadBalancerClient, properties) { protected ServiceInstance choose(ServerWebExchange exchange) { URI attribute = (URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); - return loadBalancer.choose(attribute.getHost() + "<<>>" + attribute.getPort()); + return loadBalancer + .choose(attribute.getHost() + "<<>>" + attribute.getPort()); } }; - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/get") + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/get") .build(); ServerWebExchange exchange = MockServerWebExchange.from(request); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri1); loadBalancerClientFilter.filter(exchange, chain); - assertThat(((URI)exchange.getAttributes().get(GATEWAY_REQUEST_URL_ATTR)).getHost()).isEqualTo("myservice-host1"); + assertThat( + ((URI) exchange.getAttributes().get(GATEWAY_REQUEST_URL_ATTR)).getHost()) + .isEqualTo("myservice-host1"); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri2); loadBalancerClientFilter.filter(exchange, chain); - assertThat(((URI)exchange.getAttributes().get(GATEWAY_REQUEST_URL_ATTR)).getHost()).isEqualTo("myservice-host2"); + assertThat( + ((URI) exchange.getAttributes().get(GATEWAY_REQUEST_URL_ATTR)).getHost()) + .isEqualTo("myservice-host2"); } private ServerWebExchange testFilter(MockServerHttpRequest request, URI uri) { return testFilter(MockServerWebExchange.from(request), uri, 8081); } - private ServerWebExchange testFilter(MockServerHttpRequest request, URI uri, int port) { + private ServerWebExchange testFilter(MockServerHttpRequest request, URI uri, + int port) { return testFilter(MockServerWebExchange.from(request), uri, port); } - private ServerWebExchange testFilter(ServerWebExchange exchange, URI uri, int port) { + private ServerWebExchange testFilter(ServerWebExchange exchange, URI uri, int port) { exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri); - ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class); + ArgumentCaptor captor = ArgumentCaptor + .forClass(ServerWebExchange.class); when(chain.filter(captor.capture())).thenReturn(Mono.empty()); SpringClientFactory clientFactory = mock(SpringClientFactory.class); ILoadBalancer loadBalancer = mock(ILoadBalancer.class); - when(clientFactory.getLoadBalancerContext("service1")).thenReturn(new RibbonLoadBalancerContext(loadBalancer)); + when(clientFactory.getLoadBalancerContext("service1")) + .thenReturn(new RibbonLoadBalancerContext(loadBalancer)); when(clientFactory.getLoadBalancer("service1")).thenReturn(loadBalancer); - when(loadBalancer.chooseServer(any())).thenReturn(new Server("service1-host1", port)); + when(loadBalancer.chooseServer(any())) + .thenReturn(new Server("service1-host1", port)); RibbonLoadBalancerClient client = new RibbonLoadBalancerClient(clientFactory); - LoadBalancerClientFilter filter = new LoadBalancerClientFilter(client, properties); + LoadBalancerClientFilter filter = new LoadBalancerClientFilter(client, + properties); filter.filter(exchange, chain); return captor.getValue(); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterIntegrationTests.java index b9670209..ff47234a 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -39,19 +38,18 @@ public class NettyRoutingFilterIntegrationTests extends BaseWebClientTests { @Test public void responseTimeoutWorks() { - testClient.get() - .uri("/delay/5") - .exchange() - .expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT) - .expectBody() - .jsonPath("$.status").isEqualTo(String.valueOf(HttpStatus.GATEWAY_TIMEOUT.value())) - .jsonPath("$.message").isEqualTo("Response took longer than timeout: PT3S"); + testClient.get().uri("/delay/5").exchange().expectStatus() + .isEqualTo(HttpStatus.GATEWAY_TIMEOUT).expectBody().jsonPath("$.status") + .isEqualTo(String.valueOf(HttpStatus.GATEWAY_TIMEOUT.value())) + .jsonPath("$.message") + .isEqualTo("Response took longer than timeout: PT3S"); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) public static class TestConfig { + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilterTests.java index 477ccf62..8ff35bed 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -21,11 +20,14 @@ import java.net.URI; import org.junit.Test; import org.mockito.ArgumentCaptor; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.route.Route; import org.springframework.http.HttpMethod; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.util.UriComponentsBuilder; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -34,9 +36,6 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR; -import org.springframework.web.util.UriComponentsBuilder; -import reactor.core.publisher.Mono; - /** * @author Spencer Gibb */ @@ -45,20 +44,17 @@ public class RouteToRequestUrlFilterTests { @Test public void happyPath() { MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/get?a=b") - .build(); + .get("http://localhost/get?a=b").build(); ServerWebExchange webExchange = testFilter(request, "http://myhost/mypath"); URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - assertThat(uri).hasScheme("http").hasHost("myhost") - .hasPath("/get") + assertThat(uri).hasScheme("http").hasHost("myhost").hasPath("/get") .hasParameter("a", "b"); } @Test public void happyPathLb() { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/getb") + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/getb") .build(); ServerWebExchange webExchange = testFilter(request, "lb://myhost"); @@ -68,29 +64,27 @@ public class RouteToRequestUrlFilterTests { @Test(expected = IllegalStateException.class) public void invalidHost() { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/getb") + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/getb") .build(); testFilter(request, "lb://my_host"); } @Test public void happyPathLbPlusScheme() { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/getb") + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/getb") .build(); ServerWebExchange webExchange = testFilter(request, "lb:http://myhost"); URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); assertThat(uri).hasScheme("http").hasHost("myhost"); - String schemePrefix = webExchange.getRequiredAttribute(GATEWAY_SCHEME_PREFIX_ATTR); + String schemePrefix = webExchange + .getRequiredAttribute(GATEWAY_SCHEME_PREFIX_ATTR); assertThat(schemePrefix).isEqualTo("lb"); } @Test public void noQueryParams() { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/get") + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/get") .build(); ServerWebExchange webExchange = testFilter(request, "http://myhost"); @@ -100,21 +94,20 @@ public class RouteToRequestUrlFilterTests { @Test public void encodedParameters() { - URI url = UriComponentsBuilder.fromUriString("http://localhost/get?a=b&c=d[]").buildAndExpand().encode().toUri(); + URI url = UriComponentsBuilder.fromUriString("http://localhost/get?a=b&c=d[]") + .buildAndExpand().encode().toUri(); // prove that it is encoded assertThat(url.getRawQuery()).isEqualTo("a=b&c=d%5B%5D"); assertThat(url).hasParameter("c", "d[]"); - MockServerHttpRequest request = MockServerHttpRequest - .method(HttpMethod.GET, url) + MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, url) .build(); ServerWebExchange webExchange = testFilter(request, "http://myhost"); URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - assertThat(uri).hasScheme("http").hasHost("myhost") - .hasParameter("a", "b") + assertThat(uri).hasScheme("http").hasHost("myhost").hasParameter("a", "b") .hasParameter("c", "d[]"); // prove that it is not double encoded @@ -123,21 +116,21 @@ public class RouteToRequestUrlFilterTests { @Test public void encodedUrl() { - URI url = UriComponentsBuilder.fromUriString("http://localhost/abc def/get").buildAndExpand().encode().toUri(); + URI url = UriComponentsBuilder.fromUriString("http://localhost/abc def/get") + .buildAndExpand().encode().toUri(); // prove that it is encoded assertThat(url.getRawPath()).isEqualTo("/abc%20def/get"); assertThat(url).hasPath("/abc def/get"); - MockServerHttpRequest request = MockServerHttpRequest - .method(HttpMethod.GET, url) + MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, url) .build(); - ServerWebExchange webExchange = testFilter(request, "http://myhost/abc%20def/get"); + ServerWebExchange webExchange = testFilter(request, + "http://myhost/abc%20def/get"); URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - assertThat(uri).hasScheme("http").hasHost("myhost") - .hasPath("/abc def/get"); + assertThat(uri).hasScheme("http").hasHost("myhost").hasPath("/abc def/get"); // prove that it is not double encoded assertThat(uri.getRawPath()).isEqualTo("/abc%20def/get"); @@ -150,15 +143,13 @@ public class RouteToRequestUrlFilterTests { // prove that it is unencoded assertThat(url.getRawQuery()).isEqualTo("a=b&c=d[]"); - MockServerHttpRequest request = MockServerHttpRequest - .method(HttpMethod.GET, url) + MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, url) .build(); ServerWebExchange webExchange = testFilter(request, "http://myhost"); URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - assertThat(uri).hasScheme("http").hasHost("myhost") - .hasParameter("a", "b") + assertThat(uri).hasScheme("http").hasHost("myhost").hasParameter("a", "b") .hasParameter("c", "d[]"); // prove that it is NOT encoded @@ -167,42 +158,31 @@ public class RouteToRequestUrlFilterTests { @Test public void matcherWorks() { - testMatcher(true, - "lb:a123:stuff", - "lb:abc:stuff", - "lb:a.bc:stuff", - "lb:a-bc:stuff", - "lb:a+bc:stuff" - ); - testMatcher(false, - "lb:a", - "lb:a123", - "lb:123:stuff", - "lb:a//:stuff" - ); + testMatcher(true, "lb:a123:stuff", "lb:abc:stuff", "lb:a.bc:stuff", + "lb:a-bc:stuff", "lb:a+bc:stuff"); + testMatcher(false, "lb:a", "lb:a123", "lb:123:stuff", "lb:a//:stuff"); } private void testMatcher(boolean shouldMatch, String... uris) { for (String s : uris) { URI uri = URI.create(s); boolean result = RouteToRequestUrlFilter.hasAnotherScheme(uri); - assertThat(result).as("%s should match: %s", s, result).isEqualTo(shouldMatch); + assertThat(result).as("%s should match: %s", s, result) + .isEqualTo(shouldMatch); } } private ServerWebExchange testFilter(MockServerHttpRequest request, String routeUri) { - Route value = Route.async().id("1") - .uri(URI.create(routeUri)) - .order(0) - .predicate(swe -> true) - .build(); + Route value = Route.async().id("1").uri(URI.create(routeUri)).order(0) + .predicate(swe -> true).build(); ServerWebExchange exchange = MockServerWebExchange.from(request); exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, value); GatewayFilterChain filterChain = mock(GatewayFilterChain.class); - ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class); + ArgumentCaptor captor = ArgumentCaptor + .forClass(ServerWebExchange.class); when(filterChain.filter(captor.capture())).thenReturn(Mono.empty()); RouteToRequestUrlFilter filter = new RouteToRequestUrlFilter(); @@ -210,4 +190,5 @@ public class RouteToRequestUrlFilterTests { return captor.getValue(); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilterTests.java index 495b25d0..127f0dd7 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/WebsocketRoutingFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -32,4 +31,5 @@ public class WebsocketRoutingFilterTests { assertThat(convertHttpToWs("HTTPS")).isEqualTo("wss"); assertThat(convertHttpToWs("tcp")).isEqualTo("tcp"); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilterTests.java index ec7b7515..9c2a7fce 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/WeightCalculatorWebFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter; @@ -25,6 +24,7 @@ import java.util.Random; import org.jetbrains.annotations.NotNull; import org.junit.Test; import org.mockito.ArgumentCaptor; + import org.springframework.cloud.gateway.event.PredicateArgsEvent; import org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter.GroupWeightConfig; import org.springframework.cloud.gateway.support.WeightConfig; @@ -56,13 +56,16 @@ public class WeightCalculatorWebFilterTests { assertWeightCalculation(filter, grp2, grp2idx++, 1, asList(1.0)); assertWeightCalculation(filter, grp1, grp1idx++, 3, asList(0.25, 0.75), 0.25); assertWeightCalculation(filter, grp2, grp2idx++, 1, asList(0.5, 0.5), 0.5); - assertWeightCalculation(filter, grp1, grp1idx++, 6, asList(0.1, 0.3, 0.6), 0.1, 0.4); - assertWeightCalculation(filter, grp2, grp2idx++, 2, asList(0.25, 0.25, 0.5), 0.25, 0.5); - assertWeightCalculation(filter, grp2, grp2idx++, 4, asList(0.125, 0.125, 0.25, 0.5), 0.125, 0.25, 0.5); + assertWeightCalculation(filter, grp1, grp1idx++, 6, asList(0.1, 0.3, 0.6), 0.1, + 0.4); + assertWeightCalculation(filter, grp2, grp2idx++, 2, asList(0.25, 0.25, 0.5), 0.25, + 0.5); + assertWeightCalculation(filter, grp2, grp2idx++, 4, + asList(0.125, 0.125, 0.25, 0.5), 0.125, 0.25, 0.5); } - private void assertWeightCalculation(WeightCalculatorWebFilter filter, String group, int item, - int weight, List normalized, Double... middleRanges) { + private void assertWeightCalculation(WeightCalculatorWebFilter filter, String group, + int item, int weight, List normalized, Double... middleRanges) { String routeId = route(item); filter.addWeightConfig(new WeightConfig(group, routeId, weight)); @@ -72,23 +75,19 @@ public class WeightCalculatorWebFilterTests { GroupWeightConfig config = groupWeights.get(group); assertThat(config.group).isEqualTo(group); - assertThat(config.weights).hasSize(item) - .containsEntry(routeId, weight); + assertThat(config.weights).hasSize(item).containsEntry(routeId, weight); assertThat(config.normalizedWeights).hasSize(item); for (int i = 0; i < normalized.size(); i++) { - assertThat(config.normalizedWeights) - .containsEntry(route(i+1), normalized.get(i)); + assertThat(config.normalizedWeights).containsEntry(route(i + 1), + normalized.get(i)); } for (int i = 0; i < normalized.size(); i++) { - assertThat(config.rangeIndexes) - .containsEntry(i, route(i+1)); + assertThat(config.rangeIndexes).containsEntry(i, route(i + 1)); } - assertThat(config.ranges).hasSize(item + 1) - .startsWith(0.0) - .endsWith(1.0); + assertThat(config.ranges).hasSize(item + 1).startsWith(0.0).endsWith(1.0); if (middleRanges.length > 0) { assertThat(config.ranges).contains(middleRanges); @@ -97,7 +96,7 @@ public class WeightCalculatorWebFilterTests { @NotNull private String route(int i) { - return "route"+i; + return "route" + i; } @Test @@ -109,14 +108,12 @@ public class WeightCalculatorWebFilterTests { Random random = mock(Random.class); - when(random.nextDouble()) - .thenReturn(0.05) - .thenReturn(0.2) - .thenReturn(0.6); + when(random.nextDouble()).thenReturn(0.05).thenReturn(0.2).thenReturn(0.6); filter.setRandom(random); - MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("http://localhost").build()); + MockServerWebExchange exchange = MockServerWebExchange + .from(MockServerHttpRequest.get("http://localhost").build()); WebFilterChain filterChain = mock(WebFilterChain.class); filter.filter(exchange, filterChain); @@ -144,7 +141,8 @@ public class WeightCalculatorWebFilterTests { PredicateArgsEvent event = new PredicateArgsEvent(this, "routeA", args); filter.handle(event); - ArgumentCaptor configCaptor = ArgumentCaptor.forClass(WeightConfig.class); + ArgumentCaptor configCaptor = ArgumentCaptor + .forClass(WeightConfig.class); verify(filter).addWeightConfig(configCaptor.capture()); WeightConfig weightConfig = configCaptor.getValue(); @@ -152,4 +150,5 @@ public class WeightCalculatorWebFilterTests { assertThat(weightConfig.getRouteId()).isEqualTo("routeA"); assertThat(weightConfig.getWeight()).isEqualTo(1); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderGatewayFilterFactoryTests.java index f0704191..ed674304 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,15 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; +import java.util.Map; + import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -32,8 +34,6 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; -import java.util.Map; - 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; @@ -50,26 +50,20 @@ public class AddRequestHeaderGatewayFilterFactoryTests extends BaseWebClientTest @Test public void addRequestHeaderFilterWorks() { - testClient.get() - .uri("/headers") - .header("Host", "www.addrequestheader.org") - .exchange() - .expectBody(Map.class) - .consumeWith(result -> { - Map headers = getMap(result.getResponseBody(), "headers"); - assertThat(headers).containsEntry("X-Request-Example", "ValueA"); + testClient.get().uri("/headers").header("Host", "www.addrequestheader.org") + .exchange().expectBody(Map.class).consumeWith(result -> { + Map headers = getMap(result.getResponseBody(), + "headers"); + assertThat(headers).containsEntry("X-Request-Example", "ValueA"); }); } @Test public void addRequestHeaderFilterWorksJavaDsl() { - testClient.get() - .uri("/headers") - .header("Host", "www.addrequestheaderjava.org") - .exchange() - .expectBody(Map.class) - .consumeWith(result -> { - Map headers = getMap(result.getResponseBody(), "headers"); + testClient.get().uri("/headers").header("Host", "www.addrequestheaderjava.org") + .exchange().expectBody(Map.class).consumeWith(result -> { + Map headers = getMap(result.getResponseBody(), + "headers"); assertThat(headers).containsEntry("X-Request-Acme", "ValueB"); }); } @@ -84,13 +78,14 @@ public class AddRequestHeaderGatewayFilterFactoryTests extends BaseWebClientTest @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { - return builder.routes() - .route("add_request_header_java_test", r -> - r.path("/headers").and().host("**.addrequestheaderjava.org") - .filters(f -> f.prefixPath("/httpbin").addRequestHeader("X-Request-Acme", "ValueB")) - .uri(uri)) + return builder.routes().route("add_request_header_java_test", + r -> r.path("/headers").and().host("**.addrequestheaderjava.org") + .filters(f -> f.prefixPath("/httpbin") + .addRequestHeader("X-Request-Acme", "ValueB")) + .uri(uri)) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterGatewayFilterFactoryTests.java index 732cffd5..52e96986 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -24,6 +23,7 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -66,60 +66,65 @@ public class AddRequestParameterGatewayFilterFactoryTests extends BaseWebClientT @Test public void addRequestParameterFilterWorksEncodedQueryJavaDsl() { - testRequestParameterFilter("www.addreqparamjava.org", "ValueB", "javaname", "%E6%89%8E%E6%A0%B9"); + testRequestParameterFilter("www.addreqparamjava.org", "ValueB", "javaname", + "%E6%89%8E%E6%A0%B9"); } private void testRequestParameterFilter(String name, String value) { testRequestParameterFilter("www.addrequestparameter.org", "ValueA", name, value); } - private void testRequestParameterFilter(String host, String expectedValue, String name, String value) { + private void testRequestParameterFilter(String host, String expectedValue, + String name, String value) { String query; if (name != null) { query = "?" + name + "=" + value; - } else { + } + else { query = ""; } - URI uri = UriComponentsBuilder.fromUriString(this.baseUri+"/get" + query).build(true).toUri(); + URI uri = UriComponentsBuilder.fromUriString(this.baseUri + "/get" + query) + .build(true).toUri(); boolean checkForEncodedValue = containsEncodedParts(uri); - testClient.get() - .uri(uri) - .header("Host", host) - .exchange() - .expectBody(Map.class) + testClient.get().uri(uri).header("Host", host).exchange().expectBody(Map.class) .consumeWith(response -> { Map args = getMap(response.getResponseBody(), "args"); - assertThat(args).containsEntry("example", expectedValue); - if (name != null) { - if (checkForEncodedValue) { - try { - assertThat(args).containsEntry(name, URLDecoder.decode(value, "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } else { - assertThat(args).containsEntry(name, value); - } - } - }); + assertThat(args).containsEntry("example", expectedValue); + if (name != null) { + if (checkForEncodedValue) { + try { + assertThat(args).containsEntry(name, + URLDecoder.decode(value, "UTF-8")); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + else { + assertThat(args).containsEntry(name, value); + } + } + }); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) public static class TestConfig { + @Value("${test.uri}") String uri; @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { - return builder.routes() - .route("add_request_param_java_test", r -> - r.path("/get").and().host("**.addreqparamjava.org") - .filters(f -> f.prefixPath("/httpbin").addRequestParameter("example", "ValueB")) - .uri(uri)) + return builder.routes().route("add_request_param_java_test", + r -> r.path("/get").and().host("**.addreqparamjava.org") + .filters(f -> f.prefixPath("/httpbin") + .addRequestParameter("example", "ValueB")) + .uri(uri)) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddResponseParameterGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddResponseParameterGatewayFilterFactoryTests.java index 7b76f003..c2e9129e 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddResponseParameterGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/AddResponseParameterGatewayFilterFactoryTests.java @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -43,34 +42,33 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen public class AddResponseParameterGatewayFilterFactoryTests extends BaseWebClientTests { @Test - public void testResposneParameterFilter() { - URI uri = UriComponentsBuilder.fromUriString(this.baseUri+"/get").build(true).toUri(); + public void testResposneParameterFilter() { + URI uri = UriComponentsBuilder.fromUriString(this.baseUri + "/get").build(true) + .toUri(); String host = "www.addresponseparamjava.org"; String expectedValue = "myresponsevalue"; - testClient.get() - .uri(uri) - .header("Host", host) - .exchange() - .expectHeader().valueEquals("example", expectedValue); + testClient.get().uri(uri).header("Host", host).exchange().expectHeader() + .valueEquals("example", expectedValue); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) public static class TestConfig { + @Value("${test.uri}") String uri; @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { - return builder.routes() - .route("add_response_param_java_test", r -> - r.path("/get").and().host("**.addresponseparamjava.org") - .filters(f -> f.prefixPath("/httpbin") - .addResponseHeader("example", "myresponsevalue")) - .uri(uri)) + return builder.routes().route("add_response_param_java_test", + r -> r.path("/get").and().host("**.addresponseparamjava.org") + .filters(f -> f.prefixPath("/httpbin") + .addResponseHeader("example", "myresponsevalue")) + .uri(uri)) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/HystrixGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/HystrixGatewayFilterFactoryTests.java index e806383e..578e9469 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/HystrixGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/HystrixGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -42,96 +41,80 @@ public class HystrixGatewayFilterFactoryTests extends BaseWebClientTests { @Test public void hystrixFilterWorks() { - testClient.get().uri("/get") - .header("Host", "www.hystrixsuccess.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "hystrix_success_test"); + testClient.get().uri("/get").header("Host", "www.hystrixsuccess.org").exchange() + .expectStatus().isOk().expectHeader() + .valueEquals(ROUTE_ID_HEADER, "hystrix_success_test"); } @Test public void hystrixFilterTimesout() { - testClient.get().uri("/delay/3") - .header("Host", "www.hystrixfailure.org") - .exchange() - .expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT) - .expectBody() - .jsonPath("$.status") + testClient.get().uri("/delay/3").header("Host", "www.hystrixfailure.org") + .exchange().expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT) + .expectBody().jsonPath("$.status") .isEqualTo(String.valueOf(HttpStatus.GATEWAY_TIMEOUT.value())); } /* - * Tests that timeouts bubbling from the underpinning WebClient are treated the same as - * Hystrix timeouts in terms of outside response. (Internally, timeouts from the WebClient - * are seen as command failures and trigger the opening of circuit breakers the same way - * timeouts do; it may be confusing in terms of the Hystrix metrics though) + * Tests that timeouts bubbling from the underpinning WebClient are treated the same + * as Hystrix timeouts in terms of outside response. (Internally, timeouts from the + * WebClient are seen as command failures and trigger the opening of circuit breakers + * the same way timeouts do; it may be confusing in terms of the Hystrix metrics + * though) */ @Test public void hystrixTimeoutFromWebClient() { - testClient.get().uri("/delay/10") - .header("Host", "www.hystrixresponsestall.org") - .exchange() - .expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT); + testClient.get().uri("/delay/10").header("Host", "www.hystrixresponsestall.org") + .exchange().expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT); } @Test public void hystrixFilterFallback() { - testClient.get().uri("/delay/3?a=b") - .header("Host", "www.hystrixfallback.org") - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"from\":\"fallbackcontroller\"}"); + testClient.get().uri("/delay/3?a=b").header("Host", "www.hystrixfallback.org") + .exchange().expectStatus().isOk().expectBody() + .json("{\"from\":\"fallbackcontroller\"}"); } @Test public void hystrixFilterExceptionFallback() { testClient.get().uri("/delay/3") - .header("Host", "www.hystrixexceptionfallback.org") - .exchange() - .expectStatus().isOk() - .expectHeader().value(RETRIEVED_EXCEPTION, containsString("HystrixTimeoutException")); + .header("Host", "www.hystrixexceptionfallback.org").exchange() + .expectStatus().isOk().expectHeader() + .value(RETRIEVED_EXCEPTION, containsString("HystrixTimeoutException")); } @Test public void hystrixFilterWorksJavaDsl() { - testClient.get().uri("/get") - .header("Host", "www.hystrixjava.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "hystrix_java"); + testClient.get().uri("/get").header("Host", "www.hystrixjava.org").exchange() + .expectStatus().isOk().expectHeader() + .valueEquals(ROUTE_ID_HEADER, "hystrix_java"); } @Test public void hystrixFilterFallbackJavaDsl() { - testClient.get().uri("/delay/3") - .header("Host", "www.hystrixjava.org") - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"from\":\"fallbackcontroller2\"}"); + testClient.get().uri("/delay/3").header("Host", "www.hystrixjava.org").exchange() + .expectStatus().isOk().expectBody() + .json("{\"from\":\"fallbackcontroller2\"}"); } @Test public void hystrixFilterConnectFailure() { - testClient.get().uri("/delay/3") - .header("Host", "www.hystrixconnectfail.org") - .exchange() - .expectStatus().is5xxServerError(); + testClient.get().uri("/delay/3").header("Host", "www.hystrixconnectfail.org") + .exchange().expectStatus().is5xxServerError(); } @Test public void hystrixFilterErrorPage() { - testClient.get().uri("/delay/3") - .header("Host", "www.hystrixconnectfail.org") - .accept(TEXT_HTML) - .exchange() - .expectStatus().is5xxServerError() + testClient.get().uri("/delay/3").header("Host", "www.hystrixconnectfail.org") + .accept(TEXT_HTML).exchange().expectStatus().is5xxServerError() .expectBody().consumeWith(res -> { - final String body = new String(res.getResponseBody(), UTF_8); + final String body = new String(res.getResponseBody(), UTF_8); - Assert.isTrue(body.contains("

Whitelabel Error Page

"), - "Cannot find the expected white-label error page title in the response"); - Assert.isTrue(body.contains("(type=Internal Server Error, status=500)"), - "Cannot find the expected error status report in the response"); - }); + Assert.isTrue(body.contains("

Whitelabel Error Page

"), + "Cannot find the expected white-label error page title in the response"); + Assert.isTrue( + body.contains("(type=Internal Server Error, status=500)"), + "Cannot find the expected error status report in the response"); + }); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/HystrixTestConfig.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/HystrixTestConfig.java index c4876881..5746d088 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/HystrixTestConfig.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/HystrixTestConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -69,24 +68,23 @@ public class HystrixTestConfig { @Bean public RouteLocator hystrixRouteLocator(RouteLocatorBuilder builder) { - return builder.routes() - .route("hystrix_java", r -> r.host("**.hystrixjava.org") - .filters(f -> f.prefixPath("/httpbin") - .hystrix(config -> config.setFallbackUri("forward:/fallbackcontroller2"))) - .uri(uri)) - .route("hystrix_connection_failure", r -> r.host("**.hystrixconnectfail.org") - .filters(f -> f.prefixPath("/httpbin") - .hystrix(config -> { - })) - .uri("lb:badservice")) + return builder.routes().route("hystrix_java", r -> r.host("**.hystrixjava.org") + .filters(f -> f.prefixPath("/httpbin").hystrix( + config -> config.setFallbackUri("forward:/fallbackcontroller2"))) + .uri(uri)) + .route("hystrix_connection_failure", + r -> r.host("**.hystrixconnectfail.org") + .filters(f -> f.prefixPath("/httpbin").hystrix(config -> { + })).uri("lb:badservice")) /* * This is a route encapsulated in a hystrix command that is ready to wait * for a response far longer than the underpinning WebClient would. */ - .route("hystrix_response_stall", r -> r.host("**.hystrixresponsestall.org") - .filters(f -> f.prefixPath("/httpbin") - .hystrix(config -> config.setName("stalling-command"))) - .uri(uri)) + .route("hystrix_response_stall", + r -> r.host("**.hystrixresponsestall.org") + .filters(f -> f.prefixPath("/httpbin").hystrix( + config -> config.setName("stalling-command"))) + .uri(uri)) .build(); } @@ -96,9 +94,12 @@ public class HystrixTestConfig { } @Bean - RouterFunction routerFunction(ExceptionFallbackHandler exceptionFallbackHandler) { - return route(GET("/exceptionFallback"), exceptionFallbackHandler::retrieveExceptionInfo); + RouterFunction routerFunction( + ExceptionFallbackHandler exceptionFallbackHandler) { + return route(GET("/exceptionFallback"), + exceptionFallbackHandler::retrieveExceptionInfo); } + } class ExceptionFallbackHandler { @@ -107,11 +108,10 @@ class ExceptionFallbackHandler { Mono retrieveExceptionInfo(ServerRequest serverRequest) { String exceptionName = serverRequest.attribute(HYSTRIX_EXECUTION_EXCEPTION_ATTR) - .map(exception -> exception.getClass().getName()) - .orElse(""); - return ServerResponse.ok().header(RETRIEVED_EXCEPTION, exceptionName) - .build(); + .map(exception -> exception.getClass().getName()).orElse(""); + return ServerResponse.ok().header(RETRIEVED_EXCEPTION, exceptionName).build(); } + } @Configuration @@ -124,4 +124,5 @@ class TestBadRibbonConfig { public ServerList ribbonServerList() { return new StaticServerList<>(new Server("https", "localhost", this.port)); } -} \ No newline at end of file + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactoryTest.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactoryTest.java index 28e469dd..89c5e30c 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactoryTest.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/PrefixPathGatewayFilterFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,16 +12,17 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ -package org.springframework.cloud.gateway.filter.factory; -import reactor.core.publisher.Mono; +package org.springframework.cloud.gateway.filter.factory; import java.net.URI; import java.util.LinkedHashSet; + import org.junit.Test; import org.mockito.ArgumentCaptor; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; @@ -44,19 +45,18 @@ public class PrefixPathGatewayFilterFactoryTest { testPrefixPathFilter("/foo", "/hello%20world", "/foo/hello%20world"); } - private void testPrefixPathFilter(String prefix, String path, String expectedPath) { - GatewayFilter filter = new PrefixPathGatewayFilterFactory().apply(c -> c.setPrefix(prefix)); + GatewayFilter filter = new PrefixPathGatewayFilterFactory() + .apply(c -> c.setPrefix(prefix)); MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost" + path) - .build(); + .get("http://localhost" + path).build(); ServerWebExchange exchange = MockServerWebExchange.from(request); - GatewayFilterChain filterChain = mock(GatewayFilterChain.class); - ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class); + ArgumentCaptor captor = ArgumentCaptor + .forClass(ServerWebExchange.class); when(filterChain.filter(captor.capture())).thenReturn(Mono.empty()); filter.filter(exchange, filterChain); @@ -64,8 +64,9 @@ public class PrefixPathGatewayFilterFactoryTest { ServerWebExchange webExchange = captor.getValue(); assertThat(webExchange.getRequest().getURI()).hasPath(expectedPath); - LinkedHashSet uris = webExchange.getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + LinkedHashSet uris = webExchange + .getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); assertThat(uris).contains(request.getURI()); } -} \ No newline at end of file +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactoryTests.java index 8ad5876c..7977a4a3 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -21,6 +20,7 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -37,7 +37,6 @@ 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; - /** * @author Spencer Gibb */ @@ -48,13 +47,11 @@ public class PreserveHostHeaderGatewayFilterFactoryTests extends BaseWebClientTe @Test public void preserveHostHeaderGatewayFilterFactoryWorks() { - testClient.get().uri("/headers") - .header("Host", "www.preservehostheader.org") - .exchange() - .expectStatus().isOk() - .expectBody(Map.class) + testClient.get().uri("/headers").header("Host", "www.preservehostheader.org") + .exchange().expectStatus().isOk().expectBody(Map.class) .consumeWith(result -> { - Map headers = getMap(result.getResponseBody(), "headers"); + Map headers = getMap(result.getResponseBody(), + "headers"); assertThat(headers).containsEntry("Host", "myhost.net"); }); } @@ -63,20 +60,20 @@ public class PreserveHostHeaderGatewayFilterFactoryTests extends BaseWebClientTe @SpringBootConfiguration @Import(DefaultTestConfig.class) public static class TestConfig { + @Value("${test.uri}") String uri; @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { return builder.routes().route("test_preserve_host_header", - r -> r.order(-1) - .host("**.preservehostheader.org") - .filters(f -> f.prefixPath("/httpbin") - .preserveHostHeader() - .setRequestHeader("Host", "myhost.net")) - .uri(uri)) + r -> r.order(-1).host("**.preservehostheader.org") + .filters(f -> f.prefixPath("/httpbin").preserveHostHeader() + .setRequestHeader("Host", "myhost.net")) + .uri(uri)) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactoryTests.java index a766a326..0e619c3f 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RedirectToGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; 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; @@ -41,22 +41,16 @@ public class RedirectToGatewayFilterFactoryTests extends BaseWebClientTests { @Test public void redirectToFilterWorks() { - testClient.get() - .uri("/") - .header("Host", "www.redirectto.org") - .exchange() - .expectStatus().isEqualTo(HttpStatus.FOUND) - .expectHeader().valueEquals(HttpHeaders.LOCATION, "http://example.org"); + testClient.get().uri("/").header("Host", "www.redirectto.org").exchange() + .expectStatus().isEqualTo(HttpStatus.FOUND).expectHeader() + .valueEquals(HttpHeaders.LOCATION, "http://example.org"); } @Test public void redirectToRelativeUrlFilterWorks() { - testClient.get() - .uri("/") - .header("Host", "www.relativeredirect.org") - .exchange() - .expectStatus().isEqualTo(HttpStatus.FOUND) - .expectHeader().valueEquals(HttpHeaders.LOCATION, "/index.html#/customers"); + testClient.get().uri("/").header("Host", "www.relativeredirect.org").exchange() + .expectStatus().isEqualTo(HttpStatus.FOUND).expectHeader() + .valueEquals(HttpHeaders.LOCATION, "/index.html#/customers"); } @EnableAutoConfiguration @@ -66,12 +60,13 @@ public class RedirectToGatewayFilterFactoryTests extends BaseWebClientTests { @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { - return builder.routes() - .route("relative_redirect", r -> r.host("**.relativeredirect.org") + return builder.routes().route("relative_redirect", + r -> r.host("**.relativeredirect.org") .filters(f -> f.redirect(302, "/index.html#/customers")) .uri("no://op")) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderGatewayFilterFactoryTests.java index ebb187fb..592d69da 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -21,6 +20,7 @@ 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; @@ -40,14 +40,11 @@ public class RemoveRequestHeaderGatewayFilterFactoryTests extends BaseWebClientT @Test public void removeRequestHeaderFilterWorks() { - testClient.get() - .uri("/headers") - .header("Host", "www.removerequestheader.org") - .header("X-Request-Foo", "Bar") - .exchange() - .expectStatus().isOk() + testClient.get().uri("/headers").header("Host", "www.removerequestheader.org") + .header("X-Request-Foo", "Bar").exchange().expectStatus().isOk() .expectBody(Map.class).consumeWith(result -> { - Map headers = getMap(result.getResponseBody(), "headers"); + Map headers = getMap(result.getResponseBody(), + "headers"); assertThat(headers).doesNotContainKey("X-Request-Foo"); }); } @@ -55,6 +52,8 @@ public class RemoveRequestHeaderGatewayFilterFactoryTests extends BaseWebClientT @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderGatewayFilterFactoryTests.java index 2a15b8bb..9657402e 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; 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; @@ -36,17 +36,16 @@ public class RemoveResponseHeaderGatewayFilterFactoryTests extends BaseWebClient @Test public void removeResponseHeaderFilterWorks() { - testClient.get() - .uri("/headers") - .header("Host", "www.removereresponseheader.org") - .exchange() - .expectStatus().isOk() - .expectHeader().doesNotExist("X-Request-Foo"); + testClient.get().uri("/headers").header("Host", "www.removereresponseheader.org") + .exchange().expectStatus().isOk().expectHeader() + .doesNotExist("X-Request-Foo"); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactoryIntegrationTests.java index ab4dd0f6..f73571e1 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactoryIntegrationTests.java @@ -1,11 +1,29 @@ +/* + * Copyright 2018-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter.factory; import java.net.URI; import java.util.Optional; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.Ignore; 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; @@ -18,8 +36,6 @@ import org.springframework.context.annotation.Import; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import com.fasterxml.jackson.databind.JsonNode; - import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -31,6 +47,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @DirtiesContext public class RequestHeaderToRequestUriGatewayFilterFactoryIntegrationTests extends BaseWebClientTests { + @LocalServerPort int port; @@ -68,6 +85,7 @@ public class RequestHeaderToRequestUriGatewayFilterFactoryIntegrationTests @SpringBootConfiguration @Import(DefaultTestConfig.class) public static class TestConfig { + @Bean public RouteLocator routeLocator(RouteLocatorBuilder builder) { return builder.routes() @@ -80,5 +98,7 @@ public class RequestHeaderToRequestUriGatewayFilterFactoryIntegrationTests .uri("http://example.com")) .build(); } + } -} \ No newline at end of file + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactoryTests.java index d5ed8e31..7c5e117f 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestHeaderToRequestUriGatewayFilterFactoryTests.java @@ -1,9 +1,27 @@ +/* + * Copyright 2018-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter.factory; import java.net.URI; import org.junit.Test; import org.mockito.ArgumentCaptor; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; @@ -15,8 +33,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; -import reactor.core.publisher.Mono; - /** * @author Toshiaki Maki */ @@ -81,4 +97,5 @@ public class RequestHeaderToRequestUriGatewayFilterFactoryTests { assertThat(uri).isNotNull(); assertThat(uri.toURL().toString()).isEqualTo("http://localhost"); } -} \ No newline at end of file + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestRateLimiterGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestRateLimiterGatewayFilterFactoryTests.java index d5280a67..c682e14e 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestRateLimiterGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestRateLimiterGatewayFilterFactoryTests.java @@ -1,7 +1,28 @@ +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter.factory; +import java.util.Collections; +import java.util.Map; + import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.SpringBootConfiguration; @@ -30,13 +51,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; -import reactor.core.publisher.Mono; - -import java.util.Collections; -import java.util.Map; - /** - * see https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L36-L62 + * see + * https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L36-L62 + * * @author Spencer Gibb */ @RunWith(SpringRunner.class) @@ -44,6 +62,10 @@ import java.util.Map; @DirtiesContext public class RequestRateLimiterGatewayFilterFactoryTests extends BaseWebClientTests { + @Autowired + @Qualifier("resolver2") + KeyResolver resolver2; + @Autowired private ApplicationContext context; @@ -53,10 +75,6 @@ public class RequestRateLimiterGatewayFilterFactoryTests extends BaseWebClientTe @MockBean private GatewayFilterChain filterChain; - @Autowired - @Qualifier("resolver2") - KeyResolver resolver2; - @Test public void allowedWorks() { // tests that auto wired as default works @@ -65,7 +83,8 @@ public class RequestRateLimiterGatewayFilterFactoryTests extends BaseWebClientTe @Test public void notAllowedWorks() { - assertFilterFactory(resolver2, "notallowedkey", false, HttpStatus.TOO_MANY_REQUESTS); + assertFilterFactory(resolver2, "notallowedkey", false, + HttpStatus.TOO_MANY_REQUESTS); } @Test @@ -78,16 +97,18 @@ public class RequestRateLimiterGatewayFilterFactoryTests extends BaseWebClientTe assertFilterFactory(exchange -> Mono.empty(), null, true, HttpStatus.OK, false); } - private void assertFilterFactory(KeyResolver keyResolver, String key, boolean allowed, HttpStatus expectedStatus) { + private void assertFilterFactory(KeyResolver keyResolver, String key, boolean allowed, + HttpStatus expectedStatus) { assertFilterFactory(keyResolver, key, allowed, expectedStatus, null); } - private void assertFilterFactory(KeyResolver keyResolver, String key, boolean allowed, HttpStatus expectedStatus, - Boolean denyEmptyKey) { + private void assertFilterFactory(KeyResolver keyResolver, String key, boolean allowed, + HttpStatus expectedStatus, Boolean denyEmptyKey) { String tokensRemaining = allowed ? "1" : "0"; - Map headers = Collections.singletonMap("X-Tokens-Remaining", tokensRemaining); + Map headers = Collections.singletonMap("X-Tokens-Remaining", + tokensRemaining); if (key != null) { when(rateLimiter.isAllowed("myroute", key)) @@ -98,22 +119,24 @@ public class RequestRateLimiterGatewayFilterFactoryTests extends BaseWebClientTe MockServerWebExchange exchange = MockServerWebExchange.from(request); exchange.getResponse().setStatusCode(HttpStatus.OK); exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, - Route.async().id("myroute").predicate(ex -> true) - .uri("http://localhost").build()); + Route.async().id("myroute").predicate(ex -> true).uri("http://localhost") + .build()); when(this.filterChain.filter(exchange)).thenReturn(Mono.empty()); - RequestRateLimiterGatewayFilterFactory factory = this.context.getBean(RequestRateLimiterGatewayFilterFactory.class); + RequestRateLimiterGatewayFilterFactory factory = this.context + .getBean(RequestRateLimiterGatewayFilterFactory.class); if (denyEmptyKey != null) { factory.setDenyEmptyKey(denyEmptyKey); } - GatewayFilter filter = factory.apply(config -> config.setKeyResolver(keyResolver)); + GatewayFilter filter = factory + .apply(config -> config.setKeyResolver(keyResolver)); Mono response = filter.filter(exchange, this.filterChain); response.subscribe(aVoid -> { assertThat(exchange.getResponse().getStatusCode()).isEqualTo(expectedStatus); - assertThat(exchange.getResponse().getHeaders()). - containsEntry("X-Tokens-Remaining", Collections.singletonList(tokensRemaining)); + assertThat(exchange.getResponse().getHeaders()).containsEntry( + "X-Tokens-Remaining", Collections.singletonList(tokensRemaining)); }); } @@ -122,6 +145,7 @@ public class RequestRateLimiterGatewayFilterFactoryTests extends BaseWebClientTe @SpringBootConfiguration @Import(BaseWebClientTests.DefaultTestConfig.class) public static class TestConfig { + @Bean @Primary KeyResolver resolver1() { @@ -132,5 +156,7 @@ public class RequestRateLimiterGatewayFilterFactoryTests extends BaseWebClientTe KeyResolver resolver2() { return exchange -> Mono.just("notallowedkey"); } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestSizeGatewayFilterFactoryTest.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestSizeGatewayFilterFactoryTest.java index f11a2201..2257b696 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestSizeGatewayFilterFactoryTest.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RequestSizeGatewayFilterFactoryTest.java @@ -1,7 +1,24 @@ +/* + * Copyright 2018-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter.factory; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -25,30 +42,35 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @SpringBootTest(webEnvironment = RANDOM_PORT) @DirtiesContext public class RequestSizeGatewayFilterFactoryTest extends BaseWebClientTests { - - private static final String responseMesssage = "Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB"; + + private static final String responseMesssage = "Request size is larger than permissible limit. Request size is 6.0 MB " + + "where permissible limit is 5.0 MB"; @Test public void setRequestSizeFilterWorks() { - testClient.get().uri("/headers") - .header("Host", "www.setrequestsize.org") - .header("content-length", "6000000") - .exchange().expectStatus().isEqualTo(HttpStatus.PAYLOAD_TOO_LARGE) - .expectHeader().valueMatches("errorMessage", responseMesssage); + testClient.get().uri("/headers").header("Host", "www.setrequestsize.org") + .header("content-length", "6000000").exchange().expectStatus() + .isEqualTo(HttpStatus.PAYLOAD_TOO_LARGE).expectHeader() + .valueMatches("errorMessage", responseMesssage); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) public static class TestConfig { + @Value("${test.uri}") String uri; @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { - return builder.routes().route("test_request_size", - r -> r.order(-1).host("**.setrequestsize.org").filters(f -> f.setRequestSize(5000000L)).uri(uri)) + return builder.routes() + .route("test_request_size", + r -> r.order(-1).host("**.setrequestsize.org") + .filters(f -> f.setRequestSize(5000000L)).uri(uri)) .build(); } + } -} \ No newline at end of file + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java index 38185049..f99106f1 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -59,59 +58,44 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest @Test public void retryFilterGet() { - testClient.get() - .uri("/retry?key=get") - .exchange() - .expectStatus().isOk() + testClient.get().uri("/retry?key=get").exchange().expectStatus().isOk() .expectBody(String.class).isEqualTo("3"); } @Test public void retryFilterFailure() { - testClient.get() - .uri("/retryalwaysfail?key=getjavafailure&count=4") - .header(HttpHeaders.HOST, "www.retryjava.org") - .exchange() - .expectStatus().is5xxServerError() - .expectBody(String.class).consumeWith(result -> { + testClient.get().uri("/retryalwaysfail?key=getjavafailure&count=4") + .header(HttpHeaders.HOST, "www.retryjava.org").exchange().expectStatus() + .is5xxServerError().expectBody(String.class).consumeWith(result -> { assertThat(result.getResponseBody()).contains("permanently broken"); - }); + }); } @Test public void retryFilterGetJavaDsl() { - testClient.get() - .uri("/retry?key=getjava&count=2") - .header(HttpHeaders.HOST, "www.retryjava.org") - .exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("2"); + testClient.get().uri("/retry?key=getjava&count=2") + .header(HttpHeaders.HOST, "www.retryjava.org").exchange().expectStatus() + .isOk().expectBody(String.class).isEqualTo("2"); } @Test - //TODO: support post + // TODO: support post public void retryFilterPost() { - testClient.post() - .uri("/retry?key=post") - .exchange() - .expectStatus().is5xxServerError(); - // .expectBody(String.class).isEqualTo("3"); + testClient.post().uri("/retry?key=post").exchange().expectStatus() + .is5xxServerError(); + // .expectBody(String.class).isEqualTo("3"); } @Test @SuppressWarnings("unchecked") public void retryFilterLoadBalancedWithMultipleServers() { String host = "www.retrywithloadbalancer.org"; - testClient.get() - .uri("/get") - .header(HttpHeaders.HOST, host) - .exchange() - .expectStatus().isOk() - .expectBody(Map.class) - .consumeWith(res -> { + testClient.get().uri("/get").header(HttpHeaders.HOST, host).exchange() + .expectStatus().isOk().expectBody(Map.class).consumeWith(res -> { Map body = res.getResponseBody(); assertThat(body).isNotNull(); - Map headers = (Map) body.get("headers"); + Map headers = (Map) body + .get("headers"); assertThat(headers).containsEntry("X-Forwarded-Host", host); }); } @@ -122,40 +106,40 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest @Import(DefaultTestConfig.class) @RibbonClient(name = "badservice2", configuration = TestBadRibbonConfig.class) public static class TestConfig { + Log log = LogFactory.getLog(getClass()); + ConcurrentHashMap map = new ConcurrentHashMap<>(); + @Value("${test.uri}") private String uri; - ConcurrentHashMap map = new ConcurrentHashMap<>(); - @RequestMapping("/httpbin/retryalwaysfail") - public ResponseEntity retryalwaysfail(@RequestParam("key") String key, @RequestParam(name = "count", defaultValue = "3") int count) { + public ResponseEntity retryalwaysfail(@RequestParam("key") String key, + @RequestParam(name = "count", defaultValue = "3") int count) { AtomicInteger num = map.computeIfAbsent(key, s -> new AtomicInteger()); int i = num.incrementAndGet(); - log.warn("Retry count: "+i); + log.warn("Retry count: " + i); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .header("X-Retry-Count", String.valueOf(i)) .body("permanently broken"); } @RequestMapping("/httpbin/retry") - public ResponseEntity retry(@RequestParam("key") String key, @RequestParam(name = "count", defaultValue = "3") int count) { + public ResponseEntity retry(@RequestParam("key") String key, + @RequestParam(name = "count", defaultValue = "3") int count) { AtomicInteger num = map.computeIfAbsent(key, s -> new AtomicInteger()); int i = num.incrementAndGet(); - log.warn("Retry count: "+i); + log.warn("Retry count: " + i); String body = String.valueOf(i); if (i < count) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .header("X-Retry-Count", body) - .body("temporarily broken"); + .header("X-Retry-Count", body).body("temporarily broken"); } - return ResponseEntity.status(HttpStatus.OK) - .header("X-Retry-Count", body) + return ResponseEntity.status(HttpStatus.OK).header("X-Retry-Count", body) .body(body); } - @Bean public RouteLocator hystrixRouteLocator(RouteLocatorBuilder builder) { return builder.routes() @@ -163,12 +147,14 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest .filters(f -> f.prefixPath("/httpbin") .retry(config -> config.setRetries(2))) .uri(uri)) - .route("retry_with_loadbalancer", r -> r.host("**.retrywithloadbalancer.org") - .filters(f -> f.prefixPath("/httpbin") - .retry(config -> config.setRetries(2))) - .uri("lb://badservice2")) + .route("retry_with_loadbalancer", + r -> r.host("**.retrywithloadbalancer.org") + .filters(f -> f.prefixPath("/httpbin") + .retry(config -> config.setRetries(2))) + .uri("lb://badservice2")) .build(); } + } protected static class TestBadRibbonConfig { @@ -178,8 +164,11 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest @Bean public ServerList ribbonServerList() { - return new StaticServerList<>(new Server("https", "localhost.domain.doesnot.exist", this.port), new Server("localhost", this.port)); + return new StaticServerList<>( + new Server("https", "localhost.domain.doesnot.exist", this.port), + new Server("localhost", this.port)); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactoryIntegrationTests.java index 5fb5b9b1..84538884 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; 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; @@ -36,16 +36,15 @@ public class RewritePathGatewayFilterFactoryIntegrationTests extends BaseWebClie @Test public void rewritePathFilterWorks() { - testClient.get() - .uri("/foo/get") - .header("Host", "www.baz.org") - .exchange() + testClient.get().uri("/foo/get").header("Host", "www.baz.org").exchange() .expectStatus().isOk(); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactoryTests.java index e49abd95..17d576d1 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -22,6 +21,8 @@ import java.util.LinkedHashSet; import org.junit.Test; import org.mockito.ArgumentCaptor; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.http.HttpMethod; @@ -36,8 +37,6 @@ import static org.mockito.Mockito.when; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; -import reactor.core.publisher.Mono; - /** * @author Spencer Gibb */ @@ -55,22 +54,26 @@ public class RewritePathGatewayFilterFactoryTests { @Test public void rewritePathFilterWithNamedGroupWorks() { - testRewriteFilter("/foo/(?\\d.*)", "/bar/baz/$\\{id}", "/foo/123", "/bar/baz/123"); + testRewriteFilter("/foo/(?\\d.*)", "/bar/baz/$\\{id}", "/foo/123", + "/bar/baz/123"); } - private ServerWebExchange testRewriteFilter(String regex, String replacement, String actualPath, String expectedPath) { - GatewayFilter filter = new RewritePathGatewayFilterFactory().apply(c -> c.setRegexp(regex).setReplacement(replacement)); + private ServerWebExchange testRewriteFilter(String regex, String replacement, + String actualPath, String expectedPath) { + GatewayFilter filter = new RewritePathGatewayFilterFactory() + .apply(c -> c.setRegexp(regex).setReplacement(replacement)); - URI url = UriComponentsBuilder.fromUriString("http://localhost"+ actualPath).build(true).toUri(); - MockServerHttpRequest request = MockServerHttpRequest - .method(HttpMethod.GET, url) + URI url = UriComponentsBuilder.fromUriString("http://localhost" + actualPath) + .build(true).toUri(); + MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, url) .build(); ServerWebExchange exchange = MockServerWebExchange.from(request); GatewayFilterChain filterChain = mock(GatewayFilterChain.class); - ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class); + ArgumentCaptor captor = ArgumentCaptor + .forClass(ServerWebExchange.class); when(filterChain.filter(captor.capture())).thenReturn(Mono.empty()); filter.filter(exchange, filterChain); @@ -80,8 +83,10 @@ public class RewritePathGatewayFilterFactoryTests { assertThat(webExchange.getRequest().getURI()).hasPath(expectedPath); URI requestUrl = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - assertThat(requestUrl).hasScheme("http").hasHost("localhost").hasNoPort().hasPath(expectedPath); - LinkedHashSet uris = webExchange.getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + assertThat(requestUrl).hasScheme("http").hasHost("localhost").hasNoPort() + .hasPath(expectedPath); + LinkedHashSet uris = webExchange + .getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); assertThat(uris).contains(request.getURI()); return webExchange; @@ -90,10 +95,10 @@ public class RewritePathGatewayFilterFactoryTests { @Test public void rewritePathWithEncodedParams() { ServerWebExchange exchange = testRewriteFilter("/foo", "/baz", - "/foo/bar?name=%E6%89%8E%E6%A0%B9", - "/baz/bar"); + "/foo/bar?name=%E6%89%8E%E6%A0%B9", "/baz/bar"); URI uri = exchange.getRequest().getURI(); assertThat(uri.getRawQuery()).isEqualTo("name=%E6%89%8E%E6%A0%B9"); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactoryTests.java index e974a49c..cb8eb0ae 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; 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; @@ -36,17 +36,16 @@ public class RewriteResponseHeaderGatewayFilterFactoryTests extends BaseWebClien @Test public void rewriteResponseHeaderFilterWorks() { - testClient.get() - .uri("/headers") - .header("Host", "www.rewriteresponseheader.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals("X-Request-Foo", "/42?user=ford&password=***&flag=true"); + testClient.get().uri("/headers").header("Host", "www.rewriteresponseheader.org") + .exchange().expectStatus().isOk().expectHeader() + .valueEquals("X-Request-Foo", "/42?user=ford&password=***&flag=true"); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactoryUnitTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactoryUnitTests.java index 7598e62f..b6a81f3d 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactoryUnitTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteResponseHeaderGatewayFilterFactoryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,15 +12,15 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; + public class RewriteResponseHeaderGatewayFilterFactoryUnitTests { private RewriteResponseHeaderGatewayFilterFactory filterFactory; @@ -32,14 +32,14 @@ public class RewriteResponseHeaderGatewayFilterFactoryUnitTests { @Test public void testRewriteDollarSlash() { - Assert.assertEquals("/bar/bar/42", filterFactory.rewrite( - "/foo/bar", "/foo/(?.*)", "/$\\{segment}/$\\{segment}/42")); + assertThat(filterFactory.rewrite("/foo/bar", "/foo/(?.*)", + "/$\\{segment}/$\\{segment}/42")).isEqualTo("/bar/bar/42"); } @Test public void testRewriteMultiple() { - Assert.assertEquals("/foo/cafe/wat/cafe", filterFactory.rewrite( - "/foo/bar/wat/bar", "bar", "cafe")); + assertThat(filterFactory.rewrite("/foo/bar/wat/bar", "bar", "cafe")) + .isEqualTo("/foo/cafe/wat/cafe"); } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactoryTests.java index 5e216b24..7b0938c1 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 the original author or authors. + * Copyright 2018-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.cloud.gateway.filter.factory; import java.time.Duration; @@ -21,6 +22,9 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -32,8 +36,6 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.server.WebSession; import org.springframework.web.server.session.WebSessionManager; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -58,15 +60,12 @@ public class SaveSessionGatewayFilterFactoryTests extends BaseWebClientTests { when(mockWebSession.getAttributes()).thenReturn(new HashMap<>()); when(mockWebSession.save()).thenReturn(Mono.empty()); - Mono result = webClient.get() - .uri("/get") - .exchange() - .flatMap(response -> response.body(toMono(Map.class))); + Mono result = webClient.get().uri("/get").exchange() + .flatMap(response -> response.body(toMono(Map.class))); - StepVerifier.create(result) - .consumeNextWith(response -> {/* Don't care about data, just need to catch signal */}) - .expectComplete() - .verify(Duration.ofMinutes(10)); + StepVerifier.create(result).consumeNextWith(response -> { + // Don't care about data, just need to catch signal + }).expectComplete().verify(Duration.ofMinutes(10)); verify(mockWebSession).save(); } @@ -80,6 +79,7 @@ public class SaveSessionGatewayFilterFactoryTests extends BaseWebClientTests { WebSessionManager webSessionManager() { return exchange -> Mono.just(mockWebSession); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactoryTests.java index db6f8a10..01fc187a 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,15 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -42,9 +44,6 @@ import static org.springframework.cloud.gateway.filter.factory.SecureHeadersGate import static org.springframework.cloud.gateway.filter.factory.SecureHeadersGatewayFilterFactory.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 @@ -52,34 +51,38 @@ public class SecureHeadersGatewayFilterFactoryTests extends BaseWebClientTests { @Test public void secureHeadersFilterWorks() { - Mono result = webClient.get() - .uri("/headers") - .header("Host", "www.secureheaders.org") - .exchange(); + 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); + 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 @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryIntegrationTests.java index 34b72437..78949db7 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; 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; @@ -36,27 +36,23 @@ public class SetPathGatewayFilterFactoryIntegrationTests extends BaseWebClientTe @Test public void setPathFilterDefaultValuesWork() { - testClient.get() - .uri("/foo/get") - .header("Host", "www.setpath.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "set_path_test"); + testClient.get().uri("/foo/get").header("Host", "www.setpath.org").exchange() + .expectStatus().isOk().expectHeader() + .valueEquals(ROUTE_ID_HEADER, "set_path_test"); } @Test public void setPathViaHostFilterWork() { - testClient.get() - .uri("/") - .header("Host", "get.setpathhost.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "set_path_host_test"); + testClient.get().uri("/").header("Host", "get.setpathhost.org").exchange() + .expectStatus().isOk().expectHeader() + .valueEquals(ROUTE_ID_HEADER, "set_path_host_test"); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryTests.java index 4b840c35..de473054 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -45,20 +44,20 @@ public class SetPathGatewayFilterFactoryTests { @Test public void setPathFilterWorks() { HashMap variables = new HashMap<>(); - testFilter("/baz/bar","/baz/bar", variables); + testFilter("/baz/bar", "/baz/bar", variables); } @Test public void setEncodedPathFilterWorks() { HashMap variables = new HashMap<>(); - testFilter("/baz/foo%20bar","/baz/foo%20bar", variables); + testFilter("/baz/foo%20bar", "/baz/foo%20bar", variables); } @Test public void setPathFilterWithTemplateVarsWorks() { HashMap variables = new HashMap<>(); variables.put("id", "123"); - testFilter("/bar/baz/{id}","/bar/baz/123", variables); + testFilter("/bar/baz/{id}", "/bar/baz/123", variables); } @Test @@ -66,7 +65,7 @@ public class SetPathGatewayFilterFactoryTests { HashMap variables = new HashMap<>(); variables.put("org", "123"); variables.put("scope", "abc"); - testFilter("/{org}/{scope}/function","/123/abc/function", variables); + testFilter("/{org}/{scope}/function", "/123/abc/function", variables); } @Test @@ -76,11 +75,12 @@ public class SetPathGatewayFilterFactoryTests { testFilter("/bar/baz/{id}", "/bar/baz/12 3", variables); } - private void testFilter(String template, String expectedPath, HashMap variables) { - GatewayFilter filter = new SetPathGatewayFilterFactory().apply(c -> c.setTemplate(template)); + private void testFilter(String template, String expectedPath, + HashMap variables) { + GatewayFilter filter = new SetPathGatewayFilterFactory() + .apply(c -> c.setTemplate(template)); - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost") + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost") .build(); ServerWebExchange exchange = MockServerWebExchange.from(request); @@ -88,7 +88,8 @@ public class SetPathGatewayFilterFactoryTests { GatewayFilterChain filterChain = mock(GatewayFilterChain.class); - ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class); + ArgumentCaptor captor = ArgumentCaptor + .forClass(ServerWebExchange.class); when(filterChain.filter(captor.capture())).thenReturn(Mono.empty()); filter.filter(exchange, filterChain); @@ -96,7 +97,9 @@ public class SetPathGatewayFilterFactoryTests { ServerWebExchange webExchange = captor.getValue(); assertThat(webExchange.getRequest().getURI()).hasPath(expectedPath); - LinkedHashSet uris = webExchange.getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + LinkedHashSet uris = webExchange + .getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); assertThat(uris).contains(request.getURI()); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetRequestHeaderGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetRequestHeaderGatewayFilterFactoryTests.java index 2d180a2b..c41d4e9e 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetRequestHeaderGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetRequestHeaderGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -21,6 +20,7 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -37,7 +37,6 @@ 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; - /** * @author Spencer Gibb * @author Biju Kunjummen @@ -49,13 +48,11 @@ public class SetRequestHeaderGatewayFilterFactoryTests extends BaseWebClientTest @Test public void setRequestHeaderFilterWorks() { - testClient.get().uri("/headers") - .header("Host", "www.setrequestheader.org") - .exchange() - .expectStatus().isOk() - .expectBody(Map.class) + testClient.get().uri("/headers").header("Host", "www.setrequestheader.org") + .exchange().expectStatus().isOk().expectBody(Map.class) .consumeWith(result -> { - Map headers = getMap(result.getResponseBody(), "headers"); + Map headers = getMap(result.getResponseBody(), + "headers"); assertThat(headers).containsEntry("X-Req-Foo", "Second"); }); } @@ -64,20 +61,21 @@ public class SetRequestHeaderGatewayFilterFactoryTests extends BaseWebClientTest @SpringBootConfiguration @Import(DefaultTestConfig.class) public static class TestConfig { + @Value("${test.uri}") String uri; @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { return builder.routes().route("test_set_request_header", - r -> r.order(-1) - .host("**.setrequestheader.org") - .filters(f -> f.prefixPath("/httpbin") - .addRequestHeader("X-Req-Foo", "First") - .setRequestHeader("X-Req-Foo", "Second")) - .uri(uri)) + r -> r.order(-1).host("**.setrequestheader.org") + .filters(f -> f.prefixPath("/httpbin") + .addRequestHeader("X-Req-Foo", "First") + .setRequestHeader("X-Req-Foo", "Second")) + .uri(uri)) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderGatewayFilterFactoryTests.java index a51c30f7..f469c47e 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; 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; @@ -36,17 +36,16 @@ public class SetResponseHeaderGatewayFilterFactoryTests extends BaseWebClientTes @Test public void setResponseHeaderFilterWorks() { - testClient.get() - .uri("/headers") - .header("Host", "www.setreresponseheader.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals("X-Request-Foo", "Bar"); + testClient.get().uri("/headers").header("Host", "www.setreresponseheader.org") + .exchange().expectStatus().isOk().expectHeader() + .valueEquals("X-Request-Foo", "Bar"); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } 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 17975963..11923154 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.factory; @@ -57,27 +56,24 @@ public class SetStatusGatewayFilterFactoryTests extends BaseWebClientTests { } private void setStatusStringTest(String host, HttpStatus status) { - testClient.get() - .uri("/headers") - .header("Host", host) - .exchange() - .expectStatus().isEqualTo(status); + testClient.get().uri("/headers").header("Host", host).exchange().expectStatus() + .isEqualTo(status); } @Test public void nonStandardCodeWorks() { HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.HOST, "www.setcustomstatus.org"); - ResponseEntity response = new TestRestTemplate().exchange(baseUri + "/headers", - HttpMethod.GET, new HttpEntity<>(headers), String.class); + ResponseEntity response = new TestRestTemplate().exchange( + baseUri + "/headers", HttpMethod.GET, new HttpEntity<>(headers), + String.class); assertThat(response.getStatusCodeValue()).isEqualTo(432); // https://jira.spring.io/browse/SPR-16748 - /*testClient.get() - .uri("/status/432") - .exchange() - .expectStatus().isEqualTo(432) - .expectBody(String.class).isEqualTo("Failed with 432");*/ + /* + * testClient.get() .uri("/status/432") .exchange() .expectStatus().isEqualTo(432) + * .expectBody(String.class).isEqualTo("Failed with 432"); + */ } @EnableAutoConfiguration @@ -92,10 +88,10 @@ public class SetStatusGatewayFilterFactoryTests extends BaseWebClientTests { public RouteLocator myRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("test_custom_http_status", r -> r.host("*.setcustomstatus.org") - .filters(f -> f.setStatus(432)) - .uri(uri)) + .filters(f -> f.setStatus(432)).uri(uri)) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactoryIntegrationTests.java index c3768a45..5a8a38a2 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,12 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ + package org.springframework.cloud.gateway.filter.factory; 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; @@ -38,16 +39,15 @@ public class StripPrefixGatewayFilterFactoryIntegrationTests extends BaseWebClie @Test public void stripPrefixFilterDefaultValuesWork() { - testClient.get() - .uri("/foo/bar/get") - .header("Host", "www.stripprefix.org") - .exchange() - .expectStatus().isOk(); + testClient.get().uri("/foo/bar/get").header("Host", "www.stripprefix.org") + .exchange().expectStatus().isOk(); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { -} \ No newline at end of file + } + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactoryTests.java index 5aa69576..fe8d0df7 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/StripPrefixGatewayFilterFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,17 +12,17 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ -package org.springframework.cloud.gateway.filter.factory; -import reactor.core.publisher.Mono; +package org.springframework.cloud.gateway.filter.factory; import java.net.URI; import java.util.LinkedHashSet; import org.junit.Test; import org.mockito.ArgumentCaptor; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; @@ -51,23 +51,24 @@ public class StripPrefixGatewayFilterFactoryTests { testStripPrefixFilter("/", "/", 1); testStripPrefixFilter("/", "/", 2); testStripPrefixFilter("", "/", 2); - testStripPrefixFilter("/this/is/a/long/path/with/a/lot/of/slashes", "/path/with/a/lot/of/slashes", 4); + testStripPrefixFilter("/this/is/a/long/path/with/a/lot/of/slashes", + "/path/with/a/lot/of/slashes", 4); } - - private void testStripPrefixFilter(String actualPath, String expectedPath, int parts) { - GatewayFilter filter = new StripPrefixGatewayFilterFactory().apply( - c -> c.setParts(parts)); + private void testStripPrefixFilter(String actualPath, String expectedPath, + int parts) { + GatewayFilter filter = new StripPrefixGatewayFilterFactory() + .apply(c -> c.setParts(parts)); MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost"+ actualPath) - .build(); + .get("http://localhost" + actualPath).build(); ServerWebExchange exchange = MockServerWebExchange.from(request); GatewayFilterChain filterChain = mock(GatewayFilterChain.class); - ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class); + ArgumentCaptor captor = ArgumentCaptor + .forClass(ServerWebExchange.class); when(filterChain.filter(captor.capture())).thenReturn(Mono.empty()); filter.filter(exchange, filterChain); @@ -77,9 +78,11 @@ public class StripPrefixGatewayFilterFactoryTests { assertThat(webExchange.getRequest().getURI()).hasPath(expectedPath); URI requestUrl = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); - assertThat(requestUrl).hasScheme("http").hasHost("localhost").hasNoPort().hasPath(expectedPath); - LinkedHashSet uris = webExchange.getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + assertThat(requestUrl).hasScheme("http").hasHost("localhost").hasNoPort() + .hasPath(expectedPath); + LinkedHashSet uris = webExchange + .getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); assertThat(uris).contains(request.getURI()); } -} \ No newline at end of file +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java index e121a210..41e3c866 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; @@ -42,100 +41,113 @@ import static org.springframework.cloud.gateway.filter.headers.ForwardedHeadersF */ public class ForwardedHeadersFilterTests { + public static Map map(String... values) { + if (values.length % 2 != 0) { + throw new IllegalArgumentException( + "values must have even number of items: " + Arrays.asList(values)); + } + HashMap map = new HashMap<>(); + for (int i = 0; i < values.length; i++) { + map.put(values[i], values[++i]); + } + return map; + } + @Test public void forwardedHeaderDoesNotExist() throws UnknownHostException { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) - .header(HttpHeaders.HOST, "myhost") - .build(); + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/get") + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .header(HttpHeaders.HOST, "myhost").build(); ForwardedHeadersFilter filter = new ForwardedHeadersFilter(); - HttpHeaders headers = filter.filter(request.getHeaders(), MockServerWebExchange.from(request)); + HttpHeaders headers = filter.filter(request.getHeaders(), + MockServerWebExchange.from(request)); assertThat(headers.get(FORWARDED_HEADER)).hasSize(1); - List forwardeds = ForwardedHeadersFilter.parse(headers.get(FORWARDED_HEADER)); + List forwardeds = ForwardedHeadersFilter + .parse(headers.get(FORWARDED_HEADER)); assertThat(forwardeds).hasSize(1); Forwarded forwarded = forwardeds.get(0); - assertThat(forwarded.getValues()) - .containsEntry("host", "myhost") - .containsEntry("proto", "http") - .containsEntry("for", "\"10.0.0.1:80\""); + assertThat(forwarded.getValues()).containsEntry("host", "myhost") + .containsEntry("proto", "http").containsEntry("for", "\"10.0.0.1:80\""); } @Test public void forwardedHeaderExists() throws UnknownHostException { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) - .header(FORWARDED_HEADER, "for=12.34.56.78;host=example.com;proto=https; for=23.45.67.89") + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/get") + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .header(FORWARDED_HEADER, + "for=12.34.56.78;host=example.com;proto=https; for=23.45.67.89") .build(); ForwardedHeadersFilter filter = new ForwardedHeadersFilter(); - HttpHeaders headers = filter.filter(request.getHeaders(), MockServerWebExchange.from(request)); + HttpHeaders headers = filter.filter(request.getHeaders(), + MockServerWebExchange.from(request)); assertThat(headers.get(FORWARDED_HEADER)).hasSize(2); - - List forwardeds = ForwardedHeadersFilter.parse(headers.get(FORWARDED_HEADER)); + List forwardeds = ForwardedHeadersFilter + .parse(headers.get(FORWARDED_HEADER)); assertThat(forwardeds).hasSize(2); Forwarded addedForwardedHeader = forwardeds.get(0); Forwarded existingForwardedHeader = forwardeds.get(1); - assertThat(existingForwardedHeader.getValues()) - .containsEntry("proto", "http") + assertThat(existingForwardedHeader.getValues()).containsEntry("proto", "http") .containsEntry("for", "\"10.0.0.1:80\""); - assertThat(addedForwardedHeader.getValues()) - .containsEntry("proto", "https") + assertThat(addedForwardedHeader.getValues()).containsEntry("proto", "https") .containsEntry("for", "23.45.67.89"); } @Test public void noHostHeader() throws UnknownHostException { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/get") + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) .build(); ForwardedHeadersFilter filter = new ForwardedHeadersFilter(); - HttpHeaders headers = filter.filter(request.getHeaders(), MockServerWebExchange.from(request)); + HttpHeaders headers = filter.filter(request.getHeaders(), + MockServerWebExchange.from(request)); assertThat(headers.get(FORWARDED_HEADER)).hasSize(1); - List forwardeds = ForwardedHeadersFilter.parse(headers.get(FORWARDED_HEADER)); + List forwardeds = ForwardedHeadersFilter + .parse(headers.get(FORWARDED_HEADER)); assertThat(forwardeds).hasSize(1); Forwarded forwarded = forwardeds.get(0); - assertThat(forwarded.getValues()) - .containsEntry("proto", "http") + assertThat(forwarded.getValues()).containsEntry("proto", "http") .containsEntry("for", "\"10.0.0.1:80\""); } @Test public void forwardedParsedCorrectly() { - String[] valid = new String[]{ - "for=\"_gazonk\"", + String[] valid = new String[] { "for=\"_gazonk\"", "for=192.0.2.60;proto=http;by=203.0.113.43", "for=192.0.2.43, for=198.51.100.17", "for=12.34.56.78;host=example.com;proto=https, for=23.45.67.89", "for=12.34.56.78, for=23.45.67.89;secret=egah2CGj55fSJFs, for=10.1.2.3", - "For=\"[2001:db8:cafe::17]:4711\"", - }; + "For=\"[2001:db8:cafe::17]:4711\"", }; List>> expectedFor = new ArrayList<>(); expectedFor.add(Arrays.asList(map("for", "\"_gazonk\""))); - expectedFor.add(Arrays.asList(map("for", "192.0.2.60", "proto", "http", "by", "203.0.113.43"))); - expectedFor.add(Arrays.asList(map("for", "192.0.2.43"), map("for", "198.51.100.17"))); - expectedFor.add(Arrays.asList(map("for", "12.34.56.78", "host", "example.com", "proto", "https"), + expectedFor.add(Arrays + .asList(map("for", "192.0.2.60", "proto", "http", "by", "203.0.113.43"))); + expectedFor.add( + Arrays.asList(map("for", "192.0.2.43"), map("for", "198.51.100.17"))); + expectedFor.add(Arrays.asList( + map("for", "12.34.56.78", "host", "example.com", "proto", "https"), map("for", "23.45.67.89"))); expectedFor.add(Arrays.asList(map("for", "12.34.56.78"), map("for", "23.45.67.89", "secret", "egah2CGj55fSJFs"), @@ -164,16 +176,4 @@ public class ForwardedHeadersFilterTests { } } - public static Map map(String... values) { - if (values.length % 2 != 0) { - throw new IllegalArgumentException("values must have even number of items: " - + Arrays.asList(values)); - } - HashMap map = new HashMap<>(); - for (int i = 0; i < values.length; i++) { - map.put(values[i], values[++i]); - } - return map; - } - } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilterMixedTypeTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilterMixedTypeTests.java index 02acc8e5..e9f3ac30 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilterMixedTypeTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilterMixedTypeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 the original author or authors. + * Copyright 2018-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; @@ -42,20 +41,17 @@ public class HttpHeadersFilterMixedTypeTests { .header("header1", "value1").header("header2", "value2") .header("header3", "value3").build(); - HttpHeadersFilter filter1 = filterRemovingHeaders(Type.RESPONSE, - "header1"); + HttpHeadersFilter filter1 = filterRemovingHeaders(Type.RESPONSE, "header1"); - HttpHeadersFilter filter2 = filterRemovingHeaders(Type.REQUEST, - "header2"); + HttpHeadersFilter filter2 = filterRemovingHeaders(Type.REQUEST, "header2"); - HttpHeaders result = HttpHeadersFilter.filterRequest(Arrays.asList(filter1, filter2), - MockServerWebExchange.from(mockRequest)); + HttpHeaders result = HttpHeadersFilter.filterRequest( + Arrays.asList(filter1, filter2), MockServerWebExchange.from(mockRequest)); assertThat(result).containsOnlyKeys("header1", "header3"); } - - private HttpHeadersFilter filterRemovingHeaders(Type type, - String... headerNames) { + + private HttpHeadersFilter filterRemovingHeaders(Type type, String... headerNames) { Set headerNamesSet = new HashSet<>(Arrays.asList(headerNames)); HttpHeadersFilter filter = new HttpHeadersFilter() { @Override @@ -76,4 +72,5 @@ public class HttpHeadersFilterMixedTypeTests { }; return filter; } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilterTests.java index f0ae6661..922e1534 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpHeadersFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; @@ -58,4 +57,5 @@ public class HttpHeadersFilterTests { return filtered; } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpStatusInResponseHeadersFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpStatusInResponseHeadersFilterTests.java index bfa28ea2..b69da547 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpStatusInResponseHeadersFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/HttpStatusInResponseHeadersFilterTests.java @@ -12,14 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; 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; @@ -41,14 +40,11 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT) @DirtiesContext -public class HttpStatusInResponseHeadersFilterTests extends BaseWebClientTests { +public class HttpStatusInResponseHeadersFilterTests extends BaseWebClientTests { @Test public void statusCodeAvailableInResponseHttpHeadersFilter() { - testClient.get() - .uri("/get") - .exchange() - .expectStatus().isOk(); + testClient.get().uri("/get").exchange().expectStatus().isOk(); } @EnableAutoConfiguration @@ -74,4 +70,5 @@ public class HttpStatusInResponseHeadersFilterTests extends BaseWebClientTests } } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/NonStandardHeadersInResponseTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/NonStandardHeadersInResponseTests.java index 5d06bf34..a9f413f2 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/NonStandardHeadersInResponseTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/NonStandardHeadersInResponseTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,15 +12,18 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; +import java.net.URI; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -37,19 +40,12 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.util.UriComponentsBuilder; -import reactor.core.publisher.Mono; -import java.net.URI; -import java.util.List; - -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; @RunWith(SpringRunner.class) -@SpringBootTest( - webEnvironment = DEFINED_PORT, - properties = {"server.port=62175"} -) +@SpringBootTest(webEnvironment = DEFINED_PORT, properties = { "server.port=62175" }) @DirtiesContext public class NonStandardHeadersInResponseTests extends BaseWebClientTests { @@ -57,30 +53,27 @@ public class NonStandardHeadersInResponseTests extends BaseWebClientTests { @Test public void nonStandardHeadersInResponse() { - URI uri = UriComponentsBuilder - .fromUriString(this.baseUri + "/get-image") - .build(true) - .toUri(); + URI uri = UriComponentsBuilder.fromUriString(this.baseUri + "/get-image") + .build(true).toUri(); - String contentType = WebClient.builder() - .baseUrl(baseUri) - .build() - .get() - .uri(uri) - .exchange() - .map(clientResponse -> clientResponse.headers().asHttpHeaders().getFirst(HttpHeaders.CONTENT_TYPE)) + String contentType = WebClient.builder().baseUrl(baseUri).build().get().uri(uri) + .exchange().map(clientResponse -> clientResponse.headers().asHttpHeaders() + .getFirst(HttpHeaders.CONTENT_TYPE)) .block(); - assertEquals(CONTENT_TYPE_IMAGE, contentType); + assertThat(contentType).isEqualTo(CONTENT_TYPE_IMAGE); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) public static class TestConfig { + private static final Log log = LogFactory.getLog(TestConfig.class); + @Value("${test.uri}") String uri; + @Value("${server.port}") int port; @@ -91,7 +84,8 @@ public class NonStandardHeadersInResponseTests extends BaseWebClientTests { log.info("addNonStandardHeaderFilter pre phase"); return chain.filter(exchange).then(Mono.fromRunnable(() -> { log.info("addNonStandardHeaderFilter post phase"); - exchange.getResponse().getHeaders().set(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_IMAGE); + exchange.getResponse().getHeaders().set(HttpHeaders.CONTENT_TYPE, + CONTENT_TYPE_IMAGE); })); }; } @@ -99,17 +93,12 @@ public class NonStandardHeadersInResponseTests extends BaseWebClientTests { @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route("non_standard_header_route", r -> - r.path("/get-image/**") - .filters(f -> f - .addRequestHeader(HttpHeaders.HOST, "www.addrequestparameter.org") - .stripPrefix(1) - ) - .uri("http://localhost:" + port + "/get")) - .route("internal_route", r -> - r.path("/get/**") - .filters(f -> f.prefixPath("/httpbin")) - .uri(uri)) + .route("non_standard_header_route", r -> r.path("/get-image/**") + .filters(f -> f.addRequestHeader(HttpHeaders.HOST, + "www.addrequestparameter.org").stripPrefix(1)) + .uri("http://localhost:" + port + "/get")) + .route("internal_route", r -> r.path("/get/**") + .filters(f -> f.prefixPath("/httpbin")).uri(uri)) .build(); } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java index af385eb9..525a0d83 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/RemoveHopByHopHeadersFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; @@ -22,6 +21,7 @@ import java.util.HashSet; import java.util.Set; import org.junit.Test; + import org.springframework.http.HttpHeaders; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; @@ -38,8 +38,9 @@ public class RemoveHopByHopHeadersFilterTests { public void happyPath() { MockServerHttpRequest.BaseBuilder builder = MockServerHttpRequest .get("http://localhost/get"); - - HEADERS_REMOVED_ON_REQUEST.forEach(header -> builder.header(header, header+"1")); + + HEADERS_REMOVED_ON_REQUEST + .forEach(header -> builder.header(header, header + "1")); testFilter(MockServerWebExchange.from(builder)); } @@ -49,7 +50,8 @@ public class RemoveHopByHopHeadersFilterTests { MockServerHttpRequest.BaseBuilder builder = MockServerHttpRequest .get("http://localhost/get"); - HEADERS_REMOVED_ON_REQUEST.forEach(header -> builder.header(header.toLowerCase(), header+"1")); + HEADERS_REMOVED_ON_REQUEST + .forEach(header -> builder.header(header.toLowerCase(), header + "1")); testFilter(MockServerWebExchange.from(builder)); } @@ -74,4 +76,5 @@ public class RemoveHopByHopHeadersFilterTests { toRemove.addAll(Arrays.asList(additionalHeaders)); assertThat(headers).doesNotContainKeys(toRemove.toArray(new String[0])); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java index 4049fe7c..c064f3ea 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.headers; @@ -20,7 +19,6 @@ package org.springframework.cloud.gateway.filter.headers; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; -import java.net.UnknownHostException; import java.util.LinkedHashSet; import org.junit.Test; @@ -48,16 +46,16 @@ public class XForwardedHeadersFilterTests { @Test public void remoteAddressIsNull() throws Exception { MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost:8080/get") - .header(HttpHeaders.HOST, "myhost") + .get("http://localhost:8080/get").header(HttpHeaders.HOST, "myhost") .build(); XForwardedHeadersFilter filter = new XForwardedHeadersFilter(); - HttpHeaders headers = filter.filter(request.getHeaders(), MockServerWebExchange.from(request)); + HttpHeaders headers = filter.filter(request.getHeaders(), + MockServerWebExchange.from(request)); - assertThat(headers).containsKeys(X_FORWARDED_HOST_HEADER, - X_FORWARDED_PORT_HEADER, X_FORWARDED_PROTO_HEADER); + assertThat(headers).containsKeys(X_FORWARDED_HOST_HEADER, X_FORWARDED_PORT_HEADER, + X_FORWARDED_PROTO_HEADER); assertThat(headers.getFirst(X_FORWARDED_HOST_HEADER)).isEqualTo("localhost:8080"); assertThat(headers.getFirst(X_FORWARDED_PORT_HEADER)).isEqualTo("8080"); @@ -68,13 +66,14 @@ public class XForwardedHeadersFilterTests { public void xForwardedHeadersDoNotExist() throws Exception { MockServerHttpRequest request = MockServerHttpRequest .get("http://localhost:8080/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) - .header(HttpHeaders.HOST, "myhost") - .build(); + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .header(HttpHeaders.HOST, "myhost").build(); XForwardedHeadersFilter filter = new XForwardedHeadersFilter(); - HttpHeaders headers = filter.filter(request.getHeaders(), MockServerWebExchange.from(request)); + HttpHeaders headers = filter.filter(request.getHeaders(), + MockServerWebExchange.from(request)); assertThat(headers).containsKeys(X_FORWARDED_FOR_HEADER, X_FORWARDED_HOST_HEADER, X_FORWARDED_PORT_HEADER, X_FORWARDED_PROTO_HEADER); @@ -87,15 +86,15 @@ public class XForwardedHeadersFilterTests { @Test public void defaultPort() throws Exception { - MockServerHttpRequest request = MockServerHttpRequest - .get("http://localhost/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) - .header(HttpHeaders.HOST, "myhost") - .build(); + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/get") + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .header(HttpHeaders.HOST, "myhost").build(); XForwardedHeadersFilter filter = new XForwardedHeadersFilter(); - HttpHeaders headers = filter.filter(request.getHeaders(), MockServerWebExchange.from(request)); + HttpHeaders headers = filter.filter(request.getHeaders(), + MockServerWebExchange.from(request)); assertThat(headers).containsKeys(X_FORWARDED_FOR_HEADER, X_FORWARDED_HOST_HEADER, X_FORWARDED_PORT_HEADER, X_FORWARDED_PROTO_HEADER); @@ -110,22 +109,25 @@ public class XForwardedHeadersFilterTests { public void appendsValues() throws Exception { MockServerHttpRequest request = MockServerHttpRequest .get("http://localhost:8080/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) .header(X_FORWARDED_FOR_HEADER, "192.168.0.2") .header(X_FORWARDED_HOST_HEADER, "example.com") .header(X_FORWARDED_PORT_HEADER, "443") - .header(X_FORWARDED_PROTO_HEADER, "https") - .build(); + .header(X_FORWARDED_PROTO_HEADER, "https").build(); XForwardedHeadersFilter filter = new XForwardedHeadersFilter(); - HttpHeaders headers = filter.filter(request.getHeaders(), MockServerWebExchange.from(request)); + HttpHeaders headers = filter.filter(request.getHeaders(), + MockServerWebExchange.from(request)); assertThat(headers).containsKeys(X_FORWARDED_FOR_HEADER, X_FORWARDED_HOST_HEADER, X_FORWARDED_PORT_HEADER, X_FORWARDED_PROTO_HEADER); - assertThat(headers.getFirst(X_FORWARDED_FOR_HEADER)).isEqualTo("192.168.0.2,10.0.0.1"); - assertThat(headers.getFirst(X_FORWARDED_HOST_HEADER)).isEqualTo("example.com,localhost:8080"); + assertThat(headers.getFirst(X_FORWARDED_FOR_HEADER)) + .isEqualTo("192.168.0.2,10.0.0.1"); + assertThat(headers.getFirst(X_FORWARDED_HOST_HEADER)) + .isEqualTo("example.com,localhost:8080"); assertThat(headers.getFirst(X_FORWARDED_PORT_HEADER)).isEqualTo("443,8080"); assertThat(headers.getFirst(X_FORWARDED_PROTO_HEADER)).isEqualTo("https,http"); } @@ -134,13 +136,13 @@ public class XForwardedHeadersFilterTests { public void appendDisabled() throws Exception { MockServerHttpRequest request = MockServerHttpRequest .get("http://localhost:8080/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) .header(X_FORWARDED_FOR_HEADER, "192.168.0.2") .header(X_FORWARDED_HOST_HEADER, "example.com") .header(X_FORWARDED_PORT_HEADER, "443") .header(X_FORWARDED_PROTO_HEADER, "https") - .header(X_FORWARDED_PREFIX_HEADER,"/prefix") - .build(); + .header(X_FORWARDED_PREFIX_HEADER, "/prefix").build(); XForwardedHeadersFilter filter = new XForwardedHeadersFilter(); filter.setForAppend(false); @@ -149,10 +151,12 @@ public class XForwardedHeadersFilterTests { filter.setProtoAppend(false); filter.setPrefixAppend(false); - HttpHeaders headers = filter.filter(request.getHeaders(), MockServerWebExchange.from(request)); + HttpHeaders headers = filter.filter(request.getHeaders(), + MockServerWebExchange.from(request)); assertThat(headers).containsKeys(X_FORWARDED_FOR_HEADER, X_FORWARDED_HOST_HEADER, - X_FORWARDED_PORT_HEADER, X_FORWARDED_PROTO_HEADER,X_FORWARDED_PREFIX_HEADER); + X_FORWARDED_PORT_HEADER, X_FORWARDED_PROTO_HEADER, + X_FORWARDED_PREFIX_HEADER); assertThat(headers.getFirst(X_FORWARDED_FOR_HEADER)).isEqualTo("10.0.0.1"); assertThat(headers.getFirst(X_FORWARDED_HOST_HEADER)).isEqualTo("localhost:8080"); @@ -161,12 +165,12 @@ public class XForwardedHeadersFilterTests { assertThat(headers.getFirst(X_FORWARDED_PREFIX_HEADER)).isEqualTo("/prefix"); } - @Test public void prefixToInfer() throws Exception { MockServerHttpRequest request = MockServerHttpRequest .get("http://originalhost:8080/prefix/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) .build(); XForwardedHeadersFilter filter = new XForwardedHeadersFilter(); @@ -175,9 +179,12 @@ public class XForwardedHeadersFilterTests { ServerWebExchange exchange = MockServerWebExchange.from(request); LinkedHashSet originalUris = new LinkedHashSet<>(); - originalUris.add(UriComponentsBuilder.fromUriString("http://originalhost:8080/prefix/get/").build().toUri()); //trailing slash + originalUris.add(UriComponentsBuilder + .fromUriString("http://originalhost:8080/prefix/get/").build().toUri()); // trailing + // slash exchange.getAttributes().put(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, originalUris); - URI requestUri = UriComponentsBuilder.fromUriString("http://routedservice:8090/get").build().toUri(); + URI requestUri = UriComponentsBuilder + .fromUriString("http://routedservice:8090/get").build().toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUri); HttpHeaders headers = filter.filter(request.getHeaders(), exchange); @@ -191,7 +198,8 @@ public class XForwardedHeadersFilterTests { public void prefixAddedWithoutTrailingSlash() throws Exception { MockServerHttpRequest request = MockServerHttpRequest .get("http://originalhost:8080/foo/bar") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) .build(); XForwardedHeadersFilter filter = new XForwardedHeadersFilter(); @@ -200,9 +208,11 @@ public class XForwardedHeadersFilterTests { ServerWebExchange exchange = MockServerWebExchange.from(request); LinkedHashSet originalUris = new LinkedHashSet<>(); - originalUris.add(UriComponentsBuilder.fromUriString("http://originalhost:8080/foo/bar").build().toUri()); + originalUris.add(UriComponentsBuilder + .fromUriString("http://originalhost:8080/foo/bar").build().toUri()); exchange.getAttributes().put(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, originalUris); - URI requestUri = UriComponentsBuilder.fromUriString("http://routedservice:8090/").build().toUri(); + URI requestUri = UriComponentsBuilder.fromUriString("http://routedservice:8090/") + .build().toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUri); HttpHeaders headers = filter.filter(request.getHeaders(), exchange); @@ -214,7 +224,8 @@ public class XForwardedHeadersFilterTests { public void noPrefixToInfer() throws Exception { MockServerHttpRequest request = MockServerHttpRequest .get("http://originalhost:8080/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) .build(); XForwardedHeadersFilter filter = new XForwardedHeadersFilter(); @@ -227,9 +238,11 @@ public class XForwardedHeadersFilterTests { ServerWebExchange exchange = MockServerWebExchange.from(request); LinkedHashSet originalUris = new LinkedHashSet<>(); - originalUris.add(UriComponentsBuilder.fromUriString("http://originalhost:8080/get/").build().toUri()); + originalUris.add(UriComponentsBuilder + .fromUriString("http://originalhost:8080/get/").build().toUri()); exchange.getAttributes().put(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, originalUris); - URI requestUri = UriComponentsBuilder.fromUriString("http://routedservice:8090/get").build().toUri(); + URI requestUri = UriComponentsBuilder + .fromUriString("http://routedservice:8090/get").build().toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUri); HttpHeaders headers = filter.filter(request.getHeaders(), exchange); @@ -241,7 +254,8 @@ public class XForwardedHeadersFilterTests { public void routedPathInRequestPathButNotPrefix() throws Exception { MockServerHttpRequest request = MockServerHttpRequest .get("http://originalhost:8080/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) .build(); XForwardedHeadersFilter filter = new XForwardedHeadersFilter(); @@ -254,9 +268,11 @@ public class XForwardedHeadersFilterTests { ServerWebExchange exchange = MockServerWebExchange.from(request); LinkedHashSet originalUris = new LinkedHashSet<>(); - originalUris.add(UriComponentsBuilder.fromUriString("http://originalhost:8080/one/two/three").build().toUri()); + originalUris.add(UriComponentsBuilder + .fromUriString("http://originalhost:8080/one/two/three").build().toUri()); exchange.getAttributes().put(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, originalUris); - URI requestUri = UriComponentsBuilder.fromUriString("http://routedservice:8090/two").build().toUri(); + URI requestUri = UriComponentsBuilder + .fromUriString("http://routedservice:8090/two").build().toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUri); HttpHeaders headers = filter.filter(request.getHeaders(), exchange); @@ -268,7 +284,8 @@ public class XForwardedHeadersFilterTests { public void allDisabled() throws Exception { MockServerHttpRequest request = MockServerHttpRequest .get("http://localhost:8080/get") - .remoteAddress(new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) + .remoteAddress( + new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 80)) .build(); XForwardedHeadersFilter filter = new XForwardedHeadersFilter(); @@ -278,8 +295,10 @@ public class XForwardedHeadersFilterTests { filter.setProtoEnabled(false); filter.setPrefixEnabled(false); - HttpHeaders headers = filter.filter(request.getHeaders(), MockServerWebExchange.from(request)); + HttpHeaders headers = filter.filter(request.getHeaders(), + MockServerWebExchange.from(request)); assertThat(headers).isEmpty(); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolverIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolverIntegrationTests.java index dfc0099b..2109ddff 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolverIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolverIntegrationTests.java @@ -1,11 +1,11 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -26,6 +26,8 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -51,21 +53,22 @@ import org.springframework.web.bind.annotation.RestController; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication; -import reactor.core.publisher.Mono; - @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = DEFINED_PORT) @ActiveProfiles("principalname") public class PrincipalNameKeyResolverIntegrationTests { + @LocalServerPort protected int port = 0; protected WebTestClient client; + protected String baseUri; @BeforeClass public static void beforeClass() { - System.setProperty("server.port", String.valueOf(SocketUtils.findAvailableTcpPort())); + System.setProperty("server.port", + String.valueOf(SocketUtils.findAvailableTcpPort())); } @AfterClass @@ -81,17 +84,11 @@ public class PrincipalNameKeyResolverIntegrationTests { @Test public void keyResolverWorks() { - this.client.mutate() - .filter(basicAuthentication("user", "password")) - .build() - .get() - .uri("/myapi/1") - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"user\":\"1\"}"); + this.client.mutate().filter(basicAuthentication("user", "password")).build().get() + .uri("/myapi/1").exchange().expectStatus().isOk().expectBody() + .json("{\"user\":\"1\"}"); } - @RestController @RequestMapping("/downstream") @EnableAutoConfiguration @@ -108,12 +105,11 @@ public class PrincipalNameKeyResolverIntegrationTests { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { - return builder.routes() - .route(r -> r.path("/myapi/**") - .filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(myRateLimiter())) - .prefixPath("/downstream")) - .uri("http://localhost:"+port)) - .build(); + return builder.routes().route(r -> r.path("/myapi/**") + .filters(f -> f + .requestRateLimiter(c -> c.setRateLimiter(myRateLimiter())) + .prefixPath("/downstream")) + .uri("http://localhost:" + port)).build(); } @Bean @@ -124,17 +120,14 @@ public class PrincipalNameKeyResolverIntegrationTests { @Bean SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) { - return http.httpBasic().and() - .authorizeExchange() - .pathMatchers("/myapi/**").authenticated() - .anyExchange().permitAll() - .and() - .build(); + return http.httpBasic().and().authorizeExchange().pathMatchers("/myapi/**") + .authenticated().anyExchange().permitAll().and().build(); } @Bean public MapReactiveUserDetailsService reactiveUserDetailsService() { - UserDetails user = User.withUsername("user").password("{noop}password").roles("USER").build(); + UserDetails user = User.withUsername("user").password("{noop}password") + .roles("USER").build(); return new MapReactiveUserDetailsService(user); } @@ -162,6 +155,9 @@ public class PrincipalNameKeyResolverIntegrationTests { public Object newConfig() { return null; } + } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterConfigTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterConfigTests.java index 5610a363..7a90a93a 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterConfigTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.ratelimit; @@ -20,6 +19,7 @@ package org.springframework.cloud.gateway.filter.ratelimit; 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; @@ -51,34 +51,33 @@ public class RedisRateLimiterConfigTests { @Before public void init() { - routeLocator.getRoutes().collectList().block(); // prime routes since getRoutes() no longer blocks + routeLocator.getRoutes().collectList().block(); // prime routes since getRoutes() + // no longer blocks } @Test public void redisRateConfiguredFromEnvironment() { - assertFilter("redis_rate_limiter_config_test", 10, 20, - false); + assertFilter("redis_rate_limiter_config_test", 10, 20, false); } @Test public void redisRateConfiguredFromJavaAPI() { - assertFilter("custom_redis_rate_limiter", 20, 40, - false); + assertFilter("custom_redis_rate_limiter", 20, 40, false); } @Test public void redisRateConfiguredFromJavaAPIDirectBean() { - assertFilter("alt_custom_redis_rate_limiter", 30, 60, - true); + assertFilter("alt_custom_redis_rate_limiter", 30, 60, true); } private void assertFilter(String key, int replenishRate, int burstCapacity, - boolean useDefaultConfig) { + boolean useDefaultConfig) { RedisRateLimiter.Config config; if (useDefaultConfig) { config = rateLimiter.getDefaultConfig(); - } else { + } + else { assertThat(rateLimiter.getConfig()).containsKey(key); config = rateLimiter.getConfig().get(key); } @@ -86,7 +85,8 @@ public class RedisRateLimiterConfigTests { assertThat(config.getReplenishRate()).isEqualTo(replenishRate); assertThat(config.getBurstCapacity()).isEqualTo(burstCapacity); - Route route = routeLocator.getRoutes().filter(r -> r.getId().equals(key)).next().block(); + Route route = routeLocator.getRoutes().filter(r -> r.getId().equals(key)).next() + .block(); assertThat(route).isNotNull(); assertThat(route.getFilters()).hasSize(1); } @@ -97,16 +97,16 @@ public class RedisRateLimiterConfigTests { @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { - return builder.routes() - .route("custom_redis_rate_limiter", r -> r.path("/custom") - .filters(f -> f.requestRateLimiter() - .rateLimiter(RedisRateLimiter.class, - rl -> rl.setBurstCapacity(40).setReplenishRate(20)) - .and()) - .uri("http://localhost")) - .route("alt_custom_redis_rate_limiter", r -> r.path("/custom") - .filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(myRateLimiter()))) - .uri("http://localhost")) + return builder.routes().route("custom_redis_rate_limiter", + r -> r.path("/custom").filters(f -> f.requestRateLimiter() + .rateLimiter(RedisRateLimiter.class, + rl -> rl.setBurstCapacity(40).setReplenishRate(20)) + .and()).uri("http://localhost")) + .route("alt_custom_redis_rate_limiter", + r -> r.path("/custom") + .filters(f -> f.requestRateLimiter( + c -> c.setRateLimiter(myRateLimiter()))) + .uri("http://localhost")) .build(); } @@ -115,5 +115,7 @@ public class RedisRateLimiterConfigTests { public RedisRateLimiter myRateLimiter() { return new RedisRateLimiter(30, 60); } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterDefaultFilterConfigTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterDefaultFilterConfigTests.java index 0566666b..6e7d8b21 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterDefaultFilterConfigTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterDefaultFilterConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.filter.ratelimit; @@ -50,7 +49,8 @@ public class RedisRateLimiterDefaultFilterConfigTests { @Before public void init() { - routeLocator.getRoutes().collectList().block(); // prime routes since getRoutes() no longer blocks + routeLocator.getRoutes().collectList().block(); // prime routes since getRoutes() + // no longer blocks } @Test @@ -60,17 +60,22 @@ public class RedisRateLimiterDefaultFilterConfigTests { assertConfigAndRoute(routeId, 70, 80, config); } - private void assertConfigAndRoute(String key, int replenishRate, int burstCapacity, RedisRateLimiter.Config config) { + private void assertConfigAndRoute(String key, int replenishRate, int burstCapacity, + RedisRateLimiter.Config config) { assertThat(config).isNotNull(); assertThat(config.getReplenishRate()).isEqualTo(replenishRate); assertThat(config.getBurstCapacity()).isEqualTo(burstCapacity); - Route route = routeLocator.getRoutes().filter(r -> r.getId().equals(key)).next().block(); + Route route = routeLocator.getRoutes().filter(r -> r.getId().equals(key)).next() + .block(); assertThat(route).isNotNull(); assertThat(route.getFilters()).isNotEmpty(); } @EnableAutoConfiguration @SpringBootConfiguration - public static class TestConfig { } + public static class TestConfig { + + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java index 87d89710..e56e5cfa 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.filter.ratelimit; import java.util.UUID; @@ -5,12 +21,12 @@ import java.util.UUID; import org.junit.Rule; 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.test.context.SpringBootTest; import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter.Response; -import org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator; import org.springframework.cloud.gateway.test.BaseWebClientTests; import org.springframework.cloud.gateway.test.support.redis.RedisRule; import org.springframework.context.annotation.Import; @@ -24,7 +40,9 @@ import static org.junit.Assume.assumeThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** - * see https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L36-L62 + * see + * https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L36-L62 + * * @author Spencer Gibb */ @RunWith(SpringRunner.class) @@ -40,8 +58,7 @@ public class RedisRateLimiterTests extends BaseWebClientTests { @Test public void redisRateLimiterWorks() throws Exception { - assumeThat("Ignore on Circle", - System.getenv("CIRCLECI"), is(nullValue())); + assumeThat("Ignore on Circle", System.getenv("CIRCLECI"), is(nullValue())); String id = UUID.randomUUID().toString(); @@ -50,42 +67,47 @@ public class RedisRateLimiterTests extends BaseWebClientTests { String routeId = "myroute"; rateLimiter.getConfig().put(routeId, new RedisRateLimiter.Config() - .setBurstCapacity(burstCapacity) - .setReplenishRate(replenishRate)); + .setBurstCapacity(burstCapacity).setReplenishRate(replenishRate)); // Bursts work for (int i = 0; i < burstCapacity; i++) { Response response = rateLimiter.isAllowed(routeId, id).block(); assertThat(response.isAllowed()).as("Burst # %s is allowed", i).isTrue(); - assertThat(response.getHeaders()).containsKey(RedisRateLimiter.REMAINING_HEADER); - assertThat(response.getHeaders()). - containsEntry(RedisRateLimiter.REPLENISH_RATE_HEADER, String.valueOf(replenishRate)); - assertThat(response.getHeaders()). - containsEntry(RedisRateLimiter.BURST_CAPACITY_HEADER, String.valueOf(burstCapacity)); + assertThat(response.getHeaders()) + .containsKey(RedisRateLimiter.REMAINING_HEADER); + assertThat(response.getHeaders()).containsEntry( + RedisRateLimiter.REPLENISH_RATE_HEADER, + String.valueOf(replenishRate)); + assertThat(response.getHeaders()).containsEntry( + RedisRateLimiter.BURST_CAPACITY_HEADER, + String.valueOf(burstCapacity)); } Response response = rateLimiter.isAllowed(routeId, id).block(); - if (response.isAllowed()) { //TODO: sometimes there is an off by one error + if (response.isAllowed()) { // TODO: sometimes there is an off by one error response = rateLimiter.isAllowed(routeId, id).block(); } - assertThat(response.isAllowed()).as("Burst # %s is not allowed", burstCapacity).isFalse(); + assertThat(response.isAllowed()).as("Burst # %s is not allowed", burstCapacity) + .isFalse(); Thread.sleep(1000); // # After the burst is done, check the steady state for (int i = 0; i < replenishRate; i++) { response = rateLimiter.isAllowed(routeId, id).block(); - assertThat(response.isAllowed()).as("steady state # %s is allowed", i).isTrue(); + assertThat(response.isAllowed()).as("steady state # %s is allowed", i) + .isTrue(); } response = rateLimiter.isAllowed(routeId, id).block(); - assertThat(response.isAllowed()).as("steady state # %s is allowed", replenishRate).isFalse(); + assertThat(response.isAllowed()).as("steady state # %s is allowed", replenishRate) + .isFalse(); } @Test public void keysUseRedisKeyHashTags() { - assertThat(RedisRateLimiter.getKeys("1")) - .containsExactly("request_rate_limiter.{1}.tokens", "request_rate_limiter.{1}.timestamp"); + assertThat(RedisRateLimiter.getKeys("1")).containsExactly( + "request_rate_limiter.{1}.tokens", "request_rate_limiter.{1}.timestamp"); } @EnableAutoConfiguration @@ -94,4 +116,5 @@ public class RedisRateLimiterTests extends BaseWebClientTests { public static class TestConfig { } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingIntegrationTests.java index 2a47ca2a..5de0e65e 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler; @@ -53,15 +52,15 @@ public class RoutePredicateHandlerMappingIntegrationTests extends BaseWebClientT @Test public void requestsToManagementPortReturn404() { - testClient.mutate().baseUrl("http://localhost:"+managementPort).build() - .get().uri("/get") - .exchange() - .expectStatus().isNotFound(); + testClient.mutate().baseUrl("http://localhost:" + managementPort).build().get() + .uri("/get").exchange().expectStatus().isNotFound(); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingTests.java index 9c1f8fd7..3f44bd68 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMappingTests.java @@ -1,17 +1,34 @@ +/* + * Copyright 2018-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.handler; import org.junit.Rule; import org.junit.Test; import org.mockito.Mockito; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + import org.springframework.boot.test.rule.OutputCapture; import org.springframework.cloud.gateway.config.GlobalCorsProperties; import org.springframework.cloud.gateway.route.Route; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.mock.env.MockEnvironment; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; import static org.hamcrest.Matchers.containsString; @@ -25,75 +42,61 @@ public class RoutePredicateHandlerMappingTests { @Test public void lookupRouteFromSyncPredicates() { - Route routeFalse = Route.async() - .id("routeFalse") - .uri("http://localhost") - .predicate(swe -> false) - .build(); - Route routeFail = Route.async() - .id("routeFail") - .uri("http://localhost") - .predicate(swe -> { throw new IllegalStateException("boom"); }) - .build(); - Route routeTrue = Route.async() - .id("routeTrue") - .uri("http://localhost") - .predicate(swe -> true) - .build(); - RouteLocator routeLocator = - () -> Flux.just(routeFalse, routeFail, routeTrue).hide(); - RoutePredicateHandlerMapping mapping = - new RoutePredicateHandlerMapping(null, routeLocator, new GlobalCorsProperties(), new MockEnvironment()); + Route routeFalse = Route.async().id("routeFalse").uri("http://localhost") + .predicate(swe -> false).build(); + Route routeFail = Route.async().id("routeFail").uri("http://localhost") + .predicate(swe -> { + throw new IllegalStateException("boom"); + }).build(); + Route routeTrue = Route.async().id("routeTrue").uri("http://localhost") + .predicate(swe -> true).build(); + RouteLocator routeLocator = () -> Flux.just(routeFalse, routeFail, routeTrue) + .hide(); + RoutePredicateHandlerMapping mapping = new RoutePredicateHandlerMapping(null, + routeLocator, new GlobalCorsProperties(), new MockEnvironment()); - final Mono routeMono = - mapping.lookupRoute(Mockito.mock(ServerWebExchange.class)); + final Mono routeMono = mapping + .lookupRoute(Mockito.mock(ServerWebExchange.class)); - StepVerifier.create(routeMono.map(Route::getId)) - .expectNext("routeTrue") + StepVerifier.create(routeMono.map(Route::getId)).expectNext("routeTrue") .verifyComplete(); - outputCapture.expect(containsString("Error applying predicate for route: routeFail")); + outputCapture + .expect(containsString("Error applying predicate for route: routeFail")); outputCapture.expect(containsString("java.lang.IllegalStateException: boom")); } @Test public void lookupRouteFromAsyncPredicates() { - Route routeFalse = Route.async() - .id("routeFalse") - .uri("http://localhost") - .asyncPredicate(swe -> Mono.just(false)) - .build(); - Route routeError = Route.async() - .id("routeError") - .uri("http://localhost") + Route routeFalse = Route.async().id("routeFalse").uri("http://localhost") + .asyncPredicate(swe -> Mono.just(false)).build(); + Route routeError = Route.async().id("routeError").uri("http://localhost") .asyncPredicate(swe -> Mono.error(new IllegalStateException("boom1"))) .build(); - Route routeFail = Route.async() - .id("routeFail") - .uri("http://localhost") - .asyncPredicate(swe -> { throw new IllegalStateException("boom2"); }) - .build(); - Route routeTrue = Route.async() - .id("routeTrue") - .uri("http://localhost") - .asyncPredicate(swe -> Mono.just(true)) - .build(); - RouteLocator routeLocator = - () -> Flux.just(routeFalse, routeError, routeFail, routeTrue).hide(); - RoutePredicateHandlerMapping mapping = - new RoutePredicateHandlerMapping(null, routeLocator, new GlobalCorsProperties(), new MockEnvironment()); + Route routeFail = Route.async().id("routeFail").uri("http://localhost") + .asyncPredicate(swe -> { + throw new IllegalStateException("boom2"); + }).build(); + Route routeTrue = Route.async().id("routeTrue").uri("http://localhost") + .asyncPredicate(swe -> Mono.just(true)).build(); + RouteLocator routeLocator = () -> Flux + .just(routeFalse, routeError, routeFail, routeTrue).hide(); + RoutePredicateHandlerMapping mapping = new RoutePredicateHandlerMapping(null, + routeLocator, new GlobalCorsProperties(), new MockEnvironment()); - final Mono routeMono = - mapping.lookupRoute(Mockito.mock(ServerWebExchange.class)); + final Mono routeMono = mapping + .lookupRoute(Mockito.mock(ServerWebExchange.class)); - StepVerifier.create(routeMono.map(Route::getId)) - .expectNext("routeTrue") + StepVerifier.create(routeMono.map(Route::getId)).expectNext("routeTrue") .verifyComplete(); - outputCapture.expect(containsString("Error applying predicate for route: routeError")); + outputCapture + .expect(containsString("Error applying predicate for route: routeError")); outputCapture.expect(containsString("java.lang.IllegalStateException: boom1")); - outputCapture.expect(containsString("Error applying predicate for route: routeFail")); + outputCapture + .expect(containsString("Error applying predicate for route: routeFail")); outputCapture.expect(containsString("java.lang.IllegalStateException: boom2")); } -} \ No newline at end of file + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactoryTests.java index b9248179..80a86c90 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/AfterRoutePredicateFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -74,7 +73,9 @@ public class AfterRoutePredicateFactoryTests { @Test public void testPredicates() { - boolean result = new AfterRoutePredicateFactory().apply(c -> c.setDatetime(ZonedDateTime.now().minusHours(2))).test(getExchange()); + boolean result = new AfterRoutePredicateFactory() + .apply(c -> c.setDatetime(ZonedDateTime.now().minusHours(2))) + .test(getExchange()); assertThat(result).isTrue(); } @@ -87,4 +88,5 @@ public class AfterRoutePredicateFactoryTests { return factory.apply(config).test(getExchange()); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BeforeRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BeforeRoutePredicateFactoryTests.java index a76e0ad9..24995078 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BeforeRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BeforeRoutePredicateFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,16 +12,15 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; -import org.junit.Test; - import java.time.ZonedDateTime; import java.util.HashMap; +import org.junit.Test; + import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory.DATETIME_KEY; import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.bindConfig; @@ -74,7 +73,9 @@ public class BeforeRoutePredicateFactoryTests { @Test public void testPredicates() { - boolean result = new BeforeRoutePredicateFactory().apply(c -> c.setDatetime(ZonedDateTime.now().minusHours(2))).test(getExchange()); + boolean result = new BeforeRoutePredicateFactory() + .apply(c -> c.setDatetime(ZonedDateTime.now().minusHours(2))) + .test(getExchange()); assertThat(result).isFalse(); } @@ -88,4 +89,5 @@ public class BeforeRoutePredicateFactoryTests { return factory.apply(config).test(getExchange()); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BetweenRoutePredicateFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BetweenRoutePredicateFactoryIntegrationTests.java index ed984f1d..41be82d6 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BetweenRoutePredicateFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BetweenRoutePredicateFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -21,6 +20,7 @@ import java.time.ZonedDateTime; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -44,24 +44,18 @@ public class BetweenRoutePredicateFactoryIntegrationTests extends BaseWebClientT @Test public void betweenPredicateWithValidDates() { - testClient.get() - .uri("/get") - .header(HttpHeaders.HOST, "www.betweenvalid.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(HANDLER_MAPPER_HEADER, - RoutePredicateHandlerMapping.class.getSimpleName()) + testClient.get().uri("/get").header(HttpHeaders.HOST, "www.betweenvalid.org") + .exchange().expectStatus().isOk().expectHeader() + .valueEquals(HANDLER_MAPPER_HEADER, + RoutePredicateHandlerMapping.class.getSimpleName()) .expectHeader().valueEquals(ROUTE_ID_HEADER, "test_between_valid"); } @Test public void notBetweenPredicateWorks() { - testClient.get() - .uri("/get") - .header(HttpHeaders.HOST, "www.notbetween.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(HANDLER_MAPPER_HEADER, + testClient.get().uri("/get").header(HttpHeaders.HOST, "www.notbetween.org") + .exchange().expectStatus().isOk().expectHeader() + .valueEquals(HANDLER_MAPPER_HEADER, RoutePredicateHandlerMapping.class.getSimpleName()) // should NOT be not_between_test because Between dates are in the past .expectHeader().valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); @@ -78,12 +72,14 @@ public class BetweenRoutePredicateFactoryIntegrationTests extends BaseWebClientT @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route("test_between_valid", r -> r.host("**.betweenvalid.org") - .and().between(ZonedDateTime.now().minusDays(1), ZonedDateTime.now().plusDays(1)) - .filters(f -> f.prefixPath("/httpbin")) - .uri(uri)) + .route("test_between_valid", + r -> r.host("**.betweenvalid.org").and() + .between(ZonedDateTime.now().minusDays(1), + ZonedDateTime.now().plusDays(1)) + .filters(f -> f.prefixPath("/httpbin")).uri(uri)) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BetweenRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BetweenRoutePredicateFactoryTests.java index a075e157..c3d7708c 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BetweenRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/BetweenRoutePredicateFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -22,6 +21,7 @@ import java.time.format.DateTimeFormatter; import java.util.HashMap; import org.junit.Test; + import org.springframework.boot.convert.ApplicationConversionService; import org.springframework.cloud.gateway.support.ConfigurationUtils; import org.springframework.cloud.gateway.support.StringToZonedDateTimeConverter; @@ -38,6 +38,43 @@ import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePr */ public class BetweenRoutePredicateFactoryTests { + static T bindConfig(HashMap properties, + AbstractRoutePredicateFactory factory) { + T config = factory.newConfig(); + + ApplicationConversionService conversionService = new ApplicationConversionService(); + conversionService.addConverter(new StringToZonedDateTimeConverter()); + ConfigurationUtils.bind(config, properties, "", "myname", null, + conversionService); + return config; + } + + static String minusHoursMillis(int hours) { + final int millis = hours * 1000 * 60 * 60; + return String.valueOf(System.currentTimeMillis() - millis); + } + + static String plusHoursMillis(int hours) { + final int millis = hours * 1000 * 60 * 60; + return String.valueOf(System.currentTimeMillis() + millis); + } + + static String minusHours(int hours) { + return ZonedDateTime.now().minusHours(hours) + .format(DateTimeFormatter.ISO_ZONED_DATE_TIME); + } + + static String plusHours(int hours) { + return ZonedDateTime.now().plusHours(hours) + .format(DateTimeFormatter.ISO_ZONED_DATE_TIME); + } + + static ServerWebExchange getExchange() { + MockServerHttpRequest request = MockServerHttpRequest.get("http://example.com") + .build(); + return MockServerWebExchange.from(request); + } + @Test public void beforeStringWorks() { String dateString1 = plusHours(1); @@ -57,7 +94,8 @@ public class BetweenRoutePredicateFactoryTests { final boolean result = runPredicate(dateString1, dateString2); - assertThat(result).as("Now is not between %s and %s", dateString1, dateString2).isTrue(); + assertThat(result).as("Now is not between %s and %s", dateString1, dateString2) + .isTrue(); } @Test @@ -87,7 +125,8 @@ public class BetweenRoutePredicateFactoryTests { final boolean result = runPredicate(dateString1, dateString2); - assertThat(result).as("Now is not between %s and %s", dateString1, dateString2).isTrue(); + assertThat(result).as("Now is not between %s and %s", dateString1, dateString2) + .isTrue(); } @Test @@ -121,36 +160,4 @@ public class BetweenRoutePredicateFactoryTests { return factory.apply(config).test(getExchange()); } - static T bindConfig(HashMap properties, - AbstractRoutePredicateFactory factory) { - T config = factory.newConfig(); - - ApplicationConversionService conversionService = new ApplicationConversionService(); - conversionService.addConverter(new StringToZonedDateTimeConverter()); - ConfigurationUtils.bind(config, properties, "", "myname", null, conversionService); - return config; - } - - static String minusHoursMillis(int hours) { - final int millis = hours * 1000 * 60 * 60; - return String.valueOf(System.currentTimeMillis() - millis); - } - - static String plusHoursMillis(int hours) { - final int millis = hours * 1000 * 60 * 60; - return String.valueOf(System.currentTimeMillis() + millis); - } - - static String minusHours(int hours) { - return ZonedDateTime.now().minusHours(hours).format(DateTimeFormatter.ISO_ZONED_DATE_TIME); - } - - static String plusHours(int hours) { - return ZonedDateTime.now().plusHours(hours).format(DateTimeFormatter.ISO_ZONED_DATE_TIME); - } - - static ServerWebExchange getExchange() { - MockServerHttpRequest request = MockServerHttpRequest.get("http://example.com").build(); - return MockServerWebExchange.from(request); - } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactoryIntegrationTests.java index 14701593..33d4f38a 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactoryIntegrationTests.java @@ -1,7 +1,25 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.handler.predicate; +import com.fasterxml.jackson.databind.JsonNode; 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; @@ -14,8 +32,6 @@ import org.springframework.context.annotation.Import; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import com.fasterxml.jackson.databind.JsonNode; - import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -27,6 +43,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @DirtiesContext public class CloudFoundryRouteServiceRoutePredicateFactoryIntegrationTests extends BaseWebClientTests { + @LocalServerPort int port; @@ -65,6 +82,7 @@ public class CloudFoundryRouteServiceRoutePredicateFactoryIntegrationTests @SpringBootConfiguration @Import(DefaultTestConfig.class) public static class TestConfig { + @Bean public RouteLocator routeLocator(RouteLocatorBuilder builder) { return builder.routes().route(r -> r.cloudFoundryRouteService().and() @@ -72,5 +90,7 @@ public class CloudFoundryRouteServiceRoutePredicateFactoryIntegrationTests .filters(f -> f.requestHeaderToRequestUri("X-CF-Forwarded-Url")) .uri("http://example.com")).build(); } + } -} \ No newline at end of file + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactoryTest.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactoryTest.java index c6d15b0a..20e647f3 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactoryTest.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CloudFoundryRouteServiceRoutePredicateFactoryTest.java @@ -1,15 +1,32 @@ -package org.springframework.cloud.gateway.handler.predicate; +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import static org.assertj.core.api.Assertions.assertThat; +package org.springframework.cloud.gateway.handler.predicate; import java.util.function.Predicate; import org.junit.Before; import org.junit.Test; + import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.server.ServerWebExchange; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Andrew Fitzgerald */ @@ -58,4 +75,5 @@ public class CloudFoundryRouteServiceRoutePredicateFactoryTest { assertThat(predicate.test(exchange)).isFalse(); } -} \ No newline at end of file + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CookieRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CookieRoutePredicateFactoryTests.java index 4dbd9c0b..634ba839 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CookieRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/CookieRoutePredicateFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/HeaderRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/HeaderRoutePredicateFactoryTests.java index a4a1f18d..a4eb5e8a 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/HeaderRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/HeaderRoutePredicateFactoryTests.java @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -41,38 +41,30 @@ public class HeaderRoutePredicateFactoryTests extends BaseWebClientTests { @Test public void headerRouteWorks() { - testClient.get() - .uri("/get") - .header("Foo", "bar") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(HANDLER_MAPPER_HEADER, - RoutePredicateHandlerMapping.class.getSimpleName()) + testClient.get().uri("/get").header("Foo", "bar").exchange().expectStatus().isOk() + .expectHeader() + .valueEquals(HANDLER_MAPPER_HEADER, + RoutePredicateHandlerMapping.class.getSimpleName()) .expectHeader().valueEquals(ROUTE_ID_HEADER, "header_test"); } @Test @SuppressWarnings("Duplicates") public void headerRouteIgnoredWhenHeaderMissing() { - testClient.get() - .uri("/get") + testClient.get().uri("/get") // no headers set. Test used to throw a null pointer exception. - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(HANDLER_MAPPER_HEADER, + .exchange().expectStatus().isOk().expectHeader() + .valueEquals(HANDLER_MAPPER_HEADER, RoutePredicateHandlerMapping.class.getSimpleName()) .expectHeader().valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); } @Test public void headerExistsWorksWithDsl() { - testClient.get() - .uri("/get") - .header("X-Foo", "bar") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(HANDLER_MAPPER_HEADER, - RoutePredicateHandlerMapping.class.getSimpleName()) + testClient.get().uri("/get").header("X-Foo", "bar").exchange().expectStatus() + .isOk().expectHeader() + .valueEquals(HANDLER_MAPPER_HEADER, + RoutePredicateHandlerMapping.class.getSimpleName()) .expectHeader().valueEquals(ROUTE_ID_HEADER, "header_exists_dsl"); } @@ -86,13 +78,10 @@ public class HeaderRoutePredicateFactoryTests extends BaseWebClientTests { @Bean RouteLocator queryRouteLocator(RouteLocatorBuilder builder) { - return builder.routes() - .route("header_exists_dsl", r -> - r.header("X-Foo") - .filters(f -> f.prefixPath("/httpbin")) - .uri(uri)) - .build(); + return builder.routes().route("header_exists_dsl", r -> r.header("X-Foo") + .filters(f -> f.prefixPath("/httpbin")).uri(uri)).build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateFactoryTests.java index 90699a4a..5e582aeb 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/HostRoutePredicateFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -45,12 +45,10 @@ public class HostRoutePredicateFactoryTests extends BaseWebClientTests { } public void expectHostRoute(String host, String routeId) { - testClient.get() - .uri("/get") - .header("Host", host) - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(HANDLER_MAPPER_HEADER, RoutePredicateHandlerMapping.class.getSimpleName()) + testClient.get().uri("/get").header("Host", host).exchange().expectStatus().isOk() + .expectHeader() + .valueEquals(HANDLER_MAPPER_HEADER, + RoutePredicateHandlerMapping.class.getSimpleName()) .expectHeader().valueEquals(ROUTE_ID_HEADER, routeId); } @@ -61,7 +59,8 @@ public class HostRoutePredicateFactoryTests extends BaseWebClientTests { @Test public void hostRouteBackwardsCompatibleShortcutWorks() { - expectHostRoute("www.hostpatternshortcut.org", "host_backwards_compatible_shortcut_test"); + expectHostRoute("www.hostpatternshortcut.org", + "host_backwards_compatible_shortcut_test"); } @Test @@ -87,11 +86,12 @@ public class HostRoutePredicateFactoryTests extends BaseWebClientTests { @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route("host_multi_dsl", r -> r.host("**.hostmultidsl1.org", "**.hostmultidsl2.org") - .filters(f -> f.prefixPath("/httpbin")) - .uri(uri)) + .route("host_multi_dsl", + r -> r.host("**.hostmultidsl1.org", "**.hostmultidsl2.org") + .filters(f -> f.prefixPath("/httpbin")).uri(uri)) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/MethodRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/MethodRoutePredicateFactoryTests.java index fd157719..116e87aa 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/MethodRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/MethodRoutePredicateFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ 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; @@ -37,18 +37,18 @@ public class MethodRoutePredicateFactoryTests extends BaseWebClientTests { @Test public void methodRouteWorks() { - testClient.get() - .uri("/get") - .header("Host", "www.method.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(HANDLER_MAPPER_HEADER, RoutePredicateHandlerMapping.class.getSimpleName()) + testClient.get().uri("/get").header("Host", "www.method.org").exchange() + .expectStatus().isOk().expectHeader() + .valueEquals(HANDLER_MAPPER_HEADER, + RoutePredicateHandlerMapping.class.getSimpleName()) .expectHeader().valueEquals(ROUTE_ID_HEADER, "method_test"); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java index d371b678..4e58c53c 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -47,24 +47,23 @@ public class PathRoutePredicateFactoryTests extends BaseWebClientTests { @Test public void trailingSlashReturns404() { - //since the configuration does not allow the trailing / to match this should fail + // since the configuration does not allow the trailing / to match this should fail testClient.get().uri("/abc/123/function/") - .header(HttpHeaders.HOST, "www.path.org") - .exchange() - .expectStatus().isNotFound(); + .header(HttpHeaders.HOST, "www.path.org").exchange().expectStatus() + .isNotFound(); } @Test public void defaultPathRouteWorks() { - expectPathRoute("/get", "www.thispathshouldnotmatch.org", "default_path_to_httpbin"); + expectPathRoute("/get", "www.thispathshouldnotmatch.org", + "default_path_to_httpbin"); } private void expectPathRoute(String uri, String host, String routeId) { - testClient.get().uri(uri) - .header(HttpHeaders.HOST, host) - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(HANDLER_MAPPER_HEADER, RoutePredicateHandlerMapping.class.getSimpleName()) + testClient.get().uri(uri).header(HttpHeaders.HOST, host).exchange().expectStatus() + .isOk().expectHeader() + .valueEquals(HANDLER_MAPPER_HEADER, + RoutePredicateHandlerMapping.class.getSimpleName()) .expectHeader().valueEquals(ROUTE_ID_HEADER, routeId); } @@ -72,13 +71,15 @@ public class PathRoutePredicateFactoryTests extends BaseWebClientTests { public void mulitPathRouteWorks() { expectPathRoute("/anything/multi11", "www.pathmulti.org", "path_multi"); expectPathRoute("/anything/multi22", "www.pathmulti.org", "path_multi"); - expectPathRoute("/anything/multi33", "www.pathmulti.org", "default_path_to_httpbin"); + expectPathRoute("/anything/multi33", "www.pathmulti.org", + "default_path_to_httpbin"); } @Test public void mulitPathDslRouteWorks() { expectPathRoute("/anything/multidsl1", "www.pathmultidsl.org", "path_multi_dsl"); - expectPathRoute("/anything/multidsl2", "www.pathmultidsl.org", "default_path_to_httpbin"); + expectPathRoute("/anything/multidsl2", "www.pathmultidsl.org", + "default_path_to_httpbin"); expectPathRoute("/anything/multidsl3", "www.pathmultidsl.org", "path_multi_dsl"); } @@ -93,12 +94,12 @@ public class PathRoutePredicateFactoryTests extends BaseWebClientTests { @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route("path_multi_dsl", r -> r.host("**.pathmultidsl.org") - .and().path(false, "/anything/multidsl1", "/anything/multidsl3") - .filters(f -> f.prefixPath("/httpbin")) - .uri(uri)) + .route("path_multi_dsl", r -> r.host("**.pathmultidsl.org").and() + .path(false, "/anything/multidsl1", "/anything/multidsl3") + .filters(f -> f.prefixPath("/httpbin")).uri(uri)) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryTests.java index f97e08e4..9e934a49 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/QueryRoutePredicateFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -48,30 +47,24 @@ public class QueryRoutePredicateFactoryTests extends BaseWebClientTests { @Test public void noQueryParamWorks() { - testClient.get().uri("/get") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin");; - - output.expect(not(containsString("Error applying predicate for route: foo_query_param"))); + testClient.get().uri("/get").exchange().expectStatus().isOk().expectHeader() + .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); + output.expect(not( + containsString("Error applying predicate for route: foo_query_param"))); } @Test public void queryParamWorks() { - testClient.get().uri("/get?foo=bar") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "foo_query_param");; + testClient.get().uri("/get?foo=bar").exchange().expectStatus().isOk() + .expectHeader().valueEquals(ROUTE_ID_HEADER, "foo_query_param"); } @Test public void emptyQueryParamWorks() { - testClient.get().uri("/get?foo") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin");; - - output.expect(not(containsString("Error applying predicate for route: foo_query_param"))); + testClient.get().uri("/get?foo").exchange().expectStatus().isOk().expectHeader() + .valueEquals(ROUTE_ID_HEADER, "default_path_to_httpbin"); + output.expect(not( + containsString("Error applying predicate for route: foo_query_param"))); } @EnableAutoConfiguration @@ -85,12 +78,12 @@ public class QueryRoutePredicateFactoryTests extends BaseWebClientTests { @Bean RouteLocator queryRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route("foo_query_param", r -> - r.query("foo", "bar") - .filters(f -> f.prefixPath("/httpbin")) - .uri(uri)) + .route("foo_query_param", + r -> r.query("foo", "bar") + .filters(f -> f.prefixPath("/httpbin")).uri(uri)) .build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactoryTest.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactoryTest.java index 67e7ca2a..6390376f 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactoryTest.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,15 +12,17 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; - import java.util.function.Predicate; + +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; 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; @@ -44,9 +46,6 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.function.BodyInserters; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; - import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** @@ -66,19 +65,14 @@ public class ReadBodyPredicateFactoryTest { Event messageEvent = new Event("message", "bar"); Event messageChannelEvent = new Event("message.channels", "bar"); - webClient - .post().uri("/events").body(BodyInserters.fromObject(messageEvent)) - .exchange() - .expectStatus().isOk() - .expectBody() - .jsonPath("$.headers.Hello").isEqualTo("World"); + webClient.post().uri("/events").body(BodyInserters.fromObject(messageEvent)) + .exchange().expectStatus().isOk().expectBody().jsonPath("$.headers.Hello") + .isEqualTo("World"); - webClient - .post().uri("/events").body(BodyInserters.fromObject(messageChannelEvent)) - .exchange() - .expectStatus().isOk() - .expectBody() - .jsonPath("$.headers.World").isEqualTo("Hello"); + webClient.post().uri("/events") + .body(BodyInserters.fromObject(messageChannelEvent)).exchange() + .expectStatus().isOk().expectBody().jsonPath("$.headers.World") + .isEqualTo("Hello"); } @@ -86,20 +80,22 @@ public class ReadBodyPredicateFactoryTest { @SpringBootConfiguration @RibbonClients({ @RibbonClient(name = "message", configuration = TestRibbonConfig.class), - @RibbonClient(name = "messageChannel", configuration = TestRibbonConfig.class) - }) + @RibbonClient(name = "messageChannel", configuration = TestRibbonConfig.class) }) @Import(PermitAllSecurityConfiguration.class) @RestController public static class TestConfig { + @Bean public RouteLocator routeLocator(RouteLocatorBuilder builder) { return builder.routes() - .route(p -> p.path("/events").and().method(HttpMethod.POST).and(). - readBody(Event.class, eventPredicate("message.channels")). - filters(f -> f.setPath("/messageChannel/events")).uri("lb://messageChannel")) - .route(p -> p.path("/events").and().method(HttpMethod.POST).and(). - readBody(Event.class, eventPredicate("message")) - .filters(f -> f.setPath("/message/events")).uri("lb://message")) + .route(p -> p.path("/events").and().method(HttpMethod.POST).and() + .readBody(Event.class, eventPredicate("message.channels")) + .filters(f -> f.setPath("/messageChannel/events")) + .uri("lb://messageChannel")) + .route(p -> p.path("/events").and().method(HttpMethod.POST).and() + .readBody(Event.class, eventPredicate("message")) + .filters(f -> f.setPath("/message/events")) + .uri("lb://message")) .build(); } @@ -116,6 +112,7 @@ public class ReadBodyPredicateFactoryTest { public String messageChannelEvents(@RequestBody Event e) { return "{\"headers\":{\"World\":\"Hello\"}}"; } + } protected static class TestRibbonConfig { @@ -127,16 +124,21 @@ public class ReadBodyPredicateFactoryTest { public ServerList ribbonServerList() { return new StaticServerList<>(new Server("localhost", this.port)); } + } } - class Event { + +class Event { + private String foo; + private String bar; - public Event() {} + Event() { + } - public Event(String foo, String bar) { + Event(String foo, String bar) { this.foo = foo; this.bar = bar; } @@ -156,4 +158,5 @@ public class ReadBodyPredicateFactoryTest { public void setBar(String bar) { this.bar = bar; } -} \ No newline at end of file + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicateFactoryTests.java index 359c12e0..441f11d1 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/RemoteAddrRoutePredicateFactoryTests.java @@ -1,10 +1,28 @@ +/* + * Copyright 2018-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.handler.predicate; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; -import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; +import java.time.Duration; import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -21,10 +39,8 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.reactive.function.client.ClientResponse; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.time.Duration; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.assertStatus; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT) @@ -45,8 +61,7 @@ public class RemoteAddrRoutePredicateFactoryTests extends BaseWebClientTests { public void remoteAddrRejects() { Mono result = webClient.get().uri("/nok/httpbin/").exchange(); - StepVerifier - .create(result) + StepVerifier.create(result) .consumeNextWith(response -> assertStatus(response, HttpStatus.NOT_FOUND)) .expectComplete().verify(DURATION); } @@ -65,6 +80,7 @@ public class RemoteAddrRoutePredicateFactoryTests extends BaseWebClientTests { @SpringBootConfiguration @Import(DefaultTestConfig.class) public static class TestConfig { + @Value("${test.uri}") String uri; @@ -76,6 +92,7 @@ public class RemoteAddrRoutePredicateFactoryTests extends BaseWebClientTests { "12.34.56.78") .filters(f -> f.setStatus(200)).uri(uri)).build(); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactoryIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactoryIntegrationTests.java index ca38051c..2a2a2421 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactoryIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -21,6 +20,7 @@ import java.util.Random; 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; @@ -48,26 +48,28 @@ public class WeightRoutePredicateFactoryIntegrationTests extends BaseWebClientTe @Autowired private WeightCalculatorWebFilter filter; + private static Random getRandom(double value) { + Random random = mock(Random.class); + when(random.nextDouble()).thenReturn(value); + return random; + } + @Test public void highWeight() { filter.setRandom(getRandom(0.9)); - testClient.get().uri("/get") - .header(HttpHeaders.HOST, "www.weighthigh.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "weight_high_test"); + testClient.get().uri("/get").header(HttpHeaders.HOST, "www.weighthigh.org") + .exchange().expectStatus().isOk().expectHeader() + .valueEquals(ROUTE_ID_HEADER, "weight_high_test"); } - @Test - public void lowWeight() { + @Test + public void lowWeight() { filter.setRandom(getRandom(0.1)); - testClient.get().uri("/get") - .header(HttpHeaders.HOST, "www.weightlow.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "weight_low_test"); + testClient.get().uri("/get").header(HttpHeaders.HOST, "www.weightlow.org") + .exchange().expectStatus().isOk().expectHeader() + .valueEquals(ROUTE_ID_HEADER, "weight_low_test"); } @EnableAutoConfiguration @@ -87,21 +89,12 @@ public class WeightRoutePredicateFactoryIntegrationTests extends BaseWebClientTe @Bean public RouteLocator testRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route("weight_low_test", r -> - r.weight("group1", 2) - .and().host("**.weightlow.org") - .filters(f -> f.prefixPath("/httpbin")) - .uri(this.uri)) + .route("weight_low_test", + r -> r.weight("group1", 2).and().host("**.weightlow.org") + .filters(f -> f.prefixPath("/httpbin")).uri(this.uri)) .build(); } } - private static Random getRandom(double value) { - Random random = mock(Random.class); - when(random.nextDouble()) - .thenReturn(value); - return random; - } - } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactoryYaml404Tests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactoryYaml404Tests.java index fa00bf44..35859a9b 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactoryYaml404Tests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/WeightRoutePredicateFactoryYaml404Tests.java @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.handler.predicate; @@ -47,15 +46,19 @@ public class WeightRoutePredicateFactoryYaml404Tests extends BaseWebClientTests @Autowired private WeightCalculatorWebFilter filter; + private static Random getRandom(double value) { + Random random = mock(Random.class); + when(random.nextDouble()).thenReturn(value); + return random; + } + @Test public void weightsFromYamlNot404() { filter.setRandom(getRandom(0.5)); - testClient.get().uri("/get") - .header(HttpHeaders.HOST, "www.weight4041.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "weight_first_404_test_1"); + testClient.get().uri("/get").header(HttpHeaders.HOST, "www.weight4041.org") + .exchange().expectStatus().isOk().expectHeader() + .valueEquals(ROUTE_ID_HEADER, "weight_first_404_test_1"); } @EnableAutoConfiguration @@ -71,11 +74,4 @@ public class WeightRoutePredicateFactoryYaml404Tests extends BaseWebClientTests } - private static Random getRandom(double value) { - Random random = mock(Random.class); - when(random.nextDouble()) - .thenReturn(value); - return random; - } - } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/CachingRouteDefinitionLocatorTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/CachingRouteDefinitionLocatorTests.java index c3aa5730..f632e018 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/CachingRouteDefinitionLocatorTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/CachingRouteDefinitionLocatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -31,31 +30,34 @@ public class CachingRouteDefinitionLocatorTests { public void getRouteDefinitionsWorks() { RouteDefinition routeDef1 = routeDef(1); RouteDefinition routeDef2 = routeDef(2); - CachingRouteDefinitionLocator locator = new CachingRouteDefinitionLocator(() -> Flux.just(routeDef2, routeDef1)); + CachingRouteDefinitionLocator locator = new CachingRouteDefinitionLocator( + () -> Flux.just(routeDef2, routeDef1)); - List routes = locator.getRouteDefinitions().collectList().block(); + List routes = locator.getRouteDefinitions().collectList() + .block(); assertThat(routes).containsExactlyInAnyOrder(routeDef1, routeDef2); } - @Test public void refreshWorks() { RouteDefinition routeDef1 = routeDef(1); RouteDefinition routeDef2 = routeDef(2); - CachingRouteDefinitionLocator locator = new CachingRouteDefinitionLocator(new RouteDefinitionLocator() { - int i = 0; + CachingRouteDefinitionLocator locator = new CachingRouteDefinitionLocator( + new RouteDefinitionLocator() { + int i = 0; - @Override - public Flux getRouteDefinitions() { - if (i++ == 0) { - return Flux.just(routeDef2); - } - return Flux.just(routeDef2, routeDef1); - } - }); + @Override + public Flux getRouteDefinitions() { + if (i++ == 0) { + return Flux.just(routeDef2); + } + return Flux.just(routeDef2, routeDef1); + } + }); - List routes = locator.getRouteDefinitions().collectList().block(); + List routes = locator.getRouteDefinitions().collectList() + .block(); assertThat(routes).containsExactlyInAnyOrder(routeDef2); routes = locator.refresh().collectList().block(); @@ -65,8 +67,9 @@ public class CachingRouteDefinitionLocatorTests { RouteDefinition routeDef(int id) { RouteDefinition def = new RouteDefinition(); def.setId(String.valueOf(id)); - def.setUri(URI.create("http://localhost/"+id)); + def.setUri(URI.create("http://localhost/" + id)); def.setOrder(id); return def; } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/CachingRouteLocatorTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/CachingRouteLocatorTests.java index ac1f2d01..4acd909e 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/CachingRouteLocatorTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/CachingRouteLocatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -30,14 +29,14 @@ public class CachingRouteLocatorTests { public void getRoutesWorks() { Route route1 = route(1); Route route2 = route(2); - CachingRouteLocator locator = new CachingRouteLocator(() -> Flux.just(route2, route1)); + CachingRouteLocator locator = new CachingRouteLocator( + () -> Flux.just(route2, route1)); List routes = locator.getRoutes().collectList().block(); assertThat(routes).containsExactly(route1, route2); } - @Test public void refreshWorks() { Route route1 = route(1); @@ -62,9 +61,8 @@ public class CachingRouteLocatorTests { } Route route(int id) { - return Route.async().id(String.valueOf(id)) - .uri("http://localhost/"+id) - .order(id) - .predicate(exchange -> true).build(); + return Route.async().id(String.valueOf(id)).uri("http://localhost/" + id) + .order(id).predicate(exchange -> true).build(); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteDefinitionRouteLocatorTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteDefinitionRouteLocatorTests.java index 29283a83..20d05d9b 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteDefinitionRouteLocatorTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteDefinitionRouteLocatorTests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.route; import java.net.URI; @@ -51,7 +67,8 @@ public class RouteDefinitionRouteLocatorTests { RouteDefinitionRouteLocator routeDefinitionRouteLocator = new RouteDefinitionRouteLocator( new PropertiesRouteDefinitionLocator(gatewayProperties), predicates, - gatewayFilterFactories, gatewayProperties, new DefaultConversionService()); + gatewayFilterFactories, gatewayProperties, + new DefaultConversionService()); List routes = routeDefinitionRouteLocator.getRoutes().collectList() .block(); @@ -73,10 +90,13 @@ public class RouteDefinitionRouteLocatorTests { } static class TestOrderedGatewayFilterFactory extends AbstractGatewayFilterFactory { + @Override public GatewayFilter apply(Object config) { return new OrderedGatewayFilter((exchange, chain) -> chain.filter(exchange), 9999); } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteRefreshListenerTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteRefreshListenerTests.java index 33fe6426..9e02e356 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteRefreshListenerTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteRefreshListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -65,4 +64,5 @@ public class RouteRefreshListenerTests { verify(publisher, times(2)).publishEvent(any(RefreshRoutesEvent.class)); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteTests.java index cfdcd33d..38f64f5f 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/RouteTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.route; @@ -30,45 +29,32 @@ public class RouteTests { @Test public void defeaultHttpPort() { - Route route = Route.async().id("1") - .predicate(exchange -> true) - .uri("http://acme.com") - .build(); + Route route = Route.async().id("1").predicate(exchange -> true) + .uri("http://acme.com").build(); - assertThat(route.getUri()).hasHost("acme.com") - .hasScheme("http") - .hasPort(80); + assertThat(route.getUri()).hasHost("acme.com").hasScheme("http").hasPort(80); } @Test public void defeaultHttpsPort() { - Route route = Route.async().id("1") - .predicate(exchange -> true) - .uri("https://acme.com") - .build(); + Route route = Route.async().id("1").predicate(exchange -> true) + .uri("https://acme.com").build(); - assertThat(route.getUri()).hasHost("acme.com") - .hasScheme("https") - .hasPort(443); + assertThat(route.getUri()).hasHost("acme.com").hasScheme("https").hasPort(443); } @Test public void fullUri() { - Route route = Route.async().id("1") - .predicate(exchange -> true) - .uri("http://acme.com:8080") - .build(); + Route route = Route.async().id("1").predicate(exchange -> true) + .uri("http://acme.com:8080").build(); - assertThat(route.getUri()).hasHost("acme.com") - .hasScheme("http") - .hasPort(8080); + assertThat(route.getUri()).hasHost("acme.com").hasScheme("http").hasPort(8080); } @Test public void nullScheme() { exception.expect(IllegalArgumentException.class); - Route.async().id("1") - .predicate(exchange -> true) - .uri("/pathonly"); + Route.async().id("1").predicate(exchange -> true).uri("/pathonly"); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpecTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpecTests.java index 80e26d56..584e64ec 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpecTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpecTests.java @@ -1,11 +1,27 @@ +/* + * Copyright 2018-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.route.builder; import org.junit.Test; -import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; import reactor.core.publisher.Mono; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; import org.springframework.cloud.gateway.route.Route; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Ordered; @@ -27,11 +43,10 @@ public class GatewayFilterSpecTests { } private void testFilter(Class type, - GatewayFilter gatewayFilter, int order) { - ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class); - Route.AsyncBuilder routeBuilder = Route.async() - .id("123") - .uri("abc:123") + GatewayFilter gatewayFilter, int order) { + ConfigurableApplicationContext context = mock( + ConfigurableApplicationContext.class); + Route.AsyncBuilder routeBuilder = Route.async().id("123").uri("abc:123") .predicate(exchange -> true); RouteLocatorBuilder.Builder routes = new RouteLocatorBuilder(context).routes(); GatewayFilterSpec spec = new GatewayFilterSpec(routeBuilder, routes); @@ -42,7 +57,8 @@ public class GatewayFilterSpecTests { assertFilter(route.getFilters().get(0), type, order); } - private void assertFilter(GatewayFilter filter, Class type, int order) { + private void assertFilter(GatewayFilter filter, Class type, + int order) { assertThat(filter).isInstanceOf(type); Ordered ordered = (Ordered) filter; assertThat(ordered.getOrder()).isEqualTo(order); @@ -50,10 +66,9 @@ public class GatewayFilterSpecTests { @Test public void testFilters() { - ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class); - Route.AsyncBuilder routeBuilder = Route.async() - .id("123") - .uri("abc:123") + ConfigurableApplicationContext context = mock( + ConfigurableApplicationContext.class); + Route.AsyncBuilder routeBuilder = Route.async().id("123").uri("abc:123") .predicate(exchange -> true); RouteLocatorBuilder.Builder routes = new RouteLocatorBuilder(context).routes(); GatewayFilterSpec spec = new GatewayFilterSpec(routeBuilder, routes); @@ -67,6 +82,7 @@ public class GatewayFilterSpecTests { } protected static class MyOrderedFilter implements GatewayFilter, Ordered { + @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { return Mono.empty(); @@ -76,12 +92,16 @@ public class GatewayFilterSpecTests { public int getOrder() { return 1000; } + } protected static class MyUnorderedFilter implements GatewayFilter { + @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { return Mono.empty(); } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/builder/RouteBuilderTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/builder/RouteBuilderTests.java index 7c4f7eca..f1f07193 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/builder/RouteBuilderTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/route/builder/RouteBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 the original author or authors. + * Copyright 2018-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,21 +12,22 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ + package org.springframework.cloud.gateway.route.builder; +import java.net.URI; + import org.junit.Test; import org.junit.runner.RunWith; +import reactor.test.StepVerifier; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.junit4.SpringRunner; -import reactor.test.StepVerifier; - -import java.net.URI; /** * @author Biju Kunjummen @@ -65,5 +66,7 @@ public class RouteBuilderTests { @EnableAutoConfiguration @Configuration public static class SpringConfig { + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/support/ShortcutConfigurableTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/support/ShortcutConfigurableTests.java index ae2d1203..add269ac 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/support/ShortcutConfigurableTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/support/ShortcutConfigurableTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.support; @@ -24,6 +23,7 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; @@ -39,11 +39,11 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest public class ShortcutConfigurableTests { - private SpelExpressionParser parser; - @Autowired BeanFactory beanFactory; + private SpelExpressionParser parser; + @Test public void testNormalizeDefaultTypeWithSpel() { parser = new SpelExpressionParser(); @@ -56,10 +56,10 @@ public class ShortcutConfigurableTests { Map args = new HashMap<>(); args.put("bean", "#{@foo}"); args.put("arg1", "val1"); - Map map = ShortcutType.DEFAULT.normalize(args, shortcutConfigurable, parser, this.beanFactory); - assertThat(map).isNotNull() - .containsEntry("bean", 42) - .containsEntry("arg1", "val1"); + Map map = ShortcutType.DEFAULT.normalize(args, + shortcutConfigurable, parser, this.beanFactory); + assertThat(map).isNotNull().containsEntry("bean", 42).containsEntry("arg1", + "val1"); } @Test @@ -81,10 +81,10 @@ public class ShortcutConfigurableTests { args.put("1", "#{@foo}"); args.put("2", "val1"); args.put("3", "val2"); - Map map = ShortcutType.GATHER_LIST.normalize(args, shortcutConfigurable, parser, this.beanFactory); + Map map = ShortcutType.GATHER_LIST.normalize(args, + shortcutConfigurable, parser, this.beanFactory); assertThat(map).isNotNull().containsKey("values"); - assertThat((List)map.get("values")) - .containsExactly(42, "val1", "val2"); + assertThat((List) map.get("values")).containsExactly(42, "val1", "val2"); } @Test @@ -118,22 +118,26 @@ public class ShortcutConfigurableTests { if (hasTailFlag) { args.put("4", "false"); } - Map map = ShortcutType.GATHER_LIST_TAIL_FLAG.normalize(args, shortcutConfigurable, parser, this.beanFactory); + Map map = ShortcutType.GATHER_LIST_TAIL_FLAG.normalize(args, + shortcutConfigurable, parser, this.beanFactory); assertThat(map).isNotNull().containsKey("values"); - assertThat((List)map.get("values")) - .containsExactly("val0", "val1", "val2"); + assertThat((List) map.get("values")).containsExactly("val0", "val1", "val2"); if (hasTailFlag) { assertThat(map.get("flag")).isEqualTo("false"); - } else { + } + else { assertThat(map).doesNotContainKeys("flag"); } } @SpringBootConfiguration protected static class TestConfig { + @Bean public Integer foo() { return 42; } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/support/ipresolver/XForwardedRemoteAddressResolverTest.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/support/ipresolver/XForwardedRemoteAddressResolverTest.java index 9368c9e2..59f52b7a 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/support/ipresolver/XForwardedRemoteAddressResolverTest.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/support/ipresolver/XForwardedRemoteAddressResolverTest.java @@ -1,14 +1,31 @@ -package org.springframework.cloud.gateway.support.ipresolver; +/* + * Copyright 2018-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import static org.assertj.core.api.Assertions.assertThat; +package org.springframework.cloud.gateway.support.ipresolver; import java.net.InetSocketAddress; import org.junit.Test; + import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.server.ServerWebExchange; +import static org.assertj.core.api.Assertions.assertThat; + public class XForwardedRemoteAddressResolverTest { private final InetSocketAddress remote0000Address = InetSocketAddress @@ -135,4 +152,5 @@ public class XForwardedRemoteAddressResolverTest { MockServerHttpRequest.BaseBuilder requestBuilder) { return MockServerWebExchange.from(requestBuilder.build()); } -} \ No newline at end of file + +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java index 957662e4..2b2b4826 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; @@ -31,6 +30,7 @@ import static org.junit.Assume.assumeThat; /** * To run this suite in an IDE, set env var GATEWAY_ADHOC_ENABLED=true in test runner. + * * @author Spencer Gibb */ @RunWith(Suite.class) @@ -122,8 +122,7 @@ import static org.junit.Assume.assumeThat; org.springframework.cloud.gateway.test.WebfluxNotIncludedTests.class, org.springframework.cloud.gateway.test.HttpStatusTests.class, org.springframework.cloud.gateway.test.GatewayIntegrationTests.class, - org.springframework.cloud.gateway.test.sse.SseIntegrationTests.class, -}) + org.springframework.cloud.gateway.test.sse.SseIntegrationTests.class }) public class AdhocTestSuite { @ClassRule @@ -133,11 +132,12 @@ public class AdhocTestSuite { @Override public Statement apply(Statement base, Description description) { - assumeThat("Adhoc Tests ignored", - System.getenv("GATEWAY_ADHOC_ENABLED"), + assumeThat("Adhoc Tests ignored", System.getenv("GATEWAY_ADHOC_ENABLED"), is(equalTo("true"))); return base; } + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java index d337c095..c7020fd0 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/BaseWebClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 the original author or authors. + * Copyright 2017-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,16 +12,18 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; import java.time.Duration; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Before; + import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.web.server.LocalServerPort; @@ -39,27 +41,27 @@ import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.client.WebClient; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; - 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-RouteDefinition-Id"; + protected static final Duration DURATION = Duration.ofSeconds(5); @LocalServerPort protected int port = 0; protected WebTestClient testClient; + protected WebClient webClient; + protected String baseUri; @Before @@ -74,13 +76,13 @@ public class BaseWebClientTests { @Configuration @RibbonClients({ - @RibbonClient(name = "testservice", configuration = TestRibbonConfig.class), - @RibbonClient(name = "myservice", configuration = TestRibbonConfig.class) - }) + @RibbonClient(name = "testservice", configuration = TestRibbonConfig.class), + @RibbonClient(name = "myservice", configuration = TestRibbonConfig.class) }) @Import(PermitAllSecurityConfiguration.class) public static class DefaultTestConfig { + private static final Log log = LogFactory.getLog(DefaultTestConfig.class); - + @Bean public HttpBinCompatibleController httpBinController() { return new HttpBinCompatibleController(); @@ -91,21 +93,26 @@ public class BaseWebClientTests { public GlobalFilter modifyResponseFilter() { return (exchange, chain) -> { log.info("modifyResponseFilter start"); - String value = exchange.getAttributeOrDefault(GATEWAY_HANDLER_MAPPER_ATTR, "N/A"); + String value = exchange.getAttributeOrDefault(GATEWAY_HANDLER_MAPPER_ATTR, + "N/A"); exchange.getResponse().getHeaders().add(HANDLER_MAPPER_HEADER, value); - Route route = exchange.getAttributeOrDefault(GATEWAY_ROUTE_ATTR,null); + Route route = exchange.getAttributeOrDefault(GATEWAY_ROUTE_ATTR, null); if (route != null) { - exchange.getResponse().getHeaders().add(ROUTE_ID_HEADER, route.getId()); + exchange.getResponse().getHeaders().add(ROUTE_ID_HEADER, + route.getId()); } return chain.filter(exchange); }; } + } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class MainConfig { } + public static class MainConfig { + + } protected static class TestRibbonConfig { @@ -116,6 +123,7 @@ public class BaseWebClientTests { public ServerList ribbonServerList() { return new StaticServerList<>(new Server("localhost", this.port)); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/FormIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/FormIntegrationTests.java index 53f44aef..d4b677bd 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/FormIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/FormIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; @@ -42,6 +41,7 @@ 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.http.MediaType.APPLICATION_FORM_URLENCODED; import static org.springframework.web.reactive.function.BodyExtractors.toMono; @RunWith(SpringRunner.class) @@ -50,7 +50,8 @@ import static org.springframework.web.reactive.function.BodyExtractors.toMono; @SuppressWarnings("unchecked") public class FormIntegrationTests extends BaseWebClientTests { - public static final MediaType FORM_URL_ENCODED_CONTENT_TYPE = new MediaType(MediaType.APPLICATION_FORM_URLENCODED, Charset.forName("UTF-8")); + public static final MediaType FORM_URL_ENCODED_CONTENT_TYPE = new MediaType( + APPLICATION_FORM_URLENCODED, Charset.forName("UTF-8")); @Test public void formUrlencodedWorks() { @@ -58,14 +59,9 @@ public class FormIntegrationTests extends BaseWebClientTests { formData.add("foo", "bar"); formData.add("baz", "bam"); - testClient.post() - .uri("/post") - .contentType(FORM_URL_ENCODED_CONTENT_TYPE) - .body(BodyInserters.fromFormData(formData)) - .exchange() - .expectStatus().isOk() - .expectBody(Map.class) - .consumeWith(result -> { + testClient.post().uri("/post").contentType(FORM_URL_ENCODED_CONTENT_TYPE) + .body(BodyInserters.fromFormData(formData)).exchange().expectStatus() + .isOk().expectBody(Map.class).consumeWith(result -> { Map map = result.getResponseBody(); Map form = getMap(map, "form"); assertThat(form).containsEntry("foo", "bar"); @@ -85,27 +81,24 @@ public class FormIntegrationTests extends BaseWebClientTests { MultiValueMap parts = new LinkedMultiValueMap<>(); parts.add("imgpart", entity); - Mono result = webClient.post() - .uri("/post") + Mono result = webClient.post().uri("/post") .contentType(MediaType.MULTIPART_FORM_DATA) - .body(BodyInserters.fromMultipartData(parts)) - .exchange() + .body(BodyInserters.fromMultipartData(parts)).exchange() .flatMap(response -> response.body(toMono(Map.class))); - StepVerifier.create(result) - .consumeNextWith(map -> { - Map files = getMap(map, "files"); - assertThat(files).containsKey("imgpart"); - String file = (String) files.get("imgpart"); - assertThat(file).startsWith("data:").contains(";base64,"); - }) - .expectComplete() - .verify(DURATION); + StepVerifier.create(result).consumeNextWith(map -> { + Map files = getMap(map, "files"); + assertThat(files).containsKey("imgpart"); + String file = (String) files.get("imgpart"); + assertThat(file).startsWith("data:").contains(";base64,"); + }).expectComplete().verify(DURATION); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ForwardTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ForwardTests.java index c3cd3e2c..e5346cf3 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ForwardTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ForwardTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,14 +12,17 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; +import java.util.Collections; +import java.util.Map; + import org.junit.Before; 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; @@ -32,9 +35,6 @@ import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.Collections; -import java.util.Map; - import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @RunWith(SpringRunner.class) @@ -42,6 +42,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @DirtiesContext @SuppressWarnings("unchecked") public class ForwardTests { + @LocalServerPort protected int port = 0; @@ -50,27 +51,21 @@ public class ForwardTests { @Before public void setup() { String baseUri = "http://localhost:" + port; - this.client = WebTestClient.bindToServer() - .baseUrl(baseUri) - .build(); + this.client = WebTestClient.bindToServer().baseUrl(baseUri).build(); } @Test public void forwardWorks() { this.client.get().uri("/localcontroller") - .header(HttpHeaders.HOST, "www.forward.org") - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"from\":\"localcontroller\"}"); + .header(HttpHeaders.HOST, "www.forward.org").exchange().expectStatus() + .isOk().expectBody().json("{\"from\":\"localcontroller\"}"); } @Test public void forwardWithCorrectPath() { - this.client.get().uri("/foo") - .header(HttpHeaders.HOST, "www.forward.org") - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"from\":\"localcontroller\"}"); + this.client.get().uri("/foo").header(HttpHeaders.HOST, "www.forward.org") + .exchange().expectStatus().isOk().expectBody() + .json("{\"from\":\"localcontroller\"}"); } @EnableAutoConfiguration @@ -83,6 +78,7 @@ public class ForwardTests { public Map localController() { return Collections.singletonMap("from", "localcontroller"); } + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java index f10a0987..0b736c27 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; @@ -63,59 +62,53 @@ public class GatewayIntegrationTests extends BaseWebClientTests { @Test public void complexContentTypeWorks() { - testClient.post() - .uri("/headers") - .contentType(MediaType.APPLICATION_JSON_UTF8) - .syncBody("testdata") - .header("Host", "www.complexcontenttype.org") - .exchange() - .expectStatus().isOk() - .expectBody(Map.class) + testClient.post().uri("/headers").contentType(MediaType.APPLICATION_JSON_UTF8) + .syncBody("testdata").header("Host", "www.complexcontenttype.org") + .exchange().expectStatus().isOk().expectBody(Map.class) .consumeWith(result -> { - Map headers = getMap(result.getResponseBody(), "headers"); - assertThat(headers).containsEntry(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); + Map headers = getMap(result.getResponseBody(), + "headers"); + assertThat(headers).containsEntry(HttpHeaders.CONTENT_TYPE, + MediaType.APPLICATION_JSON_UTF8_VALUE); }); } - @Test public void forwardedHeadersWork() { - testClient.get() - .uri("/headers") - .exchange() - .expectStatus().isOk() - .expectBody(Map.class) - .consumeWith(result -> { - Map headers = getMap(result.getResponseBody(), "headers"); - assertThat(headers).containsKeys(ForwardedHeadersFilter.FORWARDED_HEADER, + testClient.get().uri("/headers").exchange().expectStatus().isOk() + .expectBody(Map.class).consumeWith(result -> { + Map headers = getMap(result.getResponseBody(), + "headers"); + assertThat(headers).containsKeys( + ForwardedHeadersFilter.FORWARDED_HEADER, XForwardedHeadersFilter.X_FORWARDED_FOR_HEADER, XForwardedHeadersFilter.X_FORWARDED_HOST_HEADER, XForwardedHeadersFilter.X_FORWARDED_PORT_HEADER, XForwardedHeadersFilter.X_FORWARDED_PROTO_HEADER); assertThat(headers.get(ForwardedHeadersFilter.FORWARDED_HEADER)) .asString().contains("proto=http") - .contains("host=\"localhost:") - .contains("for=\"127.0.0.1:"); - assertThat(headers.get(XForwardedHeadersFilter.X_FORWARDED_HOST_HEADER)) - .asString().isEqualTo("localhost:"+this.port); - assertThat(headers.get(XForwardedHeadersFilter.X_FORWARDED_PORT_HEADER)) - .asString().isEqualTo(""+this.port); - assertThat(headers.get(XForwardedHeadersFilter.X_FORWARDED_PROTO_HEADER)) - .asString().isEqualTo("http"); + .contains("host=\"localhost:").contains("for=\"127.0.0.1:"); + assertThat( + headers.get(XForwardedHeadersFilter.X_FORWARDED_HOST_HEADER)) + .asString().isEqualTo("localhost:" + this.port); + assertThat( + headers.get(XForwardedHeadersFilter.X_FORWARDED_PORT_HEADER)) + .asString().isEqualTo("" + this.port); + assertThat( + headers.get(XForwardedHeadersFilter.X_FORWARDED_PROTO_HEADER)) + .asString().isEqualTo("http"); }); } @Test public void compositeRouteWorks() { - testClient.get().uri("/headers?foo=bar&baz") - .header("Host", "www.foo.org") - .header("X-Request-Id", "123") - .cookie("chocolate", "chip") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(HANDLER_MAPPER_HEADER, - RoutePredicateHandlerMapping.class.getSimpleName()) - .expectHeader().valueEquals(ROUTE_ID_HEADER, "host_foo_path_headers_to_httpbin") + testClient.get().uri("/headers?foo=bar&baz").header("Host", "www.foo.org") + .header("X-Request-Id", "123").cookie("chocolate", "chip").exchange() + .expectStatus().isOk().expectHeader() + .valueEquals(HANDLER_MAPPER_HEADER, + RoutePredicateHandlerMapping.class.getSimpleName()) + .expectHeader() + .valueEquals(ROUTE_ID_HEADER, "host_foo_path_headers_to_httpbin") .expectHeader().valueEquals("X-Response-Foo", "Bar"); } @@ -123,54 +116,47 @@ public class GatewayIntegrationTests extends BaseWebClientTests { public void defaultFiltersWorks() { assertThat(this.properties.getDefaultFilters()).isNotEmpty(); - testClient.get().uri("/headers") - .header("Host", "www.addresponseheader.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals("X-Response-Default-Foo", "Default-Bar") + testClient.get().uri("/headers").header("Host", "www.addresponseheader.org") + .exchange().expectStatus().isOk().expectHeader() + .valueEquals("X-Response-Default-Foo", "Default-Bar") .returnResult(Object.class).consumeWith(result -> { - HttpHeaders httpHeaders = result.getResponseHeaders(); - assertThat(httpHeaders.get("X-Response-Default-Foo")).hasSize(1); - }); + HttpHeaders httpHeaders = result.getResponseHeaders(); + assertThat(httpHeaders.get("X-Response-Default-Foo")).hasSize(1); + }); } @Test public void loadBalancerFilterWorks() { - testClient.get().uri("/get") - .header("Host", "www.loadbalancerclient.org") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals(ROUTE_ID_HEADER, "load_balancer_client_test"); + testClient.get().uri("/get").header("Host", "www.loadbalancerclient.org") + .exchange().expectStatus().isOk().expectHeader() + .valueEquals(ROUTE_ID_HEADER, "load_balancer_client_test"); } @Test public void loadBalancerFilterNoClientWorks() { - testClient.get().uri("/get") - .header("Host", "www.loadbalancerclientempty.org") - .exchange() - .expectStatus().value(new BaseMatcher() { - @Override - public boolean matches(Object item) { - if (Integer.class.isInstance(item)) { - Integer toMatch = (Integer) item; - return toMatch.intValue() == 503; - } - return false; - } + testClient.get().uri("/get").header("Host", "www.loadbalancerclientempty.org") + .exchange().expectStatus().value(new BaseMatcher() { + @Override + public boolean matches(Object item) { + if (Integer.class.isInstance(item)) { + Integer toMatch = (Integer) item; + return toMatch.intValue() == 503; + } + return false; + } - @Override - public void describeTo(Description description) { - description.appendText("Expected 503"); - } - }); + @Override + public void describeTo(Description description) { + description.appendText("Expected 503"); + } + }); } @Test // gh-374 no content type/empty body causes NPR in NettyRoutingFilter public void noContentType() { - testClient.get().uri("/nocontenttype") - .exchange() - .expectStatus().is2xxSuccessful(); + testClient.get().uri("/nocontenttype").exchange().expectStatus() + .is2xxSuccessful(); } @EnableAutoConfiguration @@ -181,6 +167,12 @@ public class GatewayIntegrationTests extends BaseWebClientTests { private static final Log log = LogFactory.getLog(TestConfig.class); + private static Mono postFilterWork(ServerWebExchange exchange) { + log.info("postFilterWork"); + exchange.getResponse().getHeaders().add("X-Post-Header", "AddedAfterRoute"); + return Mono.empty(); + } + @RequestMapping("/httpbin/nocontenttype") public ResponseEntity nocontenttype() { return ResponseEntity.status(204).build(); @@ -195,12 +187,6 @@ public class GatewayIntegrationTests extends BaseWebClientTests { }; } - private static Mono postFilterWork(ServerWebExchange exchange) { - log.info("postFilterWork"); - exchange.getResponse().getHeaders().add("X-Post-Header", "AddedAfterRoute"); - return Mono.empty(); - } - } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java index bf6f6ce4..d23e27d0 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; @@ -32,10 +31,14 @@ import org.springframework.context.annotation.Profile; @EnableAutoConfiguration public class GatewayTestApplication { + public static void main(String[] args) { + SpringApplication.run(GatewayTestApplication.class, args); + } + /* - TO test run `spring cloud configserver eureka`, - then run this app with `--spring.profiles.active=discovery` - should be able to hit http://localhost:8008/configserver/foo/default a normal configserver api + * TO test run `spring cloud configserver eureka`, then run this app with + * `--spring.profiles.active=discovery` should be able to hit + * http://localhost:8008/configserver/foo/default a normal configserver api */ @Configuration @EnableDiscoveryClient @@ -43,13 +46,11 @@ public class GatewayTestApplication { protected static class GatewayDiscoveryConfiguration { @Bean - public DiscoveryClientRouteDefinitionLocator discoveryClientRouteLocator(DiscoveryClient discoveryClient, - DiscoveryLocatorProperties properties) { + public DiscoveryClientRouteDefinitionLocator discoveryClientRouteLocator( + DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) { return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties); } + } - public static void main(String[] args) { - SpringApplication.run(GatewayTestApplication.class, args); - } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/HttpBinCompatibleController.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/HttpBinCompatibleController.java index 3f052775..6f2c0c4e 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/HttpBinCompatibleController.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/HttpBinCompatibleController.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; @@ -44,6 +43,7 @@ import org.springframework.web.server.ServerWebExchange; @RestController @RequestMapping("/httpbin") public class HttpBinCompatibleController { + private static final Log log = LogFactory.getLog(HttpBinCompatibleController.class); @RequestMapping("/") @@ -51,8 +51,8 @@ public class HttpBinCompatibleController { return "httpbin compatible home"; } - @RequestMapping(path = "/headers", method = { - RequestMethod.GET, RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_VALUE) + @RequestMapping(path = "/headers", method = { RequestMethod.GET, + RequestMethod.POST }, produces = MediaType.APPLICATION_JSON_VALUE) public Map headers(ServerWebExchange exchange) { Map result = new HashMap<>(); result.put("headers", getHeaders(exchange)); @@ -60,13 +60,15 @@ public class HttpBinCompatibleController { } @RequestMapping(path = "/delay/{sec}", produces = MediaType.APPLICATION_JSON_VALUE) - public Mono> get(ServerWebExchange exchange, @PathVariable int sec) throws InterruptedException { + public Mono> get(ServerWebExchange exchange, + @PathVariable int sec) throws InterruptedException { int delay = Math.min(sec, 10); return Mono.just(get(exchange)).delayElement(Duration.ofSeconds(delay)); } @RequestMapping(path = "/anything/{anything}", produces = MediaType.APPLICATION_JSON_VALUE) - public Map anything(ServerWebExchange exchange, @PathVariable(required = false) String anything) { + public Map anything(ServerWebExchange exchange, + @PathVariable(required = false) String anything) { return get(exchange); } @@ -75,33 +77,37 @@ public class HttpBinCompatibleController { if (log.isDebugEnabled()) { log.debug("httpbin /get"); } - HashMap result = new HashMap<>(); - HashMap params = new HashMap<>(); - exchange.getRequest().getQueryParams().forEach((name, values) -> { - params.put(name, values.get(0)); - }); - result.put("args", params); - result.put("headers", getHeaders(exchange)); - return result; + HashMap result = new HashMap<>(); + HashMap params = new HashMap<>(); + exchange.getRequest().getQueryParams().forEach((name, values) -> { + params.put(name, values.get(0)); + }); + result.put("args", params); + result.put("headers", getHeaders(exchange)); + return result; } @RequestMapping(value = "/post", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public Mono> postFormData(@RequestBody Mono> parts) { + public Mono> postFormData( + @RequestBody Mono> parts) { // StringDecoder decoder = StringDecoder.allMimeTypes(true); return parts.flux().flatMap(map -> Flux.fromIterable(map.values())) - .flatMap(Flux::fromIterable) - .filter(part -> part instanceof FilePart) + .flatMap(Flux::fromIterable).filter(part -> part instanceof FilePart) .reduce(new HashMap(), (files, part) -> { MediaType contentType = part.headers().getContentType(); long contentLength = part.headers().getContentLength(); - files.put(part.name(), "data:"+contentType+";base64,"+contentLength); //TODO: get part data + files.put(part.name(), + "data:" + contentType + ";base64," + contentLength); // TODO: + // get + // part + // data return files; }).map(files -> Collections.singletonMap("files", files)); } @RequestMapping(path = "/post", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public Mono> postUrlEncoded(ServerWebExchange exchange) throws - IOException { + public Mono> postUrlEncoded(ServerWebExchange exchange) + throws IOException { return post(exchange, null); } @@ -115,7 +121,7 @@ public class HttpBinCompatibleController { ret.put("form", form); return exchange.getFormData().flatMap(map -> { - for (Map.Entry> entry: map.entrySet()) { + for (Map.Entry> entry : map.entrySet()) { for (String value : entry.getValue()) { form.put(entry.getKey(), value); } @@ -126,10 +132,11 @@ public class HttpBinCompatibleController { @RequestMapping("/status/{status}") public ResponseEntity status(@PathVariable int status) { - return ResponseEntity.status(status).body("Failed with "+status); + return ResponseEntity.status(status).body("Failed with " + status); } public Map getHeaders(ServerWebExchange exchange) { return exchange.getRequest().getHeaders().toSingleValueMap(); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/HttpStatusTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/HttpStatusTests.java index 305b5358..e4b1e0e2 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/HttpStatusTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/HttpStatusTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; @@ -44,55 +43,47 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @SuppressWarnings("unchecked") public class HttpStatusTests extends BaseWebClientTests { + public static void main(String[] args) { + new SpringApplication(TestConfig.class).run(args); + } + @Test public void notFoundResponseWorks() { - testClient.get() - .uri("/status/404") - .exchange() - .expectStatus().isEqualTo(HttpStatus.NOT_FOUND) - .expectBody(String.class).isEqualTo("Failed with 404"); + testClient.get().uri("/status/404").exchange().expectStatus() + .isEqualTo(HttpStatus.NOT_FOUND).expectBody(String.class) + .isEqualTo("Failed with 404"); } @Test public void nonStandardCodeWorks() { - ResponseEntity response = new TestRestTemplate().getForEntity(baseUri + "/status/432", String.class); + ResponseEntity response = new TestRestTemplate() + .getForEntity(baseUri + "/status/432", String.class); assertThat(response.getStatusCodeValue()).isEqualTo(432); assertThat(response.getBody()).isEqualTo("Failed with 432"); - - /*testClient.get() - .uri("/status/432") - .exchange() - .expectStatus().isEqualTo(432) - .expectBody(String.class).isEqualTo("Failed with 432");*/ + /* + * testClient.get() .uri("/status/432") .exchange() .expectStatus().isEqualTo(432) + * .expectBody(String.class).isEqualTo("Failed with 432"); + */ } @Test public void serverErrorResponseWorks() { - testClient.get() - .uri("/status/500") - .exchange() - .expectStatus().isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR) - .expectBody(String.class).isEqualTo("Failed with 500"); + testClient.get().uri("/status/500").exchange().expectStatus() + .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectBody(String.class) + .isEqualTo("Failed with 500"); } @Test public void normalErrorPageWorks() { - testClient.get() - .uri("/exception") - .exchange() - .expectStatus().isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR) - .expectBody(Map.class).consumeWith(result -> { - assertThat(result.getResponseBody()) - .hasSize(5) - .containsKeys("timestamp", "path", "status", "error", "message"); + testClient.get().uri("/exception").exchange().expectStatus() + .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR).expectBody(Map.class) + .consumeWith(result -> { + assertThat(result.getResponseBody()).hasSize(5).containsKeys( + "timestamp", "path", "status", "error", "message"); }); } - public static void main(String[] args) { - new SpringApplication(TestConfig.class).run(args); - } - @EnableAutoConfiguration @SpringBootConfiguration @RestController diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/PermitAllSecurityConfiguration.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/PermitAllSecurityConfiguration.java index ef260d59..c55728d8 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/PermitAllSecurityConfiguration.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/PermitAllSecurityConfiguration.java @@ -1,3 +1,19 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.test; import org.springframework.context.annotation.Bean; @@ -7,12 +23,12 @@ import org.springframework.security.web.server.SecurityWebFilterChain; @Configuration public class PermitAllSecurityConfiguration { + @Bean - SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception { - return http.authorizeExchange() - .anyExchange().permitAll() - .and() - .csrf().disable() + SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) + throws Exception { + return http.authorizeExchange().anyExchange().permitAll().and().csrf().disable() .build(); } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/PostTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/PostTests.java index 15b4d35a..8c061943 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/PostTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/PostTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; @@ -21,6 +20,9 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -32,34 +34,29 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; 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") -//TODO: why does this have to be in a separate test? +// TODO: why does this have to be in a separate test? public class PostTests extends BaseWebClientTests { @Test public void postWorks() { - Mono result = webClient.post() - .uri("/post") - .header("Host", "www.example.org") - .syncBody("testdata") - .exchange() + Mono result = webClient.post().uri("/post").header("Host", "www.example.org") + .syncBody("testdata").exchange() .flatMap(response -> response.body(toMono(Map.class))); StepVerifier.create(result) .consumeNextWith(map -> assertThat(map).containsEntry("data", "testdata")) - .expectComplete() - .verify(DURATION); + .expectComplete().verify(DURATION); } @EnableAutoConfiguration @SpringBootConfiguration @Import(DefaultTestConfig.class) - public static class TestConfig { } + public static class TestConfig { + + } } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/TestUtils.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/TestUtils.java index 3a454a73..e0c7de66 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/TestUtils.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; @@ -28,6 +27,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Spencer Gibb */ public class TestUtils { + @SuppressWarnings("unchecked") public static Map getMap(Map response, String key) { assertThat(response).containsKey(key).isInstanceOf(Map.class); diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/WebfluxNotIncludedTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/WebfluxNotIncludedTests.java index f346d869..91551d3e 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/WebfluxNotIncludedTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/WebfluxNotIncludedTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -26,7 +26,7 @@ import org.springframework.cloud.test.ClassPathExclusions; import org.springframework.cloud.test.ModifiedClassPathRunner; @RunWith(ModifiedClassPathRunner.class) -@ClassPathExclusions({"spring-webflux-*.jar"}) +@ClassPathExclusions({ "spring-webflux-*.jar" }) public class WebfluxNotIncludedTests { @Test @@ -36,5 +36,8 @@ public class WebfluxNotIncludedTests { @SpringBootConfiguration @EnableAutoConfiguration - public static class Config {} + public static class Config { + + } + } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/sse/SseIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/sse/SseIntegrationTests.java index b03e2f69..c146dfa7 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/sse/SseIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/sse/SseIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test.sse; @@ -52,8 +51,7 @@ import org.springframework.web.reactive.config.EnableWebFlux; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.core.ResolvableType.forClassWithGenerics; import static org.springframework.http.MediaType.TEXT_EVENT_STREAM; import static org.springframework.web.reactive.function.BodyExtractors.toFlux; @@ -63,12 +61,12 @@ import static org.springframework.web.reactive.function.BodyExtractors.toFlux; */ public class SseIntegrationTests { + public HttpServer server; + protected Log logger = LogFactory.getLog(getClass()); protected int serverPort; - public HttpServer server; - private AnnotationConfigApplicationContext wac; private WebClient webClient; @@ -77,6 +75,14 @@ public class SseIntegrationTests { private int gatewayPort; + /** + * Return an interval stream of with n number of ticks and buffer the emissions to + * avoid back pressure failures (e.g. on slow CI server). + */ + public static Flux interval(Duration period, int count) { + return Flux.interval(period).take(count).onBackpressureBuffer(2); + } + @Before public void setup() throws Exception { this.server = new ReactorHttpServer(); @@ -86,19 +92,21 @@ public class SseIntegrationTests { // Set dynamically chosen port this.serverPort = this.server.getPort(); - logger.info("SSE Port: "+this.serverPort); - + logger.info("SSE Port: " + this.serverPort); this.gatewayContext = new SpringApplicationBuilder(GatewayConfig.class) - .properties("sse.server.port:"+this.serverPort, "server.port=0", "spring.jmx.enabled=false") + .properties("sse.server.port:" + this.serverPort, "server.port=0", + "spring.jmx.enabled=false") .run(); - ConfigurableEnvironment env = this.gatewayContext.getBean(ConfigurableEnvironment.class); + ConfigurableEnvironment env = this.gatewayContext + .getBean(ConfigurableEnvironment.class); this.gatewayPort = Integer.valueOf(env.getProperty("local.server.port")); - this.webClient = WebClient.create("http://localhost:" + this.gatewayPort + "/sse"); + this.webClient = WebClient + .create("http://localhost:" + this.gatewayPort + "/sse"); - logger.info("Gateway Port: "+this.gatewayPort); + logger.info("Gateway Port: " + this.gatewayPort); } @After @@ -120,31 +128,22 @@ public class SseIntegrationTests { @Test public void sseAsString() { - Flux result = this.webClient.get() - .uri("/string") - .accept(TEXT_EVENT_STREAM) - .exchange() + Flux result = this.webClient.get().uri("/string") + .accept(TEXT_EVENT_STREAM).exchange() .flatMapMany(response -> response.bodyToFlux(String.class)); - StepVerifier.create(result) - .expectNext("foo 0") - .expectNext("foo 1") - .thenCancel() + StepVerifier.create(result).expectNext("foo 0").expectNext("foo 1").thenCancel() .verify(Duration.ofSeconds(5L)); } @Test public void sseAsPerson() { - Flux result = this.webClient.get() - .uri("/person") - .accept(TEXT_EVENT_STREAM) - .exchange() + Flux result = this.webClient.get().uri("/person") + .accept(TEXT_EVENT_STREAM).exchange() .flatMapMany(response -> response.bodyToFlux(Person.class)); - StepVerifier.create(result) - .expectNext(new Person("foo 0")) - .expectNext(new Person("foo 1")) - .thenCancel() + StepVerifier.create(result).expectNext(new Person("foo 0")) + .expectNext(new Person("foo 1")).thenCancel() .verify(Duration.ofSeconds(5L)); } @@ -152,67 +151,49 @@ public class SseIntegrationTests { @SuppressWarnings("Duplicates") public void sseAsEvent() { ResolvableType type = forClassWithGenerics(ServerSentEvent.class, String.class); - Flux> result = this.webClient.get() - .uri("/event") - .accept(TEXT_EVENT_STREAM) - .exchange() + Flux> result = this.webClient.get().uri("/event") + .accept(TEXT_EVENT_STREAM).exchange() .flatMapMany(response -> response.body( - toFlux(new ParameterizedTypeReference>() {}))); + toFlux(new ParameterizedTypeReference>() { + }))); - StepVerifier.create(result) - .consumeNextWith( event -> { - assertEquals("0", event.id()); - assertEquals("foo", event.data()); - assertEquals("bar", event.comment()); - assertNull(event.event()); - assertNull(event.retry()); - }) - .consumeNextWith( event -> { - assertEquals("1", event.id()); - assertEquals("foo", event.data()); - assertEquals("bar", event.comment()); - assertNull(event.event()); - assertNull(event.retry()); - }) - .thenCancel() - .verify(Duration.ofSeconds(5L)); + StepVerifier.create(result).consumeNextWith(event -> { + assertThat(event.id()).isEqualTo("0"); + assertThat(event.data()).isEqualTo("foo"); + assertThat(event.comment()).isEqualTo("bar"); + assertThat(event.event()).isNull(); + assertThat(event.retry()).isNull(); + }).consumeNextWith(event -> { + assertThat(event.id()).isEqualTo("1"); + assertThat(event.data()).isEqualTo("foo"); + assertThat(event.comment()).isEqualTo("bar"); + assertThat(event.event()).isNull(); + assertThat(event.retry()).isNull(); + }).thenCancel().verify(Duration.ofSeconds(5L)); } @Test @SuppressWarnings("Duplicates") public void sseAsEventWithoutAcceptHeader() { - Flux> result = this.webClient.get() - .uri("/event") - .accept(TEXT_EVENT_STREAM) - .exchange() + Flux> result = this.webClient.get().uri("/event") + .accept(TEXT_EVENT_STREAM).exchange() .flatMapMany(response -> response.body( - toFlux(new ParameterizedTypeReference>() {}))); + toFlux(new ParameterizedTypeReference>() { + }))); - StepVerifier.create(result) - .consumeNextWith( event -> { - assertEquals("0", event.id()); - assertEquals("foo", event.data()); - assertEquals("bar", event.comment()); - assertNull(event.event()); - assertNull(event.retry()); - }) - .consumeNextWith( event -> { - assertEquals("1", event.id()); - assertEquals("foo", event.data()); - assertEquals("bar", event.comment()); - assertNull(event.event()); - assertNull(event.retry()); - }) - .thenCancel() - .verify(Duration.ofSeconds(5L)); - } - - /** - * Return an interval stream of with n number of ticks and buffer the - * emissions to avoid back pressure failures (e.g. on slow CI server). - */ - public static Flux interval(Duration period, int count) { - return Flux.interval(period).take(count).onBackpressureBuffer(2); + StepVerifier.create(result).consumeNextWith(event -> { + assertThat(event.id()).isEqualTo("0"); + assertThat(event.data()).isEqualTo("foo"); + assertThat(event.comment()).isEqualTo("bar"); + assertThat(event.event()).isNull(); + assertThat(event.retry()).isNull(); + }).consumeNextWith(event -> { + assertThat(event.id()).isEqualTo("1"); + assertThat(event.data()).isEqualTo("foo"); + assertThat(event.comment()).isEqualTo("bar"); + assertThat(event.event()).isNull(); + assertThat(event.retry()).isNull(); + }).thenCancel().verify(Duration.ofSeconds(5L)); } @RestController @@ -221,7 +202,6 @@ public class SseIntegrationTests { private static final Flux INTERVAL = interval(Duration.ofMillis(100), 50); - @RequestMapping("/sse/string") Flux string() { return INTERVAL.map(l -> "foo " + l); @@ -234,15 +214,12 @@ public class SseIntegrationTests { @RequestMapping("/sse/event") Flux> sse() { - return INTERVAL.map(l -> ServerSentEvent.builder("foo") - .id(Long.toString(l)) - .comment("bar") - .build()); + return INTERVAL.map(l -> ServerSentEvent.builder("foo").id(Long.toString(l)) + .comment("bar").build()); } } - @Configuration @EnableWebFlux @SuppressWarnings("unused") @@ -252,6 +229,7 @@ public class SseIntegrationTests { public SseController sseController() { return new SseController(); } + } @Configuration @@ -265,22 +243,22 @@ public class SseIntegrationTests { @Bean public RouteLocator sseRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route("sse_route", r -> r.alwaysTrue() - .uri("http://localhost:"+this.port)) + .route("sse_route", + r -> r.alwaysTrue().uri("http://localhost:" + this.port)) .build(); } - } + } @SuppressWarnings("unused") private static class Person { private String name; - public Person() { + Person() { } - public Person(String name) { + Person(String name) { this.name = name; } @@ -301,7 +279,8 @@ public class SseIntegrationTests { return false; } Person person = (Person) o; - return !(this.name != null ? !this.name.equals(person.name) : person.name != null); + return !(this.name != null ? !this.name.equals(person.name) + : person.name != null); } @Override @@ -313,6 +292,7 @@ public class SseIntegrationTests { public String toString() { return "Person{name='" + this.name + '\'' + '}'; } + } -} \ No newline at end of file +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/MultiCertSSLTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/MultiCertSSLTests.java index 663e1dc1..d2499e37 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/MultiCertSSLTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/MultiCertSSLTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,24 +12,23 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test.ssl; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - import org.junit.runner.RunWith; + import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT) @DirtiesContext @ActiveProfiles("multi-cert-ssl") public class MultiCertSSLTests extends SingleCertSSLTests { - } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SSLHandshakeTimeoutDeprecatedTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SSLHandshakeTimeoutDeprecatedTests.java index 50efc66f..aec67156 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SSLHandshakeTimeoutDeprecatedTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SSLHandshakeTimeoutDeprecatedTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test.ssl; @@ -30,8 +29,10 @@ import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @RunWith(SpringRunner.class) -// this test works because it assumes TLS hand shake cannot be done in 1ms. It takes closer to 80ms -@SpringBootTest(webEnvironment = RANDOM_PORT, properties = {"spring.cloud.gateway.httpclient.ssl.handshake-timeout-millis=1"}) +// this test works because it assumes TLS hand shake cannot be done in 1ms. It takes +// closer to 80ms +@SpringBootTest(webEnvironment = RANDOM_PORT, properties = { + "spring.cloud.gateway.httpclient.ssl.handshake-timeout-millis=1" }) @DirtiesContext @ActiveProfiles("ssl") // this is testing that the deprecated handshake-timeout-millis property still works @@ -46,6 +47,5 @@ public class SSLHandshakeTimeoutDeprecatedTests extends SingleCertSSLTests { JsonPathAssertions jsonPath = responseSpec.expectBody().jsonPath("message"); jsonPath.isEqualTo("handshake timed out"); } - } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SSLHandshakeTimeoutTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SSLHandshakeTimeoutTests.java index 894bdba3..72990b62 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SSLHandshakeTimeoutTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SSLHandshakeTimeoutTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,15 +12,13 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test.ssl; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; @@ -28,9 +26,13 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.JsonPathAssertions; import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + @RunWith(SpringRunner.class) -// this test works because it assumes TLS hand shake cannot be done in 1ms. It takes closer to 80ms -@SpringBootTest(webEnvironment = RANDOM_PORT, properties = {"spring.cloud.gateway.httpclient.ssl.handshake-timeout=1ms"}) +// this test works because it assumes TLS hand shake cannot be done in 1ms. It takes +// closer to 80ms +@SpringBootTest(webEnvironment = RANDOM_PORT, properties = { + "spring.cloud.gateway.httpclient.ssl.handshake-timeout=1ms" }) @DirtiesContext @ActiveProfiles("ssl") public class SSLHandshakeTimeoutTests extends SingleCertSSLTests { @@ -43,6 +45,5 @@ public class SSLHandshakeTimeoutTests extends SingleCertSSLTests { JsonPathAssertions jsonPath = responseSpec.expectBody().jsonPath("message"); jsonPath.isEqualTo("handshake timed out"); } - } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SingleCertSSLTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SingleCertSSLTests.java index 2938b4c5..d4510185 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SingleCertSSLTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/ssl/SingleCertSSLTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test.ssl; @@ -43,7 +42,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.function.client.WebClient; -import static org.junit.Assert.assertTrue; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @RunWith(SpringRunner.class) @@ -65,11 +63,12 @@ public class SingleCertSSLTests extends BaseWebClientTests { baseUri = "https://localhost:" + port; this.webClient = WebClient.builder().clientConnector(httpConnector) .baseUrl(baseUri).build(); - this.testClient = WebTestClient.bindToServer(httpConnector).baseUrl(baseUri).build(); + this.testClient = WebTestClient.bindToServer(httpConnector).baseUrl(baseUri) + .build(); } catch (SSLException e) { throw new RuntimeException(e); - } + } } @Test diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/AbstractHttpServer.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/AbstractHttpServer.java index 58fe6635..8e1a31b1 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/AbstractHttpServer.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/AbstractHttpServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test.support; @@ -33,6 +32,8 @@ import org.springframework.util.StopWatch; */ public abstract class AbstractHttpServer implements HttpServer { + private final Object lifecycleMonitor = new Object(); + protected Log logger = LogFactory.getLog(getClass().getName()); private String host = "0.0.0.0"; @@ -45,16 +46,18 @@ public abstract class AbstractHttpServer implements HttpServer { private volatile boolean running; - private final Object lifecycleMonitor = new Object(); - + public String getHost() { + return host; + } @Override public void setHost(String host) { this.host = host; } - public String getHost() { - return host; + @Override + public int getPort() { + return this.port; } @Override @@ -62,11 +65,6 @@ public abstract class AbstractHttpServer implements HttpServer { this.port = port; } - @Override - public int getPort() { - return this.port; - } - @Override public void setHandler(HttpHandler handler) { this.httpHandler = handler; @@ -88,18 +86,19 @@ public abstract class AbstractHttpServer implements HttpServer { } protected HttpHandler resolveHttpHandler() { - return (getHttpHandlerMap() != null ? - new ContextPathCompositeHandler(getHttpHandlerMap()) : getHttpHandler()); + return (getHttpHandlerMap() != null + ? new ContextPathCompositeHandler(getHttpHandlerMap()) + : getHttpHandler()); } - // InitializingBean @Override public final void afterPropertiesSet() throws Exception { Assert.notNull(this.host, "Host must not be null"); Assert.isTrue(this.port >= 0, "Port must not be a negative number"); - Assert.isTrue(this.httpHandler != null || this.handlerMap != null, "No HttpHandler configured"); + Assert.isTrue(this.httpHandler != null || this.handlerMap != null, + "No HttpHandler configured"); Assert.state(!this.running, "Cannot reconfigure while running"); synchronized (this.lifecycleMonitor) { @@ -109,7 +108,6 @@ public abstract class AbstractHttpServer implements HttpServer { protected abstract void initServer() throws Exception; - // Lifecycle @Override @@ -127,7 +125,8 @@ public abstract class AbstractHttpServer implements HttpServer { startInternal(); long millis = stopWatch.getTotalTimeMillis(); if (logger.isDebugEnabled()) { - logger.debug("Server started on port " + getPort() + "(" + millis + " millis)."); + logger.debug("Server started on port " + getPort() + "(" + millis + + " millis)."); } } catch (Throwable ex) { @@ -151,7 +150,8 @@ public abstract class AbstractHttpServer implements HttpServer { StopWatch stopWatch = new StopWatch(); stopWatch.start(); stopInternal(); - logger.debug("Server stopped (" + stopWatch.getTotalTimeMillis() + " millis)."); + logger.debug("Server stopped (" + stopWatch.getTotalTimeMillis() + + " millis)."); } catch (Throwable ex) { throw new IllegalStateException(ex); @@ -170,7 +170,6 @@ public abstract class AbstractHttpServer implements HttpServer { return this.running; } - private void reset() { this.host = "0.0.0.0"; this.port = 0; diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/HttpServer.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/HttpServer.java index ba4a2ee4..99ba178c 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/HttpServer.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/HttpServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test.support; @@ -28,10 +27,10 @@ public interface HttpServer extends InitializingBean, Lifecycle { void setHost(String host); - void setPort(int port); - int getPort(); + void setPort(int port); + void setHandler(HttpHandler handler); } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/ReactorHttpServer.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/ReactorHttpServer.java index 5c1e202d..218657b9 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/ReactorHttpServer.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/ReactorHttpServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test.support; @@ -34,13 +33,11 @@ public class ReactorHttpServer extends AbstractHttpServer { private AtomicReference serverRef = new AtomicReference<>(); - @Override protected void initServer() { this.reactorHandler = createHttpHandlerAdapter(); this.reactorServer = reactor.netty.http.server.HttpServer.create() - .tcpConfiguration(server -> server.host(getHost())) - .port(getPort()); + .tcpConfiguration(server -> server.host(getHost())).port(getPort()); } private ReactorHttpHandlerAdapter createHttpHandlerAdapter() { @@ -49,7 +46,8 @@ public class ReactorHttpServer extends AbstractHttpServer { @Override protected void startInternal() { - DisposableServer server = this.reactorServer.handle(this.reactorHandler).bind().block(); + DisposableServer server = this.reactorServer.handle(this.reactorHandler).bind() + .block(); setPort(server.address().getPort()); this.serverRef.set(server); } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/redis/RedisRule.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/redis/RedisRule.java index b2e29e2a..3f23b267 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/redis/RedisRule.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/support/redis/RedisRule.java @@ -1,3 +1,19 @@ +/* + * Copyright 2018-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.test.support.redis; import java.io.IOException; @@ -11,11 +27,26 @@ import redis.embedded.RedisServer; import static java.lang.String.format; import static java.util.stream.IntStream.range; -public class RedisRule extends ExternalResource { +public final class RedisRule extends ExternalResource { + + public static final int DEFAULT_REDIS_PORT = 6379; + + private final int port; + + private final boolean ignoreDefaultPortFailure; private Log log = LogFactory.getLog(getClass()); - public static final int DEFAULT_REDIS_PORT = 6379; + private RedisServer redisServer; + + private RedisRule(int port) { + this(port, false); + } + + private RedisRule(int port, boolean ignoreDefaultPortFailure) { + this.port = port; + this.ignoreDefaultPortFailure = ignoreDefaultPortFailure; + } public static RedisRule bindToDefaultPort() { return new RedisRule(DEFAULT_REDIS_PORT, true); @@ -30,47 +61,38 @@ public class RedisRule extends ExternalResource { } private static int findOpenPort(final int startInclusive, final int endExclusive) { - return range(startInclusive, endExclusive) - .filter(RedisRule::testPort) - .findFirst() - .orElseThrow(() ->new IllegalStateException(format( - "No open port found in the range [%d, %d]", startInclusive, endExclusive))); + return range(startInclusive, endExclusive).filter(RedisRule::testPort).findFirst() + .orElseThrow(() -> new IllegalStateException( + format("No open port found in the range [%d, %d]", startInclusive, + endExclusive))); } private static boolean testPort(int port) { try { new ServerSocket(port).close(); return true; - } catch (final IOException ex) { + } + catch (final IOException ex) { return false; } } - private final int port; - private final boolean ignoreDefaultPortFailure; - - private RedisServer redisServer; - - private RedisRule(int port) { - this(port, false); - } - - private RedisRule(int port, boolean ignoreDefaultPortFailure) { - this.port = port; - this.ignoreDefaultPortFailure = ignoreDefaultPortFailure; - } - @Override protected void before() { try { - redisServer = RedisServer.builder().port(port).setting("maxmemory 16MB").build(); + redisServer = RedisServer.builder().port(port).setting("maxmemory 16MB") + .build(); redisServer.start(); - } catch (final Exception e) { + } + catch (final Exception e) { if (port == DEFAULT_REDIS_PORT && ignoreDefaultPortFailure) { - log.info("Unable to start embedded Redis on default port. Ignoring error. Assuming redis is already running."); - } else { - throw new RuntimeException(format("Error while initializing the Redis server" - + " on port %d", port), e); + log.info( + "Unable to start embedded Redis on default port. Ignoring error. Assuming redis is already running."); + } + else { + throw new RuntimeException(format( + "Error while initializing the Redis server" + " on port %d", + port), e); } } } @@ -80,4 +102,4 @@ public class RedisRule extends ExternalResource { redisServer.stop(); } -} \ No newline at end of file +} diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/websocket/WebSocketIntegrationTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/websocket/WebSocketIntegrationTests.java index f9c1bae7..f19ad3ab 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/websocket/WebSocketIntegrationTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/test/websocket/WebSocketIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.test.websocket; @@ -34,7 +33,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.reactivestreams.Publisher; -import org.springframework.web.reactive.socket.CloseStatus; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.MonoProcessor; @@ -63,6 +61,7 @@ import org.springframework.util.StringUtils; import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.HandlerMapping; import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; +import org.springframework.web.reactive.socket.CloseStatus; import org.springframework.web.reactive.socket.HandshakeInfo; import org.springframework.web.reactive.socket.WebSocketHandler; import org.springframework.web.reactive.socket.WebSocketMessage; @@ -77,11 +76,11 @@ import org.springframework.web.reactive.socket.server.upgrade.ReactorNettyReques import org.springframework.web.server.adapter.WebHttpHandlerBuilder; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; import static org.springframework.cloud.gateway.filter.WebsocketRoutingFilter.SEC_WEBSOCKET_PROTOCOL; /** - * Original is here {@see https://github.com/spring-projects/spring-framework/blob/master/spring-webflux/src/test/java/org/springframework/web/reactive/socket/WebSocketIntegrationTests.java} + * Original is here + * {@see https://github.com/spring-projects/spring-framework/blob/master/spring-webflux/src/test/java/org/springframework/web/reactive/socket/WebSocketIntegrationTests.java} * Integration tests with server-side {@link WebSocketHandler}s. * * @author Rossen Stoyanchev @@ -90,14 +89,24 @@ public class WebSocketIntegrationTests { private static final Log logger = LogFactory.getLog(WebSocketIntegrationTests.class); + protected int serverPort; + private WebSocketClient client; private HttpServer server; - protected int serverPort; private ConfigurableApplicationContext gatewayContext; + private int gatewayPort; + private static Mono doSend(WebSocketSession session, + Publisher output) { + return session.send(output); + // workaround for suspected RxNetty WebSocket client issue + // https://github.com/ReactiveX/RxNetty/issues/560 + // return session.send(Mono.delay(Duration.ofMillis(100)).thenMany(output)); + } + @Before public void setup() throws Exception { this.client = new ReactorNettyWebSocketClient(); @@ -115,10 +124,12 @@ public class WebSocketIntegrationTests { } this.gatewayContext = new SpringApplicationBuilder(GatewayConfig.class) - .properties("ws.server.port:"+this.serverPort, "server.port=0", "spring.jmx.enabled=false") + .properties("ws.server.port:" + this.serverPort, "server.port=0", + "spring.jmx.enabled=false") .run(); - ConfigurableEnvironment env = this.gatewayContext.getBean(ConfigurableEnvironment.class); + ConfigurableEnvironment env = this.gatewayContext + .getBean(ConfigurableEnvironment.class); this.gatewayPort = Integer.valueOf(env.getProperty("local.server.port")); } @@ -142,13 +153,114 @@ public class WebSocketIntegrationTests { protected URI getUrl(String path) throws URISyntaxException { // return new URI("ws://localhost:" + this.serverPort + path); - return new URI("ws://localhost:" + this.gatewayPort + path); + return new URI("ws://localhost:" + this.gatewayPort + path); } protected URI getHttpUrl(String path) throws URISyntaxException { return new URI("http://localhost:" + this.gatewayPort + path); } + @Test + public void echo() throws Exception { + int count = 100; + Flux input = Flux.range(1, count).map(index -> "msg-" + index); + ReplayProcessor output = ReplayProcessor.create(count); + + client.execute(getUrl("/echo"), session -> { + logger.debug("Starting to send messages"); + return session + .send(input.doOnNext(s -> logger.debug("outbound " + s)) + .map(s -> session.textMessage(s))) + .thenMany(session.receive().take(count) + .map(WebSocketMessage::getPayloadAsText)) + .subscribeWith(output).doOnNext(s -> logger.debug("inbound " + s)) + .then().doOnSuccessOrError((aVoid, ex) -> logger.debug( + "Done with " + (ex != null ? ex.getMessage() : "success"))); + }).block(Duration.ofMillis(5000)); + + assertThat(output.collectList().block(Duration.ofMillis(5000))) + .isEqualTo(input.collectList().block(Duration.ofMillis(5000))); + } + + @Test + public void echoForHttp() throws Exception { + int count = 100; + Flux input = Flux.range(1, count).map(index -> "msg-" + index); + ReplayProcessor output = ReplayProcessor.create(count); + + client.execute(getHttpUrl("/echoForHttp"), session -> { + logger.debug("Starting to send messages"); + return session + .send(input.doOnNext(s -> logger.debug("outbound " + s)) + .map(s -> session.textMessage(s))) + .thenMany(session.receive().take(count) + .map(WebSocketMessage::getPayloadAsText)) + .subscribeWith(output).doOnNext(s -> logger.debug("inbound " + s)) + .then().doOnSuccessOrError((aVoid, ex) -> logger.debug( + "Done with " + (ex != null ? ex.getMessage() : "success"))); + }).block(Duration.ofMillis(5000)); + + assertThat(output.collectList().block(Duration.ofMillis(5000))) + .isEqualTo(input.collectList().block(Duration.ofMillis(5000))); + } + + @Test + public void subProtocol() throws Exception { + String protocol = "echo-v1"; + String protocol2 = "echo-v2"; + AtomicReference infoRef = new AtomicReference<>(); + MonoProcessor output = MonoProcessor.create(); + + client.execute(getUrl("/sub-protocol"), new WebSocketHandler() { + @Override + public List getSubProtocols() { + return Arrays.asList(protocol, protocol2); + } + + @Override + public Mono handle(WebSocketSession session) { + infoRef.set(session.getHandshakeInfo()); + return session.receive().map(WebSocketMessage::getPayloadAsText) + .subscribeWith(output).then(); + } + }).block(Duration.ofMillis(5000)); + + HandshakeInfo info = infoRef.get(); + assertThat(info.getHeaders().getFirst("Upgrade")) + .isEqualToIgnoringCase("websocket"); + + assertThat(info.getHeaders().getFirst("Sec-WebSocket-Protocol")) + .isEqualTo(protocol); + assertThat(info.getSubProtocol()).as("Wrong protocol accepted") + .isEqualTo(protocol); + assertThat(output.block(Duration.ofSeconds(5))) + .as("Wrong protocol detected on the server side").isEqualTo(protocol); + } + + @Test + public void customHeader() throws Exception { + HttpHeaders headers = new HttpHeaders(); + headers.add("my-header", "my-value"); + MonoProcessor output = MonoProcessor.create(); + + client.execute(getUrl("/custom-header"), headers, session -> session.receive() + .map(WebSocketMessage::getPayloadAsText).subscribeWith(output).then()) + .block(Duration.ofMillis(5000)); + + assertThat(output.block(Duration.ofMillis(5000))).isEqualTo("my-header:my-value"); + } + + @Test + public void sessionClosing() throws Exception { + this.client.execute(getUrl("/close"), session -> { + logger.debug("Starting.."); + return session.receive().doOnNext(s -> logger.debug("inbound " + s)).then() + .doFinally(signalType -> { + logger.debug("Completed with: " + signalType); + }); + }).block(Duration.ofMillis(5000)); + } + @Configuration static class WebSocketTestConfig { @@ -184,124 +296,7 @@ public class WebSocketIntegrationTests { mapping.setUrlMap(map); return mapping; } - } - @Test - public void echo() throws Exception { - int count = 100; - Flux input = Flux.range(1, count).map(index -> "msg-" + index); - ReplayProcessor output = ReplayProcessor.create(count); - - client.execute(getUrl("/echo"), - session -> { - logger.debug("Starting to send messages"); - return session - .send(input.doOnNext(s -> logger.debug("outbound " + s)).map(s -> session.textMessage(s))) - .thenMany(session.receive().take(count).map(WebSocketMessage::getPayloadAsText)) - .subscribeWith(output) - .doOnNext(s -> logger.debug("inbound " + s)) - .then() - .doOnSuccessOrError((aVoid, ex) -> - logger.debug("Done with " + (ex != null ? ex.getMessage() : "success"))); - }) - .block(Duration.ofMillis(5000)); - - assertEquals(input.collectList().block(Duration.ofMillis(5000)), - output.collectList().block(Duration.ofMillis(5000))); - } - - @Test - public void echoForHttp() throws Exception { - int count = 100; - Flux input = Flux.range(1, count).map(index -> "msg-" + index); - ReplayProcessor output = ReplayProcessor.create(count); - - client.execute(getHttpUrl("/echoForHttp"), - session -> { - logger.debug("Starting to send messages"); - return session - .send(input.doOnNext(s -> logger.debug("outbound " + s)).map(s -> session.textMessage(s))) - .thenMany(session.receive().take(count).map(WebSocketMessage::getPayloadAsText)) - .subscribeWith(output) - .doOnNext(s -> logger.debug("inbound " + s)) - .then() - .doOnSuccessOrError((aVoid, ex) -> - logger.debug("Done with " + (ex != null ? ex.getMessage() : "success"))); - }) - .block(Duration.ofMillis(5000)); - - assertEquals(input.collectList().block(Duration.ofMillis(5000)), - output.collectList().block(Duration.ofMillis(5000))); - } - - - @Test - public void subProtocol() throws Exception { - String protocol = "echo-v1"; - String protocol2 = "echo-v2"; - AtomicReference infoRef = new AtomicReference<>(); - MonoProcessor output = MonoProcessor.create(); - - client.execute(getUrl("/sub-protocol"), - new WebSocketHandler() { - @Override - public List getSubProtocols() { - return Arrays.asList(protocol, protocol2); - } - @Override - public Mono handle(WebSocketSession session) { - infoRef.set(session.getHandshakeInfo()); - return session.receive() - .map(WebSocketMessage::getPayloadAsText) - .subscribeWith(output) - .then(); - } - }) - .block(Duration.ofMillis(5000)); - - HandshakeInfo info = infoRef.get(); - assertThat(info.getHeaders().getFirst("Upgrade")) - .isEqualToIgnoringCase("websocket"); - - assertThat(info.getHeaders().getFirst("Sec-WebSocket-Protocol")) - .isEqualTo(protocol); - assertThat(info.getSubProtocol()) - .as("Wrong protocol accepted") - .isEqualTo(protocol); - assertThat(output.block(Duration.ofSeconds(5))) - .as("Wrong protocol detected on the server side") - .isEqualTo(protocol); - } - - @Test - public void customHeader() throws Exception { - HttpHeaders headers = new HttpHeaders(); - headers.add("my-header", "my-value"); - MonoProcessor output = MonoProcessor.create(); - - client.execute(getUrl("/custom-header"), headers, - session -> session.receive() - .map(WebSocketMessage::getPayloadAsText) - .subscribeWith(output) - .then()) - .block(Duration.ofMillis(5000)); - - assertEquals("my-header:my-value", output.block(Duration.ofMillis(5000))); - } - - @Test - public void sessionClosing() throws Exception { - this.client.execute(getUrl("/close"), - session -> { - logger.debug("Starting.."); - return session.receive() - .doOnNext(s -> logger.debug("inbound " + s)) - .then() - .doFinally(signalType -> { - logger.debug("Completed with: " + signalType); - }); - }) - .block(Duration.ofMillis(5000)); } private static class EchoWebSocketHandler implements WebSocketHandler { @@ -311,6 +306,7 @@ public class WebSocketIntegrationTests { // Use retain() for Reactor Netty return session.send(session.receive().doOnNext(WebSocketMessage::retain)); } + } private static class SubProtocolWebSocketHandler implements WebSocketHandler { @@ -326,11 +322,13 @@ public class WebSocketIntegrationTests { if (!StringUtils.hasText(protocol)) { return Mono.error(new IllegalStateException("Missing protocol")); } - List protocols = session.getHandshakeInfo().getHeaders().get(SEC_WEBSOCKET_PROTOCOL); + List protocols = session.getHandshakeInfo().getHeaders() + .get(SEC_WEBSOCKET_PROTOCOL); assertThat(protocols).contains("echo-v1,echo-v2"); WebSocketMessage message = session.textMessage(protocol); return doSend(session, Mono.just(message)); } + } private static class CustomHeaderHandler implements WebSocketHandler { @@ -345,6 +343,7 @@ public class WebSocketIntegrationTests { WebSocketMessage message = session.textMessage(payload); return doSend(session, Mono.just(message)); } + } private static class SessionClosingHandler implements WebSocketHandler { @@ -353,13 +352,7 @@ public class WebSocketIntegrationTests { public Mono handle(WebSocketSession session) { return Flux.never().mergeWith(session.close(CloseStatus.GOING_AWAY)).then(); } - } - private static Mono doSend(WebSocketSession session, Publisher output) { - return session.send(output); - // workaround for suspected RxNetty WebSocket client issue - // https://github.com/ReactiveX/RxNetty/issues/560 - // return session.send(Mono.delay(Duration.ofMillis(100)).thenMany(output)); } @Configuration @@ -371,12 +364,10 @@ public class WebSocketIntegrationTests { @Bean public RouteLocator wsRouteLocator(RouteLocatorBuilder builder) { return builder.routes() - .route(r->r.path("/echoForHttp") - .uri("lb://wsservice")) - .route(r -> r.alwaysTrue() - .uri("lb:ws://wsservice")) - .build(); + .route(r -> r.path("/echoForHttp").uri("lb://wsservice")) + .route(r -> r.alwaysTrue().uri("lb:ws://wsservice")).build(); } + } public static class LocalRibbonClientConfiguration { diff --git a/spring-cloud-gateway-core/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt b/spring-cloud-gateway-core/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt index 6a86d18c..4506efa2 100644 --- a/spring-cloud-gateway-core/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt +++ b/spring-cloud-gateway-core/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,88 +34,88 @@ import java.net.URI @SpringBootTest(classes = arrayOf(Config::class)) class RouteDslTests { - @Autowired - lateinit var builder: RouteLocatorBuilder + @Autowired + lateinit var builder: RouteLocatorBuilder - @Test - fun sampleRouteDsl() { - val routeLocator = builder.routes { - route(id = "test") { - host("**.abc.org") and path("/image/png") - filters { - addResponseHeader("X-TestHeader", "foobar") - } - uri("http://httpbin.org:80") - } + @Test + fun sampleRouteDsl() { + val routeLocator = builder.routes { + route(id = "test") { + host("**.abc.org") and path("/image/png") + filters { + addResponseHeader("X-TestHeader", "foobar") + } + uri("http://httpbin.org:80") + } - route(id = "test2") { - path("/image/webp") or path("/image/anotherone") - filters { - addResponseHeader("X-AnotherHeader", "baz") - addResponseHeader("X-AnotherHeader-2", "baz-2") - } - uri("https://httpbin.org:443") - } - } + route(id = "test2") { + path("/image/webp") or path("/image/anotherone") + filters { + addResponseHeader("X-AnotherHeader", "baz") + addResponseHeader("X-AnotherHeader-2", "baz-2") + } + uri("https://httpbin.org:443") + } + } - StepVerifier - .create(routeLocator.routes) - .expectNextMatches({ - it.id == "test" && it.filters.size == 1 && it.uri == URI.create("http://httpbin.org:80") - }) - .expectNextMatches({ - it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443") - }) - .expectComplete() - .verify() + StepVerifier + .create(routeLocator.routes) + .expectNextMatches({ + it.id == "test" && it.filters.size == 1 && it.uri == URI.create("http://httpbin.org:80") + }) + .expectNextMatches({ + it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443") + }) + .expectComplete() + .verify() - val sampleExchange: ServerWebExchange = MockServerWebExchange.from(MockServerHttpRequest.get("/image/webp") - .header("Host", "test.abc.org").build()) + val sampleExchange: ServerWebExchange = MockServerWebExchange.from(MockServerHttpRequest.get("/image/webp") + .header("Host", "test.abc.org").build()) - val filteredRoutes = routeLocator.routes.filter({ it.predicate.apply(sampleExchange).toMono().block() }) + val filteredRoutes = routeLocator.routes.filter({ it.predicate.apply(sampleExchange).toMono().block() }) - StepVerifier.create(filteredRoutes) - .expectNextMatches({ - it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443") - }) - .expectComplete() - .verify() - } + StepVerifier.create(filteredRoutes) + .expectNextMatches({ + it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443") + }) + .expectComplete() + .verify() + } - @Test - fun dslWithFunctionParameters() { - val routerLocator = builder.routes { - route(id = "test1", order = 10, uri = "http://httpbin.org") { - host("**.abc.org") - } - route(id = "test2", order = 10, uri = "http://someurl") { - host("**.abc.org") - uri("http://override-url") - } - } + @Test + fun dslWithFunctionParameters() { + val routerLocator = builder.routes { + route(id = "test1", order = 10, uri = "http://httpbin.org") { + host("**.abc.org") + } + route(id = "test2", order = 10, uri = "http://someurl") { + host("**.abc.org") + uri("http://override-url") + } + } - StepVerifier.create(routerLocator.routes) - .expectNextMatches({ - it.id == "test1" && - it.uri == URI.create("http://httpbin.org:80") && - it.order == 10 && - it.predicate.apply(MockServerWebExchange - .from(MockServerHttpRequest - .get("/someuri").header("Host", "test.abc.org"))) - .toMono().block() - }) - .expectNextMatches({ - it.id == "test2" && - it.uri == URI.create("http://override-url:80") && - it.order == 10 && - it.predicate.apply(MockServerWebExchange - .from(MockServerHttpRequest - .get("/someuri").header("Host", "test.abc.org"))) - .toMono().block() - }) - .expectComplete() - .verify() - } + StepVerifier.create(routerLocator.routes) + .expectNextMatches({ + it.id == "test1" && + it.uri == URI.create("http://httpbin.org:80") && + it.order == 10 && + it.predicate.apply(MockServerWebExchange + .from(MockServerHttpRequest + .get("/someuri").header("Host", "test.abc.org"))) + .toMono().block() + }) + .expectNextMatches({ + it.id == "test2" && + it.uri == URI.create("http://override-url:80") && + it.order == 10 && + it.predicate.apply(MockServerWebExchange + .from(MockServerHttpRequest + .get("/someuri").header("Host", "test.abc.org"))) + .toMono().block() + }) + .expectComplete() + .verify() + } } @Configuration diff --git a/spring-cloud-gateway-core/src/test/resources/application-redis-rate-limiter-config.yml b/spring-cloud-gateway-core/src/test/resources/application-redis-rate-limiter-config.yml index 2143adbf..1fd28017 100644 --- a/spring-cloud-gateway-core/src/test/resources/application-redis-rate-limiter-config.yml +++ b/spring-cloud-gateway-core/src/test/resources/application-redis-rate-limiter-config.yml @@ -14,7 +14,7 @@ spring: filters: - name: RequestRateLimiter args: - redis-rate-limiter: - replenish-rate: 10 - burst-capacity: 20 + redis-rate-limiter: + replenish-rate: 10 + burst-capacity: 20 diff --git a/spring-cloud-gateway-core/src/test/resources/application-redis-rate-limiter-default-config.yml b/spring-cloud-gateway-core/src/test/resources/application-redis-rate-limiter-default-config.yml index 7f5c4b29..603bd9b3 100644 --- a/spring-cloud-gateway-core/src/test/resources/application-redis-rate-limiter-default-config.yml +++ b/spring-cloud-gateway-core/src/test/resources/application-redis-rate-limiter-default-config.yml @@ -5,14 +5,14 @@ spring: cloud: gateway: default-filters: - - name: RequestRateLimiter - args: - redis-rate-limiter: - replenish-rate: 70 - burst-capacity: 80 + - name: RequestRateLimiter + args: + redis-rate-limiter: + replenish-rate: 70 + burst-capacity: 80 routes: # ===================================== - id: redis_rate_limiter_config_default_test uri: ${test.uri} predicates: - - Path=/default + - Path=/default diff --git a/spring-cloud-gateway-core/src/test/resources/application-removenonproxyheaders.yml b/spring-cloud-gateway-core/src/test/resources/application-removenonproxyheaders.yml index 2a9cc127..d4fa2732 100644 --- a/spring-cloud-gateway-core/src/test/resources/application-removenonproxyheaders.yml +++ b/spring-cloud-gateway-core/src/test/resources/application-removenonproxyheaders.yml @@ -1,4 +1,3 @@ - spring: cloud: gateway: @@ -8,7 +7,7 @@ spring: routes: # ===================================== - id: remove_request_header_test -# uri: http://httpbin.org:80 + # uri: http://httpbin.org:80 uri: lb://testservice predicates: - Host=**.removenonproxyheaders.org diff --git a/spring-cloud-gateway-core/src/test/resources/application-weights-404.yml b/spring-cloud-gateway-core/src/test/resources/application-weights-404.yml index cc264b95..95b46084 100644 --- a/spring-cloud-gateway-core/src/test/resources/application-weights-404.yml +++ b/spring-cloud-gateway-core/src/test/resources/application-weights-404.yml @@ -5,18 +5,18 @@ spring: cloud: gateway: default-filters: - - PrefixPath=/httpbin + - PrefixPath=/httpbin routes: - # ===================================== + # ===================================== - id: weight_first_404_test_1 uri: ${test.uri} predicates: - - Host=**.weight4041.org - - Weight=group404, 8 + - Host=**.weight4041.org + - Weight=group404, 8 # ===================================== - id: weight_first_404_test_2 uri: ${test.uri} predicates: - - Host=**.weight4042.org - - Weight=group404, 2 + - Host=**.weight4042.org + - Weight=group404, 2 diff --git a/spring-cloud-gateway-core/src/test/resources/application.yml b/spring-cloud-gateway-core/src/test/resources/application.yml index 6a598243..0a0b2491 100644 --- a/spring-cloud-gateway-core/src/test/resources/application.yml +++ b/spring-cloud-gateway-core/src/test/resources/application.yml @@ -1,7 +1,7 @@ test: hostport: httpbin.org:80 -# hostport: localhost:5000 -# uri: http://${test.hostport} + # hostport: localhost:5000 + # uri: http://${test.hostport} uri: lb://testservice spring: @@ -85,9 +85,9 @@ spring: - id: host_backwards_compatible_test uri: ${test.uri} predicates: - - name: Host - args: - pattern: '**.hostpatternarg.org' + - name: Host + args: + pattern: '**.hostpatternarg.org' # ===================================== - id: host_backwards_compatible_shortcut_test @@ -187,10 +187,10 @@ spring: - id: path_multi uri: ${test.uri} predicates: - - Host=**.pathmulti.org - - Path=/anything/multi1{num},/anything/multi2{num} + - Host=**.pathmulti.org + - Path=/anything/multi1{num},/anything/multi2{num} filters: - - SetPath=/anything/multi{num} + - SetPath=/anything/multi{num} # ===================================== - id: redirect_to_test @@ -249,9 +249,9 @@ spring: - id: set_path_host_test uri: ${test.uri} predicates: - - Host={subdomain}.setpathhost.org + - Host={subdomain}.setpathhost.org filters: - - SetPath=/{subdomain} + - SetPath=/{subdomain} # ===================================== - id: strip_prefix_test diff --git a/spring-cloud-gateway-dependencies/pom.xml b/spring-cloud-gateway-dependencies/pom.xml index bdb8a42d..d1b556d6 100644 --- a/spring-cloud-gateway-dependencies/pom.xml +++ b/spring-cloud-gateway-dependencies/pom.xml @@ -1,5 +1,6 @@ - 4.0.0 diff --git a/spring-cloud-gateway-mvc/pom.xml b/spring-cloud-gateway-mvc/pom.xml index 3b2cad64..89f340fd 100644 --- a/spring-cloud-gateway-mvc/pom.xml +++ b/spring-cloud-gateway-mvc/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 spring-cloud-gateway-mvc diff --git a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java index 12518e63..fcb4642e 100644 --- a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java +++ b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/ProxyExchange.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -70,7 +70,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBody * Spring will inject one of these into your MVC handler method, and you get return a * ResponseEntity that you get from one of the HTTP methods {@link #get()}, * {@link #post()}, {@link #put()}, {@link #patch()}, {@link #delete()} etc. Example: - * + * *
  * @GetMapping("/proxy/{id}")
  * public ResponseEntity<?> proxy(@PathVariable Integer id, ProxyExchange<?> proxy)
@@ -78,7 +78,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBody
  * 	return proxy.uri("http://localhost:9000/foos/" + id).get();
  * }
  * 
- * + * *

* By default the incoming request body and headers are sent intact to the downstream * service (with the exception of "sensitive" headers). To manipulate the downstream @@ -99,7 +99,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBody *

* To manipulate the response use the overloaded HTTP methods with a Function * argument and pass in code to transform the response. E.g. - * + * *

  * @PostMapping("/proxy")
  * public ResponseEntity<Foo> proxy(ProxyExchange<Foo> proxy) throws Exception {
@@ -110,9 +110,9 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBody
  * 					.body(response.getBody()) //
  * 			);
  * }
- * 
+ *
  * 
- * + * *

*

* The full machinery of Spring {@link HttpMessageConverter message converters} is applied @@ -125,13 +125,17 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBody *

* As well as the HTTP methods for a backend call you can also use * {@link #forward(String)} for a local in-container dispatch. + *

*

- * + * * @author Dave Syer * */ public class ProxyExchange { + /** + * Contains headers that are considered case-sensitive by default. + */ public static Set DEFAULT_SENSITIVE = new HashSet<>( Arrays.asList("cookie", "authorization")); @@ -173,7 +177,6 @@ public class ProxyExchange { * request downstream without changing it. If you want to transform the incoming * request you can declare it as a @RequestBody in your * @RequestMapping in the usual Spring MVC way. - * * @param body the request body to send downstream * @return this for convenience */ @@ -184,9 +187,8 @@ public class ProxyExchange { /** * Sets a header for the downstream call. - * - * @param name - * @param value + * @param name Header name + * @param value Header values * @return this for convenience */ public ProxyExchange header(String name, String... value) { @@ -197,7 +199,6 @@ public class ProxyExchange { /** * Additional headers, or overrides of the incoming ones, to be used in the downstream * call. - * * @param headers the http headers to use in the downstream call * @return this for convenience */ @@ -209,7 +210,6 @@ public class ProxyExchange { /** * Sets the names of sensitive headers that are not passed downstream to the backend * service. - * * @param names the names of sensitive headers * @return this for convenience */ @@ -225,7 +225,6 @@ public class ProxyExchange { /** * Sets the uri for the backend call when triggered by the HTTP methods. - * * @param uri the backend uri to send the request to * @return this for convenience */ @@ -429,7 +428,6 @@ public class ProxyExchange { * Search for the request body if it was already deserialized using * @RequestBody. If it is not found then deserialize it in the same way * that it would have been for a @RequestBody. - * * @return the request body */ private Object getRequestBody() { @@ -453,6 +451,23 @@ public class ProxyExchange { return result.getTarget(); } + protected static class BodyGrabber { + + public Object body(@RequestBody Object body) { + return body; + } + + } + + protected static class BodySender { + + @ResponseBody + public Object body() { + return null; + } + + } + /** * A servlet request wrapper that can be safely passed downstream to an internal * forward dispatch, caching its body, and making it available in converted form using @@ -460,7 +475,9 @@ public class ProxyExchange { * */ class BodyForwardingHttpServletRequest extends HttpServletRequestWrapper { + private HttpServletRequest request; + private HttpServletResponse response; BodyForwardingHttpServletRequest(HttpServletRequest request, @@ -521,19 +538,7 @@ public class ProxyExchange { } return super.getHeader(name); } - } - protected static class BodyGrabber { - public Object body(@RequestBody Object body) { - return body; - } - } - - protected static class BodySender { - @ResponseBody - public Object body() { - return null; - } } } @@ -545,7 +550,7 @@ public class ProxyExchange { * will need to be read and analysed more than once. Apart from using the message * converters the other main feature of this class is that the request body is cached and * can be read repeatedly as necessary. - * + * * @author Dave Syer * */ @@ -553,7 +558,7 @@ class ServletOutputToInputConverter extends HttpServletResponseWrapper { private StringBuilder builder = new StringBuilder(); - public ServletOutputToInputConverter(HttpServletResponse response) { + ServletOutputToInputConverter(HttpServletResponse response) { super(response); } diff --git a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java index e665b100..8ed1aaf9 100644 --- a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java +++ b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyExchangeArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,49 +35,49 @@ import org.springframework.web.method.support.ModelAndViewContainer; */ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResolver { - private RestTemplate rest; + private RestTemplate rest; - private HttpHeaders headers; + private HttpHeaders headers; - private Set sensitive; + private Set sensitive; - public ProxyExchangeArgumentResolver(RestTemplate builder) { - this.rest = builder; - } + public ProxyExchangeArgumentResolver(RestTemplate builder) { + this.rest = builder; + } - public void setHeaders(HttpHeaders headers) { - this.headers = headers; - } + public void setHeaders(HttpHeaders headers) { + this.headers = headers; + } - public void setSensitive(Set sensitive) { - this.sensitive = sensitive; - } + public void setSensitive(Set sensitive) { + this.sensitive = sensitive; + } - @Override - public boolean supportsParameter(MethodParameter parameter) { - return ProxyExchange.class.isAssignableFrom(parameter.getParameterType()); - } + @Override + public boolean supportsParameter(MethodParameter parameter) { + return ProxyExchange.class.isAssignableFrom(parameter.getParameterType()); + } - @Override - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) throws Exception { - ProxyExchange proxy = new ProxyExchange<>(rest, webRequest, mavContainer, - binderFactory, type(parameter)); - proxy.headers(headers); - if (sensitive != null) { - proxy.sensitive(sensitive.toArray(new String[0])); - } - return proxy; - } + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) throws Exception { + ProxyExchange proxy = new ProxyExchange<>(rest, webRequest, mavContainer, + binderFactory, type(parameter)); + proxy.headers(headers); + if (sensitive != null) { + proxy.sensitive(sensitive.toArray(new String[0])); + } + return proxy; + } - private Type type(MethodParameter parameter) { - Type type = parameter.getGenericParameterType(); - if (type instanceof ParameterizedType) { - ParameterizedType param = (ParameterizedType) type; - type = param.getActualTypeArguments()[0]; - } - return type; - } + private Type type(MethodParameter parameter) { + Type type = parameter.getGenericParameterType(); + if (type instanceof ParameterizedType) { + ParameterizedType param = (ParameterizedType) type; + type = param.getActualTypeArguments()[0]; + } + return type; + } } diff --git a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyProperties.java b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyProperties.java index 5fad5f66..3cf6179a 100644 --- a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyProperties.java +++ b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,44 +27,45 @@ import org.springframework.http.HttpHeaders; /** * Configuration properties for the {@link ProxyExchange} argument handler in * @RequestMapping methods. + * * @author Dave Syer * */ @ConfigurationProperties("spring.cloud.gateway.proxy") public class ProxyProperties { - /** - * Fixed header values that will be added to all downstream requests. - */ - private Map headers = new LinkedHashMap<>(); + /** + * Fixed header values that will be added to all downstream requests. + */ + private Map headers = new LinkedHashMap<>(); - /** - * A set of sensitive header names that will not be sent downstream by default. - */ - private Set sensitive = null; + /** + * A set of sensitive header names that will not be sent downstream by default. + */ + private Set sensitive = null; - public Map getHeaders() { - return headers; - } + public Map getHeaders() { + return headers; + } - public void setHeaders(Map headers) { - this.headers = headers; - } + public void setHeaders(Map headers) { + this.headers = headers; + } - public Set getSensitive() { - return sensitive; - } + public Set getSensitive() { + return sensitive; + } - public void setSensitive(Set sensitive) { - this.sensitive = sensitive; - } + public void setSensitive(Set sensitive) { + this.sensitive = sensitive; + } - public HttpHeaders convertHeaders() { - HttpHeaders headers = new HttpHeaders(); - for (String key : this.headers.keySet()) { - headers.set(key, this.headers.get(key)); - } - return headers; - } + public HttpHeaders convertHeaders() { + HttpHeaders headers = new HttpHeaders(); + for (String key : this.headers.keySet()) { + headers.set(key, this.headers.get(key)); + } + return headers; + } } diff --git a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyResponseAutoConfiguration.java b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyResponseAutoConfiguration.java index 818fcb62..1707e018 100644 --- a/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyResponseAutoConfiguration.java +++ b/spring-cloud-gateway-mvc/src/main/java/org/springframework/cloud/gateway/mvc/config/ProxyResponseAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,4 +86,5 @@ public class ProxyResponseAutoConfiguration implements WebMvcConfigurer { } } + } diff --git a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java index e2688445..9a714615 100644 --- a/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java +++ b/spring-cloud-gateway-mvc/src/test/java/org/springframework/cloud/gateway/mvc/ProductionConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,6 @@ import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -60,184 +59,190 @@ import static org.assertj.core.api.Assertions.assertThat; @ContextConfiguration(classes = TestApplication.class) public class ProductionConfigurationTests { - @Autowired - private TestRestTemplate rest; + @Autowired + private TestRestTemplate rest; - @Autowired - private TestApplication application; + @Autowired + private TestApplication application; - @LocalServerPort - private int port; + @LocalServerPort + private int port; - @Before - public void init() throws Exception { - application.setHome(new URI("http://localhost:" + port)); - } - - @Test - public void get() throws Exception { - assertThat(rest.getForObject("/proxy/0", Foo.class).getName()).isEqualTo("bye"); - } - - @Test - public void path() throws Exception { - assertThat(rest.getForObject("/proxy/path/1", Foo.class).getName()) - .isEqualTo("foo"); - } - - @Test - public void resource() throws Exception { - assertThat(rest.getForObject("/proxy/html/test.html", String.class)) - .contains("Test"); - } - - @Test - public void resourceWithNoType() throws Exception { - assertThat(rest.getForObject("/proxy/typeless/test.html", String.class)) - .contains("Test"); - } - - @Test - public void missing() throws Exception { - assertThat(rest.getForEntity("/proxy/missing/0", Foo.class).getStatusCode()) - .isEqualTo(HttpStatus.NOT_FOUND); - } - - @Test - public void uri() throws Exception { - assertThat(rest.getForObject("/proxy/0", Foo.class).getName()).isEqualTo("bye"); - } - - @Test - public void post() throws Exception { - assertThat(rest.postForObject("/proxy/0", Collections.singletonMap("name", "foo"), - Bar.class).getName()).isEqualTo("host=localhost;foo"); - } - - @Test - public void forward() throws Exception { - assertThat(rest.getForObject("/forward/foos/0", Foo.class).getName()) - .isEqualTo("bye"); - } - - @Test - public void forwardHeader() throws Exception { - ResponseEntity result = rest.getForEntity("/forward/special/foos/0", - Foo.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody().getName()).isEqualTo("FOO"); - } - - @Test - public void postForwardHeader() throws Exception { - ResponseEntity> result = rest.exchange( - RequestEntity - .post(rest.getRestTemplate().getUriTemplateHandler().expand( - "/forward/special/bars")) - .body(Collections.singletonList(Collections.singletonMap("name", "foo"))), - new ParameterizedTypeReference>() { - }); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody().iterator().next().getName()).isEqualTo("FOOfoo"); - } - - @Test - public void postForwardBody() throws Exception { - ResponseEntity result = rest.exchange( - RequestEntity - .post(rest.getRestTemplate().getUriTemplateHandler().expand( - "/forward/body/bars")) - .body(Collections.singletonList(Collections.singletonMap("name", "foo"))), - String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).contains("foo"); - } - - @Test - public void postForwardForgetBody() throws Exception { - ResponseEntity result = rest.exchange( - RequestEntity - .post(rest.getRestTemplate().getUriTemplateHandler().expand( - "/forward/forget/bars")) - .body(Collections.singletonList(Collections.singletonMap("name", "foo"))), - String.class); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody()).contains("foo"); - } - - @Test - public void postForwardBodyFoo() throws Exception { - ResponseEntity> result = rest.exchange( - RequestEntity - .post(rest.getRestTemplate().getUriTemplateHandler().expand( - "/forward/body/bars")) - .body(Collections.singletonList(Collections.singletonMap("name", "foo"))), - new ParameterizedTypeReference>() { - }); - assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(result.getBody().iterator().next().getName()).isEqualTo("foo"); - } - - @Test - public void list() throws Exception { - assertThat(rest.exchange( - RequestEntity - .post(rest.getRestTemplate().getUriTemplateHandler().expand( - "/proxy")) - .body(Collections.singletonList(Collections.singletonMap("name", "foo"))), - new ParameterizedTypeReference>() { - }).getBody().iterator().next().getName()).isEqualTo("host=localhost;foo"); - } - - @Test - public void bodyless() throws Exception { - assertThat(rest.postForObject("/proxy/0", Collections.singletonMap("name", "foo"), - Bar.class).getName()).isEqualTo("host=localhost;foo"); - } - - @Test - public void entity() throws Exception { - assertThat(rest.exchange( - RequestEntity - .post(rest.getRestTemplate().getUriTemplateHandler() - .expand("/proxy/entity")) - .body(Collections.singletonMap("name", "foo")), - new ParameterizedTypeReference>() { - }).getBody().iterator().next().getName()).isEqualTo("host=localhost;foo"); - } - - @Test - public void entityWithType() throws Exception { - assertThat(rest.exchange( - RequestEntity - .post(rest.getRestTemplate().getUriTemplateHandler() - .expand("/proxy/type")) - .body(Collections.singletonMap("name", "foo")), - new ParameterizedTypeReference>() { - }).getBody().iterator().next().getName()).isEqualTo("host=localhost;foo"); - } - - @Test - public void single() throws Exception { - assertThat(rest.postForObject("/proxy/single", - Collections.singletonMap("name", "foobar"), Bar.class).getName()) - .isEqualTo("host=localhost;foobar"); - } - - @Test - public void converter() throws Exception { - assertThat(rest.postForObject("/proxy/converter", - Collections.singletonMap("name", "foobar"), Bar.class).getName()) - .isEqualTo("host=localhost;foobar"); - } + @Before + public void init() throws Exception { + application.setHome(new URI("http://localhost:" + port)); + } @Test - @SuppressWarnings({"Duplicates", "unchecked"}) + public void get() throws Exception { + assertThat(rest.getForObject("/proxy/0", Foo.class).getName()).isEqualTo("bye"); + } + + @Test + public void path() throws Exception { + assertThat(rest.getForObject("/proxy/path/1", Foo.class).getName()) + .isEqualTo("foo"); + } + + @Test + public void resource() throws Exception { + assertThat(rest.getForObject("/proxy/html/test.html", String.class)) + .contains("Test"); + } + + @Test + public void resourceWithNoType() throws Exception { + assertThat(rest.getForObject("/proxy/typeless/test.html", String.class)) + .contains("Test"); + } + + @Test + public void missing() throws Exception { + assertThat(rest.getForEntity("/proxy/missing/0", Foo.class).getStatusCode()) + .isEqualTo(HttpStatus.NOT_FOUND); + } + + @Test + public void uri() throws Exception { + assertThat(rest.getForObject("/proxy/0", Foo.class).getName()).isEqualTo("bye"); + } + + @Test + public void post() throws Exception { + assertThat(rest.postForObject("/proxy/0", Collections.singletonMap("name", "foo"), + Bar.class).getName()).isEqualTo("host=localhost;foo"); + } + + @Test + public void forward() throws Exception { + assertThat(rest.getForObject("/forward/foos/0", Foo.class).getName()) + .isEqualTo("bye"); + } + + @Test + public void forwardHeader() throws Exception { + ResponseEntity result = rest.getForEntity("/forward/special/foos/0", + Foo.class); + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(result.getBody().getName()).isEqualTo("FOO"); + } + + @Test + public void postForwardHeader() throws Exception { + ResponseEntity> result = rest.exchange( + RequestEntity + .post(rest.getRestTemplate().getUriTemplateHandler() + .expand("/forward/special/bars")) + .body(Collections + .singletonList(Collections.singletonMap("name", "foo"))), + new ParameterizedTypeReference>() { + }); + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(result.getBody().iterator().next().getName()).isEqualTo("FOOfoo"); + } + + @Test + public void postForwardBody() throws Exception { + ResponseEntity result = rest.exchange( + RequestEntity + .post(rest.getRestTemplate().getUriTemplateHandler() + .expand("/forward/body/bars")) + .body(Collections + .singletonList(Collections.singletonMap("name", "foo"))), + String.class); + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(result.getBody()).contains("foo"); + } + + @Test + public void postForwardForgetBody() throws Exception { + ResponseEntity result = rest.exchange( + RequestEntity + .post(rest.getRestTemplate().getUriTemplateHandler() + .expand("/forward/forget/bars")) + .body(Collections + .singletonList(Collections.singletonMap("name", "foo"))), + String.class); + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(result.getBody()).contains("foo"); + } + + @Test + public void postForwardBodyFoo() throws Exception { + ResponseEntity> result = rest.exchange( + RequestEntity + .post(rest.getRestTemplate().getUriTemplateHandler() + .expand("/forward/body/bars")) + .body(Collections + .singletonList(Collections.singletonMap("name", "foo"))), + new ParameterizedTypeReference>() { + }); + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(result.getBody().iterator().next().getName()).isEqualTo("foo"); + } + + @Test + public void list() throws Exception { + assertThat(rest.exchange( + RequestEntity + .post(rest.getRestTemplate().getUriTemplateHandler() + .expand("/proxy")) + .body(Collections + .singletonList(Collections.singletonMap("name", "foo"))), + new ParameterizedTypeReference>() { + }).getBody().iterator().next().getName()).isEqualTo("host=localhost;foo"); + } + + @Test + public void bodyless() throws Exception { + assertThat(rest.postForObject("/proxy/0", Collections.singletonMap("name", "foo"), + Bar.class).getName()).isEqualTo("host=localhost;foo"); + } + + @Test + public void entity() throws Exception { + assertThat(rest.exchange( + RequestEntity + .post(rest.getRestTemplate().getUriTemplateHandler() + .expand("/proxy/entity")) + .body(Collections.singletonMap("name", "foo")), + new ParameterizedTypeReference>() { + }).getBody().iterator().next().getName()).isEqualTo("host=localhost;foo"); + } + + @Test + public void entityWithType() throws Exception { + assertThat(rest.exchange( + RequestEntity + .post(rest.getRestTemplate().getUriTemplateHandler() + .expand("/proxy/type")) + .body(Collections.singletonMap("name", "foo")), + new ParameterizedTypeReference>() { + }).getBody().iterator().next().getName()).isEqualTo("host=localhost;foo"); + } + + @Test + public void single() throws Exception { + assertThat(rest.postForObject("/proxy/single", + Collections.singletonMap("name", "foobar"), Bar.class).getName()) + .isEqualTo("host=localhost;foobar"); + } + + @Test + public void converter() throws Exception { + assertThat(rest.postForObject("/proxy/converter", + Collections.singletonMap("name", "foobar"), Bar.class).getName()) + .isEqualTo("host=localhost;foobar"); + } + + @Test + @SuppressWarnings({ "Duplicates", "unchecked" }) public void headers() throws Exception { - Map> headers = rest.exchange(RequestEntity.get(rest.getRestTemplate().getUriTemplateHandler() - .expand("/proxy/headers")).header("foo", "bar").header("abc", "xyz").build(), Map.class).getBody(); - assertThat(headers).doesNotContainKey("foo") - .doesNotContainKey("hello") + Map> headers = rest.exchange(RequestEntity + .get(rest.getRestTemplate().getUriTemplateHandler() + .expand("/proxy/headers")) + .header("foo", "bar").header("abc", "xyz").build(), Map.class).getBody(); + assertThat(headers).doesNotContainKey("foo").doesNotContainKey("hello") .containsKeys("bar", "abc"); assertThat(headers.get("bar")).containsOnly("hello"); @@ -245,137 +250,145 @@ public class ProductionConfigurationTests { } @SpringBootApplication - static class TestApplication { + static class TestApplication { - @RestController - static class ProxyController { + @Autowired + private ProxyController controller; - private URI home; + public void setHome(URI home) { + controller.setHome(home); + } - public void setHome(URI home) { - this.home = home; - } + @RestController + static class ProxyController { - @GetMapping("/proxy/{id}") - public ResponseEntity proxyFoos(@PathVariable Integer id, - ProxyExchange proxy) throws Exception { - return proxy.uri(home.toString() + "/foos/" + id).get(); - } + private URI home; - @GetMapping("/proxy/path/**") - public ResponseEntity proxyPath(ProxyExchange proxy, - UriComponentsBuilder uri) throws Exception { - String path = proxy.path("/proxy/path/"); - return proxy.uri(home.toString() + "/foos/" + path).get(); - } + public void setHome(URI home) { + this.home = home; + } - @GetMapping("/proxy/html/**") - public ResponseEntity proxyHtml(ProxyExchange proxy, - UriComponentsBuilder uri) throws Exception { - String path = proxy.path("/proxy/html"); - return proxy.uri(home.toString() + path).get(); - } + @GetMapping("/proxy/{id}") + public ResponseEntity proxyFoos(@PathVariable Integer id, + ProxyExchange proxy) throws Exception { + return proxy.uri(home.toString() + "/foos/" + id).get(); + } - @GetMapping("/proxy/typeless/**") - public ResponseEntity proxyTypeless(ProxyExchange proxy, - UriComponentsBuilder uri) throws Exception { - String path = proxy.path("/proxy/typeless"); - return proxy.uri(home.toString() + path).get(); - } + @GetMapping("/proxy/path/**") + public ResponseEntity proxyPath(ProxyExchange proxy, + UriComponentsBuilder uri) throws Exception { + String path = proxy.path("/proxy/path/"); + return proxy.uri(home.toString() + "/foos/" + path).get(); + } - @GetMapping("/proxy/missing/{id}") - public ResponseEntity proxyMissing(@PathVariable Integer id, - ProxyExchange proxy) throws Exception { - return proxy.uri(home.toString() + "/missing/" + id).get(); - } + @GetMapping("/proxy/html/**") + public ResponseEntity proxyHtml(ProxyExchange proxy, + UriComponentsBuilder uri) throws Exception { + String path = proxy.path("/proxy/html"); + return proxy.uri(home.toString() + path).get(); + } - @GetMapping("/proxy") - public ResponseEntity proxyUri(ProxyExchange proxy) throws Exception { - return proxy.uri(home.toString() + "/foos").get(); - } + @GetMapping("/proxy/typeless/**") + public ResponseEntity proxyTypeless(ProxyExchange proxy, + UriComponentsBuilder uri) throws Exception { + String path = proxy.path("/proxy/typeless"); + return proxy.uri(home.toString() + path).get(); + } - @PostMapping("/proxy/{id}") - public ResponseEntity proxyBars(@PathVariable Integer id, - @RequestBody Map body, - ProxyExchange> proxy) throws Exception { - body.put("id", id); - return proxy.uri(home.toString() + "/bars").body(Arrays.asList(body)) - .post(this::first); - } + @GetMapping("/proxy/missing/{id}") + public ResponseEntity proxyMissing(@PathVariable Integer id, + ProxyExchange proxy) throws Exception { + return proxy.uri(home.toString() + "/missing/" + id).get(); + } - @PostMapping("/proxy") - public ResponseEntity barsWithNoBody(ProxyExchange proxy) - throws Exception { - return proxy.uri(home.toString() + "/bars").post(); - } + @GetMapping("/proxy") + public ResponseEntity proxyUri(ProxyExchange proxy) throws Exception { + return proxy.uri(home.toString() + "/foos").get(); + } - @PostMapping("/proxy/entity") - public ResponseEntity explicitEntity(@RequestBody Foo foo, - ProxyExchange proxy) throws Exception { - return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)) - .post(); - } + @PostMapping("/proxy/{id}") + public ResponseEntity proxyBars(@PathVariable Integer id, + @RequestBody Map body, + ProxyExchange> proxy) throws Exception { + body.put("id", id); + return proxy.uri(home.toString() + "/bars").body(Arrays.asList(body)) + .post(this::first); + } - @PostMapping("/proxy/type") - public ResponseEntity> explicitEntityWithType(@RequestBody Foo foo, - ProxyExchange> proxy) throws Exception { - return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)) - .post(); - } + @PostMapping("/proxy") + public ResponseEntity barsWithNoBody(ProxyExchange proxy) + throws Exception { + return proxy.uri(home.toString() + "/bars").post(); + } - @PostMapping("/proxy/single") - public ResponseEntity implicitEntity(@RequestBody Foo foo, - ProxyExchange> proxy) throws Exception { - return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)) - .post(this::first); - } + @PostMapping("/proxy/entity") + public ResponseEntity explicitEntity(@RequestBody Foo foo, + ProxyExchange proxy) throws Exception { + return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)) + .post(); + } - @PostMapping("/proxy/converter") - public ResponseEntity implicitEntityWithConverter(@RequestBody Foo foo, - ProxyExchange> proxy) throws Exception { - return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)) - .post(response -> ResponseEntity.status(response.getStatusCode()) - .headers(response.getHeaders()) - .body(response.getBody().iterator().next())); - } + @PostMapping("/proxy/type") + public ResponseEntity> explicitEntityWithType(@RequestBody Foo foo, + ProxyExchange> proxy) throws Exception { + return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)) + .post(); + } - @GetMapping("/forward/**") - public void forward(ProxyExchange proxy) throws Exception { - String path = proxy.path("/forward"); - if (path.startsWith("/special")) { - proxy.header("X-Custom", "FOO"); - path = proxy.path("/forward/special"); - } - proxy.forward(path); - } + @PostMapping("/proxy/single") + public ResponseEntity implicitEntity(@RequestBody Foo foo, + ProxyExchange> proxy) throws Exception { + return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)) + .post(this::first); + } - @PostMapping("/forward/**") - public void postForward(ProxyExchange proxy) throws Exception { - String path = proxy.path("/forward"); - if (path.startsWith("/special")) { - proxy.header("X-Custom", "FOO"); - path = proxy.path("/forward/special"); - } - proxy.forward(path); - } + @PostMapping("/proxy/converter") + public ResponseEntity implicitEntityWithConverter(@RequestBody Foo foo, + ProxyExchange> proxy) throws Exception { + return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)) + .post(response -> ResponseEntity.status(response.getStatusCode()) + .headers(response.getHeaders()) + .body(response.getBody().iterator().next())); + } - @PostMapping("/forward/body/**") - public void postForwardBody(@RequestBody byte[] body, ProxyExchange proxy) - throws Exception { - String path = proxy.path("/forward/body"); - proxy.body(body).forward(path); - } + @GetMapping("/forward/**") + public void forward(ProxyExchange proxy) throws Exception { + String path = proxy.path("/forward"); + if (path.startsWith("/special")) { + proxy.header("X-Custom", "FOO"); + path = proxy.path("/forward/special"); + } + proxy.forward(path); + } - @PostMapping("/forward/forget/**") - public void postForwardForgetBody(@RequestBody byte[] body, - ProxyExchange proxy) throws Exception { - String path = proxy.path("/forward/forget"); - proxy.forward(path); - } + @PostMapping("/forward/**") + public void postForward(ProxyExchange proxy) throws Exception { + String path = proxy.path("/forward"); + if (path.startsWith("/special")) { + proxy.header("X-Custom", "FOO"); + path = proxy.path("/forward/special"); + } + proxy.forward(path); + } + + @PostMapping("/forward/body/**") + public void postForwardBody(@RequestBody byte[] body, ProxyExchange proxy) + throws Exception { + String path = proxy.path("/forward/body"); + proxy.body(body).forward(path); + } + + @PostMapping("/forward/forget/**") + public void postForwardForgetBody(@RequestBody byte[] body, + ProxyExchange proxy) throws Exception { + String path = proxy.path("/forward/forget"); + proxy.forward(path); + } @GetMapping("/proxy/headers") @SuppressWarnings("Duplicates") - public ResponseEntity>> headers(ProxyExchange>> proxy) { + public ResponseEntity>> headers( + ProxyExchange>> proxy) { proxy.sensitive("foo"); proxy.sensitive("hello"); proxy.header("bar", "hello"); @@ -384,90 +397,89 @@ public class ProductionConfigurationTests { return proxy.uri(home.toString() + "/headers").get(); } - private ResponseEntity first(ResponseEntity> response) { - return ResponseEntity.status(response.getStatusCode()) - .headers(response.getHeaders()) - .body(response.getBody().iterator().next()); - } + private ResponseEntity first(ResponseEntity> response) { + return ResponseEntity.status(response.getStatusCode()) + .headers(response.getHeaders()) + .body(response.getBody().iterator().next()); + } - } + } - @Autowired - private ProxyController controller; + @RestController + static class TestController { - public void setHome(URI home) { - controller.setHome(home); - } + @GetMapping("/foos") + public List foos() { + return Arrays.asList(new Foo("hello")); + } - @RestController - static class TestController { + @GetMapping("/foos/{id}") + public Foo foo(@PathVariable Integer id, @RequestHeader HttpHeaders headers) { + String custom = headers.getFirst("X-Custom"); + return new Foo(id == 1 ? "foo" : custom != null ? custom : "bye"); + } - @GetMapping("/foos") - public List foos() { - return Arrays.asList(new Foo("hello")); - } - - @GetMapping("/foos/{id}") - public Foo foo(@PathVariable Integer id, @RequestHeader HttpHeaders headers) { - String custom = headers.getFirst("X-Custom"); - return new Foo(id == 1 ? "foo" : custom != null ? custom : "bye"); - } - - @PostMapping("/bars") - public List bars(@RequestBody List foos, - @RequestHeader HttpHeaders headers) { - String custom = headers.getFirst("X-Custom"); - custom = custom == null ? "" : custom; - custom = headers.getFirst("forwarded")==null ? custom : headers.getFirst("forwarded") + ";" + custom; - return Arrays.asList(new Bar(custom + foos.iterator().next().getName())); - } + @PostMapping("/bars") + public List bars(@RequestBody List foos, + @RequestHeader HttpHeaders headers) { + String custom = headers.getFirst("X-Custom"); + custom = custom == null ? "" : custom; + custom = headers.getFirst("forwarded") == null ? custom + : headers.getFirst("forwarded") + ";" + custom; + return Arrays.asList(new Bar(custom + foos.iterator().next().getName())); + } @GetMapping("/headers") public Map> headers(@RequestHeader HttpHeaders headers) { return new LinkedMultiValueMap<>(headers); } - } - @JsonIgnoreProperties(ignoreUnknown = true) - static class Foo { - private String name; + } - public Foo() { - } + @JsonIgnoreProperties(ignoreUnknown = true) + static class Foo { - public Foo(String name) { - this.name = name; - } + private String name; - public String getName() { - return name; - } + Foo() { + } - public void setName(String name) { - this.name = name; - } - } + Foo(String name) { + this.name = name; + } - @JsonIgnoreProperties(ignoreUnknown = true) - static class Bar { - private String name; + public String getName() { + return name; + } - public Bar() { - } + public void setName(String name) { + this.name = name; + } - public Bar(String name) { - this.name = name; - } + } - public String getName() { - return name; - } + @JsonIgnoreProperties(ignoreUnknown = true) + static class Bar { - public void setName(String name) { - this.name = name; - } - } + private String name; - } + Bar() { + } -} \ No newline at end of file + Bar(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } + + } + +} diff --git a/spring-cloud-gateway-sample/pom.xml b/spring-cloud-gateway-sample/pom.xml index 7291bf52..fbc929c6 100644 --- a/spring-cloud-gateway-sample/pom.xml +++ b/spring-cloud-gateway-sample/pom.xml @@ -1,6 +1,6 @@ - 4.0.0 @@ -85,7 +85,7 @@ org.springframework.boot spring-boot-maven-plugin - + org.jetbrains.kotlin kotlin-maven-plugin diff --git a/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java b/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java index 58b5fc19..6144d389 100644 --- a/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java +++ b/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.sample; @@ -46,9 +45,14 @@ import org.springframework.web.reactive.function.server.ServerResponse; public class GatewaySampleApplication { public static final String HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS = "hello from fake /actuator/metrics/gateway.requests"; + @Value("${test.uri:http://httpbin.org:80}") String uri; + public static void main(String[] args) { + SpringApplication.run(GatewaySampleApplication.class, args); + } + @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { //@formatter:off @@ -82,7 +86,7 @@ public class GatewaySampleApplication { .addResponseHeader("X-TestHeader", "rewrite_request_upper") .modifyRequestBody(String.class, String.class, (exchange, s) -> { - return Mono.just(s.toUpperCase()+s.toUpperCase()); + return Mono.just(s.toUpperCase() + s.toUpperCase()); }) ).uri(uri) ) @@ -138,16 +142,19 @@ public class GatewaySampleApplication { public RouterFunction testWhenMetricPathIsNotMeet() { RouterFunction route = RouterFunctions.route( RequestPredicates.path("/actuator/metrics/gateway.requests"), - request -> ServerResponse.ok().body(BodyInserters.fromObject(HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS))); + request -> ServerResponse.ok().body(BodyInserters + .fromObject(HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS))); return route; } static class Hello { + String message; - public Hello() { } + Hello() { + } - public Hello(String message) { + Hello(String message) { this.message = message; } @@ -158,9 +165,7 @@ public class GatewaySampleApplication { public void setMessage(String message) { this.message = message; } + } - public static void main(String[] args) { - SpringApplication.run(GatewaySampleApplication.class, args); - } } diff --git a/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/ThrottleGatewayFilter.java b/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/ThrottleGatewayFilter.java index 19efe6b0..2f03db13 100644 --- a/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/ThrottleGatewayFilter.java +++ b/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/ThrottleGatewayFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.sample; @@ -23,24 +22,27 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.isomorphism.util.TokenBucket; import org.isomorphism.util.TokenBuckets; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.http.HttpStatus; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - /** - * Sample throttling filter. - * See https://github.com/bbeck/token-bucket + * Sample throttling filter. See https://github.com/bbeck/token-bucket */ public class ThrottleGatewayFilter implements GatewayFilter { + private static final Log log = LogFactory.getLog(ThrottleGatewayFilter.class); - int capacity; - int refillTokens; - int refillPeriod; - TimeUnit refillUnit; + int capacity; + + int refillTokens; + + int refillPeriod; + + TimeUnit refillUnit; public int getCapacity() { return capacity; @@ -81,18 +83,18 @@ public class ThrottleGatewayFilter implements GatewayFilter { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - TokenBucket tokenBucket = TokenBuckets.builder() - .withCapacity(capacity) + TokenBucket tokenBucket = TokenBuckets.builder().withCapacity(capacity) .withFixedIntervalRefillStrategy(refillTokens, refillPeriod, refillUnit) .build(); - //TODO: get a token bucket for a key - log.debug("TokenBucket capacity: " + tokenBucket.getCapacity()); - boolean consumed = tokenBucket.tryConsume(); - if (consumed) { - return chain.filter(exchange); - } - exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); - return exchange.getResponse().setComplete(); + // TODO: get a token bucket for a key + log.debug("TokenBucket capacity: " + tokenBucket.getCapacity()); + boolean consumed = tokenBucket.tryConsume(); + if (consumed) { + return chain.filter(exchange); + } + exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); + return exchange.getResponse().setComplete(); } + } diff --git a/spring-cloud-gateway-sample/src/main/kotlin/org/springframework/cloud/gateway/sample/AdditionalRoutes.kt b/spring-cloud-gateway-sample/src/main/kotlin/org/springframework/cloud/gateway/sample/AdditionalRoutes.kt index c6107205..b8703ebc 100644 --- a/spring-cloud-gateway-sample/src/main/kotlin/org/springframework/cloud/gateway/sample/AdditionalRoutes.kt +++ b/spring-cloud-gateway-sample/src/main/kotlin/org/springframework/cloud/gateway/sample/AdditionalRoutes.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.gateway.sample import org.springframework.beans.factory.annotation.Value @@ -10,19 +26,19 @@ import org.springframework.context.annotation.Configuration @Configuration open class AdditionalRoutes { - @Value("\${test.uri:http://httpbin.org:80}") - var uri: String? = null + @Value("\${test.uri:http://httpbin.org:80}") + var uri: String? = null - @Bean - open fun additionalRouteLocator(builder: RouteLocatorBuilder) = builder.routes { - route(id = "test-kotlin") { - host("kotlin.abc.org") and path("/image/png") - filters { - prefixPath("/httpbin") - addResponseHeader("X-TestHeader", "foobar") - } - uri(uri) - } - } + @Bean + open fun additionalRouteLocator(builder: RouteLocatorBuilder) = builder.routes { + route(id = "test-kotlin") { + host("kotlin.abc.org") and path("/image/png") + filters { + prefixPath("/httpbin") + addResponseHeader("X-TestHeader", "foobar") + } + uri(uri) + } + } } \ No newline at end of file diff --git a/spring-cloud-gateway-sample/src/main/resources/application.yml b/spring-cloud-gateway-sample/src/main/resources/application.yml index 01bb0a3f..4cf839df 100644 --- a/spring-cloud-gateway-sample/src/main/resources/application.yml +++ b/spring-cloud-gateway-sample/src/main/resources/application.yml @@ -1,7 +1,7 @@ test: -# hostport: httpbin.org:80 -# hostport: localhost:5000 -# uri: http://${test.hostport} + # hostport: httpbin.org:80 + # hostport: localhost:5000 + # uri: http://${test.hostport} uri: lb://httpbin diff --git a/spring-cloud-gateway-sample/src/test/java/org/springframework/cloud/gateway/sample/GatewaySampleApplicationTests.java b/spring-cloud-gateway-sample/src/test/java/org/springframework/cloud/gateway/sample/GatewaySampleApplicationTests.java index c07d26d3..6788a96f 100644 --- a/spring-cloud-gateway-sample/src/test/java/org/springframework/cloud/gateway/sample/GatewaySampleApplicationTests.java +++ b/spring-cloud-gateway-sample/src/test/java/org/springframework/cloud/gateway/sample/GatewaySampleApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.sample; @@ -46,23 +45,23 @@ import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * @author Spencer Gibb */ @RunWith(SpringRunner.class) -@SpringBootTest(classes = { GatewaySampleApplicationTests.TestConfig.class}, - webEnvironment = RANDOM_PORT, properties = "management.server.port=${test.port}") +@SpringBootTest(classes = { + GatewaySampleApplicationTests.TestConfig.class }, webEnvironment = RANDOM_PORT, properties = "management.server.port=${test.port}") public class GatewaySampleApplicationTests { + protected static int managementPort; + @LocalServerPort protected int port = 0; - protected static int managementPort; - protected WebTestClient webClient; + protected String baseUri; @BeforeClass @@ -80,109 +79,81 @@ public class GatewaySampleApplicationTests { @Before public void setup() { baseUri = "http://localhost:" + port; - this.webClient = WebTestClient.bindToServer().responseTimeout(Duration.ofSeconds(10)).baseUrl(baseUri).build(); + this.webClient = WebTestClient.bindToServer() + .responseTimeout(Duration.ofSeconds(10)).baseUrl(baseUri).build(); } @Test public void contextLoads() { - webClient.get() - .uri("/get") - .exchange() - .expectStatus().isOk(); + webClient.get().uri("/get").exchange().expectStatus().isOk(); } @Test @SuppressWarnings("unchecked") public void readBodyPredicateStringWorks() { - webClient.post() - .uri("/post") - .header("Host", "www.readbody.org") - .syncBody("hi") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals("X-TestHeader", "read_body_pred") - .expectBody(Map.class) - .consumeWith(result -> - assertThat(result.getResponseBody()).containsEntry("data", "hi")); + webClient.post().uri("/post").header("Host", "www.readbody.org").syncBody("hi") + .exchange().expectStatus().isOk().expectHeader() + .valueEquals("X-TestHeader", "read_body_pred").expectBody(Map.class) + .consumeWith(result -> assertThat(result.getResponseBody()) + .containsEntry("data", "hi")); } @Test @SuppressWarnings("unchecked") public void rewriteRequestBodyStringWorks() { - webClient.post() - .uri("/post") - .header("Host", "www.rewriterequestupper.org") - .syncBody("hello") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals("X-TestHeader", "rewrite_request_upper") + webClient.post().uri("/post").header("Host", "www.rewriterequestupper.org") + .syncBody("hello").exchange().expectStatus().isOk().expectHeader() + .valueEquals("X-TestHeader", "rewrite_request_upper") .expectBody(Map.class) - .consumeWith(result -> - assertThat(result.getResponseBody()).containsEntry("data", "HELLOHELLO")); + .consumeWith(result -> assertThat(result.getResponseBody()) + .containsEntry("data", "HELLOHELLO")); } @Test @SuppressWarnings("unchecked") public void rewriteRequestBodyObjectWorks() { - webClient.post() - .uri("/post") - .header("Host", "www.rewriterequestobj.org") - .syncBody("hello") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals("X-TestHeader", "rewrite_request") - .expectBody(Map.class) - .consumeWith(result -> - assertThat(result.getResponseBody()).containsEntry("data", "{\"message\":\"HELLO\"}")); + webClient.post().uri("/post").header("Host", "www.rewriterequestobj.org") + .syncBody("hello").exchange().expectStatus().isOk().expectHeader() + .valueEquals("X-TestHeader", "rewrite_request").expectBody(Map.class) + .consumeWith(result -> assertThat(result.getResponseBody()) + .containsEntry("data", "{\"message\":\"HELLO\"}")); } @Test @SuppressWarnings("unchecked") public void rewriteResponseBodyStringWorks() { - webClient.post() - .uri("/post") - .header("Host", "www.rewriteresponseupper.org") - .syncBody("hello") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals("X-TestHeader", "rewrite_response_upper") + webClient.post().uri("/post").header("Host", "www.rewriteresponseupper.org") + .syncBody("hello").exchange().expectStatus().isOk().expectHeader() + .valueEquals("X-TestHeader", "rewrite_response_upper") .expectBody(Map.class) - .consumeWith(result -> - assertThat(result.getResponseBody()).containsEntry("DATA", "HELLO")); + .consumeWith(result -> assertThat(result.getResponseBody()) + .containsEntry("DATA", "HELLO")); } @Test @SuppressWarnings("unchecked") public void rewriteResponeBodyObjectWorks() { - webClient.post() - .uri("/post") - .header("Host", "www.rewriteresponseobj.org") - .syncBody("hello") - .exchange() - .expectStatus().isOk() - .expectHeader().valueEquals("X-TestHeader", "rewrite_response_obj") + webClient.post().uri("/post").header("Host", "www.rewriteresponseobj.org") + .syncBody("hello").exchange().expectStatus().isOk().expectHeader() + .valueEquals("X-TestHeader", "rewrite_response_obj") .expectBody(String.class) - .consumeWith(result -> - assertThat(result.getResponseBody()).isEqualTo("hello")); + .consumeWith(result -> assertThat(result.getResponseBody()) + .isEqualTo("hello")); } @Test public void complexPredicate() { - webClient.get() - .uri("/anything/png") - .header("Host", "www.abc.org") - .exchange() - .expectHeader().valueEquals("X-TestHeader", "foobar") - .expectStatus().isOk(); + webClient.get().uri("/anything/png").header("Host", "www.abc.org").exchange() + .expectHeader().valueEquals("X-TestHeader", "foobar").expectStatus() + .isOk(); } @Test public void actuatorManagementPort() { webClient.get() - .uri("http://localhost:"+managementPort+"/actuator/gateway/routes") - .exchange() - .expectStatus().isOk(); + .uri("http://localhost:" + managementPort + "/actuator/gateway/routes") + .exchange().expectStatus().isOk(); } @Test @@ -197,8 +168,9 @@ public class GatewaySampleApplicationTests { try { JsonNode actualObj = mapper.readTree(body); JsonNode findValue = actualObj.findValue("name"); - assertEquals("Expected to find metric with name gateway.requests", - "gateway.requests", findValue.asText()); + assertThat(findValue.asText()) + .as("Expected to find metric with name gateway.requests") + .isEqualTo("gateway.requests"); } catch (IOException e) { throw new IllegalStateException(e); @@ -211,6 +183,7 @@ public class GatewaySampleApplicationTests { @RibbonClient(name = "httpbin", configuration = RibbonConfig.class) @Import(GatewaySampleApplication.class) protected static class TestConfig { + @Bean public HttpBinCompatibleController httpBinCompatibleController() { return new HttpBinCompatibleController(); @@ -219,6 +192,7 @@ public class GatewaySampleApplicationTests { } protected static class RibbonConfig { + @LocalServerPort int port; @@ -227,6 +201,7 @@ public class GatewaySampleApplicationTests { public ServerList ribbonServerList() { return new StaticServerList<>(new Server("localhost", port)); } + } } diff --git a/spring-cloud-gateway-sample/src/test/java/org/springframework/cloud/gateway/sample/GatewaySampleApplicationWithoutMetricsTests.java b/spring-cloud-gateway-sample/src/test/java/org/springframework/cloud/gateway/sample/GatewaySampleApplicationWithoutMetricsTests.java index 3ea50935..6fa9448a 100644 --- a/spring-cloud-gateway-sample/src/test/java/org/springframework/cloud/gateway/sample/GatewaySampleApplicationWithoutMetricsTests.java +++ b/spring-cloud-gateway-sample/src/test/java/org/springframework/cloud/gateway/sample/GatewaySampleApplicationWithoutMetricsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.gateway.sample; @@ -24,6 +23,7 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.gateway.sample.GatewaySampleApplicationTests.TestConfig; @@ -35,13 +35,15 @@ import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.SocketUtils; @RunWith(ModifiedClassPathRunner.class) -@ClassPathExclusions({ "micrometer-*.jar", "spring-boot-actuator-*.jar", "spring-boot-actuator-autoconfigure-*.jar" }) +@ClassPathExclusions({ "micrometer-*.jar", "spring-boot-actuator-*.jar", + "spring-boot-actuator-autoconfigure-*.jar" }) @DirtiesContext public class GatewaySampleApplicationWithoutMetricsTests { static protected int port; protected WebTestClient webClient; + protected String baseUri; @BeforeClass diff --git a/spring-cloud-gateway-webflux/pom.xml b/spring-cloud-gateway-webflux/pom.xml index 0ab65d6f..edae55ad 100644 --- a/spring-cloud-gateway-webflux/pom.xml +++ b/spring-cloud-gateway-webflux/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 spring-cloud-gateway-webflux diff --git a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/ProxyExchange.java b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/ProxyExchange.java index a59812ca..f4afe2c8 100644 --- a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/ProxyExchange.java +++ b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/ProxyExchange.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import java.util.function.Function; import java.util.stream.Collectors; import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.io.buffer.DataBuffer; @@ -44,14 +45,12 @@ import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - /** * A @RequestMapping argument type that can proxy the request to a backend. * Spring will inject one of these into your MVC handler method, and you get return a * ResponseEntity that you get from one of the HTTP methods {@link #get()}, * {@link #post()}, {@link #put()}, {@link #patch()}, {@link #delete()} etc. Example: - * + * *
  * @GetMapping("/proxy/{id}")
  * public Mono<ResponseEntity<?>> proxy(@PathVariable Integer id, ProxyExchange<?> proxy)
@@ -59,7 +58,7 @@ import reactor.core.publisher.Mono;
  * 	return proxy.uri("http://localhost:9000/foos/" + id).get();
  * }
  * 
- * + * *

* By default the incoming request body and headers are sent intact to the downstream * service (with the exception of "sensitive" headers). To manipulate the downstream @@ -73,15 +72,14 @@ import reactor.core.publisher.Mono; * the response body, so it comes out in the {@link ResponseEntity} that you return from * your @RequestMapping. If you don't care about the type of the request and * response body (e.g. if it's just a passthru) then use a wildcard, or - * byte[] (Object probably won't work unless you provide a converter). - * Use a concrete type if you want to - * transform or manipulate the response, or if you want to assert that it is convertible - * to the type you declare. + * byte[] (Object probably won't work unless you provide a + * converter). Use a concrete type if you want to transform or manipulate the response, or + * if you want to assert that it is convertible to the type you declare. *

*

* To manipulate the response use the overloaded HTTP methods with a Function * argument and pass in code to transform the response. E.g. - * + * *

  * @PostMapping("/proxy")
  * public Mono<ResponseEntity<Foo>> proxy(ProxyExchange<Foo> proxy) throws Exception {
@@ -92,9 +90,9 @@ import reactor.core.publisher.Mono;
  * 					.body(response.getBody()) //
  * 			);
  * }
- * 
+ *
  * 
- * + * *

*

* The full machinery of Spring {@link HttpMessageConverter message converters} is applied @@ -104,12 +102,15 @@ import reactor.core.publisher.Mono; * {@link ProxyExchange#ProxyExchange(WebClient, ServerWebExchange, BindingContext, Type) * constructor} for details). *

- * + * * @author Dave Syer * */ public class ProxyExchange { + /** + * Contains headers that are considered case-sensitive by default. + */ public static Set DEFAULT_SENSITIVE = new HashSet<>( Arrays.asList("cookie", "authorization")); @@ -120,8 +121,9 @@ public class ProxyExchange { private Publisher body; private boolean hasBody = false; - + private ServerWebExchange exchange; + private BindingContext bindingContext; private Set sensitive; @@ -146,7 +148,6 @@ public class ProxyExchange { * request downstream without changing it. If you want to transform the incoming * request you can declare it as a @RequestBody in your * @RequestMapping in the usual Spring MVC way. - * * @param body the request body to send downstream * @return this for convenience */ @@ -161,7 +162,6 @@ public class ProxyExchange { * request downstream without changing it. If you want to transform the incoming * request you can declare it as a @RequestBody in your * @RequestMapping in the usual Spring MVC way. - * * @param body the request body to send downstream * @return this for convenience */ @@ -173,9 +173,8 @@ public class ProxyExchange { /** * Sets a header for the downstream call. - * - * @param name - * @param value + * @param name Header name + * @param value Header values * @return this for convenience */ public ProxyExchange header(String name, String... value) { @@ -186,7 +185,6 @@ public class ProxyExchange { /** * Additional headers, or overrides of the incoming ones, to be used in the downstream * call. - * * @param headers the http headers to use in the downstream call * @return this for convenience */ @@ -198,7 +196,6 @@ public class ProxyExchange { /** * Sets the names of sensitive headers that are not passed downstream to the backend * service. - * * @param names the names of sensitive headers * @return this for convenience */ @@ -214,7 +211,6 @@ public class ProxyExchange { /** * Sets the uri for the backend call when triggered by the HTTP methods. - * * @param uri the backend uri to send the request to * @return this for convenience */ @@ -335,29 +331,31 @@ public class ProxyExchange { } else { if (hasBody) { - result = builder.headers( - headers -> addHeaders(headers, exchange.getRequest().getHeaders())) + result = builder + .headers(headers -> addHeaders(headers, + exchange.getRequest().getHeaders())) .body(exchange.getRequest().getBody(), DataBuffer.class) .exchange(); } else { - result = builder.headers( - headers -> addHeaders(headers, exchange.getRequest().getHeaders())) - .exchange(); + result = builder.headers(headers -> addHeaders(headers, + exchange.getRequest().getHeaders())).exchange(); } } - return result.flatMap(response -> response.toEntity(ParameterizedTypeReference.forType(type))); + return result.flatMap( + response -> response.toEntity(ParameterizedTypeReference.forType(type))); } private void addHeaders(HttpHeaders headers, HttpHeaders toAdd) { Set filteredKeys = filterHeaderKeys(toAdd); - filteredKeys.stream() - .filter(key -> !headers.containsKey(key)) + filteredKeys.stream().filter(key -> !headers.containsKey(key)) .forEach(header -> headers.addAll(header, toAdd.get(header))); } private Set filterHeaderKeys(HttpHeaders headers) { - return headers.keySet().stream().filter(header -> !sensitive.contains(header.toLowerCase())).collect(Collectors.toSet()); + return headers.keySet().stream() + .filter(header -> !sensitive.contains(header.toLowerCase())) + .collect(Collectors.toSet()); } private BodyBuilder headers(BodyBuilder builder) { @@ -423,7 +421,6 @@ public class ProxyExchange { * Search for the request body if it was already deserialized using * @RequestBody. If it is not found then deserialize it in the same way * that it would have been for a @RequestBody. - * * @return the request body */ private Mono getRequestBody() { @@ -438,16 +435,20 @@ public class ProxyExchange { } protected static class BodyGrabber { + public Publisher body(@RequestBody Publisher body) { return body; } + } protected static class BodySender { + @ResponseBody public Publisher body() { return null; } + } } diff --git a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyExchangeArgumentResolver.java b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyExchangeArgumentResolver.java index 973c2897..16c72a5b 100644 --- a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyExchangeArgumentResolver.java +++ b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyExchangeArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.Set; +import reactor.core.publisher.Mono; + import org.springframework.cloud.gateway.webflux.ProxyExchange; import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; @@ -30,8 +32,6 @@ import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - /** * @author Dave Syer * diff --git a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyProperties.java b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyProperties.java index d9575bd4..2b4876eb 100644 --- a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyProperties.java +++ b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,44 +27,45 @@ import org.springframework.http.HttpHeaders; /** * Configuration properties for the {@link ProxyExchange} argument handler in * @RequestMapping methods. + * * @author Dave Syer * */ @ConfigurationProperties("spring.cloud.gateway.proxy") public class ProxyProperties { - /** - * Fixed header values that will be added to all downstream requests. - */ - private Map headers = new LinkedHashMap<>(); + /** + * Fixed header values that will be added to all downstream requests. + */ + private Map headers = new LinkedHashMap<>(); - /** - * A set of sensitive header names that will not be sent downstream by default. - */ - private Set sensitive = null; + /** + * A set of sensitive header names that will not be sent downstream by default. + */ + private Set sensitive = null; - public Map getHeaders() { - return headers; - } + public Map getHeaders() { + return headers; + } - public void setHeaders(Map headers) { - this.headers = headers; - } + public void setHeaders(Map headers) { + this.headers = headers; + } - public Set getSensitive() { - return sensitive; - } + public Set getSensitive() { + return sensitive; + } - public void setSensitive(Set sensitive) { - this.sensitive = sensitive; - } + public void setSensitive(Set sensitive) { + this.sensitive = sensitive; + } - public HttpHeaders convertHeaders() { - HttpHeaders headers = new HttpHeaders(); - for (String key : this.headers.keySet()) { - headers.set(key, this.headers.get(key)); - } - return headers; - } + public HttpHeaders convertHeaders() { + HttpHeaders headers = new HttpHeaders(); + for (String key : this.headers.keySet()) { + headers.set(key, this.headers.get(key)); + } + return headers; + } } diff --git a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyResponseAutoConfiguration.java b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyResponseAutoConfiguration.java index 420b5eed..55cc588c 100644 --- a/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyResponseAutoConfiguration.java +++ b/spring-cloud-gateway-webflux/src/main/java/org/springframework/cloud/gateway/webflux/config/ProxyResponseAutoConfiguration.java @@ -66,4 +66,5 @@ public class ProxyResponseAutoConfiguration implements WebFluxConfigurer { configurer .addCustomResolver(context.getBean(ProxyExchangeArgumentResolver.class)); } + } diff --git a/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ProductionConfigurationTests.java b/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ProductionConfigurationTests.java index f2cf0746..ca5340be 100644 --- a/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ProductionConfigurationTests.java +++ b/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ProductionConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,10 +23,11 @@ import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -55,11 +56,6 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.util.UriComponentsBuilder; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @@ -123,16 +119,18 @@ public class ProductionConfigurationTests { @Test public void list() throws Exception { - ResponseEntity> result = rest.exchange( - RequestEntity - .post(rest.getRestTemplate().getUriTemplateHandler() - .expand("/proxy")) - .contentType(MediaType.APPLICATION_JSON) - .body(Collections - .singletonList(Collections.singletonMap("name", "foo"))), - new ParameterizedTypeReference>() { - }); - assertThat(result.getBody().iterator().next().getName()).isEqualTo("host=localhost;foo"); + ResponseEntity> result = rest + .exchange( + RequestEntity + .post(rest.getRestTemplate().getUriTemplateHandler() + .expand("/proxy")) + .contentType(MediaType.APPLICATION_JSON) + .body(Collections.singletonList( + Collections.singletonMap("name", "foo"))), + new ParameterizedTypeReference>() { + }); + assertThat(result.getBody().iterator().next().getName()) + .isEqualTo("host=localhost;foo"); } @Test @@ -178,12 +176,13 @@ public class ProductionConfigurationTests { } @Test - @SuppressWarnings({"Duplicates", "unchecked"}) + @SuppressWarnings({ "Duplicates", "unchecked" }) public void headers() throws Exception { - Map> headers = rest.exchange(RequestEntity.get(rest.getRestTemplate().getUriTemplateHandler() - .expand("/proxy/headers")).header("foo", "bar").header("abc", "xyz").build(), Map.class).getBody(); - assertThat(headers).doesNotContainKey("foo") - .doesNotContainKey("hello") + Map> headers = rest.exchange(RequestEntity + .get(rest.getRestTemplate().getUriTemplateHandler() + .expand("/proxy/headers")) + .header("foo", "bar").header("abc", "xyz").build(), Map.class).getBody(); + assertThat(headers).doesNotContainKey("foo").doesNotContainKey("hello") .containsKeys("bar", "abc"); assertThat(headers.get("bar")).containsOnly("hello"); @@ -193,6 +192,13 @@ public class ProductionConfigurationTests { @SpringBootApplication static class TestApplication { + @Autowired + private ProxyController controller; + + public void setHome(URI home) { + controller.setHome(home); + } + @RestController static class ProxyController { @@ -203,14 +209,14 @@ public class ProductionConfigurationTests { } @GetMapping("/proxy/{id}") - public Mono> proxyFoos(@PathVariable Integer id, ProxyExchange proxy) - throws Exception { + public Mono> proxyFoos(@PathVariable Integer id, + ProxyExchange proxy) throws Exception { return proxy.uri(home.toString() + "/foos/" + id).get(); } @GetMapping("/proxy/path/**") - public Mono> proxyPath(ProxyExchange proxy, UriComponentsBuilder uri) - throws Exception { + public Mono> proxyPath(ProxyExchange proxy, + UriComponentsBuilder uri) throws Exception { String path = proxy.path("/proxy/path/"); return proxy.uri(home.toString() + "/foos/" + path).get(); } @@ -223,20 +229,21 @@ public class ProductionConfigurationTests { } @GetMapping("/proxy/typeless/**") - public Mono> proxyTypeless(ProxyExchange proxy, UriComponentsBuilder uri) - throws Exception { + public Mono> proxyTypeless(ProxyExchange proxy, + UriComponentsBuilder uri) throws Exception { String path = proxy.path("/proxy/typeless"); return proxy.uri(home.toString() + path).get(); } @GetMapping("/proxy/missing/{id}") - public Mono> proxyMissing(@PathVariable Integer id, ProxyExchange proxy) - throws Exception { + public Mono> proxyMissing(@PathVariable Integer id, + ProxyExchange proxy) throws Exception { return proxy.uri(home.toString() + "/missing/" + id).get(); } @GetMapping("/proxy") - public Mono> proxyUri(ProxyExchange proxy) throws Exception { + public Mono> proxyUri(ProxyExchange proxy) + throws Exception { return proxy.uri(home.toString() + "/foos").get(); } @@ -250,7 +257,8 @@ public class ProductionConfigurationTests { } @PostMapping("/proxy") - public Mono>> barsWithNoBody(ProxyExchange> proxy) throws Exception { + public Mono>> barsWithNoBody( + ProxyExchange> proxy) throws Exception { return proxy.uri(home.toString() + "/bars").post(); } @@ -285,7 +293,8 @@ public class ProductionConfigurationTests { } @GetMapping("/proxy/headers") - public Mono>>> headers(ProxyExchange>> proxy) { + public Mono>>> headers( + ProxyExchange>> proxy) { proxy.sensitive("foo"); proxy.sensitive("hello"); proxy.header("bar", "hello"); @@ -302,13 +311,6 @@ public class ProductionConfigurationTests { } - @Autowired - private ProxyController controller; - - public void setHome(URI home) { - controller.setHome(home); - } - @RestController static class TestController { @@ -342,12 +344,13 @@ public class ProductionConfigurationTests { @JsonIgnoreProperties(ignoreUnknown = true) static class Foo { + private String name; - public Foo() { + Foo() { } - public Foo(String name) { + Foo(String name) { this.name = name; } @@ -358,16 +361,18 @@ public class ProductionConfigurationTests { public void setName(String name) { this.name = name; } + } @JsonIgnoreProperties(ignoreUnknown = true) static class Bar { + private String name; - public Bar() { + Bar() { } - public Bar(String name) { + Bar(String name) { this.name = name; } @@ -378,8 +383,9 @@ public class ProductionConfigurationTests { public void setName(String name) { this.name = name; } + } } -} \ No newline at end of file +} diff --git a/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ReactiveTests.java b/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ReactiveTests.java index 3f0e795b..7a5d8676 100644 --- a/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ReactiveTests.java +++ b/spring-cloud-gateway-webflux/src/test/java/org/springframework/cloud/gateway/webflux/ReactiveTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,10 @@ import java.util.List; import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - import org.junit.Test; import org.junit.runner.RunWith; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -53,9 +54,6 @@ import org.springframework.web.server.ServerWebExchange; import static org.assertj.core.api.Assertions.assertThat; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @ContextConfiguration(classes = TestApplication.class) @@ -187,12 +185,13 @@ public class ReactiveTests { @JsonIgnoreProperties(ignoreUnknown = true) static class Foo { + private String name; - public Foo() { + Foo() { } - public Foo(String name) { + Foo(String name) { this.name = name; } @@ -203,16 +202,18 @@ public class ReactiveTests { public void setName(String name) { this.name = name; } + } @JsonIgnoreProperties(ignoreUnknown = true) static class Bar { + private String name; - public Bar() { + Bar() { } - public Bar(String name) { + Bar(String name) { this.name = name; } @@ -223,8 +224,9 @@ public class ReactiveTests { public void setName(String name) { this.name = name; } + } } -} \ No newline at end of file +} diff --git a/spring-cloud-gateway-webflux/src/test/resources/application.properties b/spring-cloud-gateway-webflux/src/test/resources/application.properties index d443a79a..39b304fd 100644 --- a/spring-cloud-gateway-webflux/src/test/resources/application.properties +++ b/spring-cloud-gateway-webflux/src/test/resources/application.properties @@ -1 +1 @@ -logging.level.org.springframework.web.reactive=DEBUG \ No newline at end of file +logging.level.org.springframework.web.reactive=DEBUG diff --git a/spring-cloud-starter-gateway/pom.xml b/spring-cloud-starter-gateway/pom.xml index 2f202879..4a1f6cb1 100644 --- a/spring-cloud-starter-gateway/pom.xml +++ b/spring-cloud-starter-gateway/pom.xml @@ -1,5 +1,6 @@ - 4.0.0 diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml new file mode 100644 index 00000000..9adff450 --- /dev/null +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file