Removes maintenance mode bits of ribbon and hystrix
This commit is contained in:
20
pom.xml
20
pom.xml
@@ -82,21 +82,6 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
<version>${spring-cloud-netflix.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-transport</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxnetty</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-test-support</artifactId>
|
||||
@@ -108,11 +93,6 @@
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
<version>${spring-cloud-netflix.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
<version>${spring-cloud-netflix.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-circuitbreaker-dependencies</artifactId>
|
||||
|
||||
@@ -38,16 +38,6 @@
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-loadbalancer</artifactId>
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.gateway.config;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.hystrix.HystrixObservableCommand;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
@@ -29,7 +28,6 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.netty.resources.ConnectionProvider;
|
||||
import reactor.netty.tcp.ProxyProvider;
|
||||
import rx.RxReactiveStreams;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
@@ -66,9 +64,7 @@ import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayF
|
||||
import org.springframework.cloud.gateway.filter.factory.AddRequestParameterGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.AddResponseHeaderGatewayFilterFactory;
|
||||
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.PrefixPathGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.PreserveHostHeaderGatewayFilterFactory;
|
||||
@@ -160,7 +156,7 @@ import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool
|
||||
@EnableConfigurationProperties
|
||||
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class,
|
||||
WebFluxAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ GatewayLoadBalancerClientAutoConfiguration.class,
|
||||
@AutoConfigureAfter({ GatewayReactiveLoadBalancerClientAutoConfiguration.class,
|
||||
GatewayClassPathWarningAutoConfiguration.class })
|
||||
@ConditionalOnClass(DispatcherHandler.class)
|
||||
public class GatewayAutoConfiguration {
|
||||
@@ -712,24 +708,6 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ HystrixObservableCommand.class, RxReactiveStreams.class })
|
||||
protected static class HystrixConfiguration {
|
||||
|
||||
@Bean
|
||||
public HystrixGatewayFilterFactory hystrixGatewayFilterFactory(
|
||||
ObjectProvider<DispatcherHandler> dispatcherHandler) {
|
||||
return new HystrixGatewayFilterFactory(dispatcherHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(FallbackHeadersGatewayFilterFactory.class)
|
||||
public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() {
|
||||
return new FallbackHeadersGatewayFilterFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Health.class)
|
||||
protected static class GatewayActuatorConfiguration {
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
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.filter.factory.FallbackHeadersGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SpringCloudCircuitBreakerHystrixFilterFactory;
|
||||
import org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.hystrix.ReactiveHystrixCircuitBreakerAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.hystrix.ReactiveHystrixCircuitBreakerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
|
||||
@AutoConfigureAfter({ ReactiveHystrixCircuitBreakerAutoConfiguration.class })
|
||||
@ConditionalOnClass({ DispatcherHandler.class,
|
||||
HystrixCircuitBreakerAutoConfiguration.class, ReactiveCircuitBreakerFactory.class,
|
||||
ReactiveHystrixCircuitBreakerFactory.class })
|
||||
public class GatewayHystrixCircuitBreakerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ReactiveHystrixCircuitBreakerFactory.class)
|
||||
public SpringCloudCircuitBreakerHystrixFilterFactory springCloudCircuitBreakerHystrixFilterFactory(
|
||||
ReactiveHystrixCircuitBreakerFactory reactiveCircuitBreakerFactory,
|
||||
ObjectProvider<DispatcherHandler> dispatcherHandler) {
|
||||
return new SpringCloudCircuitBreakerHystrixFilterFactory(
|
||||
reactiveCircuitBreakerFactory, dispatcherHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(FallbackHeadersGatewayFilterFactory.class)
|
||||
public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() {
|
||||
return new FallbackHeadersGatewayFilterFactory();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
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.filter.LoadBalancerClientFilter;
|
||||
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
|
||||
/**
|
||||
* AutoConfiguration for {@link LoadBalancerClientFilter}.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ LoadBalancerClient.class, RibbonAutoConfiguration.class,
|
||||
DispatcherHandler.class })
|
||||
@AutoConfigureAfter(RibbonAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(LoadBalancerProperties.class)
|
||||
public class GatewayLoadBalancerClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(LoadBalancerClient.class)
|
||||
@ConditionalOnMissingBean({ LoadBalancerClientFilter.class,
|
||||
ReactiveLoadBalancerClientFilter.class })
|
||||
public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client,
|
||||
LoadBalancerProperties properties) {
|
||||
return new LoadBalancerClientFilter(client, properties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,17 +24,17 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
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.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
|
||||
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
|
||||
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER;
|
||||
import static org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR;
|
||||
|
||||
@@ -42,14 +42,14 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingClass("org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration")
|
||||
@ConditionalOnMissingBean(LoadBalancerClient.class)
|
||||
@ConditionalOnMissingClass("org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer")
|
||||
@ConditionalOnMissingBean(ReactiveLoadBalancer.class)
|
||||
@EnableConfigurationProperties(LoadBalancerProperties.class)
|
||||
@AutoConfigureAfter(GatewayLoadBalancerClientAutoConfiguration.class)
|
||||
@AutoConfigureAfter(GatewayReactiveLoadBalancerClientAutoConfiguration.class)
|
||||
public class GatewayNoLoadBalancerClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoadBalancerClientFilter.class)
|
||||
@ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class)
|
||||
public NoLoadBalancerClientFilter noLoadBalancerClientFilter(
|
||||
LoadBalancerProperties properties) {
|
||||
return new NoLoadBalancerClientFilter(properties.isUse404());
|
||||
|
||||
@@ -17,20 +17,15 @@
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
|
||||
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
|
||||
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
|
||||
@@ -43,7 +38,6 @@ import org.springframework.web.reactive.DispatcherHandler;
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ ReactiveLoadBalancer.class, LoadBalancerAutoConfiguration.class,
|
||||
DispatcherHandler.class })
|
||||
@AutoConfigureBefore(GatewayLoadBalancerClientAutoConfiguration.class)
|
||||
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(LoadBalancerProperties.class)
|
||||
public class GatewayReactiveLoadBalancerClientAutoConfiguration {
|
||||
@@ -51,29 +45,9 @@ public class GatewayReactiveLoadBalancerClientAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnBean(LoadBalancerClientFactory.class)
|
||||
@ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class)
|
||||
@Conditional(OnNoRibbonDefaultCondition.class)
|
||||
public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(
|
||||
LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
|
||||
return new ReactiveLoadBalancerClientFilter(clientFactory, properties);
|
||||
}
|
||||
|
||||
private static final class OnNoRibbonDefaultCondition extends AnyNestedCondition {
|
||||
|
||||
private OnNoRibbonDefaultCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
|
||||
havingValue = "false")
|
||||
static class RibbonNotEnabled {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnMissingClass("org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient")
|
||||
static class RibbonLoadBalancerNotPresent {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* 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.filter;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
|
||||
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
|
||||
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
|
||||
|
||||
/**
|
||||
* @deprecated in favour of {@link ReactiveLoadBalancerClientFilter}
|
||||
* @author Spencer Gibb
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
@Deprecated
|
||||
public class LoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
|
||||
/**
|
||||
* Filter order for {@link LoadBalancerClientFilter}.
|
||||
*/
|
||||
public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10100;
|
||||
|
||||
private static final Log log = LogFactory.getLog(LoadBalancerClientFilter.class);
|
||||
|
||||
protected final LoadBalancerClient loadBalancer;
|
||||
|
||||
private LoadBalancerProperties properties;
|
||||
|
||||
public LoadBalancerClientFilter(LoadBalancerClient loadBalancer,
|
||||
LoadBalancerProperties properties) {
|
||||
this.loadBalancer = loadBalancer;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return LOAD_BALANCER_CLIENT_FILTER_ORDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("Duplicates")
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
|
||||
if (url == null
|
||||
|| (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
// preserve the original url
|
||||
addOriginalRequestUrl(exchange, url);
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("LoadBalancerClientFilter url before: " + url);
|
||||
}
|
||||
|
||||
final ServiceInstance instance = choose(exchange);
|
||||
|
||||
if (instance == null) {
|
||||
throw NotFoundException.create(properties.isUse404(),
|
||||
"Unable to find instance for " + url.getHost());
|
||||
}
|
||||
|
||||
URI uri = exchange.getRequest().getURI();
|
||||
|
||||
// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
|
||||
// if the loadbalancer doesn't provide one.
|
||||
String overrideScheme = instance.isSecure() ? "https" : "http";
|
||||
if (schemePrefix != null) {
|
||||
overrideScheme = url.getScheme();
|
||||
}
|
||||
|
||||
URI requestUrl = loadBalancer.reconstructURI(
|
||||
new DelegatingServiceInstance(instance, overrideScheme), uri);
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
|
||||
}
|
||||
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
protected ServiceInstance choose(ServerWebExchange exchange) {
|
||||
return loadBalancer.choose(
|
||||
((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,7 +52,10 @@ public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(ReactiveLoadBalancerClientFilter.class);
|
||||
|
||||
private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;
|
||||
/**
|
||||
* Order of filter.
|
||||
*/
|
||||
public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;
|
||||
|
||||
private final LoadBalancerClientFactory clientFactory;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
@@ -23,10 +24,7 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static org.apache.commons.lang.exception.ExceptionUtils.getRootCause;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR;
|
||||
|
||||
/**
|
||||
* @author Olga Maciaszek-Sharma
|
||||
@@ -47,41 +45,50 @@ public class FallbackHeadersGatewayFilterFactory
|
||||
@Override
|
||||
public GatewayFilter apply(Config config) {
|
||||
return (exchange, chain) -> {
|
||||
ServerWebExchange filteredExchange = ofNullable(ofNullable(
|
||||
(Throwable) exchange.getAttribute(HYSTRIX_EXECUTION_EXCEPTION_ATTR))
|
||||
.orElseGet(() -> exchange.getAttribute(
|
||||
CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR)))
|
||||
.map(executionException -> {
|
||||
ServerHttpRequest.Builder requestBuilder = exchange
|
||||
.getRequest().mutate();
|
||||
requestBuilder.header(
|
||||
config.executionExceptionTypeHeaderName,
|
||||
executionException.getClass()
|
||||
.getName());
|
||||
requestBuilder.header(
|
||||
config.executionExceptionMessageHeaderName,
|
||||
executionException.getMessage());
|
||||
ofNullable(
|
||||
getRootCause(executionException))
|
||||
.ifPresent(rootCause -> {
|
||||
requestBuilder.header(
|
||||
config.rootCauseExceptionTypeHeaderName,
|
||||
rootCause
|
||||
.getClass()
|
||||
.getName());
|
||||
requestBuilder.header(
|
||||
config.rootCauseExceptionMessageHeaderName,
|
||||
rootCause
|
||||
.getMessage());
|
||||
});
|
||||
return exchange.mutate()
|
||||
.request(requestBuilder.build())
|
||||
.build();
|
||||
}).orElse(exchange);
|
||||
Throwable exception = exchange
|
||||
.getAttribute(CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR);
|
||||
ServerWebExchange filteredExchange;
|
||||
if (exception == null) {
|
||||
filteredExchange = exchange;
|
||||
}
|
||||
else {
|
||||
filteredExchange = addFallbackHeaders(config, exchange, exception);
|
||||
}
|
||||
return chain.filter(filteredExchange);
|
||||
};
|
||||
}
|
||||
|
||||
private ServerWebExchange addFallbackHeaders(Config config,
|
||||
ServerWebExchange exchange, Throwable executionException) {
|
||||
ServerHttpRequest.Builder requestBuilder = exchange.getRequest().mutate();
|
||||
requestBuilder.header(config.executionExceptionTypeHeaderName,
|
||||
executionException.getClass().getName());
|
||||
requestBuilder.header(config.executionExceptionMessageHeaderName,
|
||||
executionException.getMessage());
|
||||
Throwable rootCause = getRootCause(executionException);
|
||||
if (rootCause != null) {
|
||||
requestBuilder.header(config.rootCauseExceptionTypeHeaderName,
|
||||
rootCause.getClass().getName());
|
||||
requestBuilder.header(config.rootCauseExceptionMessageHeaderName,
|
||||
rootCause.getMessage());
|
||||
}
|
||||
return exchange.mutate().request(requestBuilder.build()).build();
|
||||
}
|
||||
|
||||
private static Throwable getRootCause(final Throwable throwable) {
|
||||
final List<Throwable> list = getThrowableList(throwable);
|
||||
return list.isEmpty() ? null : list.get(list.size() - 1);
|
||||
}
|
||||
|
||||
private static List<Throwable> getThrowableList(Throwable throwable) {
|
||||
final List<Throwable> list = new ArrayList<>();
|
||||
while (throwable != null && !list.contains(throwable)) {
|
||||
list.add(throwable);
|
||||
throwable = throwable.getCause();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
|
||||
private static final String EXECUTION_EXCEPTION_TYPE = "Execution-Exception-Type";
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
/*
|
||||
* 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.filter.factory;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
import com.netflix.hystrix.HystrixCommandKey;
|
||||
import com.netflix.hystrix.HystrixObservableCommand;
|
||||
import com.netflix.hystrix.HystrixObservableCommand.Setter;
|
||||
import com.netflix.hystrix.exception.HystrixRuntimeException;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.context.Context;
|
||||
import rx.Observable;
|
||||
import rx.RxReactiveStreams;
|
||||
import rx.Subscription;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.support.ServiceUnavailableException;
|
||||
import org.springframework.cloud.gateway.support.TimeoutException;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.removeAlreadyRouted;
|
||||
|
||||
/**
|
||||
* Depends on `spring-cloud-starter-netflix-hystrix`,
|
||||
* {@see https://cloud.spring.io/spring-cloud-netflix/}.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Michele Mancioppi
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
public class HystrixGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<HystrixGatewayFilterFactory.Config> {
|
||||
|
||||
private final ObjectProvider<DispatcherHandler> dispatcherHandlerProvider;
|
||||
|
||||
// do not use this dispatcherHandler directly, use getDispatcherHandler() instead.
|
||||
private volatile DispatcherHandler dispatcherHandler;
|
||||
|
||||
public HystrixGatewayFilterFactory(
|
||||
ObjectProvider<DispatcherHandler> dispatcherHandlerProvider) {
|
||||
super(Config.class);
|
||||
this.dispatcherHandlerProvider = dispatcherHandlerProvider;
|
||||
}
|
||||
|
||||
private DispatcherHandler getDispatcherHandler() {
|
||||
if (dispatcherHandler == null) {
|
||||
dispatcherHandler = dispatcherHandlerProvider.getIfAvailable();
|
||||
}
|
||||
|
||||
return dispatcherHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> shortcutFieldOrder() {
|
||||
return singletonList(NAME_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
// TODO: make Config implement HasRouteId and remove this method.
|
||||
public GatewayFilter apply(String routeId, Consumer<Config> consumer) {
|
||||
Config config = newConfig();
|
||||
consumer.accept(config);
|
||||
|
||||
if (StringUtils.isEmpty(config.getName()) && !StringUtils.isEmpty(routeId)) {
|
||||
config.setName(routeId);
|
||||
}
|
||||
|
||||
return apply(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Setter} based on incoming request attribute. <br>
|
||||
* This could be useful for example to create a Setter with {@link HystrixCommandKey}
|
||||
* being set as the target service's host:port, as obtained from
|
||||
* {@link ServerWebExchange#getRequest()} to do per service instance level circuit
|
||||
* breaking.
|
||||
*/
|
||||
protected Setter createCommandSetter(Config config, ServerWebExchange exchange) {
|
||||
return config.setter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Config config) {
|
||||
// TODO: if no name is supplied, generate one from command id (useful for default
|
||||
// filter)
|
||||
if (config.setter == null) {
|
||||
Assert.notNull(config.name,
|
||||
"A name must be supplied for the Hystrix Command Key");
|
||||
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory
|
||||
.asKey(getClass().getSimpleName());
|
||||
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(config.name);
|
||||
|
||||
config.setter = Setter.withGroupKey(groupKey).andCommandKey(commandKey);
|
||||
}
|
||||
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
return Mono.deferWithContext(context -> {
|
||||
RouteHystrixCommand command = new RouteHystrixCommand(
|
||||
createCommandSetter(config, exchange), config.fallbackUri,
|
||||
exchange, chain, context);
|
||||
|
||||
return Mono.create(s -> {
|
||||
Subscription sub = command.toObservable().subscribe(s::success,
|
||||
s::error, s::success);
|
||||
s.onCancel(sub::unsubscribe);
|
||||
}).onErrorResume((Function<Throwable, Mono<Void>>) throwable -> {
|
||||
if (throwable instanceof HystrixRuntimeException) {
|
||||
HystrixRuntimeException e = (HystrixRuntimeException) throwable;
|
||||
HystrixRuntimeException.FailureType failureType = e
|
||||
.getFailureType();
|
||||
|
||||
switch (failureType) {
|
||||
case TIMEOUT:
|
||||
return Mono.error(new TimeoutException());
|
||||
case SHORTCIRCUIT:
|
||||
return Mono.error(new ServiceUnavailableException());
|
||||
case COMMAND_EXCEPTION: {
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
/*
|
||||
* We forsake here the null check for cause as
|
||||
* HystrixRuntimeException will always have a cause if the
|
||||
* failure type is COMMAND_EXCEPTION.
|
||||
*/
|
||||
if (cause instanceof ResponseStatusException
|
||||
|| AnnotatedElementUtils.findMergedAnnotation(
|
||||
cause.getClass(),
|
||||
ResponseStatus.class) != null) {
|
||||
return Mono.error(cause);
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Mono.error(throwable);
|
||||
}).then();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(HystrixGatewayFilterFactory.this)
|
||||
.append("name", config.getName())
|
||||
.append("fallback", config.fallbackUri).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
|
||||
private String name;
|
||||
|
||||
private Setter setter;
|
||||
|
||||
private URI fallbackUri;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Config setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Config setFallbackUri(String fallbackUri) {
|
||||
if (fallbackUri != null) {
|
||||
setFallbackUri(URI.create(fallbackUri));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public URI getFallbackUri() {
|
||||
return fallbackUri;
|
||||
}
|
||||
|
||||
public void setFallbackUri(URI fallbackUri) {
|
||||
if (fallbackUri != null && !"forward".equals(fallbackUri.getScheme())) {
|
||||
throw new IllegalArgumentException(
|
||||
"Hystrix Filter currently only supports 'forward' URIs, found "
|
||||
+ fallbackUri);
|
||||
}
|
||||
this.fallbackUri = fallbackUri;
|
||||
}
|
||||
|
||||
public Config setSetter(Setter setter) {
|
||||
this.setter = setter;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: replace with HystrixMonoCommand that we write
|
||||
private class RouteHystrixCommand extends HystrixObservableCommand<Void> {
|
||||
|
||||
private final URI fallbackUri;
|
||||
|
||||
private final ServerWebExchange exchange;
|
||||
|
||||
private final GatewayFilterChain chain;
|
||||
|
||||
private final Context context;
|
||||
|
||||
RouteHystrixCommand(Setter setter, URI fallbackUri, ServerWebExchange exchange,
|
||||
GatewayFilterChain chain, Context context) {
|
||||
super(setter);
|
||||
this.fallbackUri = fallbackUri;
|
||||
this.exchange = exchange;
|
||||
this.chain = chain;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Observable<Void> construct() {
|
||||
return RxReactiveStreams
|
||||
.toObservable(this.chain.filter(exchange).subscriberContext(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Observable<Void> resumeWithFallback() {
|
||||
if (this.fallbackUri == null) {
|
||||
return super.resumeWithFallback();
|
||||
}
|
||||
|
||||
// TODO: copied from RouteToRequestUrlFilter
|
||||
URI uri = exchange.getRequest().getURI();
|
||||
// TODO: assume always?
|
||||
boolean encoded = containsEncodedParts(uri);
|
||||
URI requestUrl = UriComponentsBuilder.fromUri(uri).host(null).port(null)
|
||||
.uri(this.fallbackUri).scheme(null).build(encoded).toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
|
||||
addExceptionDetails();
|
||||
|
||||
ServerHttpRequest request = this.exchange.getRequest().mutate()
|
||||
.uri(requestUrl).build();
|
||||
ServerWebExchange mutated = exchange.mutate().request(request).build();
|
||||
// Before we continue on remove the already routed attribute since the
|
||||
// fallback may go back through the route handler if the fallback
|
||||
// is to another route in the Gateway
|
||||
removeAlreadyRouted(mutated);
|
||||
return RxReactiveStreams.toObservable(getDispatcherHandler().handle(mutated));
|
||||
}
|
||||
|
||||
private void addExceptionDetails() {
|
||||
Throwable executionException = getExecutionException();
|
||||
ofNullable(executionException).ifPresent(exception -> exchange.getAttributes()
|
||||
.put(HYSTRIX_EXECUTION_EXCEPTION_ATTR, exception));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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.filter.factory;
|
||||
|
||||
import com.netflix.hystrix.exception.HystrixRuntimeException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory;
|
||||
import org.springframework.cloud.gateway.support.ServiceUnavailableException;
|
||||
import org.springframework.cloud.gateway.support.TimeoutException;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class SpringCloudCircuitBreakerHystrixFilterFactory
|
||||
extends SpringCloudCircuitBreakerFilterFactory {
|
||||
|
||||
public SpringCloudCircuitBreakerHystrixFilterFactory(
|
||||
ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory,
|
||||
ObjectProvider<DispatcherHandler> dispatcherHandlerProvider) {
|
||||
super(reactiveCircuitBreakerFactory, dispatcherHandlerProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Void> handleErrorWithoutFallback(Throwable throwable) {
|
||||
if (throwable instanceof HystrixRuntimeException) {
|
||||
HystrixRuntimeException e = (HystrixRuntimeException) throwable;
|
||||
HystrixRuntimeException.FailureType failureType = e.getFailureType();
|
||||
|
||||
switch (failureType) {
|
||||
case TIMEOUT:
|
||||
return Mono.error(new TimeoutException());
|
||||
case SHORTCIRCUIT:
|
||||
return Mono.error(new ServiceUnavailableException());
|
||||
case COMMAND_EXCEPTION: {
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
/*
|
||||
* We forsake here the null check for cause as HystrixRuntimeException
|
||||
* will always have a cause if the failure type is COMMAND_EXCEPTION.
|
||||
*/
|
||||
if (cause instanceof ResponseStatusException
|
||||
|| AnnotatedElementUtils.findMergedAnnotation(cause.getClass(),
|
||||
ResponseStatus.class) != null) {
|
||||
return Mono.error(cause);
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Mono.error(throwable);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,7 +42,6 @@ import org.springframework.cloud.gateway.filter.factory.AddResponseHeaderGateway
|
||||
import org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory.Strategy;
|
||||
import org.springframework.cloud.gateway.filter.factory.FallbackHeadersGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.MapRequestHeaderGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.PrefixPathGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.PreserveHostHeaderGatewayFilterFactory;
|
||||
@@ -74,7 +73,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;
|
||||
|
||||
@@ -199,27 +197,6 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
c -> c.setStrategy(Strategy.valueOf(strategy)).setName(headerName)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the route in a Hystrix command. Depends on @{code
|
||||
* org.springframework.cloud::spring-cloud-starter-netflix-hystrix} being on the
|
||||
* classpath, {@see https://cloud.spring.io/spring-cloud-netflix/}
|
||||
* @param configConsumer a {@link Consumer} which provides configuration for the
|
||||
* Hystrix command
|
||||
* @return a {@link GatewayFilterSpec} that can be used to apply additional filters
|
||||
*/
|
||||
public GatewayFilterSpec hystrix(
|
||||
Consumer<HystrixGatewayFilterFactory.Config> configConsumer) {
|
||||
HystrixGatewayFilterFactory factory;
|
||||
try {
|
||||
factory = getBean(HystrixGatewayFilterFactory.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
throw new NoSuchBeanDefinitionException(HystrixGatewayFilterFactory.class,
|
||||
"This is probably because Hystrix is missing from the classpath, which can be resolved by adding dependency on 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'");
|
||||
}
|
||||
return filter(factory.apply(this.routeBuilder.getId(), configConsumer));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec circuitBreaker(
|
||||
Consumer<SpringCloudCircuitBreakerFilterFactory.Config> configConsumer) {
|
||||
SpringCloudCircuitBreakerFilterFactory filterFactory;
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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.Map;
|
||||
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
@Deprecated
|
||||
public abstract class ConfigurationUtils {
|
||||
|
||||
@Deprecated
|
||||
public static void bind(Object o, Map<String, Object> properties,
|
||||
String configurationPropertyName, String bindingName, Validator validator) {
|
||||
bind(o, properties, configurationPropertyName, bindingName, validator, null);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void bind(Object o, Map<String, Object> properties,
|
||||
String configurationPropertyName, String bindingName, Validator validator,
|
||||
ConversionService conversionService) {
|
||||
Object toBind = getTargetObject(o);
|
||||
Bindable<?> bindable = Bindable.ofInstance(toBind);
|
||||
ConfigurationService.bindOrCreate(bindable, properties,
|
||||
configurationPropertyName/* , bindingName */, validator,
|
||||
conversionService);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getTargetObject(Object candidate) {
|
||||
return ConfigurationService.getTargetObject(candidate);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
/*
|
||||
* 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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.reactive.function.BodyExtractor;
|
||||
import org.springframework.web.reactive.function.BodyExtractors;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ClientResponse}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Brian Clozel
|
||||
* @since 5.0
|
||||
* @deprecated Will be removed in future release.
|
||||
*/
|
||||
@Deprecated
|
||||
public class DefaultClientResponse implements ClientResponse {
|
||||
|
||||
private final ClientHttpResponse response;
|
||||
|
||||
private final Headers headers;
|
||||
|
||||
private final ExchangeStrategies strategies;
|
||||
|
||||
public DefaultClientResponse(ClientHttpResponse response,
|
||||
ExchangeStrategies strategies) {
|
||||
this.response = response;
|
||||
this.strategies = strategies;
|
||||
this.headers = new DefaultHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExchangeStrategies strategies() {
|
||||
return this.strategies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus statusCode() {
|
||||
return this.response.getStatusCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rawStatusCode() {
|
||||
return this.response.getRawStatusCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers headers() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, ResponseCookie> cookies() {
|
||||
return this.response.getCookies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor) {
|
||||
return extractor.extract(this.response, new BodyExtractor.Context() {
|
||||
@Override
|
||||
public List<HttpMessageReader<?>> messageReaders() {
|
||||
return strategies.messageReaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ServerHttpResponse> serverResponse() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> hints() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> bodyToMono(Class<? extends T> elementClass) {
|
||||
if (Void.class.isAssignableFrom(elementClass)) {
|
||||
return consumeAndCancel();
|
||||
}
|
||||
else {
|
||||
return body(BodyExtractors.toMono(elementClass));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> releaseBody() {
|
||||
return body(BodyExtractors.toDataBuffers()).map(DataBufferUtils::release).then();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Void>> toBodilessEntity() {
|
||||
return releaseBody().then(toEntityInternal(Mono.empty()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<WebClientResponseException> createException() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Mono<T> consumeAndCancel() {
|
||||
return (Mono<T>) this.response.getBody().map(buffer -> {
|
||||
DataBufferUtils.release(buffer);
|
||||
throw new ReadCancellationException();
|
||||
}).onErrorResume(ReadCancellationException.class, ex -> Mono.empty()).then();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> typeReference) {
|
||||
if (Void.class.isAssignableFrom(typeReference.getType().getClass())) {
|
||||
return consumeAndCancel();
|
||||
}
|
||||
else {
|
||||
return body(BodyExtractors.toMono(typeReference));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> bodyToFlux(Class<? extends T> elementClass) {
|
||||
if (Void.class.isAssignableFrom(elementClass)) {
|
||||
return Flux.from(consumeAndCancel());
|
||||
}
|
||||
else {
|
||||
return body(BodyExtractors.toFlux(elementClass));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> typeReference) {
|
||||
if (Void.class.isAssignableFrom(typeReference.getType().getClass())) {
|
||||
return Flux.from(consumeAndCancel());
|
||||
}
|
||||
else {
|
||||
return body(BodyExtractors.toFlux(typeReference));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType) {
|
||||
if (Void.class.isAssignableFrom(bodyType)) {
|
||||
return toEntityInternal(consumeAndCancel());
|
||||
}
|
||||
else {
|
||||
return toEntityInternal(bodyToMono(bodyType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<ResponseEntity<T>> toEntity(
|
||||
ParameterizedTypeReference<T> typeReference) {
|
||||
if (Void.class.isAssignableFrom(typeReference.getType().getClass())) {
|
||||
return toEntityInternal(consumeAndCancel());
|
||||
}
|
||||
else {
|
||||
return toEntityInternal(bodyToMono(typeReference));
|
||||
}
|
||||
}
|
||||
|
||||
private <T> Mono<ResponseEntity<T>> toEntityInternal(Mono<T> bodyMono) {
|
||||
HttpHeaders headers = headers().asHttpHeaders();
|
||||
HttpStatus statusCode = statusCode();
|
||||
return bodyMono.map(body -> new ResponseEntity<>(body, headers, statusCode))
|
||||
.switchIfEmpty(Mono.defer(
|
||||
() -> Mono.just(new ResponseEntity<>(headers, statusCode))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<ResponseEntity<List<T>>> toEntityList(Class<T> responseType) {
|
||||
return toEntityListInternal(bodyToFlux(responseType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<ResponseEntity<List<T>>> toEntityList(
|
||||
ParameterizedTypeReference<T> typeReference) {
|
||||
return toEntityListInternal(bodyToFlux(typeReference));
|
||||
}
|
||||
|
||||
private <T> Mono<ResponseEntity<List<T>>> toEntityListInternal(Flux<T> bodyFlux) {
|
||||
HttpHeaders headers = headers().asHttpHeaders();
|
||||
HttpStatus statusCode = statusCode();
|
||||
return bodyFlux.collectList()
|
||||
.map(body -> new ResponseEntity<>(body, headers, statusCode));
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class ReadCancellationException extends RuntimeException {
|
||||
|
||||
}
|
||||
|
||||
private class DefaultHeaders implements Headers {
|
||||
|
||||
private HttpHeaders delegate() {
|
||||
return response.getHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalLong contentLength() {
|
||||
return toOptionalLong(delegate().getContentLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MediaType> contentType() {
|
||||
return Optional.ofNullable(delegate().getContentType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> header(String headerName) {
|
||||
List<String> headerValues = delegate().get(headerName);
|
||||
return (headerValues != null ? headerValues : Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders asHttpHeaders() {
|
||||
return HttpHeaders.readOnlyHttpHeaders(delegate());
|
||||
}
|
||||
|
||||
private OptionalLong toOptionalLong(long value) {
|
||||
return (value != -1 ? OptionalLong.of(value) : OptionalLong.empty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
/*
|
||||
* 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.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRange;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.multipart.Part;
|
||||
import org.springframework.http.server.PathContainer;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.reactive.function.BodyExtractor;
|
||||
import org.springframework.web.reactive.function.BodyExtractors;
|
||||
import org.springframework.web.reactive.function.UnsupportedMediaTypeException;
|
||||
import org.springframework.web.reactive.function.server.HandlerStrategies;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
import org.springframework.web.server.WebSession;
|
||||
import org.springframework.web.util.UriBuilder;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* {@code ServerRequest} implementation based on a {@link ServerWebExchange}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
* @deprecated Will be removed in a future release.
|
||||
*/
|
||||
@Deprecated
|
||||
public class DefaultServerRequest implements ServerRequest {
|
||||
|
||||
private static final Function<UnsupportedMediaTypeException, UnsupportedMediaTypeStatusException> ERROR_MAPPER = ex -> (ex
|
||||
.getContentType() != null
|
||||
? new UnsupportedMediaTypeStatusException(ex.getContentType(),
|
||||
ex.getSupportedMediaTypes())
|
||||
: new UnsupportedMediaTypeStatusException(ex.getMessage()));
|
||||
|
||||
private final ServerWebExchange exchange;
|
||||
|
||||
private final Headers headers;
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
public DefaultServerRequest(ServerWebExchange exchange) {
|
||||
this(exchange, HandlerStrategies.withDefaults().messageReaders());
|
||||
}
|
||||
|
||||
public DefaultServerRequest(ServerWebExchange exchange,
|
||||
List<HttpMessageReader<?>> messageReaders) {
|
||||
this.exchange = exchange;
|
||||
this.messageReaders = Collections
|
||||
.unmodifiableList(new ArrayList<>(messageReaders));
|
||||
this.headers = new DefaultHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String methodName() {
|
||||
return request().getMethodValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI uri() {
|
||||
return request().getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriBuilder uriBuilder() {
|
||||
return UriComponentsBuilder.fromHttpRequest(new ServerRequestAdapter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathContainer pathContainer() {
|
||||
return request().getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers headers() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, HttpCookie> cookies() {
|
||||
return request().getCookies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor) {
|
||||
return body(extractor, Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InetSocketAddress> remoteAddress() {
|
||||
return Optional.of(request().getRemoteAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HttpMessageReader<?>> messageReaders() {
|
||||
return this.messageReaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor,
|
||||
Map<String, Object> hints) {
|
||||
return extractor.extract(request(), new BodyExtractor.Context() {
|
||||
@Override
|
||||
public List<HttpMessageReader<?>> messageReaders() {
|
||||
return messageReaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ServerHttpResponse> serverResponse() {
|
||||
return Optional.of(exchange().getResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> hints() {
|
||||
return hints;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> bodyToMono(Class<? extends T> elementClass) {
|
||||
Mono<T> mono = body(BodyExtractors.toMono(elementClass));
|
||||
return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> typeReference) {
|
||||
Mono<T> mono = body(BodyExtractors.toMono(typeReference));
|
||||
return mono.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> bodyToFlux(Class<? extends T> elementClass) {
|
||||
Flux<T> flux = body(BodyExtractors.toFlux(elementClass));
|
||||
return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> typeReference) {
|
||||
Flux<T> flux = body(BodyExtractors.toFlux(typeReference));
|
||||
return flux.onErrorMap(UnsupportedMediaTypeException.class, ERROR_MAPPER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> attributes() {
|
||||
return this.exchange.getAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, String> queryParams() {
|
||||
return request().getQueryParams();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> pathVariables() {
|
||||
return this.exchange.getAttributeOrDefault(
|
||||
RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE, Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<WebSession> session() {
|
||||
return this.exchange.getSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<? extends Principal> principal() {
|
||||
return this.exchange.getPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<MultiValueMap<String, String>> formData() {
|
||||
return this.exchange.getFormData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<MultiValueMap<String, Part>> multipartData() {
|
||||
return this.exchange.getMultipartData();
|
||||
}
|
||||
|
||||
private ServerHttpRequest request() {
|
||||
return this.exchange.getRequest();
|
||||
}
|
||||
|
||||
public ServerWebExchange exchange() {
|
||||
return this.exchange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s %s", method(), path());
|
||||
}
|
||||
|
||||
private class DefaultHeaders implements Headers {
|
||||
|
||||
private HttpHeaders delegate() {
|
||||
return request().getHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> accept() {
|
||||
return delegate().getAccept();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Charset> acceptCharset() {
|
||||
return delegate().getAcceptCharset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Locale.LanguageRange> acceptLanguage() {
|
||||
return delegate().getAcceptLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalLong contentLength() {
|
||||
long value = delegate().getContentLength();
|
||||
return (value != -1 ? OptionalLong.of(value) : OptionalLong.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MediaType> contentType() {
|
||||
return Optional.ofNullable(delegate().getContentType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress host() {
|
||||
return delegate().getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HttpRange> range() {
|
||||
return delegate().getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> header(String headerName) {
|
||||
List<String> headerValues = delegate().get(headerName);
|
||||
return (headerValues != null ? headerValues : Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders asHttpHeaders() {
|
||||
return HttpHeaders.readOnlyHttpHeaders(delegate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate().toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class ServerRequestAdapter implements HttpRequest {
|
||||
|
||||
@Override
|
||||
public String getMethodValue() {
|
||||
return methodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return uri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return request().getHeaders();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.reactive.function.BodyInserter;
|
||||
import org.springframework.web.reactive.function.server.HandlerStrategies;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@Deprecated
|
||||
public class DefaultServerResponse<T> implements ServerResponse {
|
||||
|
||||
private final ServerWebExchange exchange;
|
||||
|
||||
private final BodyInserter<T, ? super ServerHttpResponse> inserter;
|
||||
|
||||
private final Map<String, Object> hints;
|
||||
|
||||
public DefaultServerResponse(ServerWebExchange exchange,
|
||||
BodyInserter<T, ? super ServerHttpResponse> body, Map<String, Object> hints) {
|
||||
this.exchange = exchange;
|
||||
Assert.notNull(exchange, "ServerWebExchange must not be null");
|
||||
Assert.notNull(body, "BodyInserter must not be null");
|
||||
this.inserter = body;
|
||||
this.hints = hints;
|
||||
}
|
||||
|
||||
private ServerHttpResponse response() {
|
||||
return exchange.getResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final HttpStatus statusCode() {
|
||||
// TODO: non standard status code
|
||||
return HttpStatus.valueOf(response().getStatusCode().value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rawStatusCode() {
|
||||
return response().getStatusCode().value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final HttpHeaders headers() {
|
||||
return response().getHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, ResponseCookie> cookies() {
|
||||
return response().getCookies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Mono<Void> writeTo(ServerWebExchange exchange, Context context) {
|
||||
return this.inserter.insert(exchange.getResponse(), new BodyInserter.Context() {
|
||||
@Override
|
||||
public List<HttpMessageWriter<?>> messageWriters() {
|
||||
return context.messageWriters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ServerHttpRequest> serverRequest() {
|
||||
return Optional.of(exchange.getRequest());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> hints() {
|
||||
return hints;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class HandlerStrategiesResponseContext
|
||||
implements ServerResponse.Context {
|
||||
|
||||
private HandlerStrategies strategies = HandlerStrategies.withDefaults();
|
||||
|
||||
public HandlerStrategiesResponseContext() {
|
||||
}
|
||||
|
||||
public HandlerStrategiesResponseContext(HandlerStrategies strategies) {
|
||||
this.strategies = strategies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HttpMessageWriter<?>> messageWriters() {
|
||||
return this.strategies.messageWriters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViewResolver> viewResolvers() {
|
||||
return this.strategies.viewResolvers();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -122,12 +122,6 @@ public final class ServerWebExchangeUtils {
|
||||
*/
|
||||
public static final String ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR = "original_response_content_type";
|
||||
|
||||
/**
|
||||
* Hystrix execution exception attribute name.
|
||||
*/
|
||||
public static final String HYSTRIX_EXECUTION_EXCEPTION_ATTR = qualify(
|
||||
"hystrixExecutionException");
|
||||
|
||||
/**
|
||||
* CircuitBreaker execution exception attribute name.
|
||||
*/
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,\
|
||||
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
|
||||
org.springframework.cloud.gateway.config.GatewayHystrixCircuitBreakerAutoConfiguration,\
|
||||
org.springframework.cloud.gateway.config.GatewayResilience4JCircuitBreakerAutoConfiguration,\
|
||||
org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration,\
|
||||
org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration,\
|
||||
org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,\
|
||||
org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,\
|
||||
|
||||
@@ -36,8 +36,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
@RunWith(ModifiedClassPathRunner.class)
|
||||
@ClassPathExclusions({ "spring-cloud-netflix-ribbon-*.jar",
|
||||
"spring-cloud-loadbalancer-*.jar" })
|
||||
@ClassPathExclusions({ "spring-cloud-loadbalancer-*.jar" })
|
||||
public class GatewayNoLoadBalancerClientAutoConfigurationTests {
|
||||
|
||||
private static int port;
|
||||
@@ -65,7 +64,7 @@ public class GatewayNoLoadBalancerClientAutoConfigurationTests {
|
||||
public static class Config {
|
||||
|
||||
@Bean
|
||||
public RouteLocator hystrixRouteLocator(RouteLocatorBuilder builder) {
|
||||
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes()
|
||||
.route("lb_fail", r -> r.host("**.lbfail.org").uri("lb://fail"))
|
||||
.build();
|
||||
|
||||
@@ -1,418 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.filter;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
|
||||
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LoadBalancerClientFilterTests {
|
||||
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
private LoadBalancerProperties properties;
|
||||
|
||||
@Mock
|
||||
private GatewayFilterChain chain;
|
||||
|
||||
@Mock
|
||||
private LoadBalancerClient loadBalancerClient;
|
||||
|
||||
private LoadBalancerClientFilter loadBalancerClientFilter;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
properties = new LoadBalancerProperties();
|
||||
loadBalancerClientFilter = new LoadBalancerClientFilter(loadBalancerClient,
|
||||
properties);
|
||||
exchange = MockServerWebExchange
|
||||
.from(MockServerHttpRequest.get("loadbalancerclient.org").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotFilterWhenGatewayRequestUrlIsMissing() {
|
||||
loadBalancerClientFilter.filter(exchange, chain);
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
verifyNoMoreInteractions(chain);
|
||||
verifyNoInteractions(loadBalancerClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotFilterWhenGatewayRequestUrlSchemeIsNotLb() {
|
||||
URI uri = UriComponentsBuilder.fromUriString("http://myservice").build().toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri);
|
||||
|
||||
loadBalancerClientFilter.filter(exchange, chain);
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
verifyNoMoreInteractions(chain);
|
||||
verifyNoInteractions(loadBalancerClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowExceptionWhenNoServiceInstanceIsFound() {
|
||||
URI uri = UriComponentsBuilder.fromUriString("lb://myservice").build().toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri);
|
||||
|
||||
try {
|
||||
loadBalancerClientFilter.filter(exchange, chain);
|
||||
}
|
||||
catch (NotFoundException e) {
|
||||
assertThat(e.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrow4O4ExceptionWhenNoServiceInstanceIsFound() {
|
||||
URI uri = UriComponentsBuilder.fromUriString("lb://myservice").build().toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri);
|
||||
properties.setUse404(true);
|
||||
try {
|
||||
loadBalancerClientFilter.filter(exchange, chain);
|
||||
}
|
||||
catch (NotFoundException e) {
|
||||
assertThat(e.getStatus()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFilter() {
|
||||
URI url = UriComponentsBuilder.fromUriString("lb://myservice").build().toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, url);
|
||||
|
||||
ServiceInstance serviceInstance = new DefaultServiceInstance("myservice1",
|
||||
"myservice", "localhost", 8080, true);
|
||||
when(loadBalancerClient.choose("myservice")).thenReturn(serviceInstance);
|
||||
|
||||
URI requestUrl = UriComponentsBuilder.fromUriString("https://localhost:8080")
|
||||
.build().toUri();
|
||||
when(loadBalancerClient.reconstructURI(any(ServiceInstance.class),
|
||||
any(URI.class))).thenReturn(requestUrl);
|
||||
|
||||
loadBalancerClientFilter.filter(exchange, chain);
|
||||
|
||||
LinkedHashSet<URI> attribute = exchange
|
||||
.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
|
||||
assertThat(attribute).contains(url);
|
||||
|
||||
verify(loadBalancerClient).choose("myservice");
|
||||
|
||||
ArgumentCaptor<URI> urlArgumentCaptor = ArgumentCaptor.forClass(URI.class);
|
||||
verify(loadBalancerClient).reconstructURI(any(), urlArgumentCaptor.capture());
|
||||
|
||||
URI uri = urlArgumentCaptor.getValue();
|
||||
assertThat(uri).isNotNull();
|
||||
assertThat(uri.toString()).isEqualTo("loadbalancerclient.org");
|
||||
|
||||
verifyNoMoreInteractions(loadBalancerClient);
|
||||
|
||||
assertThat((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR))
|
||||
.isEqualTo(requestUrl);
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
verifyNoMoreInteractions(chain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void instanceOverrideNonSecureScheme() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("https://localhost")
|
||||
.build();
|
||||
|
||||
URI lbUri = URI.create("lb://service1");
|
||||
ServerWebExchange webExchange = testFilter(request, lbUri);
|
||||
URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
assertThat(uri).hasScheme("http").hasHost("service1-host1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void instanceOverrideSecureScheme() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost")
|
||||
.build();
|
||||
|
||||
URI lbUri = URI.create("lb://service1");
|
||||
ServerWebExchange webExchange = testFilter(request, lbUri, 443);
|
||||
URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
assertThat(uri).hasScheme("https").hasHost("service1-host1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void instanceOverrideSecureSchemePrefix() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("https://localhost")
|
||||
.build();
|
||||
|
||||
URI lbUri = URI.create("http://service1");
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, "lb");
|
||||
ServerWebExchange webExchange = testFilter(exchange, lbUri, 443);
|
||||
URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
assertThat(uri).hasScheme("http").hasHost("service1-host1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void instanceOverrideNonSecureSchemePrefix() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost")
|
||||
.build();
|
||||
|
||||
URI lbUri = URI.create("https://service1");
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, "lb");
|
||||
ServerWebExchange webExchange = testFilter(exchange, lbUri, 8081);
|
||||
URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
assertThat(uri).hasScheme("https").hasHost("service1-host1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void happyPath() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("http://localhost/get?a=b").build();
|
||||
|
||||
URI lbUri = URI.create("lb://service1?a=b");
|
||||
ServerWebExchange webExchange = testFilter(request, lbUri);
|
||||
URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
assertThat(uri).hasScheme("http").hasHost("service1-host1").hasParameter("a",
|
||||
"b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noQueryParams() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/get")
|
||||
.build();
|
||||
|
||||
ServerWebExchange webExchange = testFilter(request, URI.create("lb://service1"));
|
||||
URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
assertThat(uri).hasScheme("http").hasHost("service1-host1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodedParameters() {
|
||||
URI url = UriComponentsBuilder.fromUriString("http://localhost/get?a=b&c=d[]")
|
||||
.buildAndExpand().encode().toUri();
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, url)
|
||||
.build();
|
||||
|
||||
URI lbUrl = UriComponentsBuilder.fromUriString("lb://service1?a=b&c=d[]")
|
||||
.buildAndExpand().encode().toUri();
|
||||
|
||||
// prove that it is encoded
|
||||
assertThat(lbUrl.getRawQuery()).isEqualTo("a=b&c=d%5B%5D");
|
||||
|
||||
assertThat(lbUrl).hasParameter("c", "d[]");
|
||||
|
||||
ServerWebExchange webExchange = testFilter(request, lbUrl);
|
||||
URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
assertThat(uri).hasScheme("http").hasHost("service1-host1").hasParameter("a", "b")
|
||||
.hasParameter("c", "d[]");
|
||||
|
||||
// prove that it is not double encoded
|
||||
assertThat(uri.getRawQuery()).isEqualTo("a=b&c=d%5B%5D");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unencodedParameters() {
|
||||
URI url = URI.create("http://localhost/get?a=b&c=d[]");
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, url)
|
||||
.build();
|
||||
|
||||
URI lbUrl = URI.create("lb://service1?a=b&c=d[]");
|
||||
|
||||
// prove that it is unencoded
|
||||
assertThat(lbUrl.getRawQuery()).isEqualTo("a=b&c=d[]");
|
||||
|
||||
ServerWebExchange webExchange = testFilter(request, lbUrl);
|
||||
|
||||
URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
assertThat(uri).hasScheme("http").hasHost("service1-host1").hasParameter("a", "b")
|
||||
.hasParameter("c", "d[]");
|
||||
|
||||
// prove that it is NOT encoded
|
||||
assertThat(uri.getRawQuery()).isEqualTo("a=b&c=d[]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void happyPathWithAttributeRatherThanScheme() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.get("ws://localhost/get?a=b").build();
|
||||
|
||||
URI lbUri = URI.create("ws://service1?a=b");
|
||||
|
||||
exchange = MockServerWebExchange.from(request);
|
||||
exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, "lb");
|
||||
|
||||
ServerWebExchange webExchange = testFilter(exchange, lbUri, 8081);
|
||||
URI uri = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
assertThat(uri).hasScheme("ws").hasHost("service1-host1").hasParameter("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotFilterWhenGatewaySchemePrefixAttrIsNotLb() {
|
||||
URI uri = UriComponentsBuilder.fromUriString("http://myservice").build().toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri);
|
||||
exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, "xx");
|
||||
|
||||
loadBalancerClientFilter.filter(exchange, chain);
|
||||
|
||||
verify(chain).filter(exchange);
|
||||
verifyNoMoreInteractions(chain);
|
||||
verifyNoInteractions(loadBalancerClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSelectSpecifiedServer() {
|
||||
URI uri1 = UriComponentsBuilder.fromUriString("lb://myservice").port(11111)
|
||||
.build().toUri();
|
||||
URI uri2 = UriComponentsBuilder.fromUriString("lb://myservice").port(22222)
|
||||
.build().toUri();
|
||||
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
ILoadBalancer loadBalancer = mock(ILoadBalancer.class);
|
||||
when(clientFactory.getLoadBalancerContext("myservice"))
|
||||
.thenReturn(new RibbonLoadBalancerContext(loadBalancer));
|
||||
when(clientFactory.getLoadBalancer("myservice")).thenReturn(loadBalancer);
|
||||
|
||||
when(loadBalancer.chooseServer("11111"))
|
||||
.thenReturn(new Server("myservice-host1", 8081));
|
||||
when(loadBalancer.chooseServer("22222"))
|
||||
.thenReturn(new Server("myservice-host2", 8081));
|
||||
|
||||
LoadBalancerClient loadBalancerClient = new RibbonLoadBalancerClient(
|
||||
clientFactory) {
|
||||
private String loadBalancerKey;
|
||||
|
||||
public ServiceInstance choose(String serviceId) {
|
||||
String[] strings = serviceId.split("<<>>");
|
||||
loadBalancerKey = strings[1];
|
||||
return super.choose(strings[0], loadBalancerKey);
|
||||
}
|
||||
|
||||
protected Server getServer(ILoadBalancer loadBalancer) {
|
||||
return loadBalancer == null ? null
|
||||
: loadBalancer.chooseServer(StringUtils.isEmpty(loadBalancerKey)
|
||||
? "default" : loadBalancerKey);
|
||||
}
|
||||
};
|
||||
|
||||
LoadBalancerClientFilter loadBalancerClientFilter = new LoadBalancerClientFilter(
|
||||
loadBalancerClient, properties) {
|
||||
protected ServiceInstance choose(ServerWebExchange exchange) {
|
||||
URI attribute = (URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
return loadBalancer
|
||||
.choose(attribute.getHost() + "<<>>" + attribute.getPort());
|
||||
}
|
||||
};
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost/get")
|
||||
.build();
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri1);
|
||||
loadBalancerClientFilter.filter(exchange, chain);
|
||||
assertThat(
|
||||
((URI) exchange.getAttributes().get(GATEWAY_REQUEST_URL_ATTR)).getHost())
|
||||
.isEqualTo("myservice-host1");
|
||||
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri2);
|
||||
loadBalancerClientFilter.filter(exchange, chain);
|
||||
assertThat(
|
||||
((URI) exchange.getAttributes().get(GATEWAY_REQUEST_URL_ATTR)).getHost())
|
||||
.isEqualTo("myservice-host2");
|
||||
}
|
||||
|
||||
private ServerWebExchange testFilter(MockServerHttpRequest request, URI uri) {
|
||||
return testFilter(MockServerWebExchange.from(request), uri, 8081);
|
||||
}
|
||||
|
||||
private ServerWebExchange testFilter(MockServerHttpRequest request, URI uri,
|
||||
int port) {
|
||||
return testFilter(MockServerWebExchange.from(request), uri, port);
|
||||
}
|
||||
|
||||
private ServerWebExchange testFilter(ServerWebExchange exchange, URI uri, int port) {
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri);
|
||||
|
||||
ArgumentCaptor<ServerWebExchange> captor = ArgumentCaptor
|
||||
.forClass(ServerWebExchange.class);
|
||||
when(chain.filter(captor.capture())).thenReturn(Mono.empty());
|
||||
|
||||
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
|
||||
ILoadBalancer loadBalancer = mock(ILoadBalancer.class);
|
||||
|
||||
when(clientFactory.getLoadBalancerContext("service1"))
|
||||
.thenReturn(new RibbonLoadBalancerContext(loadBalancer));
|
||||
when(clientFactory.getLoadBalancer("service1")).thenReturn(loadBalancer);
|
||||
when(loadBalancer.chooseServer(any()))
|
||||
.thenReturn(new Server("service1-host1", port));
|
||||
|
||||
RibbonLoadBalancerClient client = new RibbonLoadBalancerClient(clientFactory);
|
||||
|
||||
LoadBalancerClientFilter filter = new LoadBalancerClientFilter(client,
|
||||
properties);
|
||||
filter.filter(exchange, chain);
|
||||
|
||||
return captor.getValue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* 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.filter.factory;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.PrincipalNameKeyResolver;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT, properties = "debug=true")
|
||||
@DirtiesContext
|
||||
public class HystrixGatewayFilterFactoryPrincipalTests extends BaseWebClientTests {
|
||||
|
||||
@Autowired
|
||||
private TestPrincipalGatewayFilterFactory testFilterFactory;
|
||||
|
||||
@Test
|
||||
public void hystrixPrincipalNotLost() {
|
||||
testClient.get().uri("/hystrixprincipal").headers(httpHeaders -> {
|
||||
httpHeaders.setBasicAuth("user", "password");
|
||||
httpHeaders.set("Host", "www.hystrixsecurity.org");
|
||||
}).exchange().expectStatus().isOk().expectBody().jsonPath("$.principal")
|
||||
.isEqualTo("user");
|
||||
assertThat(testFilterFactory.resolvedPrincipal).isEqualTo("user");
|
||||
}
|
||||
|
||||
@RestController
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
@RibbonClients({
|
||||
@RibbonClient(name = "testservice", configuration = TestRibbonConfig.class) })
|
||||
public static class TestConfig {
|
||||
|
||||
@Value("${test.uri}")
|
||||
private String uri;
|
||||
|
||||
@RequestMapping("/httpbin/hystrixprincipal")
|
||||
public Mono<Map<String, String>> hystrixPrincipal(Mono<Principal> principal) {
|
||||
return principal.map(Principal::getName).defaultIfEmpty("Unknown")
|
||||
.map(s -> Collections.singletonMap("principal", s));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouteLocator hystrixRouteLocator(RouteLocatorBuilder builder,
|
||||
TestPrincipalGatewayFilterFactory filterFactory) {
|
||||
return builder.routes()
|
||||
.route("hystrix_security", r -> r.host("**.hystrixsecurity.org")
|
||||
.filters(f -> f.prefixPath("/httpbin")
|
||||
.hystrix(config -> config.setName("securitycmd"))
|
||||
.filter(filterFactory.apply("")))
|
||||
.uri(uri))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestPrincipalGatewayFilterFactory testPrincipalGatewayFilterFactory() {
|
||||
return new TestPrincipalGatewayFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RecursiveHttpbinFilter recursiveHttpbinFilter() {
|
||||
return new RecursiveHttpbinFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
|
||||
return http.httpBasic().and().authorizeExchange()
|
||||
.pathMatchers("/hystrixprincipal").authenticated().anyExchange()
|
||||
.permitAll().and().build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("deprecation")
|
||||
public MapReactiveUserDetailsService reactiveUserDetailsService() {
|
||||
UserDetails user = User.withDefaultPasswordEncoder().username("user")
|
||||
.password("password").roles("USER").build();
|
||||
return new MapReactiveUserDetailsService(user);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TestPrincipalGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<Object> {
|
||||
|
||||
private final Log log = LogFactory
|
||||
.getLog(TestPrincipalGatewayFilterFactory.class);
|
||||
|
||||
private KeyResolver keyResolver = new PrincipalNameKeyResolver();
|
||||
|
||||
private String resolvedPrincipal;
|
||||
|
||||
public TestPrincipalGatewayFilterFactory() {
|
||||
super(Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Object config) {
|
||||
return (exchange, chain) -> keyResolver.resolve(exchange)
|
||||
.defaultIfEmpty("Empty Principal").flatMap(name -> {
|
||||
resolvedPrincipal = name;
|
||||
return chain.filter(exchange);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* 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.filter.factory;
|
||||
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.hystrix.Hystrix;
|
||||
import com.netflix.hystrix.metric.consumer.HealthCountsStream;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.core.StringContains.containsString;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.filter.factory.ExceptionFallbackHandler.RETRIEVED_EXCEPTION;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT, properties = "debug=true")
|
||||
@ContextConfiguration(classes = HystrixTestConfig.class)
|
||||
@DirtiesContext
|
||||
public class HystrixGatewayFilterFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void hystrixFilterWorks() {
|
||||
testClient.get().uri("/get").header("Host", "www.hystrixsuccess.org").exchange()
|
||||
.expectStatus().isOk().expectHeader()
|
||||
.valueEquals(ROUTE_ID_HEADER, "hystrix_success_test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hystrixFilterTimesout() {
|
||||
testClient.get().uri("/delay/3").header("Host", "www.hystrixfailure.org")
|
||||
.exchange().expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT)
|
||||
.expectBody().jsonPath("$.status")
|
||||
.isEqualTo(String.valueOf(HttpStatus.GATEWAY_TIMEOUT.value()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hystrixFilterServiceUnavailable() {
|
||||
HealthCountsStream.reset();
|
||||
Hystrix.reset();
|
||||
ConfigurationManager.getConfigInstance()
|
||||
.setProperty("hystrix.command.failcmd.circuitBreaker.forceOpen", true);
|
||||
|
||||
testClient.get().uri("/delay/3").header("Host", "www.hystrixfailure.org")
|
||||
.exchange().expectStatus().isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
|
||||
HealthCountsStream.reset();
|
||||
Hystrix.reset();
|
||||
ConfigurationManager.getConfigInstance()
|
||||
.setProperty("hystrix.command.failcmd.circuitBreaker.forceOpen", false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests that timeouts bubbling from the underpinning WebClient are treated the same
|
||||
* as Hystrix timeouts in terms of outside response. (Internally, timeouts from the
|
||||
* WebClient are seen as command failures and trigger the opening of circuit breakers
|
||||
* the same way timeouts do; it may be confusing in terms of the Hystrix metrics
|
||||
* though)
|
||||
*/
|
||||
@Test
|
||||
public void hystrixTimeoutFromWebClient() {
|
||||
testClient.get().uri("/delay/10").header("Host", "www.hystrixresponsestall.org")
|
||||
.exchange().expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hystrixFilterFallback() {
|
||||
testClient.get().uri("/delay/3?a=b").header("Host", "www.hystrixfallback.org")
|
||||
.exchange().expectStatus().isOk().expectBody()
|
||||
.json("{\"from\":\"fallbackcontroller\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hystrixFilterExceptionFallback() {
|
||||
testClient.get().uri("/delay/3")
|
||||
.header("Host", "www.hystrixexceptionfallback.org").exchange()
|
||||
.expectStatus().isOk().expectHeader()
|
||||
.value(RETRIEVED_EXCEPTION, containsString("HystrixTimeoutException"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hystrixFilterWorksJavaDsl() {
|
||||
testClient.get().uri("/get").header("Host", "www.hystrixjava.org").exchange()
|
||||
.expectStatus().isOk().expectHeader()
|
||||
.valueEquals(ROUTE_ID_HEADER, "hystrix_java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hystrixFilterFallbackJavaDsl() {
|
||||
testClient.get().uri("/delay/3").header("Host", "www.hystrixjava.org").exchange()
|
||||
.expectStatus().isOk().expectBody()
|
||||
.json("{\"from\":\"fallbackcontroller2\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hystrixFilterConnectFailure() {
|
||||
testClient.get().uri("/delay/3").header("Host", "www.hystrixconnectfail.org")
|
||||
.exchange().expectStatus().is5xxServerError();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hystrixFilterErrorPage() {
|
||||
testClient.get().uri("/delay/3").header("Host", "www.hystrixconnectfail.org")
|
||||
.accept(APPLICATION_JSON).exchange().expectStatus().is5xxServerError()
|
||||
.expectBody().jsonPath("$.status")
|
||||
.value(Matchers.greaterThanOrEqualTo(500)).jsonPath("$.message")
|
||||
.isNotEmpty().jsonPath("$.error").isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringFormat() {
|
||||
HystrixGatewayFilterFactory.Config config = new HystrixGatewayFilterFactory.Config()
|
||||
.setName("myname").setFallbackUri("forward:/myfallback");
|
||||
GatewayFilter filter = new HystrixGatewayFilterFactory(null).apply(config);
|
||||
assertThat(filter.toString()).contains("myname").contains("forward:/myfallback");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterFallbackForward() {
|
||||
testClient.get().uri("/delay/3?a=c").header("Host", "www.hystrixforward.org")
|
||||
.exchange().expectStatus().isOk().expectBody()
|
||||
.json("{\"from\":\"hystrixfallbackcontroller3\"}");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* 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.filter.factory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
|
||||
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(BaseWebClientTests.DefaultTestConfig.class)
|
||||
@RestController
|
||||
@RibbonClient(name = "badservice", configuration = TestBadRibbonConfig.class)
|
||||
public class HystrixTestConfig {
|
||||
|
||||
@Value("${test.uri}")
|
||||
private String uri;
|
||||
|
||||
@RequestMapping("/fallbackcontroller")
|
||||
public Map<String, String> fallbackcontroller(@RequestParam("a") String a) {
|
||||
return Collections.singletonMap("from", "fallbackcontroller");
|
||||
}
|
||||
|
||||
@RequestMapping("/fallbackcontroller2")
|
||||
public Map<String, String> fallbackcontroller2() {
|
||||
return Collections.singletonMap("from", "fallbackcontroller2");
|
||||
}
|
||||
|
||||
@RequestMapping("/hystrixFallbackController3")
|
||||
public Map<String, String> fallbackcontroller3() {
|
||||
return Collections.singletonMap("from", "hystrixfallbackcontroller3");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouteLocator hystrixRouteLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes().route("hystrix_java", r -> r.host("**.hystrixjava.org")
|
||||
.filters(f -> f.prefixPath("/httpbin").hystrix(
|
||||
config -> config.setFallbackUri("forward:/fallbackcontroller2")))
|
||||
.uri(uri))
|
||||
.route("hystrix_fallback_forward", r -> r.host("**.hystrixforward.org")
|
||||
.filters(f -> f.hystrix(
|
||||
config -> config.setFallbackUri("forward:/fallback")))
|
||||
.uri(uri))
|
||||
.route("hystrix_fallback_controller_3", r -> r.path("/fallback")
|
||||
.filters(f -> f.setPath("/hystrixFallbackController3")).uri(uri))
|
||||
.route("hystrix_connection_failure",
|
||||
r -> r.host("**.hystrixconnectfail.org")
|
||||
.filters(f -> f.prefixPath("/httpbin").hystrix(config -> {
|
||||
})).uri("lb://badservice"))
|
||||
/*
|
||||
* This is a route encapsulated in a hystrix command that is ready to wait
|
||||
* for a response far longer than the underpinning WebClient would.
|
||||
*/
|
||||
.route("hystrix_response_stall",
|
||||
r -> r.host("**.hystrixresponsestall.org")
|
||||
.filters(f -> f.prefixPath("/httpbin").hystrix(
|
||||
config -> config.setName("stalling-command")))
|
||||
.uri(uri))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ExceptionFallbackHandler exceptionFallbackHandler() {
|
||||
return new ExceptionFallbackHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
RouterFunction<ServerResponse> routerFunction(
|
||||
ExceptionFallbackHandler exceptionFallbackHandler) {
|
||||
return route(GET("/exceptionFallback"),
|
||||
exceptionFallbackHandler::retrieveExceptionInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ExceptionFallbackHandler {
|
||||
|
||||
static final String RETRIEVED_EXCEPTION = "Retrieved-Exception";
|
||||
|
||||
Mono<ServerResponse> retrieveExceptionInfo(ServerRequest serverRequest) {
|
||||
String exceptionName = serverRequest.attribute(HYSTRIX_EXECUTION_EXCEPTION_ATTR)
|
||||
.map(exception -> exception.getClass().getName()).orElse("");
|
||||
return ServerResponse.ok().header(RETRIEVED_EXCEPTION, exceptionName).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class TestBadRibbonConfig {
|
||||
|
||||
@LocalServerPort
|
||||
protected int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("https", "localhost", this.port));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,8 +22,6 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
@@ -43,8 +41,6 @@ import org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactor
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -177,7 +173,8 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@Import(DefaultTestConfig.class)
|
||||
@RibbonClient(name = "badservice2", configuration = TestBadRibbonConfig.class)
|
||||
// @RibbonClient(name = "badservice2", configuration =
|
||||
// TestBadLoadBalancerConfig.class)
|
||||
public static class TestConfig {
|
||||
|
||||
Log log = LogFactory.getLog(getClass());
|
||||
@@ -269,17 +266,17 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest
|
||||
|
||||
}
|
||||
|
||||
protected static class TestBadRibbonConfig {
|
||||
protected static class TestBadLoadBalancerConfig {
|
||||
|
||||
@LocalServerPort
|
||||
protected int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(
|
||||
new Server("https", "localhost.domain.doesnot.exist", this.port),
|
||||
new Server("localhost", this.port));
|
||||
}
|
||||
// @Bean
|
||||
// public ServerList<Server> serverList() {
|
||||
// return new StaticServerList<>(
|
||||
// new Server("https", "localhost.domain.doesnot.exist", this.port),
|
||||
// new Server("localhost", this.port));
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* 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.filter.factory;
|
||||
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.hystrix.Hystrix;
|
||||
import com.netflix.hystrix.metric.consumer.HealthCountsStream;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.netflix.hystrix.ReactiveHystrixCircuitBreakerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.core.StringContains.containsString;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.filter.factory.ExceptionFallbackHandler.RETRIEVED_EXCEPTION;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT, properties = { "debug=true",
|
||||
"spring.cloud.circuitbreaker.resilience4j.enabled=false" })
|
||||
@ContextConfiguration(classes = SpringCloudCircuitBreakerTestConfig.class)
|
||||
@DirtiesContext
|
||||
public class SpringCloudCircuitBreakerHystrixFilterFactoryTests
|
||||
extends SpringCloudCircuitBreakerFilterFactoryTests {
|
||||
|
||||
@Test
|
||||
public void hystrixFilterServiceUnavailable() {
|
||||
HealthCountsStream.reset();
|
||||
Hystrix.reset();
|
||||
ConfigurationManager.getConfigInstance()
|
||||
.setProperty("hystrix.command.failcmd.circuitBreaker.forceOpen", true);
|
||||
|
||||
testClient.get().uri("/delay/3").header("Host", "www.sccbfailure.org").exchange()
|
||||
.expectStatus().isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
|
||||
HealthCountsStream.reset();
|
||||
Hystrix.reset();
|
||||
ConfigurationManager.getConfigInstance()
|
||||
.setProperty("hystrix.command.failcmd.circuitBreaker.forceOpen", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hystrixFilterExceptionFallback() {
|
||||
testClient.get().uri("/delay/3")
|
||||
.header("Host", "www.circuitbreakerexceptionfallback.org").exchange()
|
||||
.expectStatus().isOk().expectHeader()
|
||||
.value(RETRIEVED_EXCEPTION, containsString("HystrixTimeoutException"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringFormat() {
|
||||
SpringCloudCircuitBreakerFilterFactory.Config config = new SpringCloudCircuitBreakerFilterFactory.Config()
|
||||
.setName("myname").setFallbackUri("forward:/myfallback");
|
||||
GatewayFilter filter = new SpringCloudCircuitBreakerHystrixFilterFactory(
|
||||
new ReactiveHystrixCircuitBreakerFactory(), null).apply(config);
|
||||
assertThat(filter.toString()).contains("myname").contains("forward:/myfallback");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,7 +39,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.core.StringContains.containsString;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
import static org.springframework.cloud.gateway.filter.factory.ExceptionFallbackHandler.RETRIEVED_EXCEPTION;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -53,6 +52,8 @@ import static org.springframework.cloud.gateway.filter.factory.ExceptionFallback
|
||||
public class SpringCloudCircuitBreakerResilience4JFilterFactoryTests
|
||||
extends SpringCloudCircuitBreakerFilterFactoryTests {
|
||||
|
||||
private static final String RETRIEVED_EXCEPTION = "Retrieved-Exception";
|
||||
|
||||
@Autowired
|
||||
private Resilience4JCircuitBreakerFactory factory;
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -48,7 +47,7 @@ import static org.springframework.web.reactive.function.server.RouterFunctions.r
|
||||
@SpringBootConfiguration
|
||||
@Import(BaseWebClientTests.DefaultTestConfig.class)
|
||||
@RestController
|
||||
@RibbonClient(name = "badservice", configuration = TestBadRibbonConfig.class)
|
||||
// @RibbonClient(name = "badservice", configuration = TestBadLoadBalancerConfig.class)
|
||||
public class SpringCloudCircuitBreakerTestConfig {
|
||||
|
||||
@Value("${test.uri}")
|
||||
@@ -113,17 +112,17 @@ public class SpringCloudCircuitBreakerTestConfig {
|
||||
exceptionFallbackHandler::retrieveExceptionInfo);
|
||||
}
|
||||
|
||||
}
|
||||
private static class CircuitBreakerExceptionFallbackHandler {
|
||||
|
||||
class CircuitBreakerExceptionFallbackHandler {
|
||||
static final String RETRIEVED_EXCEPTION = "Retrieved-Exception";
|
||||
|
||||
static final String RETRIEVED_EXCEPTION = "Retrieved-Exception";
|
||||
Mono<ServerResponse> retrieveExceptionInfo(ServerRequest serverRequest) {
|
||||
String exceptionName = serverRequest
|
||||
.attribute(CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR)
|
||||
.map(exception -> exception.getClass().getName()).orElse("");
|
||||
return ServerResponse.ok().header(RETRIEVED_EXCEPTION, exceptionName).build();
|
||||
}
|
||||
|
||||
Mono<ServerResponse> retrieveExceptionInfo(ServerRequest serverRequest) {
|
||||
String exceptionName = serverRequest
|
||||
.attribute(CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR)
|
||||
.map(exception -> exception.getClass().getName()).orElse("");
|
||||
return ServerResponse.ok().header(RETRIEVED_EXCEPTION, exceptionName).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -33,9 +31,6 @@ import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFact
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.cloud.gateway.test.PermitAllSecurityConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -90,10 +85,10 @@ public class ReadBodyPredicateFactoryTest {
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@RibbonClients({
|
||||
@RibbonClient(name = "message", configuration = TestRibbonConfig.class),
|
||||
@RibbonClient(name = "messageChannel",
|
||||
configuration = TestRibbonConfig.class) })
|
||||
// @RibbonClients({
|
||||
// @RibbonClient(name = "message", configuration = TestLoadBalancerConfig.class),
|
||||
// @RibbonClient(name = "messageChannel",
|
||||
// configuration = TestLoadBalancerConfig.class) })
|
||||
@Import(PermitAllSecurityConfiguration.class)
|
||||
@RestController
|
||||
public static class TestConfig {
|
||||
@@ -129,15 +124,15 @@ public class ReadBodyPredicateFactoryTest {
|
||||
|
||||
}
|
||||
|
||||
protected static class TestRibbonConfig {
|
||||
protected static class TestLoadBalancerConfig {
|
||||
|
||||
@LocalServerPort
|
||||
protected int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
// @Bean
|
||||
// public ServerList<Server> serverList() {
|
||||
// return new StaticServerList<>(new Server("localhost", this.port));
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ import static org.junit.Assume.assumeThat;
|
||||
org.springframework.cloud.gateway.handler.predicate.CloudFoundryRouteServiceRoutePredicateFactoryTest.class,
|
||||
org.springframework.cloud.gateway.handler.RoutePredicateHandlerMappingTests.class,
|
||||
org.springframework.cloud.gateway.filter.factory.RewriteResponseHeaderGatewayFilterFactoryTests.class,
|
||||
org.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactoryTests.class,
|
||||
org.springframework.cloud.gateway.filter.factory.SecureHeadersGatewayFilterFactoryTests.class,
|
||||
org.springframework.cloud.gateway.filter.factory.RedirectToGatewayFilterFactoryTests.class,
|
||||
org.springframework.cloud.gateway.filter.factory.AddRequestParameterGatewayFilterFactoryTests.class,
|
||||
@@ -96,7 +95,6 @@ import static org.junit.Assume.assumeThat;
|
||||
org.springframework.cloud.gateway.filter.ratelimit.PrincipalNameKeyResolverIntegrationTests.class,
|
||||
org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiterConfigTests.class,
|
||||
org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiterTests.class,
|
||||
org.springframework.cloud.gateway.filter.LoadBalancerClientFilterTests.class,
|
||||
org.springframework.cloud.gateway.filter.NettyRoutingFilterIntegrationTests.class,
|
||||
GatewayMetricsFilterTests.class,
|
||||
org.springframework.cloud.gateway.filter.ForwardRoutingFilterTests.class,
|
||||
|
||||
@@ -18,8 +18,6 @@ package org.springframework.cloud.gateway.test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
@@ -31,9 +29,6 @@ import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -81,8 +76,9 @@ public class BaseWebClientTests {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@RibbonClients({
|
||||
@RibbonClient(name = "testservice", configuration = TestRibbonConfig.class) })
|
||||
// @RibbonClients({
|
||||
// @RibbonClient(name = "testservice", configuration = TestLoadBalancerConfig.class)
|
||||
// })
|
||||
@Import(PermitAllSecurityConfiguration.class)
|
||||
public static class DefaultTestConfig {
|
||||
|
||||
@@ -141,15 +137,15 @@ public class BaseWebClientTests {
|
||||
|
||||
}
|
||||
|
||||
protected static class TestRibbonConfig {
|
||||
protected static class TestLoadBalancerConfig {
|
||||
|
||||
@LocalServerPort
|
||||
protected int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
// @Bean
|
||||
// public ServerList<Server> serverList() {
|
||||
// return new StaticServerList<>(new Server("localhost", this.port));
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
@@ -47,8 +45,6 @@ import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.cloud.gateway.test.PermitAllSecurityConfiguration;
|
||||
import org.springframework.cloud.gateway.test.support.HttpServer;
|
||||
import org.springframework.cloud.gateway.test.support.ReactorHttpServer;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
@@ -361,8 +357,8 @@ public class WebSocketIntegrationTests {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@Import(PermitAllSecurityConfiguration.class)
|
||||
@RibbonClient(name = "wsservice",
|
||||
configuration = LocalRibbonClientConfiguration.class)
|
||||
// @RibbonClient(name = "wsservice",
|
||||
// configuration = LocalLoadBalancerClientConfiguration.class)
|
||||
protected static class GatewayConfig {
|
||||
|
||||
@Bean
|
||||
@@ -374,15 +370,15 @@ public class WebSocketIntegrationTests {
|
||||
|
||||
}
|
||||
|
||||
public static class LocalRibbonClientConfiguration {
|
||||
public static class LocalLoadBalancerClientConfiguration {
|
||||
|
||||
@Value("${ws.server.port}")
|
||||
private int wsPort;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.wsPort));
|
||||
}
|
||||
// @Bean
|
||||
// public ServerList<Server> serverList() {
|
||||
// return new StaticServerList<>(new Server("localhost", this.wsPort));
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -81,13 +81,6 @@ public class CustomBlockHoundIntegration implements BlockHoundIntegration {
|
||||
builder.allowBlockingCallsInside("io.netty.handler.ssl.SslContext",
|
||||
"newClientContextInternal");
|
||||
|
||||
// Uses
|
||||
// org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientConnectionManagerFactory#newConnectionManager
|
||||
// Uses javax.net.ssl.SSLContext#init
|
||||
builder.allowBlockingCallsInside(
|
||||
"org.springframework.cloud.netflix.ribbon.SpringClientFactory",
|
||||
"getContext");
|
||||
|
||||
// Uses org.springframework.security.crypto.bcrypt.BCrypt#gensalt
|
||||
// Uses java.security.SecureRandom#nextBytes
|
||||
builder.allowBlockingCallsInside(
|
||||
|
||||
@@ -33,10 +33,6 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.isomorphism</groupId>
|
||||
<artifactId>token-bucket</artifactId>
|
||||
|
||||
@@ -22,8 +22,6 @@ import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
@@ -34,12 +32,9 @@ 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.test.HttpBinCompatibleController;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.SocketUtils;
|
||||
@@ -180,7 +175,7 @@ public class GatewaySampleApplicationTests {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@RibbonClient(name = "httpbin", configuration = RibbonConfig.class)
|
||||
//@RibbonClient(name = "httpbin", configuration = LoadBalancerConfig.class)
|
||||
@Import(GatewaySampleApplication.class)
|
||||
protected static class TestConfig {
|
||||
|
||||
@@ -191,16 +186,16 @@ public class GatewaySampleApplicationTests {
|
||||
|
||||
}
|
||||
|
||||
protected static class RibbonConfig {
|
||||
protected static class LoadBalancerConfig {
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", port));
|
||||
}
|
||||
//@Bean
|
||||
//@Primary
|
||||
//public ServerList<Server> serverList() {
|
||||
// return new StaticServerList<>(new Server("localhost", port));
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user