diff --git a/pom.xml b/pom.xml
index f1a76d1d0..685294713 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,21 @@
spring-boot-configuration-processor
true
+
+ org.springframework.cloud
+ spring-cloud-starter-ribbon
+ true
+
+
+ com.netflix.ribbon
+ ribbon-transport
+
+
+ io.reactivex
+ rxnetty
+
+
+
org.springframework.boot
spring-boot-devtools
@@ -75,6 +90,20 @@
pom
import
+
+ org.springframework.cloud
+ spring-cloud-commons-dependencies
+ 1.2.0.BUILD-SNAPSHOT
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-netflix-dependencies
+ 1.3.0.BUILD-SNAPSHOT
+ pom
+ import
+
diff --git a/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
index 593fa02e4..ec626f3cd 100644
--- a/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
+++ b/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
@@ -4,12 +4,15 @@ import java.util.List;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.Endpoint;
+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.actuate.GatewayEndpoint;
import org.springframework.cloud.gateway.api.RouteReader;
import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter;
import org.springframework.cloud.gateway.filter.WriteResponseFilter;
import org.springframework.cloud.gateway.filter.route.AddRequestHeaderRouteFilter;
@@ -86,6 +89,13 @@ public class GatewayAutoConfiguration {
// GlobalFilter beans
+ @Bean
+ @ConditionalOnClass(LoadBalancerClient.class)
+ @ConditionalOnBean(LoadBalancerClient.class)
+ public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client) {
+ return new LoadBalancerClientFilter(client);
+ }
+
@Bean
public RouteToRequestUrlFilter routeToRequestUrlFilter() {
return new RouteToRequestUrlFilter();
diff --git a/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java b/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java
new file mode 100644
index 000000000..b8edcd45b
--- /dev/null
+++ b/src/main/java/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java
@@ -0,0 +1,59 @@
+package org.springframework.cloud.gateway.filter;
+
+import java.net.URI;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.core.Ordered;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilterChain;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.getAttribute;
+
+import reactor.core.publisher.Mono;
+
+/**
+ * @author Spencer Gibb
+ */
+public class LoadBalancerClientFilter implements GlobalFilter, Ordered {
+
+ private static final Log log = LogFactory.getLog(LoadBalancerClientFilter.class);
+ public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10100;
+
+ private final LoadBalancerClient loadBalancer;
+
+ public LoadBalancerClientFilter(LoadBalancerClient loadBalancer) {
+ this.loadBalancer = loadBalancer;
+ }
+
+ @Override
+ public int getOrder() {
+ return LOAD_BALANCER_CLIENT_FILTER_ORDER;
+ }
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
+ URI url = getAttribute(exchange, GATEWAY_REQUEST_URL_ATTR, URI.class);
+ if (url == null || !url.getScheme().equals("lb")) {
+ return chain.filter(exchange);
+ }
+ log.trace("LoadBalancerClientFilter url before: " + url);
+
+ final ServiceInstance instance = loadBalancer.choose(url.getHost());
+
+ URI requestUrl = UriComponentsBuilder.fromUri(url)
+ .scheme(instance.isSecure()? "https" : "http") //TODO: support websockets
+ .host(instance.getHost())
+ .port(instance.getPort())
+ .build(true)
+ .toUri();
+ log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
+ exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
+ return chain.filter(exchange);
+ }
+
+}
diff --git a/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java b/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java
index 815cc9924..268666ac9 100644
--- a/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java
+++ b/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java
@@ -5,7 +5,6 @@ import java.net.URI;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.gateway.config.Route;
-import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
diff --git a/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java
index ff21418a7..e42d56c49 100644
--- a/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java
+++ b/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java
@@ -38,7 +38,8 @@ import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@RunWith(SpringRunner.class)
-@SpringBootTest(webEnvironment = RANDOM_PORT)
+@SpringBootTest(webEnvironment = RANDOM_PORT,
+ properties = "spring.cloud.bootstrap.enabled=false")
@SuppressWarnings("unchecked")
public class GatewayIntegrationTests {
@@ -209,6 +210,29 @@ public class GatewayIntegrationTests {
);
}
+ @Test
+ public void loadBalancerFilterWorks() {
+ Mono result = webClient.exchange(
+ GET("http://localhost:" + port + "/get")
+ .header("Host", "www.loadbalancerclient.org")
+ .build()
+ );
+
+ verify( () ->
+ StepVerifier.create(result)
+ .consumeNextWith(
+ response -> {
+ HttpHeaders httpHeaders = response.headers().asHttpHeaders();
+ assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER))
+ .isEqualTo("load_balancer_client_test");
+ HttpStatus statusCode = response.statusCode();
+ assertThat(statusCode).isEqualTo(HttpStatus.OK);
+ })
+ .expectComplete()
+ .verify(Duration.ofMinutes(5))
+ );
+ }
+
@Test
public void postWorks() {
ClientRequest> request = POST("http://localhost:" + port + "/post")
diff --git a/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java b/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java
index 86017a18d..47ea6b140 100644
--- a/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java
+++ b/src/test/java/org/springframework/cloud/gateway/test/GatewayTestApplication.java
@@ -9,6 +9,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
public class GatewayTestApplication {
public static void main(String[] args) {
+ System.setProperty("spring.cloud.bootstrap.enabled", "false"); //TODO: fix bootstrap
SpringApplication.run(GatewayTestApplication.class, args);
}
}
diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml
index 36e837402..11c76c046 100644
--- a/src/test/resources/application.yml
+++ b/src/test/resources/application.yml
@@ -47,6 +47,12 @@ spring:
filters:
- AddResponseHeader=X-Request-Foo, Bar
+ # =====================================
+ - id: load_balancer_client_test
+ uri: lb://myservice
+ predicates:
+ - Host=**.loadbalancerclient.org
+
# =====================================
- id: redirect_to_test
uri: http://httpbin.org:80
@@ -129,6 +135,12 @@ spring:
predicates:
- name: Url
value: /**
+
+myservice:
+ ribbon:
+ NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
+ listOfServers: httpbin.org:80
+
logging:
level:
org.springframework.cloud.gateway: TRACE