Commit 7b270c4b by Dave Syer

Add support for X-Forwarded-Prefix to Zuul proxy

Fixes gh-43
parent 9c6474fb
...@@ -32,7 +32,7 @@ public class ProxyRouteLocator { ...@@ -32,7 +32,7 @@ public class ProxyRouteLocator {
private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>(); 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) { public ProxyRouteLocator(DiscoveryClient discovery, ZuulProperties properties) {
this.discovery = discovery; this.discovery = discovery;
...@@ -40,7 +40,12 @@ public class ProxyRouteLocator { ...@@ -40,7 +40,12 @@ public class ProxyRouteLocator {
} }
public void addRoute(String path, String location) { 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(); resetRoutes();
} }
...@@ -56,7 +61,7 @@ public class ProxyRouteLocator { ...@@ -56,7 +61,7 @@ public class ProxyRouteLocator {
Map<String, String> values = new LinkedHashMap<String, String>(); Map<String, String> values = new LinkedHashMap<String, String>();
for (String key : routes.get().keySet()) { for (String key : routes.get().keySet()) {
String url = key; String url = key;
values.put(url, routes.get().get(key).getLocation()); values.put(url, routes.get().get(key).getLocation());
} }
...@@ -67,26 +72,30 @@ public class ProxyRouteLocator { ...@@ -67,26 +72,30 @@ public class ProxyRouteLocator {
public ProxyRouteSpec getMatchingRoute(String path) { public ProxyRouteSpec getMatchingRoute(String path) {
String location = null; String location = null;
String targetPath = null; String targetPath = null;
String prefix = properties.getPrefix();
for (Entry<String, ZuulRoute> entry : routes.get().entrySet()) { for (Entry<String, ZuulRoute> entry : routes.get().entrySet()) {
String pattern = entry.getKey(); String pattern = entry.getKey();
if (pathMatcher.match(pattern, path)) { if (pathMatcher.match(pattern, path)) {
ZuulRoute route = entry.getValue(); ZuulRoute route = entry.getValue();
String prefix = properties.getPrefix();
location = route.getLocation(); location = route.getLocation();
targetPath = path; targetPath = path;
if (path.startsWith(prefix) && properties.isStripPrefix()) { if (path.startsWith(prefix) && properties.isStripPrefix()) {
targetPath = path.substring(prefix.length()); targetPath = path.substring(prefix.length());
} }
if (route.isStripPrefix()) { if (route.isStripPrefix()) {
int index = route.getPath().indexOf("*"); int index = route.getPath().indexOf("*") - 1;
index = index > 0 ? index-1 : 0; if (index > 0) {
targetPath = path.substring(index); 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 // Package access so ZuulHandlerMapping can reset it's mappings
void resetRoutes() { void resetRoutes() {
routes.set(locateRoutes()); routes.set(locateRoutes());
...@@ -97,7 +106,7 @@ public class ProxyRouteLocator { ...@@ -97,7 +106,7 @@ public class ProxyRouteLocator {
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>(); LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
addConfiguredRoutes(routesMap); addConfiguredRoutes(routesMap);
addStaticRoutes(routesMap); routesMap.putAll(staticRoutes);
// Add routes for discovery services by default // Add routes for discovery services by default
List<String> services = discovery.getServices(); List<String> services = discovery.getServices();
...@@ -120,7 +129,7 @@ public class ProxyRouteLocator { ...@@ -120,7 +129,7 @@ public class ProxyRouteLocator {
LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>(); LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) { for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
String path = entry.getKey(); String path = entry.getKey();
// Prepend with slash if not already present. // Prepend with slash if not already present.
if (!path.startsWith("/")) { if (!path.startsWith("/")) {
...@@ -133,7 +142,7 @@ public class ProxyRouteLocator { ...@@ -133,7 +142,7 @@ public class ProxyRouteLocator {
path = "/" + path; path = "/" + path;
} }
} }
values.put(path, entry.getValue()); values.put(path, entry.getValue());
} }
...@@ -142,12 +151,6 @@ public class ProxyRouteLocator { ...@@ -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) { protected void addConfiguredRoutes(Map<String, ZuulRoute> routes) {
Map<String, ZuulRoute> routeEntries = properties.getRoutesWithDefaultServiceIds(); Map<String, ZuulRoute> routeEntries = properties.getRoutesWithDefaultServiceIds();
for (ZuulRoute entry : routeEntries.values()) { for (ZuulRoute entry : routeEntries.values()) {
...@@ -162,13 +165,14 @@ public class ProxyRouteLocator { ...@@ -162,13 +165,14 @@ public class ProxyRouteLocator {
public String getTargetPath(String matchingRoute, String requestURI) { public String getTargetPath(String matchingRoute, String requestURI) {
String path = getRoutes().get(matchingRoute); String path = getRoutes().get(matchingRoute);
if (path==null) { if (path == null) {
path = requestURI; path = requestURI;
} else { }
else {
} }
return path; return path;
} }
@Data @Data
...@@ -176,6 +180,7 @@ public class ProxyRouteLocator { ...@@ -176,6 +180,7 @@ public class ProxyRouteLocator {
public static class ProxyRouteSpec { public static class ProxyRouteSpec {
private String path; private String path;
private String location; private String location;
private String prefix;
} }
} }
...@@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory; ...@@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.ProxyRouteLocator; import org.springframework.cloud.netflix.zuul.ProxyRouteLocator;
import org.springframework.cloud.netflix.zuul.ProxyRouteLocator.ProxyRouteSpec; import org.springframework.cloud.netflix.zuul.ProxyRouteLocator.ProxyRouteSpec;
import org.springframework.cloud.netflix.zuul.ZuulProperties; import org.springframework.cloud.netflix.zuul.ZuulProperties;
import org.springframework.util.StringUtils;
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.context.RequestContext;
...@@ -49,7 +50,7 @@ public class PreDecorationFilter extends ZuulFilter { ...@@ -49,7 +50,7 @@ public class PreDecorationFilter extends ZuulFilter {
ProxyRouteSpec route = routeLocator.getMatchingRoute(requestURI); ProxyRouteSpec route = routeLocator.getMatchingRoute(requestURI);
if (route!=null) { if (route != null) {
String location = route.getLocation(); String location = route.getLocation();
...@@ -73,6 +74,9 @@ public class PreDecorationFilter extends ZuulFilter { ...@@ -73,6 +74,9 @@ public class PreDecorationFilter extends ZuulFilter {
"X-Forwarded-Host", "X-Forwarded-Host",
ctx.getRequest().getServerName() + ":" ctx.getRequest().getServerName() + ":"
+ String.valueOf(ctx.getRequest().getServerPort())); + String.valueOf(ctx.getRequest().getServerPort()));
if (StringUtils.hasText(route.getPrefix())) {
ctx.addZuulRequestHeader("X-Forwarded-Prefix", route.getPrefix());
}
} }
} }
} }
......
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;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment