Add support for forward:/// uri's.
Let's spring's `DispatcherHandler` handle forwarded requests. fixes gh-78
This commit is contained in:
@@ -582,9 +582,17 @@ In either case, the HTTP status of the response will be set to 401.
|
||||
|
||||
The `GlobalFilter` interface has the same signature as `WebFilter`. These are special filters that are conditionally applied to all routes. (This interface and usage are subject to change in future milestones).
|
||||
|
||||
=== Combined Global Filter and WebFilter Ordering
|
||||
|
||||
TODO: document ordering
|
||||
|
||||
=== Forward Routing Filter
|
||||
|
||||
The `ForwardRoutingFilter` looks for a URI in the exchange attribute `ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR`. If the url has a `forward` scheme (ie `forward:///localendpoint`), it will use the Spring `DispatcherHandler` to handler the request. The unmodified original url is appended to the list in the `ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR` attribute.
|
||||
|
||||
=== LoadBalancerClient Filter
|
||||
|
||||
The `LoadBalancerClientFilter` looks for a URI in the exchange attribute `ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR`. If the url has a `lb` scheme (ie `lb://myservice`), it will use the Spring Cloud `LoadBalancerClient` to resolve the name (`myservice` in the previous example) to an actual host and port and replace the URI in the same attribute. The unmodified original url is placed in the `ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR` attribute.
|
||||
The `LoadBalancerClientFilter` looks for a URI in the exchange attribute `ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR`. If the url has a `lb` scheme (ie `lb://myservice`), it will use the Spring Cloud `LoadBalancerClient` to resolve the name (`myservice` in the previous example) to an actual host and port and replace the URI in the same attribute. The unmodified original url is appended to the list in the `ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR` attribute.
|
||||
|
||||
=== Netty Routing Filter
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.gateway.actuate.GatewayEndpoint;
|
||||
import org.springframework.cloud.gateway.filter.ForwardRoutingFilter;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.NettyRoutingFilter;
|
||||
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
|
||||
@@ -81,6 +82,7 @@ import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
|
||||
import org.springframework.web.reactive.socket.client.WebSocketClient;
|
||||
import org.springframework.web.reactive.socket.server.WebSocketService;
|
||||
@@ -199,6 +201,12 @@ public class GatewayAutoConfiguration {
|
||||
return new RouteToRequestUrlFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(DispatcherHandler.class)
|
||||
public ForwardRoutingFilter forwardRoutingFilter(DispatcherHandler dispatcherHandler) {
|
||||
return new ForwardRoutingFilter(dispatcherHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebSocketService webSocketService() {
|
||||
return new HandshakeWebSocketService();
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.springframework.cloud.gateway.filter;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.isAlreadyRouted;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setAlreadyRouted;
|
||||
|
||||
public class ForwardRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ForwardRoutingFilter.class);
|
||||
|
||||
private final DispatcherHandler dispatcherHandler;
|
||||
|
||||
public ForwardRoutingFilter(DispatcherHandler dispatcherHandler) {
|
||||
this.dispatcherHandler = dispatcherHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
|
||||
String scheme = requestUrl.getScheme();
|
||||
if (isAlreadyRouted(exchange) || !scheme.equals("forward")) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
setAlreadyRouted(exchange);
|
||||
|
||||
//TODO: translate url?
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Forwarding to URI: "+requestUrl);
|
||||
}
|
||||
|
||||
return this.dispatcherHandler.handle(exchange);
|
||||
}
|
||||
}
|
||||
@@ -17,18 +17,25 @@
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class PrefixPathWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
private static final Log log = LogFactory.getLog(PrefixPathWebFilterFactory.class);
|
||||
|
||||
public static final String PREFIX_KEY = "prefix";
|
||||
|
||||
@Override
|
||||
@@ -43,12 +50,19 @@ public class PrefixPathWebFilterFactory implements WebFilterFactory {
|
||||
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
addOriginalRequestUrl(exchange, req.getURI());
|
||||
String newPath = prefix + req.getURI().getPath();
|
||||
|
||||
ServerHttpRequest request = req.mutate()
|
||||
.path(newPath)
|
||||
.build();
|
||||
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Prefixed URI with: "+prefix+" -> "+request.getURI());
|
||||
}
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
import org.springframework.web.util.pattern.PathPattern.PathMatchInfo;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
|
||||
|
||||
@@ -66,6 +67,8 @@ public class SetPathWebFilterFactory implements WebFilterFactory {
|
||||
URI uri = uriTemplate.expand(uriVariables);
|
||||
String newPath = uri.getPath();
|
||||
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri);
|
||||
|
||||
ServerHttpRequest request = req.mutate()
|
||||
.path(newPath)
|
||||
.build();
|
||||
|
||||
@@ -37,7 +37,7 @@ public class ServerWebExchangeUtils {
|
||||
public static final String CLIENT_RESPONSE_ATTR = qualify("webHandlerClientResponse");
|
||||
public static final String GATEWAY_ROUTE_ATTR = qualify("gatewayRoute");
|
||||
public static final String GATEWAY_REQUEST_URL_ATTR = qualify("gatewayRequestUrl");
|
||||
public static final String GATEWAY_ORIGINAL_REQUEST_URL_ATTR = qualify("gatewayOribinalRequestUrl");
|
||||
public static final String GATEWAY_ORIGINAL_REQUEST_URL_ATTR = qualify("gatewayOriginalRequestUrl");
|
||||
public static final String GATEWAY_HANDLER_MAPPER_ATTR = qualify("gatewayHandlerMapper");
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.test;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ForwardTests {
|
||||
@LocalServerPort
|
||||
protected int port = 0;
|
||||
|
||||
protected WebTestClient client;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
String baseUri = "http://localhost:" + port;
|
||||
this.client = WebTestClient.bindToServer()
|
||||
.baseUrl(baseUri)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardWorks() {
|
||||
this.client
|
||||
.get()
|
||||
.uri("/localcontroller")
|
||||
.header(HttpHeaders.HOST, "www.forward.org")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody().json("{\"from\":\"localcontroller\"}");
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@RestController
|
||||
@Import(PermitAllSecurityConfiguration.class)
|
||||
public static class TestConfig {
|
||||
|
||||
@RequestMapping("/httpbin/localcontroller")
|
||||
public Map<String, String> localController() {
|
||||
return Collections.singletonMap("from", "localcontroller");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -58,6 +58,12 @@ spring:
|
||||
filters:
|
||||
- AddResponseHeader=X-Request-Foo, Bar
|
||||
|
||||
# =====================================
|
||||
- id: forward_test
|
||||
uri: forward:///localcontroller
|
||||
predicates:
|
||||
- Host=**.forward.org
|
||||
|
||||
# =====================================
|
||||
- id: hystrix_failure_test
|
||||
uri: ${test.uri}
|
||||
|
||||
Reference in New Issue
Block a user