Commit 8dd9b830 by Dave Syer

Add logic for servlet prefix in ProxyRouteLocator

If the ServerProperties contain a servletPath then the handler mapping does not contain that prefix, but the incoming request URI does. This leads to some interesting prefix stripping gymnastics. All the existing tests assumed that the prefix was empty (the default for a Spring boot app). See gh-199
parent 6f569976
......@@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
......@@ -59,16 +60,21 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
@Autowired
private ZuulProperties zuulProperties;
@Autowired
private ServerProperties server;
@Bean
@Override
public ProxyRouteLocator routeLocator() {
return new ProxyRouteLocator(this.discovery, this.zuulProperties);
return new ProxyRouteLocator(this.server.getServletPrefix(), this.discovery,
this.zuulProperties);
}
// pre filters
@Bean
public PreDecorationFilter preDecorationFilter() {
return new PreDecorationFilter(routeLocator(), this.zuulProperties.isAddProxyHeaders());
return new PreDecorationFilter(routeLocator(),
this.zuulProperties.isAddProxyHeaders());
}
// route filters
......
......@@ -52,7 +52,11 @@ public class ProxyRouteLocator implements RouteLocator {
private Map<String, ZuulRoute> staticRoutes = new LinkedHashMap<>();
public ProxyRouteLocator(DiscoveryClient discovery, ZuulProperties properties) {
private String servletPath;
public ProxyRouteLocator(String servletPath, DiscoveryClient discovery,
ZuulProperties properties) {
this.servletPath = servletPath;
this.discovery = discovery;
this.properties = properties;
}
......@@ -89,6 +93,10 @@ public class ProxyRouteLocator implements RouteLocator {
String targetPath = null;
String id = null;
String prefix = this.properties.getPrefix();
if (StringUtils.hasText(this.servletPath) && !this.servletPath.equals("/")
&& path.startsWith(this.servletPath)) {
path = path.substring(this.servletPath.length());
}
Boolean retryable = this.properties.getRetryable();
for (Entry<String, ZuulRoute> entry : this.routes.get().entrySet()) {
String pattern = entry.getKey();
......
......@@ -30,6 +30,7 @@ import org.springframework.boot.test.TestRestTemplate;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.zuul.filters.ProxyRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity;
......@@ -39,6 +40,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
......@@ -86,7 +88,7 @@ public class SampleZuulProxyApplicationTests {
"http://localhost:" + this.port + "/simple/local/1", HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("Gotten!", result.getBody());
assertEquals("Gotten 1!", result.getBody());
}
@Test
......@@ -97,7 +99,20 @@ public class SampleZuulProxyApplicationTests {
"http://localhost:" + this.port + "/self/1", HttpMethod.DELETE,
new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("Deleted!", result.getBody());
assertEquals("Deleted 1!", result.getBody());
}
@Test
public void stripPrefixFalseAppendsPath() {
this.routes.addRoute(new ZuulRoute("strip", "/strip/**", "strip",
"http://localhost:" + this.port + "/local", false, false));
this.endpoint.reset();
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/strip", HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
// Prefix not stripped to it goes to /local/strip
assertEquals("Gotten strip!", result.getBody());
}
@Test
......@@ -114,7 +129,7 @@ public class SampleZuulProxyApplicationTests {
"http://localhost:" + this.port + "/another/twolevel/local/1",
HttpMethod.GET, new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("Gotten!", result.getBody());
assertEquals("Gotten 1!", result.getBody());
}
}
......@@ -140,13 +155,13 @@ class SampleZuulProxyApplication {
}
@RequestMapping(value = "/local/{id}", method = RequestMethod.DELETE)
public String delete() {
return "Deleted!";
public String delete(@PathVariable String id) {
return "Deleted " + id + "!";
}
@RequestMapping(value = "/local/{id}", method = RequestMethod.GET)
public String get() {
return "Gotten!";
public String get(@PathVariable String id) {
return "Gotten " + id + "!";
}
@RequestMapping("/")
......
/*
* Copyright 2013-2015 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.netflix.zuul;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.cloud.netflix.zuul.filters.ProxyRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertEquals;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ServletPathZuulProxyApplication.class)
@WebAppConfiguration
@IntegrationTest({ "server.port: 0", "server.servletPath: /app" })
@DirtiesContext
public class ServletPathZuulProxyApplicationTests {
@Value("${local.server.port}")
private int port;
@Autowired
private ProxyRouteLocator routes;
@Autowired
private RoutesEndpoint endpoint;
@Test
public void getOnSelfViaSimpleHostRoutingFilter() {
this.routes.addRoute("/self/**", "http://localhost:" + this.port + "/app/local");
this.endpoint.reset();
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/app/self/1", HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("Gotten 1!", result.getBody());
}
@Test
public void stripPrefixFalseAppendsPath() {
this.routes.addRoute(new ZuulRoute("strip", "/strip/**", "strip",
"http://localhost:" + this.port + "/app/local", false, false));
this.endpoint.reset();
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/app/strip", HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
// Prefix not stripped to it goes to /local/strip
assertEquals("Gotten strip!", result.getBody());
}
}
// Don't use @SpringBootApplication because we don't want to component scan
@Configuration
@EnableAutoConfiguration
@RestController
@EnableZuulProxy
class ServletPathZuulProxyApplication {
@RequestMapping(value = "/local/{id}", method = RequestMethod.GET)
public String get(@PathVariable String id) {
return "Gotten " + id + "!";
}
public static void main(String[] args) {
SpringApplication.run(SampleZuulProxyApplication.class, args);
}
}
......@@ -61,7 +61,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testGetMatchingPath() throws Exception {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**"));
this.properties.init();
......@@ -73,7 +73,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testGetMatchingPathWithPrefix() throws Exception {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**"));
this.properties.setPrefix("/proxy");
......@@ -85,8 +85,20 @@ public class ProxyRouteLocatorTests {
}
@Test
public void testGetMatchingPathWithServletPath() throws Exception {
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/app", this.discovery,
this.properties);
this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**"));
this.properties.init();
routeLocator.getRoutes(); // force refresh
ProxyRouteSpec route = routeLocator.getMatchingRoute("/app/foo/1");
assertEquals("foo", route.getLocation());
assertEquals("/1", route.getPath());
}
@Test
public void testGetMatchingPathWithNoPrefixStripping() throws Exception {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.getRoutes().put("foo",
new ZuulRoute("foo", "/foo/**", "foo", null, false, null));
......@@ -100,7 +112,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testGetMatchingPathWithLocalPrefixStripping() throws Exception {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**", "foo"));
this.properties.setStripPrefix(false);
......@@ -113,7 +125,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testGetMatchingPathWithGlobalPrefixStripping() throws Exception {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.getRoutes().put("foo",
new ZuulRoute("foo", "/foo/**", "foo", null, false, null));
......@@ -126,7 +138,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testGetMatchingPathWithRoutePrefixStripping() throws Exception {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
ZuulRoute zuulRoute = new ZuulRoute("/foo/**");
zuulRoute.setStripPrefix(true);
......@@ -140,7 +152,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testGetRoutes() {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.getRoutes().put(ASERVICE, new ZuulRoute("/" + ASERVICE + "/**"));
this.properties.init();
......@@ -152,7 +164,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testGetRoutesWithMapping() {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.getRoutes().put(ASERVICE,
new ZuulRoute("/" + ASERVICE + "/**", ASERVICE));
......@@ -164,7 +176,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testGetPhysicalRoutes() {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.getRoutes().put(ASERVICE,
new ZuulRoute("/" + ASERVICE + "/**", "http://" + ASERVICE));
......@@ -176,7 +188,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testGetDefaultRoute() {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.getRoutes().put(ASERVICE, new ZuulRoute("/**", ASERVICE));
Map<String, String> routesMap = routeLocator.getRoutes();
......@@ -187,7 +199,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testGetDefaultPhysicalRoute() {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.getRoutes().put(ASERVICE,
new ZuulRoute("/**", "http://" + ASERVICE));
......@@ -199,7 +211,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testIgnoreRoutes() {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.setIgnoredServices(Collections.singletonList(IGNOREDSERVICE));
given(this.discovery.getServices()).willReturn(
......@@ -211,7 +223,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testIgnoreRoutesWithPattern() {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.setIgnoredServices(Collections.singletonList("ignore*"));
given(this.discovery.getServices()).willReturn(
......@@ -223,7 +235,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testIgnoreAllRoutes() {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.setIgnoredServices(Collections.singletonList("*"));
given(this.discovery.getServices()).willReturn(
......@@ -236,7 +248,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testIgnoredRouteIncludedIfConfiguredAndDiscovered() {
this.properties.getRoutes().put("foo", new ZuulRoute("/foo/**"));
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.setIgnoredServices(Collections.singletonList("*"));
given(this.discovery.getServices()).willReturn(Collections.singletonList("foo"));
......@@ -249,7 +261,7 @@ public class ProxyRouteLocatorTests {
public void testIgnoredRouteIncludedIfConfiguredAndNotDiscovered() {
this.properties.getRoutes()
.put("foo", new ZuulRoute("/foo/**", "http://foo.com"));
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.properties.setIgnoredServices(Collections.singletonList("*"));
given(this.discovery.getServices()).willReturn(Collections.singletonList("bar"));
......@@ -260,7 +272,7 @@ public class ProxyRouteLocatorTests {
@Test
public void testAutoRoutes() {
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
given(this.discovery.getServices()).willReturn(
Collections.singletonList(MYSERVICE));
......@@ -275,7 +287,7 @@ public class ProxyRouteLocatorTests {
ZuulRoute route = new ZuulRoute("/" + MYSERVICE + "/**", "http://example.com/"
+ MYSERVICE);
this.properties.getRoutes().put(MYSERVICE, route);
ProxyRouteLocator routeLocator = new ProxyRouteLocator(this.discovery,
ProxyRouteLocator routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
given(this.discovery.getServices()).willReturn(
Collections.singletonList(MYSERVICE));
......
......@@ -16,14 +16,12 @@
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.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.zuul.filters.ProxyRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
......@@ -33,6 +31,9 @@ import org.springframework.mock.web.MockHttpServletRequest;
import com.netflix.util.Pair;
import com.netflix.zuul.context.RequestContext;
import static org.junit.Assert.assertEquals;
import static org.mockito.MockitoAnnotations.initMocks;
/**
* @author Dave Syer
*/
......@@ -49,10 +50,13 @@ public class PreDecorationFilterTests {
private MockHttpServletRequest request = new MockHttpServletRequest();
private ServerProperties server = new ServerProperties();
@Before
public void init() {
initMocks(this);
this.routeLocator = new ProxyRouteLocator(this.discovery, this.properties);
this.routeLocator = new ProxyRouteLocator("/", this.discovery,
this.properties);
this.filter = new PreDecorationFilter(this.routeLocator, true);
RequestContext ctx = RequestContext.getCurrentContext();
ctx.setRequest(this.request);
......@@ -70,7 +74,8 @@ public class PreDecorationFilterTests {
this.properties.setPrefix("/api");
this.properties.setStripPrefix(true);
this.request.setRequestURI("/api/foo/1");
this.routeLocator.addRoute(new ZuulRoute("foo", "/foo/**", "foo", null, false, null));
this.routeLocator.addRoute(new ZuulRoute("foo", "/foo/**", "foo", null, false,
null));
this.filter.run();
RequestContext ctx = RequestContext.getCurrentContext();
assertEquals("/foo/1", ctx.get("requestURI"));
......
......@@ -2,7 +2,7 @@ server:
port: 9999
logging:
level:
# org.springframework.web: DEBUG
org.springframework.web: DEBUG
spring:
application:
name: testclient
......
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