diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index 241db0cb..03c06830 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -53,6 +53,9 @@ import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory; import org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint; import org.springframework.cloud.gateway.actuate.GatewayLegacyControllerEndpoint; +import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledFilter; +import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledGlobalFilter; +import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledPredicate; import org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter; import org.springframework.cloud.gateway.filter.ForwardPathFilter; import org.springframework.cloud.gateway.filter.ForwardRoutingFilter; @@ -118,7 +121,7 @@ import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFac import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory; -import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.WeightRoutePredicateFactory; @@ -294,27 +297,32 @@ public class GatewayAutoConfiguration { // GlobalFilter beans @Bean + @ConditionalOnEnabledGlobalFilter public AdaptCachedBodyGlobalFilter adaptCachedBodyGlobalFilter() { return new AdaptCachedBodyGlobalFilter(); } @Bean + @ConditionalOnEnabledGlobalFilter public RemoveCachedBodyFilter removeCachedBodyFilter() { return new RemoveCachedBodyFilter(); } @Bean + @ConditionalOnEnabledGlobalFilter public RouteToRequestUrlFilter routeToRequestUrlFilter() { return new RouteToRequestUrlFilter(); } @Bean + @ConditionalOnEnabledGlobalFilter public ForwardRoutingFilter forwardRoutingFilter( ObjectProvider dispatcherHandler) { return new ForwardRoutingFilter(dispatcherHandler); } @Bean + @ConditionalOnEnabledGlobalFilter public ForwardPathFilter forwardPathFilter() { return new ForwardPathFilter(); } @@ -326,6 +334,7 @@ public class GatewayAutoConfiguration { } @Bean + @ConditionalOnEnabledGlobalFilter public WebsocketRoutingFilter websocketRoutingFilter(WebSocketClient webSocketClient, WebSocketService webSocketService, ObjectProvider> headersFilters) { @@ -352,68 +361,81 @@ public class GatewayAutoConfiguration { // Predicate Factory beans @Bean + @ConditionalOnEnabledPredicate public AfterRoutePredicateFactory afterRoutePredicateFactory() { return new AfterRoutePredicateFactory(); } @Bean + @ConditionalOnEnabledPredicate public BeforeRoutePredicateFactory beforeRoutePredicateFactory() { return new BeforeRoutePredicateFactory(); } @Bean + @ConditionalOnEnabledPredicate public BetweenRoutePredicateFactory betweenRoutePredicateFactory() { return new BetweenRoutePredicateFactory(); } @Bean + @ConditionalOnEnabledPredicate public CookieRoutePredicateFactory cookieRoutePredicateFactory() { return new CookieRoutePredicateFactory(); } @Bean + @ConditionalOnEnabledPredicate public HeaderRoutePredicateFactory headerRoutePredicateFactory() { return new HeaderRoutePredicateFactory(); } @Bean + @ConditionalOnEnabledPredicate public HostRoutePredicateFactory hostRoutePredicateFactory() { return new HostRoutePredicateFactory(); } @Bean + @ConditionalOnEnabledPredicate public MethodRoutePredicateFactory methodRoutePredicateFactory() { return new MethodRoutePredicateFactory(); } @Bean + @ConditionalOnEnabledPredicate public PathRoutePredicateFactory pathRoutePredicateFactory() { return new PathRoutePredicateFactory(); } @Bean + @ConditionalOnEnabledPredicate public QueryRoutePredicateFactory queryRoutePredicateFactory() { return new QueryRoutePredicateFactory(); } @Bean - public ReadBodyPredicateFactory readBodyPredicateFactory( + @ConditionalOnEnabledPredicate + public ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory( ServerCodecConfigurer codecConfigurer) { - return new ReadBodyPredicateFactory(codecConfigurer.getReaders()); + return new ReadBodyRoutePredicateFactory(codecConfigurer.getReaders()); } @Bean + @ConditionalOnEnabledPredicate public RemoteAddrRoutePredicateFactory remoteAddrRoutePredicateFactory() { return new RemoteAddrRoutePredicateFactory(); } @Bean @DependsOn("weightCalculatorWebFilter") + @ConditionalOnEnabledPredicate public WeightRoutePredicateFactory weightRoutePredicateFactory() { return new WeightRoutePredicateFactory(); } @Bean + @ConditionalOnEnabledPredicate public CloudFoundryRouteServiceRoutePredicateFactory cloudFoundryRouteServiceRoutePredicateFactory() { return new CloudFoundryRouteServiceRoutePredicateFactory(); } @@ -421,37 +443,44 @@ public class GatewayAutoConfiguration { // GatewayFilter Factory beans @Bean + @ConditionalOnEnabledFilter public AddRequestHeaderGatewayFilterFactory addRequestHeaderGatewayFilterFactory() { return new AddRequestHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public MapRequestHeaderGatewayFilterFactory mapRequestHeaderGatewayFilterFactory() { return new MapRequestHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public AddRequestParameterGatewayFilterFactory addRequestParameterGatewayFilterFactory() { return new AddRequestParameterGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public AddResponseHeaderGatewayFilterFactory addResponseHeaderGatewayFilterFactory() { return new AddResponseHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory( ServerCodecConfigurer codecConfigurer) { return new ModifyRequestBodyGatewayFilterFactory(codecConfigurer.getReaders()); } @Bean + @ConditionalOnEnabledFilter public DedupeResponseHeaderGatewayFilterFactory dedupeResponseHeaderGatewayFilterFactory() { return new DedupeResponseHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory( ServerCodecConfigurer codecConfigurer, Set bodyDecoders, Set bodyEncoders) { @@ -460,31 +489,37 @@ public class GatewayAutoConfiguration { } @Bean + @ConditionalOnEnabledFilter public PrefixPathGatewayFilterFactory prefixPathGatewayFilterFactory() { return new PrefixPathGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public PreserveHostHeaderGatewayFilterFactory preserveHostHeaderGatewayFilterFactory() { return new PreserveHostHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public RedirectToGatewayFilterFactory redirectToGatewayFilterFactory() { return new RedirectToGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public RemoveRequestHeaderGatewayFilterFactory removeRequestHeaderGatewayFilterFactory() { return new RemoveRequestHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public RemoveRequestParameterGatewayFilterFactory removeRequestParameterGatewayFilterFactory() { return new RemoveRequestParameterGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public RemoveResponseHeaderGatewayFilterFactory removeResponseHeaderGatewayFilterFactory() { return new RemoveResponseHeaderGatewayFilterFactory(); } @@ -498,83 +533,99 @@ public class GatewayAutoConfiguration { @Bean @ConditionalOnBean({ RateLimiter.class, KeyResolver.class }) + @ConditionalOnEnabledFilter public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory( RateLimiter rateLimiter, KeyResolver resolver) { return new RequestRateLimiterGatewayFilterFactory(rateLimiter, resolver); } @Bean + @ConditionalOnEnabledFilter public RewritePathGatewayFilterFactory rewritePathGatewayFilterFactory() { return new RewritePathGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public RetryGatewayFilterFactory retryGatewayFilterFactory() { return new RetryGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public SetPathGatewayFilterFactory setPathGatewayFilterFactory() { return new SetPathGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public SecureHeadersGatewayFilterFactory secureHeadersGatewayFilterFactory( SecureHeadersProperties properties) { return new SecureHeadersGatewayFilterFactory(properties); } @Bean + @ConditionalOnEnabledFilter public SetRequestHeaderGatewayFilterFactory setRequestHeaderGatewayFilterFactory() { return new SetRequestHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public SetRequestHostHeaderGatewayFilterFactory setRequestHostHeaderGatewayFilterFactory() { return new SetRequestHostHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public SetResponseHeaderGatewayFilterFactory setResponseHeaderGatewayFilterFactory() { return new SetResponseHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public RewriteResponseHeaderGatewayFilterFactory rewriteResponseHeaderGatewayFilterFactory() { return new RewriteResponseHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public RewriteLocationResponseHeaderGatewayFilterFactory rewriteLocationResponseHeaderGatewayFilterFactory() { return new RewriteLocationResponseHeaderGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public SetStatusGatewayFilterFactory setStatusGatewayFilterFactory() { return new SetStatusGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public SaveSessionGatewayFilterFactory saveSessionGatewayFilterFactory() { return new SaveSessionGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public StripPrefixGatewayFilterFactory stripPrefixGatewayFilterFactory() { return new StripPrefixGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public RequestHeaderToRequestUriGatewayFilterFactory requestHeaderToRequestUriGatewayFilterFactory() { return new RequestHeaderToRequestUriGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public RequestSizeGatewayFilterFactory requestSizeGatewayFilterFactory() { return new RequestSizeGatewayFilterFactory(); } @Bean + @ConditionalOnEnabledFilter public RequestHeaderSizeGatewayFilterFactory requestHeaderSizeGatewayFilterFactory() { return new RequestHeaderSizeGatewayFilterFactory(); } @@ -726,6 +777,7 @@ public class GatewayAutoConfiguration { } @Bean + @ConditionalOnEnabledGlobalFilter public NettyRoutingFilter routingFilter(HttpClient httpClient, ObjectProvider> headersFilters, HttpClientProperties properties) { @@ -733,6 +785,7 @@ public class GatewayAutoConfiguration { } @Bean + @ConditionalOnEnabledGlobalFilter public NettyWriteResponseFilter nettyWriteResponseFilter( GatewayProperties properties) { return new NettyWriteResponseFilter(properties.getStreamingMediaTypes()); @@ -772,6 +825,7 @@ public class GatewayAutoConfiguration { protected static class HystrixConfiguration { @Bean + @ConditionalOnEnabledFilter public HystrixGatewayFilterFactory hystrixGatewayFilterFactory( ObjectProvider dispatcherHandler) { return new HystrixGatewayFilterFactory(dispatcherHandler); @@ -779,6 +833,7 @@ public class GatewayAutoConfiguration { @Bean @ConditionalOnMissingBean(FallbackHeadersGatewayFilterFactory.class) + @ConditionalOnEnabledFilter public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() { return new FallbackHeadersGatewayFilterFactory(); } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayHystrixCircuitBreakerAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayHystrixCircuitBreakerAutoConfiguration.java index 062f87a7..79b66945 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayHystrixCircuitBreakerAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayHystrixCircuitBreakerAutoConfiguration.java @@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory; +import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledFilter; import org.springframework.cloud.gateway.filter.factory.FallbackHeadersGatewayFilterFactory; import org.springframework.cloud.gateway.filter.factory.SpringCloudCircuitBreakerHystrixFilterFactory; import org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerAutoConfiguration; @@ -53,7 +54,8 @@ public class GatewayHystrixCircuitBreakerAutoConfiguration { } @Bean - @ConditionalOnMissingBean(FallbackHeadersGatewayFilterFactory.class) + @ConditionalOnMissingBean + @ConditionalOnEnabledFilter public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() { return new FallbackHeadersGatewayFilterFactory(); } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayLoadBalancerClientAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayLoadBalancerClientAutoConfiguration.java index 6f594a53..0a3d33aa 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayLoadBalancerClientAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayLoadBalancerClientAutoConfiguration.java @@ -22,6 +22,7 @@ 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.config.conditional.ConditionalOnEnabledGlobalFilter; import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter; import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; @@ -46,6 +47,7 @@ public class GatewayLoadBalancerClientAutoConfiguration { @ConditionalOnBean(LoadBalancerClient.class) @ConditionalOnMissingBean({ LoadBalancerClientFilter.class, ReactiveLoadBalancerClientFilter.class }) + @ConditionalOnEnabledGlobalFilter public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client, LoadBalancerProperties properties) { return new LoadBalancerClientFilter(client, properties); diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayMetricsAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayMetricsAutoConfiguration.java index b7e2365b..e3be59fb 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayMetricsAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayMetricsAutoConfiguration.java @@ -68,6 +68,7 @@ public class GatewayMetricsAutoConfiguration { @ConditionalOnBean(MeterRegistry.class) @ConditionalOnProperty(name = "spring.cloud.gateway.metrics.enabled", matchIfMissing = true) + // @ConditionalOnEnabledGlobalFilter public GatewayMetricsFilter gatewayMetricFilter(MeterRegistry meterRegistry, List tagsProviders) { return new GatewayMetricsFilter(meterRegistry, tagsProviders); diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayReactiveLoadBalancerClientAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayReactiveLoadBalancerClientAutoConfiguration.java index c0fef897..b20b3379 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayReactiveLoadBalancerClientAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayReactiveLoadBalancerClientAutoConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; +import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledGlobalFilter; import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter; import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; @@ -53,6 +54,7 @@ public class GatewayReactiveLoadBalancerClientAutoConfiguration { @ConditionalOnBean(LoadBalancerClientFactory.class) @ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class) @Conditional(OnNoRibbonDefaultCondition.class) + @ConditionalOnEnabledGlobalFilter public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter( LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) { return new ReactiveLoadBalancerClientFilter(clientFactory, properties); diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayResilience4JCircuitBreakerAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayResilience4JCircuitBreakerAutoConfiguration.java index da5985e0..e13cfc85 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayResilience4JCircuitBreakerAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayResilience4JCircuitBreakerAutoConfiguration.java @@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JAutoConfiguration; import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreakerFactory; import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory; +import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledFilter; import org.springframework.cloud.gateway.filter.factory.FallbackHeadersGatewayFilterFactory; import org.springframework.cloud.gateway.filter.factory.SpringCloudCircuitBreakerResilience4JFilterFactory; import org.springframework.context.annotation.Bean; @@ -44,6 +45,7 @@ public class GatewayResilience4JCircuitBreakerAutoConfiguration { @Bean @ConditionalOnBean(ReactiveResilience4JCircuitBreakerFactory.class) + @ConditionalOnEnabledFilter public SpringCloudCircuitBreakerResilience4JFilterFactory springCloudCircuitBreakerResilience4JFilterFactory( ReactiveResilience4JCircuitBreakerFactory reactiveCircuitBreakerFactory, ObjectProvider dispatcherHandler) { @@ -52,7 +54,8 @@ public class GatewayResilience4JCircuitBreakerAutoConfiguration { } @Bean - @ConditionalOnMissingBean(FallbackHeadersGatewayFilterFactory.class) + @ConditionalOnMissingBean + @ConditionalOnEnabledFilter public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() { return new FallbackHeadersGatewayFilterFactory(); } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/ConditionalOnEnabledFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/ConditionalOnEnabledFilter.java new file mode 100644 index 00000000..a84bd9e9 --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/ConditionalOnEnabledFilter.java @@ -0,0 +1,40 @@ +/* + * 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 + * + * https://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.conditional; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory; +import org.springframework.context.annotation.Conditional; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@Conditional(OnEnabledFilter.class) +public @interface ConditionalOnEnabledFilter { + + /** + * The class component to check for. + * @return the class that must be enabled + */ + Class> value() default OnEnabledFilter.DefaultValue.class; + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/ConditionalOnEnabledGlobalFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/ConditionalOnEnabledGlobalFilter.java new file mode 100644 index 00000000..cfbf71f8 --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/ConditionalOnEnabledGlobalFilter.java @@ -0,0 +1,40 @@ +/* + * 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 + * + * https://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.conditional; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.context.annotation.Conditional; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@Conditional(OnEnabledGlobalFilter.class) +public @interface ConditionalOnEnabledGlobalFilter { + + /** + * The class component to check for. + * @return the class that must be enabled + */ + Class value() default OnEnabledGlobalFilter.DefaultValue.class; + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/ConditionalOnEnabledPredicate.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/ConditionalOnEnabledPredicate.java new file mode 100644 index 00000000..9c6fdc47 --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/ConditionalOnEnabledPredicate.java @@ -0,0 +1,40 @@ +/* + * 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 + * + * https://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.conditional; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory; +import org.springframework.context.annotation.Conditional; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@Conditional(OnEnabledPredicate.class) +public @interface ConditionalOnEnabledPredicate { + + /** + * The class components to check for. + * @return the class that must be enabled + */ + Class> value() default OnEnabledPredicate.DefaultValue.class; + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledComponent.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledComponent.java new file mode 100644 index 00000000..c0f677eb --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledComponent.java @@ -0,0 +1,101 @@ +/* + * 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 + * + * https://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.conditional; + +import java.util.Map; + +import org.springframework.boot.autoconfigure.condition.ConditionMessage; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.ConfigurationCondition; +import org.springframework.core.env.PropertyResolver; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.core.type.MethodMetadata; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +import static org.springframework.boot.autoconfigure.condition.ConditionMessage.forCondition; + +public abstract class OnEnabledComponent extends SpringBootCondition + implements ConfigurationCondition { + + private static final String PREFIX = "spring.cloud.gateway."; + + private static final String SUFFIX = ".enabled"; + + @Override + public ConfigurationPhase getConfigurationPhase() { + return ConfigurationPhase.REGISTER_BEAN; + } + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + Class candidate = getEndpointType(annotationClass(), context, + metadata); + return determineOutcome(candidate, context.getEnvironment()); + } + + @SuppressWarnings("unchecked") + protected Class getEndpointType(Class annotationClass, + ConditionContext context, AnnotatedTypeMetadata metadata) { + Map attributes = metadata + .getAnnotationAttributes(annotationClass.getName()); + if (attributes != null && attributes.containsKey("value")) { + Class target = (Class) attributes.get("value"); + if (target != defaultValueClass()) { + return (Class) target; + } + } + Assert.state( + metadata instanceof MethodMetadata + && metadata.isAnnotated(Bean.class.getName()), + getClass().getSimpleName() + + " must be used on @Bean methods when the value is not specified"); + MethodMetadata methodMetadata = (MethodMetadata) metadata; + try { + return (Class) ClassUtils.forName( + methodMetadata.getReturnTypeName(), context.getClassLoader()); + } + catch (Throwable ex) { + throw new IllegalStateException("Failed to extract endpoint id for " + + methodMetadata.getDeclaringClassName() + "." + + methodMetadata.getMethodName(), ex); + } + } + + private ConditionOutcome determineOutcome(Class componentClass, + PropertyResolver resolver) { + String key = PREFIX + normalizeComponentName(componentClass) + SUFFIX; + ConditionMessage.Builder messageBuilder = forCondition( + annotationClass().getName(), componentClass.getName()); + if ("false".equalsIgnoreCase(resolver.getProperty(key))) { + return ConditionOutcome + .noMatch(messageBuilder.because("bean is not available")); + } + return ConditionOutcome.match(); + } + + protected abstract String normalizeComponentName(Class componentClass); + + protected abstract Class annotationClass(); + + protected abstract Class defaultValueClass(); + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledFilter.java new file mode 100644 index 00000000..4f56cd0c --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledFilter.java @@ -0,0 +1,59 @@ +/* + * 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 + * + * https://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.conditional; + +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.SpringCloudCircuitBreakerFilterFactory; +import org.springframework.cloud.gateway.support.NameUtils; + +public class OnEnabledFilter extends OnEnabledComponent> { + + @Override + protected String normalizeComponentName( + Class> filterClass) { + if (SpringCloudCircuitBreakerFilterFactory.class.isAssignableFrom(filterClass)) { + return "filter." + NameUtils.normalizeToCanonicalPropertyFormat( + SpringCloudCircuitBreakerFilterFactory.NAME); + } + else { + return "filter." + + NameUtils.normalizeFilterFactoryNameAsProperty(filterClass); + } + } + + @Override + protected Class annotationClass() { + return ConditionalOnEnabledFilter.class; + } + + @Override + protected Class> defaultValueClass() { + return DefaultValue.class; + } + + static class DefaultValue implements GatewayFilterFactory { + + @Override + public GatewayFilter apply(Object config) { + throw new UnsupportedOperationException( + "class DefaultValue is never meant to be intantiated"); + } + + } + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledGlobalFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledGlobalFilter.java new file mode 100644 index 00000000..09270477 --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledGlobalFilter.java @@ -0,0 +1,54 @@ +/* + * 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 + * + * https://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.conditional; + +import reactor.core.publisher.Mono; + +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.support.NameUtils; +import org.springframework.web.server.ServerWebExchange; + +public class OnEnabledGlobalFilter extends OnEnabledComponent { + + @Override + protected String normalizeComponentName(Class filterClass) { + return "global-filter." + + NameUtils.normalizeGlobalFilterNameAsProperty(filterClass); + } + + @Override + protected Class annotationClass() { + return ConditionalOnEnabledGlobalFilter.class; + } + + @Override + protected Class defaultValueClass() { + return DefaultValue.class; + } + + static class DefaultValue implements GlobalFilter { + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + throw new UnsupportedOperationException( + "class DefaultValue is never meant to be intantiated"); + } + + } + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledPredicate.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledPredicate.java new file mode 100644 index 00000000..3c359043 --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/conditional/OnEnabledPredicate.java @@ -0,0 +1,61 @@ +/* + * 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 + * + * https://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.conditional; + +import java.util.function.Consumer; +import java.util.function.Predicate; + +import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory; +import org.springframework.cloud.gateway.support.NameUtils; +import org.springframework.web.server.ServerWebExchange; + +public class OnEnabledPredicate extends OnEnabledComponent> { + + @Override + protected String normalizeComponentName( + Class> predicateClass) { + return "predicate." + + NameUtils.normalizeRoutePredicateNameAsProperty(predicateClass); + } + + @Override + protected Class annotationClass() { + return ConditionalOnEnabledPredicate.class; + } + + @Override + protected Class> defaultValueClass() { + return DefaultValue.class; + } + + static class DefaultValue implements RoutePredicateFactory { + + @Override + public Predicate apply(Consumer consumer) { + throw new UnsupportedOperationException( + "class DefaultValue is never meant to be intantiated"); + } + + @Override + public Predicate apply(Object config) { + throw new UnsupportedOperationException( + "class DefaultValue is never meant to be intantiated"); + } + + } + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactory.java index ce5aed46..87dc9080 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/PreserveHostHeaderGatewayFilterFactory.java @@ -28,7 +28,8 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.P /** * @author Spencer Gibb */ -public class PreserveHostHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class PreserveHostHeaderGatewayFilterFactory + extends AbstractGatewayFilterFactory { public GatewayFilter apply() { return apply(o -> { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactory.java index 1acb2e12..60bb88ed 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SaveSessionGatewayFilterFactory.java @@ -35,7 +35,8 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi * * @author Greg Turnquist */ -public class SaveSessionGatewayFilterFactory extends AbstractGatewayFilterFactory { +public class SaveSessionGatewayFilterFactory + extends AbstractGatewayFilterFactory { @Override public GatewayFilter apply(Object config) { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java index 76af2e44..843feb39 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java @@ -54,6 +54,9 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.r public abstract class SpringCloudCircuitBreakerFilterFactory extends AbstractGatewayFilterFactory { + /** CircuitBreaker component name. */ + public static final String NAME = "CircuitBreaker"; + private ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory; private ReactiveCircuitBreaker cb; @@ -148,7 +151,7 @@ public abstract class SpringCloudCircuitBreakerFilterFactory extends @Override public String name() { - return "CircuitBreaker"; + return NAME; } public static class Config implements HasRouteId { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactory.java index 9dc30987..bc0514c1 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactory.java @@ -17,145 +17,22 @@ package org.springframework.cloud.gateway.handler.predicate; import java.util.List; -import java.util.Map; -import java.util.function.Predicate; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Mono; - -import org.springframework.cloud.gateway.handler.AsyncPredicate; -import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; import org.springframework.http.codec.HttpMessageReader; -import org.springframework.web.reactive.function.server.HandlerStrategies; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.server.ServerWebExchange; /** * Predicate that reads the body and applies a user provided predicate to run on the body. * The body is cached in memory so that possible subsequent calls to the predicate do not * need to deserialize again. */ -public class ReadBodyPredicateFactory - extends AbstractRoutePredicateFactory { - - protected static final Log log = 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 final List> messageReaders; +@Deprecated +public class ReadBodyPredicateFactory extends ReadBodyRoutePredicateFactory { public ReadBodyPredicateFactory() { - super(Config.class); - this.messageReaders = HandlerStrategies.withDefaults().messageReaders(); } public ReadBodyPredicateFactory(List> messageReaders) { - super(Config.class); - this.messageReaders = messageReaders; - } - - @Override - @SuppressWarnings("unchecked") - public AsyncPredicate applyAsync(Config config) { - return new AsyncPredicate() { - @Override - public Publisher apply(ServerWebExchange exchange) { - Class inClass = config.getInClass(); - - 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 - if (cachedBody != null) { - try { - boolean test = config.predicate.test(cachedBody); - exchange.getAttributes().put(TEST_ATTRIBUTE, test); - return Mono.just(test); - } - catch (ClassCastException e) { - if (log.isDebugEnabled()) { - log.debug("Predicate test failed because class in predicate " - + "does not match the cached body object", e); - } - } - return Mono.just(false); - } - else { - return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, - (serverHttpRequest) -> ServerRequest - .create(exchange.mutate().request(serverHttpRequest) - .build(), messageReaders) - .bodyToMono(inClass) - .doOnNext(objectValue -> exchange.getAttributes().put( - CACHE_REQUEST_BODY_OBJECT_KEY, objectValue)) - .map(objectValue -> config.getPredicate() - .test(objectValue))); - } - } - - @Override - public String toString() { - return String.format("ReadBody: %s", config.getInClass()); - } - }; - } - - @Override - @SuppressWarnings("unchecked") - public Predicate apply(Config config) { - throw new UnsupportedOperationException( - "ReadBodyPredicateFactory is only async."); - } - - public static class Config { - - private Class inClass; - - private Predicate predicate; - - private Map hints; - - public Class getInClass() { - return inClass; - } - - public Config setInClass(Class inClass) { - this.inClass = inClass; - return this; - } - - public Predicate getPredicate() { - return predicate; - } - - public Config setPredicate(Predicate predicate) { - this.predicate = predicate; - return this; - } - - public Config setPredicate(Class inClass, Predicate predicate) { - setInClass(inClass); - this.predicate = predicate; - return this; - } - - public Map getHints() { - return hints; - } - - public Config setHints(Map hints) { - this.hints = hints; - return this; - } - + super(messageReaders); } } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyRoutePredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyRoutePredicateFactory.java new file mode 100644 index 00000000..90aa62f7 --- /dev/null +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyRoutePredicateFactory.java @@ -0,0 +1,162 @@ +/* + * Copyright 2013-2020 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 + * + * https://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.List; +import java.util.Map; +import java.util.function.Predicate; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; + +import org.springframework.cloud.gateway.handler.AsyncPredicate; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.http.codec.HttpMessageReader; +import org.springframework.web.reactive.function.server.HandlerStrategies; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.server.ServerWebExchange; + +/** + * Predicate that reads the body and applies a user provided predicate to run on the body. + * The body is cached in memory so that possible subsequent calls to the predicate do not + * need to deserialize again. + */ +public class ReadBodyRoutePredicateFactory + extends AbstractRoutePredicateFactory { + + protected static final Log log = LogFactory + .getLog(ReadBodyRoutePredicateFactory.class); + + private static final String TEST_ATTRIBUTE = "read_body_predicate_test_attribute"; + + private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject"; + + private final List> messageReaders; + + public ReadBodyRoutePredicateFactory() { + super(Config.class); + this.messageReaders = HandlerStrategies.withDefaults().messageReaders(); + } + + public ReadBodyRoutePredicateFactory(List> messageReaders) { + super(Config.class); + this.messageReaders = messageReaders; + } + + @Override + @SuppressWarnings("unchecked") + public AsyncPredicate applyAsync(Config config) { + return new AsyncPredicate() { + @Override + public Publisher apply(ServerWebExchange exchange) { + Class inClass = config.getInClass(); + + 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 + if (cachedBody != null) { + try { + boolean test = config.predicate.test(cachedBody); + exchange.getAttributes().put(TEST_ATTRIBUTE, test); + return Mono.just(test); + } + catch (ClassCastException e) { + if (log.isDebugEnabled()) { + log.debug("Predicate test failed because class in predicate " + + "does not match the cached body object", e); + } + } + return Mono.just(false); + } + else { + return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, + (serverHttpRequest) -> ServerRequest + .create(exchange.mutate().request(serverHttpRequest) + .build(), messageReaders) + .bodyToMono(inClass) + .doOnNext(objectValue -> exchange.getAttributes().put( + CACHE_REQUEST_BODY_OBJECT_KEY, objectValue)) + .map(objectValue -> config.getPredicate() + .test(objectValue))); + } + } + + @Override + public String toString() { + return String.format("ReadBody: %s", config.getInClass()); + } + }; + } + + @Override + @SuppressWarnings("unchecked") + public Predicate apply(Config config) { + throw new UnsupportedOperationException( + "ReadBodyPredicateFactory is only async."); + } + + public static class Config { + + private Class inClass; + + private Predicate predicate; + + private Map hints; + + public Class getInClass() { + return inClass; + } + + public Config setInClass(Class inClass) { + this.inClass = inClass; + return this; + } + + public Predicate getPredicate() { + return predicate; + } + + public Config setPredicate(Predicate predicate) { + this.predicate = predicate; + return this; + } + + public Config setPredicate(Class inClass, Predicate predicate) { + setInClass(inClass); + this.predicate = predicate; + return this; + } + + public Map getHints() { + return hints; + } + + public Config setHints(Map hints) { + this.hints = hints; + return this; + } + + } + +} diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java index 5645f070..d990f46c 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java @@ -75,7 +75,6 @@ import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter; import org.springframework.cloud.gateway.route.Route; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; -import org.springframework.util.StringUtils; import org.springframework.util.unit.DataSize; import org.springframework.web.server.ServerWebExchange; @@ -386,7 +385,8 @@ public class GatewayFilterSpec extends UriSpec { /** * A filter that will set the Host header to - * {@param hostName} on the outgoing request + * {@param hostName} on the outgoing + * request. * @param hostName the updated Host header value * @return a {@link GatewayFilterSpec} that can be used to apply additional filters */ diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java index 7826cea6..f142bb87 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/PredicateSpec.java @@ -33,7 +33,7 @@ import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFac import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory; -import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.WeightRoutePredicateFactory; import org.springframework.cloud.gateway.route.Route; @@ -72,7 +72,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate to check if a request was made after a specific {@link ZonedDateTime} + * A predicate to check if a request was made after a specific {@link ZonedDateTime}. * @param datetime requests would only be routed after this {@link ZonedDateTime} * @return a {@link BooleanSpec} to be used to add logical operators */ @@ -82,7 +82,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate to check if a request was made before a specific {@link ZonedDateTime} + * A predicate to check if a request was made before a specific {@link ZonedDateTime}. * @param datetime requests will only be routed before this {@link ZonedDateTime} * @return a {@link BooleanSpec} to be used to add logical operators */ @@ -92,7 +92,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate to check if a request was made between two {@link ZonedDateTime}s + * A predicate to check if a request was made between two {@link ZonedDateTime}s. * @param datetime1 the request must have been made after this {@link ZonedDateTime} * @param datetime2 the request must be made before this {@link ZonedDateTime} * @return a {@link BooleanSpec} to be used to add logical operators @@ -103,7 +103,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate that checks if a cookie matches a given regular expression + * 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 @@ -115,7 +115,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate that checks if a given header is present on the request + * A predicate that checks if a given header is present on the request. * @param header the header name to check * @return a {@link BooleanSpec} to be used to add logical operators */ @@ -126,7 +126,7 @@ public class PredicateSpec extends UriSpec { /** * A predicate that checks if a given headers has a value which matches a regular - * expression + * 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 @@ -137,7 +137,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate that checks if the {@code host} header matches a given pattern + * 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 * @return a {@link BooleanSpec} to be used to add logical operators @@ -148,7 +148,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate that checks if the HTTP method matches + * A predicate that checks if the HTTP method matches. * @param methods the name of the HTTP methods * @return a {@link BooleanSpec} to be used to add logical operators */ @@ -161,7 +161,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate that checks if the HTTP method matches + * A predicate that checks if the HTTP method matches. * @param methods the HTTP methods * @return a {@link BooleanSpec} to be used to add logical operators */ @@ -172,7 +172,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate that checks if the path of the request matches the given pattern + * 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 * @return a {@link BooleanSpec} to be used to add logical operators @@ -183,7 +183,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate that checks if the path of the request matches the given pattern + * 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 @@ -198,7 +198,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate that checks if the path of the request matches the given pattern + * 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 @@ -220,12 +220,12 @@ public class PredicateSpec extends UriSpec { * @return a {@link BooleanSpec} to be used to add logical operators */ public BooleanSpec readBody(Class inClass, Predicate predicate) { - return asyncPredicate(getBean(ReadBodyPredicateFactory.class) + return asyncPredicate(getBean(ReadBodyRoutePredicateFactory.class) .applyAsync(c -> c.setPredicate(inClass, predicate))); } /** - * A predicate that checks if a query parameter matches a regular expression + * A predicate that checks if a query parameter matches a regular expression. * @param param the query parameter name * @param regex the regular expression to evaluate the query parameter value against * @return a {@link BooleanSpec} to be used to add logical operators @@ -236,7 +236,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate that checks if a given query parameter is present in the request URL + * A predicate that checks if a given query parameter is present in the request URL. * @param param the query parameter name * @return a {@link BooleanSpec} to be used to add logical operators */ @@ -286,7 +286,7 @@ public class PredicateSpec extends UriSpec { } /** - * 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. * @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 @@ -303,7 +303,7 @@ public class PredicateSpec extends UriSpec { } /** - * A predicate which is always true + * A predicate which is always true. * @return a {@link BooleanSpec} to be used to add logical operators */ public BooleanSpec alwaysTrue() { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/RouteLocatorBuilder.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/RouteLocatorBuilder.java index f2e717c9..7c68f5fc 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/RouteLocatorBuilder.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/RouteLocatorBuilder.java @@ -28,7 +28,7 @@ import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.context.ConfigurableApplicationContext; /** - * Used to build a {@link RouteLocator} + * Used to build a {@link RouteLocator}. */ public class RouteLocatorBuilder { @@ -39,15 +39,15 @@ public class RouteLocatorBuilder { } /** - * Creates a new {@link Builder} - * @return a new {@link Builder} + * Creates a new {@link Builder}. + * @return a new {@link Builder}. */ public Builder routes() { return new Builder(context); } /** - * A class that can be used to construct routes and return a {@link RouteLocator} + * A class that can be used to construct routes and return a {@link RouteLocator}. */ public static class Builder { @@ -60,7 +60,7 @@ public class RouteLocatorBuilder { } /** - * Creates a new {@link Route} + * 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} @@ -73,7 +73,7 @@ public class RouteLocatorBuilder { } /** - * Creates a new {@link Route} + * Creates a new {@link Route}. * @param fn a function which takes in a {@link PredicateSpec} and returns a * {@link Route.AsyncBuilder} * @return a {@link Builder} @@ -85,7 +85,7 @@ public class RouteLocatorBuilder { } /** - * Builds and returns a {@link RouteLocator} + * Builds and returns a {@link RouteLocator}. * @return a {@link RouteLocator} */ public RouteLocator build() { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java index 5ef8b214..3363cfa5 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/NameUtils.java @@ -16,6 +16,10 @@ package org.springframework.cloud.gateway.support; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory; import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory; @@ -33,6 +37,8 @@ public final class NameUtils { */ public static final String GENERATED_NAME_PREFIX = "_genkey_"; + private static final Pattern NAME_PATTERN = Pattern.compile("([A-Z][a-z0-9]+)"); + public static String generateName(int i) { return GENERATED_NAME_PREFIX + i; } @@ -43,12 +49,48 @@ public final class NameUtils { .replace(RoutePredicateFactory.class.getSimpleName(), "")); } + public static String normalizeRoutePredicateNameAsProperty( + Class clazz) { + return normalizeToCanonicalPropertyFormat(normalizeRoutePredicateName(clazz)); + } + public static String normalizeFilterFactoryName( Class clazz) { return removeGarbage(clazz.getSimpleName() .replace(GatewayFilterFactory.class.getSimpleName(), "")); } + public static String normalizeGlobalFilterName(Class clazz) { + return removeGarbage( + clazz.getSimpleName().replace(GlobalFilter.class.getSimpleName(), "")) + .replace("Filter", ""); + } + + public static String normalizeFilterFactoryNameAsProperty( + Class clazz) { + return normalizeToCanonicalPropertyFormat(normalizeFilterFactoryName(clazz)); + } + + public static String normalizeGlobalFilterNameAsProperty( + Class filterClass) { + return normalizeToCanonicalPropertyFormat(normalizeGlobalFilterName(filterClass)); + } + + public static String normalizeToCanonicalPropertyFormat(String name) { + Matcher matcher = NAME_PATTERN.matcher(name); + StringBuffer stringBuffer = new StringBuffer(); + while (matcher.find()) { + if (stringBuffer.length() != 0) { + matcher.appendReplacement(stringBuffer, + "-" + matcher.group(1).toLowerCase()); + } + else { + matcher.appendReplacement(stringBuffer, matcher.group(1).toLowerCase()); + } + } + return stringBuffer.toString(); + } + private static String removeGarbage(String s) { int garbageIdx = s.indexOf("$Mockito"); if (garbageIdx > 0) { diff --git a/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 224f46f1..7a95ae3c 100644 --- a/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,5 +1,347 @@ { "properties": [ + { + "name": "spring.cloud.gateway.filter.add-request-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the add-request-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.map-request-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the map-request-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.add-request-parameter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the add-request-parameter filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.add-response-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the add-response-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.modify-request-body.enabled", + "type": "java.lang.Boolean", + "description": "Enables the modify-request-body filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.dedupe-response-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the dedupe-response-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.modify-response-body.enabled", + "type": "java.lang.Boolean", + "description": "Enables the modify-response-body filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.prefix-path.enabled", + "type": "java.lang.Boolean", + "description": "Enables the prefix-path filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.preserve-host-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the preserve-host-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.redirect-to.enabled", + "type": "java.lang.Boolean", + "description": "Enables the redirect-to filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.remove-request-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the remove-request-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.remove-request-parameter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the remove-request-parameter filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.remove-response-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the remove-response-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.request-rate-limiter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the request-rate-limiter filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.rewrite-path.enabled", + "type": "java.lang.Boolean", + "description": "Enables the rewrite-path filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.retry.enabled", + "type": "java.lang.Boolean", + "description": "Enables the retry filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.set-path.enabled", + "type": "java.lang.Boolean", + "description": "Enables the set-path filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.secure-headers.enabled", + "type": "java.lang.Boolean", + "description": "Enables the secure-headers filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.set-request-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the set-request-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.set-request-host-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the set-request-host-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.set-response-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the set-response-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.rewrite-response-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the rewrite-response-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.rewrite-location-response-header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the rewrite-location-response-header filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.rewrite-location.enabled", + "type": "java.lang.Boolean", + "description": "Enables the rewrite-location filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.set-status.enabled", + "type": "java.lang.Boolean", + "description": "Enables the set-status filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.save-session.enabled", + "type": "java.lang.Boolean", + "description": "Enables the save-session filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.strip-prefix.enabled", + "type": "java.lang.Boolean", + "description": "Enables the strip-prefix filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.request-header-to-request-uri.enabled", + "type": "java.lang.Boolean", + "description": "Enables the request-header-to-request-uri filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.request-size.enabled", + "type": "java.lang.Boolean", + "description": "Enables the request-size filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.request-header-size.enabled", + "type": "java.lang.Boolean", + "description": "Enables the request-header-size filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.circuit-breaker.enabled", + "type": "java.lang.Boolean", + "description": "Enables the circuit-breaker filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.hystrix.enabled", + "type": "java.lang.Boolean", + "description": "Enables the hystrix filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.filter.fallback-headers.enabled", + "type": "java.lang.Boolean", + "description": "Enables the fallback-headers filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.adapt-cached-body.enabled", + "type": "java.lang.Boolean", + "description": "Enables the adapt-cached-body global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.remove-cached-body.enabled", + "type": "java.lang.Boolean", + "description": "Enables the remove-cached-body global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.route-to-request-url.enabled", + "type": "java.lang.Boolean", + "description": "Enables the route-to-request-url global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.forward-routing.enabled", + "type": "java.lang.Boolean", + "description": "Enables the forward-routing global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.forward-path.enabled", + "type": "java.lang.Boolean", + "description": "Enables the forward-path global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.websocket-routing.enabled", + "type": "java.lang.Boolean", + "description": "Enables the websocket-routing global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.netty-write-response.enabled", + "type": "java.lang.Boolean", + "description": "Enables the netty-write-response global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.netty-routing.enabled", + "type": "java.lang.Boolean", + "description": "Enables the netty-routing global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.reactive-load-balancer.enabled", + "type": "java.lang.Boolean", + "description": "Enables the reactive-loadbalancer global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.load-balancer-client.enabled", + "type": "java.lang.Boolean", + "description": "Enables the load-balancer-client global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.global-filter.metrics.enabled", + "type": "java.lang.Boolean", + "description": "Enables the metrics global filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.after.enabled", + "type": "java.lang.Boolean", + "description": "Enables the after predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.before.enabled", + "type": "java.lang.Boolean", + "description": "Enables the before predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.between.enabled", + "type": "java.lang.Boolean", + "description": "Enables the between predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.cookie.enabled", + "type": "java.lang.Boolean", + "description": "Enables the cookie predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.header.enabled", + "type": "java.lang.Boolean", + "description": "Enables the header predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.host.enabled", + "type": "java.lang.Boolean", + "description": "Enables the host predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.method.enabled", + "type": "java.lang.Boolean", + "description": "Enables the method predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.path.enabled", + "type": "java.lang.Boolean", + "description": "Enables the path predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.query.enabled", + "type": "java.lang.Boolean", + "description": "Enables the query predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.read-body.enabled", + "type": "java.lang.Boolean", + "description": "Enables the read-body predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.remote-addr.enabled", + "type": "java.lang.Boolean", + "description": "Enables the remote-addr predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.weight.enabled", + "type": "java.lang.Boolean", + "description": "Enables the weight predicate.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.predicate.cloud-foundry-route-service.enabled", + "type": "java.lang.Boolean", + "description": "Enables the cloud-foundry-route-service predicate.", + "defaultValue": "true" + }, { "name": "spring.cloud.gateway.enabled", "type": "java.lang.Boolean", diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInFiltersTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInFiltersTests.java new file mode 100644 index 00000000..212a15b4 --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInFiltersTests.java @@ -0,0 +1,128 @@ +/* + * 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 + * + * https://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.conditional; + +import java.util.List; + +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; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.MapRequestHeaderGatewayFilterFactory; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(Enclosed.class) +public class DisableBuiltInFiltersTests { + + @EnableAutoConfiguration + @SpringBootConfiguration + protected static class Config { + + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Config.class) + public static class FilterDefault { + + @Autowired + private List> gatewayFilters; + + @Test + public void shouldInjectBuiltInFilters() { + assertThat(gatewayFilters).hasSizeGreaterThanOrEqualTo(31); + } + + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Config.class, + properties = { "spring.cloud.gateway.filter.add-request-header.enabled=false", + "spring.cloud.gateway.filter.map-request-header.enabled=false" }) + @ActiveProfiles("disable-components") + public static class DisableSpecificsFiltersByProperty { + + @Autowired + private List> gatewayFilters; + + @Test + public void shouldInjectOnlyEnabledBuiltInFilters() { + assertThat(gatewayFilters).hasSizeGreaterThan(0); + assertThat(gatewayFilters).allSatisfy(filter -> assertThat(filter) + .isNotInstanceOfAny(AddRequestHeaderGatewayFilterFactory.class, + MapRequestHeaderGatewayFilterFactory.class)); + } + + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Config.class, properties = { + "spring.cloud.gateway.filter.add-request-header.enabled=false", + "spring.cloud.gateway.filter.map-request-header.enabled=false", + "spring.cloud.gateway.filter.add-request-parameter.enabled=false", + "spring.cloud.gateway.filter.add-response-header.enabled=false", + "spring.cloud.gateway.filter.modify-request-body.enabled=false", + "spring.cloud.gateway.filter.dedupe-response-header.enabled=false", + "spring.cloud.gateway.filter.modify-response-body.enabled=false", + "spring.cloud.gateway.filter.prefix-path.enabled=false", + "spring.cloud.gateway.filter.preserve-host-header.enabled=false", + "spring.cloud.gateway.filter.redirect-to.enabled=false", + "spring.cloud.gateway.filter.remove-request-header.enabled=false", + "spring.cloud.gateway.filter.remove-request-parameter.enabled=false", + "spring.cloud.gateway.filter.remove-response-header.enabled=false", + "spring.cloud.gateway.filter.request-rate-limiter.enabled=false", + "spring.cloud.gateway.filter.rewrite-path.enabled=false", + "spring.cloud.gateway.filter.retry.enabled=false", + "spring.cloud.gateway.filter.set-path.enabled=false", + "spring.cloud.gateway.filter.secure-headers.enabled=false", + "spring.cloud.gateway.filter.set-request-header.enabled=false", + "spring.cloud.gateway.filter.set-request-host-header.enabled=false", + "spring.cloud.gateway.filter.set-response-header.enabled=false", + "spring.cloud.gateway.filter.rewrite-response-header.enabled=false", + "spring.cloud.gateway.filter.rewrite-location-response-header.enabled=false", + "spring.cloud.gateway.filter.rewrite-location.enabled=false", + "spring.cloud.gateway.filter.set-status.enabled=false", + "spring.cloud.gateway.filter.save-session.enabled=false", + "spring.cloud.gateway.filter.strip-prefix.enabled=false", + "spring.cloud.gateway.filter.request-header-to-request-uri.enabled=false", + "spring.cloud.gateway.filter.request-size.enabled=false", + "spring.cloud.gateway.filter.request-header-size.enabled=false", + "spring.cloud.gateway.filter.circuit-breaker.enabled=false", + "spring.cloud.gateway.filter.hystrix.enabled=false", + "spring.cloud.gateway.filter.fallback-headers.enabled=false" }) + @ActiveProfiles("disable-components") + public static class DisableAllFiltersByProperty { + + @Autowired(required = false) + private List> gatewayFilters; + + @Test + public void shouldDisableAllBuiltInFilters() { + assertThat(gatewayFilters).isNull(); + } + + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInGlobalFiltersTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInGlobalFiltersTests.java new file mode 100644 index 00000000..5e0645df --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInGlobalFiltersTests.java @@ -0,0 +1,107 @@ +/* + * 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 + * + * https://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.conditional; + +import java.util.List; + +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; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter; +import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(Enclosed.class) +public class DisableBuiltInGlobalFiltersTests { + + @EnableAutoConfiguration + @SpringBootConfiguration + protected static class Config { + + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Config.class) + public static class GlobalFilterDefault { + + @Autowired + private List globalFilters; + + @Test + public void shouldInjectBuiltInFilters() { + assertThat(globalFilters).hasSizeGreaterThanOrEqualTo(10); + } + + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Config.class, properties = { + "spring.cloud.gateway.global-filter.remove-cached-body.enabled=false", + "spring.cloud.gateway.global-filter.route-to-request-url.enabled=false" }) + @ActiveProfiles("disable-components") + public static class DisableSpecificsFiltersByProperty { + + @Autowired + private List globalFilters; + + @Test + public void shouldInjectOnlyEnabledBuiltInFilters() { + assertThat(globalFilters).hasSizeGreaterThan(0); + assertThat(globalFilters) + .allSatisfy(filter -> assertThat(filter).isNotInstanceOfAny( + RemoveCachedBodyFilter.class, RouteToRequestUrlFilter.class)); + } + + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Config.class, properties = { + "spring.cloud.gateway.global-filter.adapt-cached-body.enabled=false", + "spring.cloud.gateway.global-filter.remove-cached-body.enabled=false", + "spring.cloud.gateway.global-filter.route-to-request-url.enabled=false", + "spring.cloud.gateway.global-filter.forward-routing.enabled=false", + "spring.cloud.gateway.global-filter.forward-path.enabled=false", + "spring.cloud.gateway.global-filter.websocket-routing.enabled=false", + "spring.cloud.gateway.global-filter.netty-write-response.enabled=false", + "spring.cloud.gateway.global-filter.netty-routing.enabled=false", + "spring.cloud.gateway.global-filter.reactive-load-balancer.enabled=false", + "spring.cloud.gateway.global-filter.load-balancer-client.enabled=false", + // FIXME: + "spring.cloud.gateway.metrics.enabled=false" }) + @ActiveProfiles("disable-components") + public static class DisableAllGlobalFiltersByProperty { + + @Autowired(required = false) + private List globalFilters; + + @Test + public void shouldDisableAllBuiltInFilters() { + assertThat(globalFilters).isNull(); + } + + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInPredicatesTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInPredicatesTests.java new file mode 100644 index 00000000..5a8cc7ac --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInPredicatesTests.java @@ -0,0 +1,108 @@ +/* + * 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 + * + * https://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.conditional; + +import java.util.List; + +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; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(Enclosed.class) +public class DisableBuiltInPredicatesTests { + + @EnableAutoConfiguration + @SpringBootConfiguration + protected static class Config { + + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Config.class) + public static class RoutePredicateDefault { + + @Autowired + private List> predicates; + + @Test + public void shouldInjectBuiltInPredicates() { + assertThat(predicates).hasSizeGreaterThanOrEqualTo(13); + } + + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Config.class, + properties = { "spring.cloud.gateway.predicate.after.enabled=false", + "spring.cloud.gateway.predicate.before.enabled=false" }) + @ActiveProfiles("disable-components") + public static class DisableSpecificsPredicatesByProperty { + + @Autowired + private List> predicates; + + @Test + public void shouldInjectOnlyEnabledBuiltInPredicates() { + assertThat(predicates).hasSizeGreaterThan(0); + assertThat(predicates).allSatisfy(filter -> assertThat(filter) + .isNotInstanceOfAny(AfterRoutePredicateFactory.class, + BeforeRoutePredicateFactory.class)); + } + + } + + @RunWith(SpringRunner.class) + @SpringBootTest(classes = Config.class, properties = { + "spring.cloud.gateway.predicate.after.enabled=false", + "spring.cloud.gateway.predicate.before.enabled=false", + "spring.cloud.gateway.predicate.between.enabled=false", + "spring.cloud.gateway.predicate.cookie.enabled=false", + "spring.cloud.gateway.predicate.header.enabled=false", + "spring.cloud.gateway.predicate.host.enabled=false", + "spring.cloud.gateway.predicate.method.enabled=false", + "spring.cloud.gateway.predicate.path.enabled=false", + "spring.cloud.gateway.predicate.query.enabled=false", + "spring.cloud.gateway.predicate.read-body.enabled=false", + "spring.cloud.gateway.predicate.remote-addr.enabled=false", + "spring.cloud.gateway.predicate.weight.enabled=false", + "spring.cloud.gateway.predicate.cloud-foundry-route-service.enabled=false" }) + @ActiveProfiles("disable-components") + public static class DisableAllPredicatesByProperty { + + @Autowired(required = false) + private List> predicates; + + @Test + public void shouldDisableAllBuiltInPredicates() { + assertThat(predicates).isNull(); + } + + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledComponentTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledComponentTests.java new file mode 100644 index 00000000..a11ce5ec --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledComponentTests.java @@ -0,0 +1,110 @@ +/* + * 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 + * + * https://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.conditional; + +import java.util.Collections; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class OnEnabledComponentTests { + + private OnEnabledComponent onEnabledComponent; + + private MockEnvironment environment; + + private ConditionContext conditionContext; + + @BeforeEach + void setUp() { + this.onEnabledComponent = createOnEnabledComponent("test-class"); + this.environment = new MockEnvironment(); + this.conditionContext = mock(ConditionContext.class); + } + + @Test + public void shouldMatchComponent() { + when(conditionContext.getEnvironment()).thenReturn(environment); + + ConditionOutcome outcome = onEnabledComponent.getMatchOutcome(conditionContext, + mockMetaData(EnabledComponent.class)); + + assertThat(outcome.isMatch()).isTrue(); + } + + @Test + public void shouldNotMatchDisabledComponent() { + String componentName = "disabled-component"; + this.onEnabledComponent = createOnEnabledComponent(componentName); + when(conditionContext.getEnvironment()).thenReturn(environment); + environment.setProperty("spring.cloud.gateway." + componentName + ".enabled", + "false"); + + ConditionOutcome outcome = onEnabledComponent.getMatchOutcome(conditionContext, + mockMetaData(DisabledComponent.class)); + + assertThat(outcome.isMatch()).isFalse(); + assertThat(outcome.getMessage()).contains("DisabledComponent") + .contains("bean is not available"); + } + + private AnnotatedTypeMetadata mockMetaData(Class value) { + AnnotatedTypeMetadata metadata = mock(AnnotatedTypeMetadata.class); + when(metadata + .getAnnotationAttributes(eq(ConditionalOnEnabledFilter.class.getName()))) + .thenReturn(Collections.singletonMap("value", value)); + return metadata; + } + + private OnEnabledComponent createOnEnabledComponent(String componentName) { + return new OnEnabledComponent() { + @Override + protected String normalizeComponentName(Class componentClass) { + return componentName; + } + + @Override + protected Class annotationClass() { + return ConditionalOnEnabledFilter.class; + } + + @Override + protected Class defaultValueClass() { + return Object.class; + } + }; + } + + protected static class EnabledComponent { + + } + + protected static class DisabledComponent { + + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledFilterTests.java new file mode 100644 index 00000000..ca0f42a2 --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledFilterTests.java @@ -0,0 +1,68 @@ +/* + * 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 + * + * https://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.conditional; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.FallbackHeadersGatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.MapRequestHeaderGatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.SpringCloudCircuitBreakerResilience4JFilterFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +class OnEnabledFilterTests { + + private OnEnabledFilter onEnabledFilter; + + @BeforeEach + void setUp() { + this.onEnabledFilter = new OnEnabledFilter(); + } + + @Test + void shouldNormalizeFiltersNames() { + List>> predicates = Arrays.asList( + AddRequestHeaderGatewayFilterFactory.class, + DedupeResponseHeaderGatewayFilterFactory.class, + FallbackHeadersGatewayFilterFactory.class, + HystrixGatewayFilterFactory.class, + MapRequestHeaderGatewayFilterFactory.class, + SpringCloudCircuitBreakerResilience4JFilterFactory.class); + + List resultNames = predicates.stream() + .map(onEnabledFilter::normalizeComponentName) + .collect(Collectors.toList()); + + List expectedNames = Stream + .of("add-request-header", "dedupe-response-header", "fallback-headers", + "hystrix", "map-request-header", "circuit-breaker") + .map(s -> "filter." + s).collect(Collectors.toList()); + + assertThat(resultNames).isEqualTo(expectedNames); + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledGlobalFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledGlobalFilterTests.java new file mode 100644 index 00000000..1f14aa49 --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledGlobalFilterTests.java @@ -0,0 +1,60 @@ +/* + * 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 + * + * https://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.conditional; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter; +import org.springframework.cloud.gateway.filter.ForwardPathFilter; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.filter.WebsocketRoutingFilter; + +import static org.assertj.core.api.Assertions.assertThat; + +class OnEnabledGlobalFilterTests { + + private OnEnabledGlobalFilter onEnabledGlobalFilter; + + @BeforeEach + void setUp() { + this.onEnabledGlobalFilter = new OnEnabledGlobalFilter(); + } + + @Test + void shouldNormalizeGlobalFiltersNames() { + List> predicates = Arrays.asList( + ForwardPathFilter.class, AdaptCachedBodyGlobalFilter.class, + WebsocketRoutingFilter.class); + + List resultNames = predicates.stream() + .map(onEnabledGlobalFilter::normalizeComponentName) + .collect(Collectors.toList()); + + List expectedNames = Stream + .of("forward-path", "adapt-cached-body", "websocket-routing") + .map(s -> "global-filter." + s).collect(Collectors.toList()); + + assertThat(resultNames).isEqualTo(expectedNames); + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledPredicateTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledPredicateTests.java new file mode 100644 index 00000000..0d1a63d7 --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/OnEnabledPredicateTests.java @@ -0,0 +1,63 @@ +/* + * 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 + * + * https://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.conditional; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.CloudFoundryRouteServiceRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +class OnEnabledPredicateTests { + + private OnEnabledPredicate onEnabledPredicate; + + @BeforeEach + void setUp() { + this.onEnabledPredicate = new OnEnabledPredicate(); + } + + @Test + void shouldNormalizePredicatesNames() { + List>> predicates = Arrays.asList( + AfterRoutePredicateFactory.class, + CloudFoundryRouteServiceRoutePredicateFactory.class, + ReadBodyRoutePredicateFactory.class, + RemoteAddrRoutePredicateFactory.class); + + List resultNames = predicates.stream() + .map(onEnabledPredicate::normalizeComponentName) + .collect(Collectors.toList()); + + List expectedNames = Stream + .of("after", "cloud-foundry-route-service", "read-body", "remote-addr") + .map(s -> "predicate." + s).collect(Collectors.toList()); + + assertThat(resultNames).isEqualTo(expectedNames); + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactoryTest.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyRoutePredicateFactoryTests.java similarity index 98% rename from spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactoryTest.java rename to spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyRoutePredicateFactoryTests.java index 20cb6fff..53124c66 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactoryTest.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyRoutePredicateFactoryTests.java @@ -29,7 +29,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.cloud.gateway.handler.AsyncPredicate; -import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory.Config; +import org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactory.Config; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.cloud.gateway.test.PermitAllSecurityConfiguration; @@ -58,7 +58,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT) @DirtiesContext -public class ReadBodyPredicateFactoryTest { +public class ReadBodyRoutePredicateFactoryTests { @Autowired private WebTestClient webClient; diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/NameUtilsTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/NameUtilsTests.java new file mode 100644 index 00000000..91a5a96f --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/NameUtilsTests.java @@ -0,0 +1,149 @@ +/* + * 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 + * + * https://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.support; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter; +import org.springframework.cloud.gateway.filter.ForwardPathFilter; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.filter.WebsocketRoutingFilter; +import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.FallbackHeadersGatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactory; +import org.springframework.cloud.gateway.filter.factory.MapRequestHeaderGatewayFilterFactory; +import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.CloudFoundryRouteServiceRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory; +import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +class NameUtilsTests { + + @Test + void shouldNormalizePredicatesNames() { + List>> predicates = Arrays.asList( + AfterRoutePredicateFactory.class, + CloudFoundryRouteServiceRoutePredicateFactory.class, + ReadBodyRoutePredicateFactory.class, + RemoteAddrRoutePredicateFactory.class); + + List resultNames = predicates.stream() + .map(NameUtils::normalizeRoutePredicateName).collect(Collectors.toList()); + + List expectedNames = Arrays.asList("After", "CloudFoundryRouteService", + "ReadBody", "RemoteAddr"); + + assertThat(resultNames).isEqualTo(expectedNames); + } + + @Test + void shouldNormalizePredicatesNamesAsProperties() { + List>> predicates = Arrays.asList( + AfterRoutePredicateFactory.class, + CloudFoundryRouteServiceRoutePredicateFactory.class, + ReadBodyRoutePredicateFactory.class, + RemoteAddrRoutePredicateFactory.class); + + List resultNames = predicates.stream() + .map(NameUtils::normalizeRoutePredicateNameAsProperty) + .collect(Collectors.toList()); + + List expectedNames = Arrays.asList("after", "cloud-foundry-route-service", + "read-body", "remote-addr"); + + assertThat(resultNames).isEqualTo(expectedNames); + } + + @Test + void shouldNormalizeFiltersNames() { + List>> predicates = Arrays.asList( + AddRequestHeaderGatewayFilterFactory.class, + DedupeResponseHeaderGatewayFilterFactory.class, + FallbackHeadersGatewayFilterFactory.class, + HystrixGatewayFilterFactory.class, + MapRequestHeaderGatewayFilterFactory.class); + + List resultNames = predicates.stream() + .map(NameUtils::normalizeFilterFactoryName).collect(Collectors.toList()); + + List expectedNames = Arrays.asList("AddRequestHeader", + "DedupeResponseHeader", "FallbackHeaders", "Hystrix", "MapRequestHeader"); + + assertThat(resultNames).isEqualTo(expectedNames); + } + + @Test + void shouldNormalizeFiltersNamesAsProperties() { + List>> predicates = Arrays.asList( + AddRequestHeaderGatewayFilterFactory.class, + DedupeResponseHeaderGatewayFilterFactory.class, + FallbackHeadersGatewayFilterFactory.class, + HystrixGatewayFilterFactory.class, + MapRequestHeaderGatewayFilterFactory.class); + + List resultNames = predicates.stream() + .map(NameUtils::normalizeFilterFactoryNameAsProperty) + .collect(Collectors.toList()); + + List expectedNames = Arrays.asList("add-request-header", + "dedupe-response-header", "fallback-headers", "hystrix", + "map-request-header"); + + assertThat(resultNames).isEqualTo(expectedNames); + } + + @Test + void shouldNormalizeGlobalFiltersNames() { + List> predicates = Arrays.asList( + ForwardPathFilter.class, AdaptCachedBodyGlobalFilter.class, + WebsocketRoutingFilter.class); + + List resultNames = predicates.stream() + .map(NameUtils::normalizeGlobalFilterName).collect(Collectors.toList()); + + List expectedNames = Arrays.asList("ForwardPath", "AdaptCachedBody", + "WebsocketRouting"); + + assertThat(resultNames).isEqualTo(expectedNames); + } + + @Test + void shouldNormalizeGlobalFiltersNamesAsProperties() { + List> predicates = Arrays.asList( + ForwardPathFilter.class, AdaptCachedBodyGlobalFilter.class, + WebsocketRoutingFilter.class); + + List resultNames = predicates.stream() + .map(NameUtils::normalizeGlobalFilterNameAsProperty) + .collect(Collectors.toList()); + + List expectedNames = Arrays.asList("forward-path", "adapt-cached-body", + "websocket-routing"); + + assertThat(resultNames).isEqualTo(expectedNames); + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java index 0ffeda31..f1f7b80d 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java @@ -51,7 +51,7 @@ import static org.junit.Assume.assumeThat; org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactoryTests.class, - org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactoryTest.class, + org.springframework.cloud.gateway.handler.predicate.ReadBodyRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactoryTests.class, org.springframework.cloud.gateway.handler.predicate.CloudFoundryRouteServiceRoutePredicateFactoryTest.class, org.springframework.cloud.gateway.handler.RoutePredicateHandlerMappingTests.class, diff --git a/spring-cloud-gateway-server/src/test/resources/application-disable-components.yml b/spring-cloud-gateway-server/src/test/resources/application-disable-components.yml new file mode 100644 index 00000000..ef57e2a4 --- /dev/null +++ b/spring-cloud-gateway-server/src/test/resources/application-disable-components.yml @@ -0,0 +1,26 @@ +server: + error: + include-message: always + +spring: + profiles: + include: logging + + cloud: + gateway: + globalcors: + cors-configurations: + '[/**]': + maxAge: 10 + allowedOrigins: "*" + allowedMethods: + - GET + default-filters: + #empty to prevent failures in tests disabling components + routes: + #empty to prevent failures in tests disabling components +management: + endpoints: + web: + exposure: + include: "*"