Move some of the routing logic into the route locator
We now support prefix stripping per service, e.g.
zuul:
routes:
customers:
path: /customers/**
stripPrefix: true
Will route /customers/101 -> /101 (on the customers service)
See gh-77
This commit is contained in:
@@ -551,7 +551,19 @@ The location of the backend can be specified as either a "serviceId"
|
||||
|
||||
Forwarding to the service is protected by a Hystrix circuit breaker so if a service is down the client will see an error, but once the circuit is open the proxy will not try to contact the service.
|
||||
|
||||
To add a prefix to the mapping, set `zuul.prefix` to a value, such as `/api`. To strip the proxy prefix from the request before the request is forwarded set `zuul.stripPrefix = true`.
|
||||
To add a prefix to all mappings, set `zuul.prefix` to a value, such as `/api`. To strip the proxy prefix from the request before the request is forwarded set `zuul.stripPrefix = true`. You can also strip the non-wildcard prefix from individual routes, e.g.
|
||||
|
||||
.application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
zuul:
|
||||
routes:
|
||||
users:
|
||||
path: /myusers/**
|
||||
stripPrefix: true
|
||||
----
|
||||
|
||||
In this example requests to "/myusers/101" will be forwarded to "/101" on the "users" service (the path is stripped up to the first wildcard character).
|
||||
|
||||
The `X-Forwarded-Host` header added to the forwarded requests by default. To turn it off set `zuul.addProxyHeaders = false`.
|
||||
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
package org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulProperties.ZuulRoute;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Slf4j
|
||||
public class ProxyRouteLocator implements ApplicationListener<EnvironmentChangeEvent> {
|
||||
|
||||
public static final String DEFAULT_ROUTE = "/";
|
||||
|
||||
private DiscoveryClient discovery;
|
||||
|
||||
private ZuulProperties properties;
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
private Field propertySourcesField;
|
||||
|
||||
private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>();
|
||||
|
||||
private Map<String, String> staticRoutes = new LinkedHashMap<String, String>();
|
||||
|
||||
public ProxyRouteLocator(DiscoveryClient discovery, ZuulProperties properties) {
|
||||
this.discovery = discovery;
|
||||
this.properties = properties;
|
||||
initField();
|
||||
}
|
||||
|
||||
private void initField() {
|
||||
propertySourcesField = ReflectionUtils.findField(CompositePropertySource.class,
|
||||
"propertySources");
|
||||
propertySourcesField.setAccessible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EnvironmentChangeEvent event) {
|
||||
for (String key : event.getKeys()) {
|
||||
if (key.startsWith("zuul.routes")) {
|
||||
resetRoutes();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addRoute(String path, String location) {
|
||||
staticRoutes.put(path, location);
|
||||
resetRoutes();
|
||||
}
|
||||
|
||||
public Collection<String> getRoutePaths() {
|
||||
return getRoutes().keySet();
|
||||
}
|
||||
|
||||
public Map<String, String> getRoutes() {
|
||||
|
||||
if (routes.get() == null) {
|
||||
routes.set(locateRoutes());
|
||||
}
|
||||
|
||||
Map<String, String> values = new LinkedHashMap<String, String>();
|
||||
|
||||
for (String key : routes.get().keySet()) {
|
||||
String url = key;
|
||||
values.put(url, routes.get().get(key).getLocation());
|
||||
}
|
||||
return values;
|
||||
|
||||
}
|
||||
|
||||
public ProxyRouteSpec getMatchingRoute(String path) {
|
||||
String location = null;
|
||||
String targetPath = null;
|
||||
for (Entry<String, ZuulRoute> entry : routes.get().entrySet()) {
|
||||
String pattern = entry.getKey();
|
||||
if (pathMatcher.match(pattern, path)) {
|
||||
ZuulRoute route = entry.getValue();
|
||||
String prefix = properties.getPrefix();
|
||||
location = route.getLocation();
|
||||
targetPath = path;
|
||||
if (path.startsWith(prefix) && properties.isStripPrefix()) {
|
||||
targetPath = path.substring(prefix.length());
|
||||
}
|
||||
if (route.isStripPrefix()) {
|
||||
int index = route.getPath().indexOf("*");
|
||||
index = index > 0 ? index-1 : 0;
|
||||
targetPath = path.substring(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
return location==null ? null : new ProxyRouteSpec(targetPath, location);
|
||||
}
|
||||
|
||||
// Package access so ZuulHandlerMapping can reset it's mappings
|
||||
void resetRoutes() {
|
||||
routes.set(locateRoutes());
|
||||
}
|
||||
|
||||
protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
|
||||
|
||||
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
|
||||
|
||||
addConfiguredRoutes(routesMap);
|
||||
addStaticRoutes(routesMap);
|
||||
|
||||
// Add routes for discovery services by default
|
||||
List<String> services = discovery.getServices();
|
||||
for (String serviceId : services) {
|
||||
// Ignore specifically ignored services and those that were manually
|
||||
// configured
|
||||
String key = "/" + serviceId + "/**";
|
||||
if (!properties.getIgnoredServices().contains(serviceId)
|
||||
&& !routesMap.containsKey(key)) {
|
||||
routesMap.put(key, new ZuulRoute(key, serviceId));
|
||||
}
|
||||
}
|
||||
|
||||
if (routesMap.get(DEFAULT_ROUTE) != null) {
|
||||
String defaultServiceId = routesMap.get(DEFAULT_ROUTE).getServiceId();
|
||||
// Move the defaultServiceId to the end
|
||||
routesMap.remove(DEFAULT_ROUTE);
|
||||
routesMap.put(DEFAULT_ROUTE, new ZuulRoute(defaultServiceId));
|
||||
}
|
||||
|
||||
LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
|
||||
for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
|
||||
|
||||
String path = entry.getKey();
|
||||
// Prepend with slash if not already present.
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(properties.getPrefix())) {
|
||||
path = properties.getPrefix() + path;
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
}
|
||||
|
||||
values.put(path, entry.getValue());
|
||||
|
||||
}
|
||||
|
||||
return values;
|
||||
|
||||
}
|
||||
|
||||
protected void addStaticRoutes(LinkedHashMap<String, ZuulRoute> routes) {
|
||||
for (Entry<String, String> entry : staticRoutes.entrySet()) {
|
||||
routes.put(entry.getKey(), new ZuulRoute(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
protected void addConfiguredRoutes(Map<String, ZuulRoute> routes) {
|
||||
Map<String, ZuulRoute> routeEntries = properties.getRoutesWithDefaultServiceIds();
|
||||
for (ZuulRoute entry : routeEntries.values()) {
|
||||
String route = entry.getPath();
|
||||
if (routes.containsKey(route)) {
|
||||
log.warn("Overwriting route {}: already defined by {}", route,
|
||||
routes.get(route));
|
||||
}
|
||||
routes.put(route, entry);
|
||||
}
|
||||
}
|
||||
|
||||
public String getTargetPath(String matchingRoute, String requestURI) {
|
||||
String path = getRoutes().get(matchingRoute);
|
||||
if (path==null) {
|
||||
path = requestURI;
|
||||
} else {
|
||||
|
||||
}
|
||||
return path;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class ProxyRouteSpec {
|
||||
private String path;
|
||||
private String location;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,8 +40,8 @@ public class ZuulConfiguration {
|
||||
private ZuulProperties zuulProperties;
|
||||
|
||||
@Bean
|
||||
public ZuulRouteLocator routes() {
|
||||
return new ZuulRouteLocator(discovery, zuulProperties);
|
||||
public ProxyRouteLocator routes() {
|
||||
return new ProxyRouteLocator(discovery, zuulProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -51,7 +51,7 @@ public class ZuulConfiguration {
|
||||
|
||||
@Bean
|
||||
public ZuulHandlerMapping zuulHandlerMapping() {
|
||||
return new ZuulHandlerMapping(routes(), zuulController(), zuulProperties);
|
||||
return new ZuulHandlerMapping(routes(), zuulController());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@@ -27,24 +26,20 @@ import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
|
||||
public class ZuulHandlerMapping extends AbstractUrlHandlerMapping implements
|
||||
ApplicationListener<InstanceRegisteredEvent>, MvcEndpoint {
|
||||
|
||||
private ZuulRouteLocator routeLocator;
|
||||
private ProxyRouteLocator routeLocator;
|
||||
|
||||
private ZuulController zuul;
|
||||
|
||||
private ZuulProperties properties;
|
||||
|
||||
@Autowired
|
||||
public ZuulHandlerMapping(ZuulRouteLocator routeLocator, ZuulController zuul,
|
||||
ZuulProperties properties) {
|
||||
public ZuulHandlerMapping(ProxyRouteLocator routeLocator, ZuulController zuul) {
|
||||
this.routeLocator = routeLocator;
|
||||
this.zuul = zuul;
|
||||
this.properties = properties;
|
||||
setOrder(-200);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(InstanceRegisteredEvent event) {
|
||||
registerHandlers(routeLocator.getRoutes().keySet());
|
||||
registerHandlers(routeLocator.getRoutePaths());
|
||||
}
|
||||
|
||||
protected void registerHandlers(Collection<String> routes) {
|
||||
@@ -53,18 +48,6 @@ public class ZuulHandlerMapping extends AbstractUrlHandlerMapping implements
|
||||
}
|
||||
else {
|
||||
for (String url : routes) {
|
||||
// Prepend with slash if not already present.
|
||||
if (!url.startsWith("/")) {
|
||||
url = "/" + url;
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(properties.getPrefix())) {
|
||||
url = properties.getPrefix() + url;
|
||||
if (!url.startsWith("/")) {
|
||||
url = "/" + url;
|
||||
}
|
||||
}
|
||||
|
||||
registerHandler(url, zuul);
|
||||
}
|
||||
}
|
||||
@@ -75,7 +58,7 @@ public class ZuulHandlerMapping extends AbstractUrlHandlerMapping implements
|
||||
@ManagedOperation
|
||||
public Map<String, String> reset() {
|
||||
routeLocator.resetRoutes();
|
||||
registerHandlers(routeLocator.getRoutes().keySet());
|
||||
registerHandlers(routeLocator.getRoutePaths());
|
||||
return getRoutes();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public class ZuulProperties {
|
||||
private String path;
|
||||
private String serviceId;
|
||||
private String url;
|
||||
private boolean stripPath = false;
|
||||
private boolean stripPrefix = false;
|
||||
|
||||
public ZuulRoute(String text) {
|
||||
String location = null;
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
package org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulProperties.ZuulRoute;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Slf4j
|
||||
public class ZuulRouteLocator implements ApplicationListener<EnvironmentChangeEvent> {
|
||||
|
||||
public static final String DEFAULT_ROUTE = "/";
|
||||
|
||||
private DiscoveryClient discovery;
|
||||
|
||||
private ZuulProperties properties;
|
||||
|
||||
private Field propertySourcesField;
|
||||
private AtomicReference<LinkedHashMap<String, String>> routes = new AtomicReference<>();
|
||||
|
||||
public ZuulRouteLocator(DiscoveryClient discovery, ZuulProperties properties) {
|
||||
this.discovery = discovery;
|
||||
this.properties = properties;
|
||||
initField();
|
||||
}
|
||||
|
||||
private void initField() {
|
||||
propertySourcesField = ReflectionUtils.findField(CompositePropertySource.class,
|
||||
"propertySources");
|
||||
propertySourcesField.setAccessible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EnvironmentChangeEvent event) {
|
||||
for (String key : event.getKeys()) {
|
||||
if (key.startsWith("zuul.route")) {
|
||||
resetRoutes();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<String> getRoutePaths() {
|
||||
return getRoutes().keySet();
|
||||
}
|
||||
|
||||
public Map<String, String> getRoutes() {
|
||||
if (routes.get() == null) {
|
||||
resetRoutes();
|
||||
}
|
||||
|
||||
return routes.get();
|
||||
}
|
||||
|
||||
//access so ZuulHandlerMapping actuator can reset it's mappings
|
||||
/*package*/ void resetRoutes() {
|
||||
LinkedHashMap<String, String> newValue = locateRoutes();
|
||||
routes.set(newValue);
|
||||
}
|
||||
|
||||
protected LinkedHashMap<String, String> locateRoutes() {
|
||||
LinkedHashMap<String, String> routesMap = new LinkedHashMap<>();
|
||||
|
||||
addConfiguredRoutes(routesMap);
|
||||
|
||||
String defaultServiceId = routesMap.get(DEFAULT_ROUTE);
|
||||
|
||||
// Add routes for discovery services by default
|
||||
List<String> services = discovery.getServices();
|
||||
for (String serviceId : services) {
|
||||
// Ignore specifically ignored services and those that were manually configured
|
||||
String key = "/" + serviceId + "/**";
|
||||
if (!properties.getIgnoredServices().contains(serviceId) && !routesMap.containsKey(key)) {
|
||||
routesMap.put(key, serviceId);
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultServiceId != null) {
|
||||
// move the defaultServiceId to the end
|
||||
routesMap.remove(DEFAULT_ROUTE);
|
||||
routesMap.put(DEFAULT_ROUTE, defaultServiceId);
|
||||
}
|
||||
return routesMap;
|
||||
}
|
||||
|
||||
protected void addConfiguredRoutes(Map<String, String> routes) {
|
||||
Map<String, ZuulRoute> routeEntries = properties.getRoutesWithDefaultServiceIds();
|
||||
for (ZuulRoute entry : routeEntries.values()) {
|
||||
String location = entry.getLocation();
|
||||
String route = entry.getPath();
|
||||
|
||||
if (routes.containsKey(route)) {
|
||||
log.warn("Overwriting route {}: already defined by {}", route,
|
||||
routes.get(route));
|
||||
}
|
||||
routes.put(route, location);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +1,27 @@
|
||||
package org.springframework.cloud.netflix.zuul.filters.pre;
|
||||
|
||||
import static com.google.common.collect.Iterables.tryFind;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulRouteLocator;
|
||||
import org.springframework.cloud.netflix.zuul.ProxyRouteLocator;
|
||||
import org.springframework.cloud.netflix.zuul.ProxyRouteLocator.ProxyRouteSpec;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulProperties;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
public class PreDecorationFilter extends ZuulFilter {
|
||||
private static Logger LOG = LoggerFactory.getLogger(PreDecorationFilter.class);
|
||||
|
||||
private ZuulRouteLocator routeLocator;
|
||||
private ProxyRouteLocator routeLocator;
|
||||
|
||||
private ZuulProperties properties;
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
public PreDecorationFilter(ZuulRouteLocator routeLocator, ZuulProperties properties) {
|
||||
public PreDecorationFilter(ProxyRouteLocator routeLocator, ZuulProperties properties) {
|
||||
this.routeLocator = routeLocator;
|
||||
this.properties = properties;
|
||||
}
|
||||
@@ -55,44 +45,27 @@ public class PreDecorationFilter extends ZuulFilter {
|
||||
public Object run() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
|
||||
String requestURI = ctx.getRequest().getRequestURI();
|
||||
final String requestURI = ctx.getRequest().getRequestURI();
|
||||
|
||||
String proxyMapping = properties.getPrefix();
|
||||
ProxyRouteSpec route = routeLocator.getMatchingRoute(requestURI);
|
||||
|
||||
final String uriPart;
|
||||
if (StringUtils.hasText(proxyMapping) && properties.isStripPrefix()
|
||||
&& requestURI.startsWith(proxyMapping)) {
|
||||
// TODO: better strategy?
|
||||
uriPart = requestURI.substring(proxyMapping.length());
|
||||
}
|
||||
else {
|
||||
uriPart = requestURI;
|
||||
}
|
||||
ctx.put("requestURI", uriPart);
|
||||
if (route!=null) {
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
String location = route.getLocation();
|
||||
|
||||
Optional<String> route = tryFind(routesMap.keySet(), new Predicate<String>() {
|
||||
@Override
|
||||
public boolean apply(@Nullable String path) {
|
||||
return pathMatcher.match(path, uriPart);
|
||||
}
|
||||
});
|
||||
if (location != null) {
|
||||
|
||||
if (route.isPresent()) {
|
||||
String target = routesMap.get(route.get());
|
||||
ctx.put("requestURI", route.getPath());
|
||||
|
||||
if (target != null) {
|
||||
|
||||
if (target.startsWith("http:") || target.startsWith("https:")) {
|
||||
ctx.setRouteHost(getUrl(target));
|
||||
ctx.addOriginResponseHeader("X-Zuul-Service", target);
|
||||
if (location.startsWith("http:") || location.startsWith("https:")) {
|
||||
ctx.setRouteHost(getUrl(location));
|
||||
ctx.addOriginResponseHeader("X-Zuul-Service", location);
|
||||
}
|
||||
else {
|
||||
// set serviceId for use in filters.route.RibbonRequest
|
||||
ctx.set("serviceId", target);
|
||||
ctx.set("serviceId", location);
|
||||
ctx.setRouteHost(null);
|
||||
ctx.addOriginResponseHeader("X-Zuul-ServiceId", target);
|
||||
ctx.addOriginResponseHeader("X-Zuul-ServiceId", location);
|
||||
}
|
||||
|
||||
if (properties.isAddProxyHeaders()) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.cloud.netflix.zuul;
|
||||
package org.springframework.cloud.netflix.zuul.filters.route;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.client.http.HttpResponse;
|
||||
@@ -19,7 +19,6 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.actuate.trace.TraceRepository;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClientPreprocessor;
|
||||
import org.springframework.cloud.netflix.zuul.RibbonCommand;
|
||||
import org.springframework.cloud.netflix.zuul.SpringFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
package org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.netflix.zuul.ProxyRouteLocator.ProxyRouteSpec;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulProperties.ZuulRoute;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ProxyRouteLocatorTests {
|
||||
|
||||
public static final String IGNOREDSERVICE = "ignoredservice";
|
||||
public static final String ASERVICE = "aservice";
|
||||
public static final String MYSERVICE = "myservice";
|
||||
|
||||
@Mock
|
||||
ConfigurableEnvironment env;
|
||||
|
||||
@Mock
|
||||
DiscoveryClient discovery;
|
||||
|
||||
private ZuulProperties properties = new ZuulProperties();
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMatchingPath() throws Exception {
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
|
||||
this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**"));
|
||||
routeLocator.getRoutes(); // force refresh
|
||||
ProxyRouteSpec route = routeLocator.getMatchingRoute("/foo/1");
|
||||
assertEquals("foo", route.getLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMatchingPathWithPrefix() throws Exception {
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
|
||||
this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**"));
|
||||
this.properties.setPrefix("/proxy");
|
||||
routeLocator.getRoutes(); // force refresh
|
||||
ProxyRouteSpec route = routeLocator.getMatchingRoute("/proxy/foo/1");
|
||||
assertEquals("foo", route.getLocation());
|
||||
assertEquals("/proxy/foo/1", route.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMatchingPathWithPrefixStripping() throws Exception {
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
|
||||
this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**"));
|
||||
this.properties.setStripPrefix(true);
|
||||
this.properties.setPrefix("/proxy");
|
||||
routeLocator.getRoutes(); // force refresh
|
||||
ProxyRouteSpec route = routeLocator.getMatchingRoute("/proxy/foo/1");
|
||||
assertEquals("foo", route.getLocation());
|
||||
assertEquals("/foo/1", route.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMatchingPathWithRoutePrefixStripping() throws Exception {
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
|
||||
ZuulRoute zuulRoute = new ZuulRoute("/foo/**");
|
||||
zuulRoute.setStripPrefix(true);
|
||||
this.properties.getRoutes().put("foo", zuulRoute);
|
||||
routeLocator.getRoutes(); // force refresh
|
||||
ProxyRouteSpec route = routeLocator.getMatchingRoute("/foo/1");
|
||||
assertEquals("foo", route.getLocation());
|
||||
assertEquals("/1", route.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRoutes() {
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
|
||||
this.properties.getRoutes().put(ASERVICE, new ZuulRoute("/"+ASERVICE + "/**"));
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, ASERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRoutesWithMapping() {
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
|
||||
this.properties.getRoutes().put(ASERVICE, new ZuulRoute("/"+ASERVICE + "/**", ASERVICE));
|
||||
this.properties.setPrefix("/foo");
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
assertMapping(routesMap, ASERVICE, "foo/" + ASERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPhysicalRoutes() {
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
|
||||
this.properties.getRoutes().put(ASERVICE, new ZuulRoute("/"+ASERVICE + "/**", "http://" + ASERVICE));
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, "http://" + ASERVICE, ASERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoreRoutes() {
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
|
||||
this.properties.setIgnoredServices(Lists.newArrayList(IGNOREDSERVICE));
|
||||
|
||||
when(discovery.getServices()).thenReturn(
|
||||
Lists.newArrayList(IGNOREDSERVICE));
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
String serviceId = routesMap.get(getMapping(IGNOREDSERVICE));
|
||||
assertNull("routes did not ignore " + IGNOREDSERVICE, serviceId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoRoutes() {
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
|
||||
|
||||
when(discovery.getServices()).thenReturn(
|
||||
Lists.newArrayList(MYSERVICE));
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, MYSERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoRoutesCanBeOverridden() {
|
||||
this.properties.getRoutes().put(MYSERVICE, new ZuulRoute("/"+MYSERVICE + "/**", "http://example.com/" + MYSERVICE));
|
||||
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
|
||||
|
||||
when(discovery.getServices()).thenReturn(
|
||||
Lists.newArrayList(MYSERVICE));
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, "http://example.com/" + MYSERVICE, MYSERVICE);
|
||||
}
|
||||
|
||||
protected void assertMapping(Map<String, String> routesMap, String serviceId) {
|
||||
assertMapping(routesMap, serviceId, serviceId);
|
||||
}
|
||||
|
||||
protected void assertMapping(Map<String, String> routesMap, String expectedRoute, String key) {
|
||||
String mapping = getMapping(key);
|
||||
String route = routesMap.get(mapping);
|
||||
assertEquals("routesMap had wrong value for " + mapping, expectedRoute,
|
||||
route);
|
||||
}
|
||||
|
||||
private String getMapping(String serviceId) {
|
||||
return "/" + serviceId + "/**";
|
||||
}
|
||||
}
|
||||
@@ -28,13 +28,13 @@ public class SampleZuulProxyApplicationTests {
|
||||
private int port;
|
||||
|
||||
@Autowired
|
||||
private ZuulRouteLocator routes;
|
||||
private ProxyRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private ZuulHandlerMapping mapping;
|
||||
|
||||
@Test
|
||||
public void bindRouteUsingPropertyEditor() {
|
||||
public void bindRouteUsingPhysicalRoute() {
|
||||
assertEquals("http://localhost:7777/local", routes.getRoutes().get("/test/**"));
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ public class SampleZuulProxyApplicationTests {
|
||||
|
||||
@Test
|
||||
public void deleteOnSelfViaSimpleHostRoutingFilter() {
|
||||
routes.getRoutes().put("/self/**", "http://localhost:" + port + "/local");
|
||||
mapping.registerHandlers(routes.getRoutes().keySet());
|
||||
routes.addRoute("/self/**", "http://localhost:" + port + "/local");
|
||||
mapping.reset();
|
||||
ResponseEntity<String> result = new TestRestTemplate().exchange(
|
||||
"http://localhost:" + port + "/self/1", HttpMethod.DELETE,
|
||||
new HttpEntity<Void>((Void) null), String.class);
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
package org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulProperties.ZuulRoute;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ZuulRouteLocatorTests {
|
||||
|
||||
public static final String IGNOREDSERVICE = "ignoredservice";
|
||||
public static final String ASERVICE = "aservice";
|
||||
public static final String MYSERVICE = "myservice";
|
||||
|
||||
@Mock
|
||||
ConfigurableEnvironment env;
|
||||
|
||||
@Mock
|
||||
DiscoveryClient discovery;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRoutes() {
|
||||
ZuulProperties properties = new ZuulProperties();
|
||||
ZuulRouteLocator routeLocator = new ZuulRouteLocator(this.discovery, properties);
|
||||
properties.getRoutes().put(ASERVICE, new ZuulRoute("/"+ASERVICE + "/**"));
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, ASERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRoutesWithMapping() {
|
||||
ZuulProperties properties = new ZuulProperties();
|
||||
ZuulRouteLocator routeLocator = new ZuulRouteLocator(this.discovery, properties);
|
||||
properties.getRoutes().put(ASERVICE, new ZuulRoute("/"+ASERVICE + "/**", ASERVICE));
|
||||
// Prefix doesn't have any impact on the routes (it's used in the filter)
|
||||
properties.setPrefix("/foo");
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, ASERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPhysicalRoutes() {
|
||||
ZuulProperties properties = new ZuulProperties();
|
||||
ZuulRouteLocator routeLocator = new ZuulRouteLocator(this.discovery, properties);
|
||||
properties.getRoutes().put(ASERVICE, new ZuulRoute("/"+ASERVICE + "/**", "http://" + ASERVICE));
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, "http://" + ASERVICE, ASERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoreRoutes() {
|
||||
ZuulProperties properties = new ZuulProperties();
|
||||
ZuulRouteLocator routeLocator = new ZuulRouteLocator(this.discovery, properties);
|
||||
properties.setIgnoredServices(Lists.newArrayList(IGNOREDSERVICE));
|
||||
|
||||
when(discovery.getServices()).thenReturn(
|
||||
Lists.newArrayList(IGNOREDSERVICE));
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
String serviceId = routesMap.get(getMapping(IGNOREDSERVICE));
|
||||
assertNull("routes did not ignore " + IGNOREDSERVICE, serviceId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoRoutes() {
|
||||
ZuulProperties properties = new ZuulProperties();
|
||||
ZuulRouteLocator routeLocator = new ZuulRouteLocator(this.discovery, properties);
|
||||
|
||||
when(discovery.getServices()).thenReturn(
|
||||
Lists.newArrayList(MYSERVICE));
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, MYSERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoRoutesCanBeOverridden() {
|
||||
ZuulProperties properties = new ZuulProperties();
|
||||
properties.getRoutes().put(MYSERVICE, new ZuulRoute("/"+MYSERVICE + "/**", "http://example.com/" + MYSERVICE));
|
||||
ZuulRouteLocator routeLocator = new ZuulRouteLocator(this.discovery, properties);
|
||||
|
||||
when(discovery.getServices()).thenReturn(
|
||||
Lists.newArrayList(MYSERVICE));
|
||||
|
||||
Map<String, String> routesMap = routeLocator.getRoutes();
|
||||
|
||||
assertNotNull("routesMap was null", routesMap);
|
||||
assertFalse("routesMap was empty", routesMap.isEmpty());
|
||||
assertMapping(routesMap, "http://example.com/" + MYSERVICE, MYSERVICE);
|
||||
}
|
||||
|
||||
protected void assertMapping(Map<String, String> routesMap, String serviceId) {
|
||||
assertMapping(routesMap, serviceId, serviceId);
|
||||
}
|
||||
|
||||
protected void assertMapping(Map<String, String> routesMap, String expectedRoute, String key) {
|
||||
String mapping = getMapping(key);
|
||||
String route = routesMap.get(mapping);
|
||||
assertEquals("routesMap had wrong value for " + mapping, expectedRoute,
|
||||
route);
|
||||
}
|
||||
|
||||
private String getMapping(String serviceId) {
|
||||
return "/" + serviceId + "/**";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user