Add support for X-Forwarded-Prefix to Zuul proxy
Fixes gh-43
This commit is contained in:
@@ -32,7 +32,7 @@ public class ProxyRouteLocator {
|
||||
|
||||
private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>();
|
||||
|
||||
private Map<String, String> staticRoutes = new LinkedHashMap<String, String>();
|
||||
private Map<String, ZuulRoute> staticRoutes = new LinkedHashMap<String, ZuulRoute>();
|
||||
|
||||
public ProxyRouteLocator(DiscoveryClient discovery, ZuulProperties properties) {
|
||||
this.discovery = discovery;
|
||||
@@ -40,7 +40,12 @@ public class ProxyRouteLocator {
|
||||
}
|
||||
|
||||
public void addRoute(String path, String location) {
|
||||
staticRoutes.put(path, location);
|
||||
staticRoutes.put(path, new ZuulRoute(path, location));
|
||||
resetRoutes();
|
||||
}
|
||||
|
||||
public void addRoute(ZuulRoute route) {
|
||||
staticRoutes.put(route.getPath(), route);
|
||||
resetRoutes();
|
||||
}
|
||||
|
||||
@@ -56,7 +61,7 @@ public class ProxyRouteLocator {
|
||||
|
||||
Map<String, String> values = new LinkedHashMap<String, String>();
|
||||
|
||||
for (String key : routes.get().keySet()) {
|
||||
for (String key : routes.get().keySet()) {
|
||||
String url = key;
|
||||
values.put(url, routes.get().get(key).getLocation());
|
||||
}
|
||||
@@ -67,26 +72,30 @@ public class ProxyRouteLocator {
|
||||
public ProxyRouteSpec getMatchingRoute(String path) {
|
||||
String location = null;
|
||||
String targetPath = null;
|
||||
String prefix = properties.getPrefix();
|
||||
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);
|
||||
int index = route.getPath().indexOf("*") - 1;
|
||||
if (index > 0) {
|
||||
targetPath = path.substring(prefix.length() + index);
|
||||
prefix = prefix
|
||||
+ path.substring(prefix.length(), prefix.length() + index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return location==null ? null : new ProxyRouteSpec(targetPath, location);
|
||||
return location == null ? null : new ProxyRouteSpec(targetPath, location, prefix);
|
||||
}
|
||||
|
||||
|
||||
// Package access so ZuulHandlerMapping can reset it's mappings
|
||||
void resetRoutes() {
|
||||
routes.set(locateRoutes());
|
||||
@@ -97,7 +106,7 @@ public class ProxyRouteLocator {
|
||||
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
|
||||
|
||||
addConfiguredRoutes(routesMap);
|
||||
addStaticRoutes(routesMap);
|
||||
routesMap.putAll(staticRoutes);
|
||||
|
||||
// Add routes for discovery services by default
|
||||
List<String> services = discovery.getServices();
|
||||
@@ -120,7 +129,7 @@ public class ProxyRouteLocator {
|
||||
|
||||
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("/")) {
|
||||
@@ -133,7 +142,7 @@ public class ProxyRouteLocator {
|
||||
path = "/" + path;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
values.put(path, entry.getValue());
|
||||
|
||||
}
|
||||
@@ -142,12 +151,6 @@ public class ProxyRouteLocator {
|
||||
|
||||
}
|
||||
|
||||
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()) {
|
||||
@@ -162,13 +165,14 @@ public class ProxyRouteLocator {
|
||||
|
||||
public String getTargetPath(String matchingRoute, String requestURI) {
|
||||
String path = getRoutes().get(matchingRoute);
|
||||
if (path==null) {
|
||||
if (path == null) {
|
||||
path = requestURI;
|
||||
} else {
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
return path;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@@ -176,6 +180,7 @@ public class ProxyRouteLocator {
|
||||
public static class ProxyRouteSpec {
|
||||
private String path;
|
||||
private String location;
|
||||
private String prefix;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory;
|
||||
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.StringUtils;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
@@ -49,7 +50,7 @@ public class PreDecorationFilter extends ZuulFilter {
|
||||
|
||||
ProxyRouteSpec route = routeLocator.getMatchingRoute(requestURI);
|
||||
|
||||
if (route!=null) {
|
||||
if (route != null) {
|
||||
|
||||
String location = route.getLocation();
|
||||
|
||||
@@ -73,6 +74,9 @@ public class PreDecorationFilter extends ZuulFilter {
|
||||
"X-Forwarded-Host",
|
||||
ctx.getRequest().getServerName() + ":"
|
||||
+ String.valueOf(ctx.getRequest().getServerPort()));
|
||||
if (StringUtils.hasText(route.getPrefix())) {
|
||||
ctx.addZuulRequestHeader("X-Forwarded-Prefix", route.getPrefix());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package org.springframework.cloud.netflix.zuul.filters.pre;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulProperties;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulProperties.ZuulRoute;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
|
||||
import com.netflix.util.Pair;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PreDecorationFilterTests {
|
||||
|
||||
private PreDecorationFilter filter;
|
||||
|
||||
@Mock
|
||||
DiscoveryClient discovery;
|
||||
|
||||
private ZuulProperties properties = new ZuulProperties();
|
||||
|
||||
private ProxyRouteLocator routeLocator;
|
||||
|
||||
private MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
initMocks(this);
|
||||
routeLocator = new ProxyRouteLocator(discovery, properties);
|
||||
filter = new PreDecorationFilter(routeLocator, properties);
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
ctx.setRequest(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicProperties() throws Exception {
|
||||
assertEquals(5, filter.filterOrder());
|
||||
assertEquals(true, filter.shouldFilter());
|
||||
assertEquals("pre", filter.filterType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefixRouteAddsHeader() throws Exception {
|
||||
properties.setPrefix("/api");
|
||||
properties.setStripPrefix(true);
|
||||
request.setRequestURI("/api/foo/1");
|
||||
routeLocator.addRoute("/foo/**", "foo");
|
||||
filter.run();
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
assertEquals("/foo/1", ctx.get("requestURI"));
|
||||
assertEquals("localhost:80", ctx.getZuulRequestHeaders().get("x-forwarded-host"));
|
||||
assertEquals("/api", ctx.getZuulRequestHeaders().get("x-forwarded-prefix"));
|
||||
assertEquals("foo", getHeader(ctx.getOriginResponseHeaders(), "x-zuul-serviceid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefixRouteWithRouteStrippingAddsHeader() throws Exception {
|
||||
properties.setPrefix("/api");
|
||||
properties.setStripPrefix(true);
|
||||
request.setRequestURI("/api/foo/1");
|
||||
routeLocator.addRoute(new ZuulRoute("/foo/**", "foo", null, true));
|
||||
filter.run();
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
assertEquals("/1", ctx.get("requestURI"));
|
||||
assertEquals("localhost:80", ctx.getZuulRequestHeaders().get("x-forwarded-host"));
|
||||
assertEquals("/api/foo", ctx.getZuulRequestHeaders().get("x-forwarded-prefix"));
|
||||
assertEquals("foo", getHeader(ctx.getOriginResponseHeaders(), "x-zuul-serviceid"));
|
||||
}
|
||||
|
||||
private Object getHeader(List<Pair<String, String>> headers,
|
||||
String key) {
|
||||
String value = null;
|
||||
for (Pair<String, String> pair : headers) {
|
||||
if (pair.first().toLowerCase().equals(key.toLowerCase())) {
|
||||
value = pair.second();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user