Commit 0235d779 by Ryan Baxter Committed by Spencer Gibb

Zuul Route Hystrix Fallback

Hystrix fallback support for Zuul routes. Fixes gh-250.
parent 6e1ca54e
......@@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
......@@ -52,6 +53,7 @@ import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonComm
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter;
import org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
......@@ -94,11 +96,14 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
@ConditionalOnRibbonHttpClient
protected static class HttpClientRibbonConfiguration {
@Autowired(required = false)
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
@Bean
@ConditionalOnMissingBean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
return new HttpClientRibbonCommandFactory(clientFactory, zuulProperties);
return new HttpClientRibbonCommandFactory(clientFactory, zuulProperties, zuulFallbackProviders);
}
}
......@@ -106,11 +111,15 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
@ConditionalOnRibbonRestClient
protected static class RestClientRibbonConfiguration {
@Autowired(required = false)
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
@Bean
@ConditionalOnMissingBean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
return new RestClientRibbonCommandFactory(clientFactory, zuulProperties);
return new RestClientRibbonCommandFactory(clientFactory, zuulProperties,
zuulFallbackProviders);
}
}
......@@ -119,11 +128,15 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
@ConditionalOnClass(name = "okhttp3.OkHttpClient")
protected static class OkHttpRibbonConfiguration {
@Autowired(required = false)
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
@Bean
@ConditionalOnMissingBean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
return new OkHttpRibbonCommandFactory(clientFactory, zuulProperties);
return new OkHttpRibbonCommandFactory(clientFactory, zuulProperties,
zuulFallbackProviders);
}
}
......
......@@ -17,20 +17,18 @@
package org.springframework.cloud.netflix.zuul.filters.route;
import static org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer.Runner.customize;
import java.io.InputStream;
import java.net.URI;
import java.util.List;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommand;
import org.springframework.util.MultiValueMap;
import com.netflix.client.http.HttpRequest;
import com.netflix.client.http.HttpResponse;
import com.netflix.niws.client.http.RestClient;
import static org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer.Runner.customize;
/**
* Hystrix wrapper around Eureka Ribbon command
*
......@@ -44,6 +42,12 @@ public class RestClientRibbonCommand extends AbstractRibbonCommand<RestClient, H
super(commandKey, client, context, zuulProperties);
}
public RestClientRibbonCommand(String commandKey, RestClient client,
RibbonCommandContext context, ZuulProperties zuulProperties,
ZuulFallbackProvider zuulFallbackProvider) {
super(commandKey, client, context, zuulProperties, zuulFallbackProvider);
}
@Deprecated
public RestClientRibbonCommand(String commandKey, RestClient restClient,
HttpRequest.Verb verb, String uri, Boolean retryable,
......
......@@ -17,27 +17,34 @@
package org.springframework.cloud.netflix.zuul.filters.route;
import java.util.Collections;
import java.util.Set;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommandFactory;
import com.netflix.client.http.HttpRequest;
import com.netflix.niws.client.http.RestClient;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
public class RestClientRibbonCommandFactory implements RibbonCommandFactory<RestClientRibbonCommand> {
public class RestClientRibbonCommandFactory extends AbstractRibbonCommandFactory {
private final SpringClientFactory clientFactory;
private SpringClientFactory clientFactory;
private ZuulProperties zuulProperties;
public RestClientRibbonCommandFactory(SpringClientFactory clientFactory) {
this(clientFactory, new ZuulProperties());
this(clientFactory, new ZuulProperties(), Collections.<ZuulFallbackProvider>emptySet());
}
public RestClientRibbonCommandFactory(SpringClientFactory clientFactory,
ZuulProperties zuulProperties) {
ZuulProperties zuulProperties,
Set<ZuulFallbackProvider> zuulFallbackProviders) {
super(zuulFallbackProviders);
this.clientFactory = clientFactory;
this.zuulProperties = zuulProperties;
}
......@@ -45,10 +52,12 @@ public class RestClientRibbonCommandFactory implements RibbonCommandFactory<Rest
@Override
@SuppressWarnings("deprecation")
public RestClientRibbonCommand create(RibbonCommandContext context) {
RestClient restClient = this.clientFactory.getClient(context.getServiceId(),
String serviceId = context.getServiceId();
ZuulFallbackProvider fallbackProvider = getFallbackProvider(serviceId);
RestClient restClient = this.clientFactory.getClient(serviceId,
RestClient.class);
return new RestClientRibbonCommand(context.getServiceId(), restClient, context,
this.zuulProperties);
this.zuulProperties, fallbackProvider);
}
public SpringClientFactory getClientFactory() {
......
/*
*
* * Copyright 2013-2016 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.filters.route;
import org.springframework.http.client.ClientHttpResponse;
/**
* Provides fallback when a failure occurs on a route.
* @author Ryan Baxter
*/
public interface ZuulFallbackProvider {
/**
* The route this fallback will be used for.
* @return The route the fallback will be used for.
*/
public String getRoute();
/**
* Provides a fallback response.
* @return The fallback response.
*/
public ClientHttpResponse fallbackResponse();
}
......@@ -22,10 +22,12 @@ import org.springframework.cloud.netflix.ribbon.apache.RibbonApacheHttpResponse;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommand;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
public class HttpClientRibbonCommand extends AbstractRibbonCommand<RibbonLoadBalancingHttpClient, RibbonApacheHttpRequest, RibbonApacheHttpResponse> {
......@@ -36,6 +38,14 @@ public class HttpClientRibbonCommand extends AbstractRibbonCommand<RibbonLoadBal
super(commandKey, client, context, zuulProperties);
}
public HttpClientRibbonCommand(final String commandKey,
final RibbonLoadBalancingHttpClient client,
final RibbonCommandContext context,
final ZuulProperties zuulProperties,
final ZuulFallbackProvider zuulFallbackProvider) {
super(commandKey, client, context, zuulProperties, zuulFallbackProvider);
}
@Override
protected RibbonApacheHttpRequest createRequest() throws Exception {
return new RibbonApacheHttpRequest(this.context);
......
......@@ -16,33 +16,46 @@
package org.springframework.cloud.netflix.zuul.filters.route.apache;
import java.util.Collections;
import java.util.Set;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
/**
* @author Christian Lohmann
* @author Ryan Baxter
*/
@RequiredArgsConstructor
public class HttpClientRibbonCommandFactory implements
RibbonCommandFactory<HttpClientRibbonCommand> {
public class HttpClientRibbonCommandFactory extends AbstractRibbonCommandFactory {
private final SpringClientFactory clientFactory;
private final ZuulProperties zuulProperties;
public HttpClientRibbonCommandFactory(SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
this(clientFactory, zuulProperties, Collections.<ZuulFallbackProvider>emptySet());
}
public HttpClientRibbonCommandFactory(SpringClientFactory clientFactory, ZuulProperties zuulProperties,
Set<ZuulFallbackProvider> fallbackProviders) {
super(fallbackProviders);
this.clientFactory = clientFactory;
this.zuulProperties = zuulProperties;
}
@Override
public HttpClientRibbonCommand create(final RibbonCommandContext context) {
ZuulFallbackProvider zuulFallbackProvider = getFallbackProvider(context.getServiceId());
final String serviceId = context.getServiceId();
final RibbonLoadBalancingHttpClient client = this.clientFactory.getClient(
serviceId, RibbonLoadBalancingHttpClient.class);
client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties);
return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties, zuulFallbackProvider);
}
}
......@@ -22,20 +22,30 @@ import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpRibbonRequest;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpRibbonResponse;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommand;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
public class OkHttpRibbonCommand extends AbstractRibbonCommand<OkHttpLoadBalancingClient, OkHttpRibbonRequest, OkHttpRibbonResponse> {
public OkHttpRibbonCommand(final String commandKey,
final OkHttpLoadBalancingClient client,
final RibbonCommandContext context,
final ZuulProperties zuulProperties) {
final OkHttpLoadBalancingClient client,
final RibbonCommandContext context,
final ZuulProperties zuulProperties) {
super(commandKey, client, context, zuulProperties);
}
public OkHttpRibbonCommand(final String commandKey,
final OkHttpLoadBalancingClient client,
final RibbonCommandContext context,
final ZuulProperties zuulProperties,
final ZuulFallbackProvider zuulFallbackProvider) {
super(commandKey, client, context, zuulProperties, zuulFallbackProvider);
}
@Override
protected OkHttpRibbonRequest createRequest() throws Exception {
return new OkHttpRibbonRequest(this.context);
......
......@@ -16,33 +16,46 @@
package org.springframework.cloud.netflix.zuul.filters.route.okhttp;
import java.util.Collections;
import java.util.Set;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.ribbon.okhttp.OkHttpLoadBalancingClient;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.AbstractRibbonCommandFactory;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
@RequiredArgsConstructor
public class OkHttpRibbonCommandFactory implements
RibbonCommandFactory<OkHttpRibbonCommand> {
public class OkHttpRibbonCommandFactory extends AbstractRibbonCommandFactory {
private final SpringClientFactory clientFactory;
private SpringClientFactory clientFactory;
private final ZuulProperties zuulProperties;
private ZuulProperties zuulProperties;
public OkHttpRibbonCommandFactory(SpringClientFactory clientFactory, ZuulProperties zuulProperties) {
this(clientFactory, zuulProperties, Collections.<ZuulFallbackProvider>emptySet());
}
public OkHttpRibbonCommandFactory(SpringClientFactory clientFactory, ZuulProperties zuulProperties,
Set<ZuulFallbackProvider> zuulFallbackProviders) {
super(zuulFallbackProviders);
this.clientFactory = clientFactory;
this.zuulProperties = zuulProperties;
}
@Override
public OkHttpRibbonCommand create(final RibbonCommandContext context) {
final String serviceId = context.getServiceId();
ZuulFallbackProvider fallbackProvider = getFallbackProvider(serviceId);
final OkHttpLoadBalancingClient client = this.clientFactory.getClient(
serviceId, OkHttpLoadBalancingClient.class);
client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
return new OkHttpRibbonCommand(serviceId, client, context, zuulProperties);
return new OkHttpRibbonCommand(serviceId, client, context, zuulProperties, fallbackProvider);
}
}
......@@ -21,8 +21,8 @@ import org.springframework.cloud.netflix.ribbon.RibbonHttpResponse;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.http.client.ClientHttpResponse;
import com.netflix.client.AbstractLoadBalancerAwareClient;
import com.netflix.client.ClientRequest;
import com.netflix.client.http.HttpResponse;
......@@ -44,6 +44,7 @@ public abstract class AbstractRibbonCommand<LBC extends AbstractLoadBalancerAwar
protected final LBC client;
protected RibbonCommandContext context;
protected ZuulFallbackProvider zuulFallbackProvider;
public AbstractRibbonCommand(LBC client, RibbonCommandContext context,
ZuulProperties zuulProperties) {
......@@ -52,9 +53,16 @@ public abstract class AbstractRibbonCommand<LBC extends AbstractLoadBalancerAwar
public AbstractRibbonCommand(String commandKey, LBC client,
RibbonCommandContext context, ZuulProperties zuulProperties) {
this(commandKey, client, context, zuulProperties, null);
}
public AbstractRibbonCommand(String commandKey, LBC client,
RibbonCommandContext context, ZuulProperties zuulProperties,
ZuulFallbackProvider fallbackProvider) {
super(getSetter(commandKey, zuulProperties));
this.client = client;
this.context = context;
this.zuulFallbackProvider = fallbackProvider;
}
protected static Setter getSetter(final String commandKey,
......@@ -101,6 +109,14 @@ public abstract class AbstractRibbonCommand<LBC extends AbstractLoadBalancerAwar
return new RibbonHttpResponse(response);
}
@Override
protected ClientHttpResponse getFallback() {
if(zuulFallbackProvider != null) {
return zuulFallbackProvider.fallbackResponse();
}
return super.getFallback();
}
public LBC getClient() {
return client;
}
......
/*
*
* * Copyright 2013-2016 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.filters.route.support;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
/**
* @author Ryan Baxter
*/
public abstract class AbstractRibbonCommandFactory implements RibbonCommandFactory {
private Map<String, ZuulFallbackProvider> fallbackProviderCache;
public AbstractRibbonCommandFactory(Set<ZuulFallbackProvider> fallbackProviders){
this.fallbackProviderCache = new HashMap<String, ZuulFallbackProvider>();
for(ZuulFallbackProvider provider : fallbackProviders) {
fallbackProviderCache.put(provider.getRoute(), provider);
}
}
protected ZuulFallbackProvider getFallbackProvider(String route) {
return fallbackProviderCache.get(route);
}
}
/*
*
* * Copyright 2013-2016 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.filters.route.apache;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonCommandFallbackTests;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.netflix.zuul.context.RequestContext;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = HttpClientRibbonCommandIntegrationTests.TestConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = {
"zuul.routes.simple: /simple/**", "zuul.routes.another: /another/twolevel/**",
"ribbon.ReadTimeout: 1"})
@DirtiesContext
public class HttpClientRibbonCommandFallbackTests extends RibbonCommandFallbackTests {
@Before
public void init() {
RequestContext.testSetCurrentContext(null);
RequestContext.getCurrentContext().unset();
}
}
......@@ -22,6 +22,9 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.http.HttpHeaders.SET_COOKIE;
import java.util.Collections;
import java.util.Set;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
......@@ -29,6 +32,7 @@ import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
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.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
......@@ -44,6 +48,7 @@ import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpCl
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.ZuulProxyTestBase;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -155,6 +160,9 @@ public class HttpClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
@RibbonClient(name = "singleton", configuration = SingletonRibbonClientConfiguration.class) })
static class TestConfig extends ZuulProxyTestBase.AbstractZuulProxyApplication {
@Autowired(required = false)
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
@RequestMapping(value = "/local/{id}", method = RequestMethod.PATCH)
public String patch(@PathVariable final String id,
@RequestBody final String body) {
......@@ -176,7 +184,8 @@ public class HttpClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
@Bean
public RibbonCommandFactory<?> ribbonCommandFactory(
final SpringClientFactory clientFactory) {
return new HttpClientRibbonCommandFactory(clientFactory, new ZuulProperties());
return new HttpClientRibbonCommandFactory(clientFactory, new ZuulProperties(),
zuulFallbackProviders);
}
@Bean
......
/*
*
* * Copyright 2013-2016 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.filters.route.okhttp;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonCommandFallbackTests;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.netflix.zuul.context.RequestContext;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = OkHttpRibbonCommandIntegrationTests.TestConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = {
"zuul.routes.simple: /simple/**", "zuul.routes.another: /another/twolevel/**",
"ribbon.ReadTimeout: 1"})
@DirtiesContext
public class OkHttpRibbonCommandFallbackTests extends RibbonCommandFallbackTests {
@Before
public void init() {
RequestContext.testSetCurrentContext(null);
RequestContext.getCurrentContext().unset();
}
}
......@@ -20,9 +20,13 @@ package org.springframework.cloud.netflix.zuul.filters.route.okhttp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.test.context.SpringBootTest;
......@@ -34,6 +38,7 @@ import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.ZuulProxyTestBase;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -100,10 +105,14 @@ public class OkHttpRibbonCommandIntegrationTests extends ZuulProxyTestBase {
@RibbonClient(name = "another", configuration = AnotherRibbonClientConfiguration.class) })
static class TestConfig extends ZuulProxyTestBase.AbstractZuulProxyApplication {
@Autowired(required = false)
private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
@Bean
public RibbonCommandFactory<?> ribbonCommandFactory(
final SpringClientFactory clientFactory) {
return new OkHttpRibbonCommandFactory(clientFactory, new ZuulProperties());
return new OkHttpRibbonCommandFactory(clientFactory, new ZuulProperties(),
zuulFallbackProviders);
}
@Bean
......
/*
*
* * Copyright 2013-2016 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.filters.route.restclient;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.zuul.filters.route.support.RibbonCommandFallbackTests;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.netflix.zuul.context.RequestContext;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RestClientRibbonCommandIntegrationTests.TestConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = {
"zuul.routes.simple: /simple/**", "zuul.routes.another: /another/twolevel/**",
"ribbon.ReadTimeout: 1"})
@DirtiesContext
public class RestClientRibbonCommandFallbackTests extends RibbonCommandFallbackTests {
@Before
public void init() {
RequestContext.testSetCurrentContext(null);
RequestContext.getCurrentContext().unset();
}
}
......@@ -26,10 +26,14 @@ import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -52,6 +56,7 @@ import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonComm
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.cloud.netflix.zuul.filters.route.support.NoEncodingFormHttpMessageConverter;
import org.springframework.cloud.netflix.zuul.filters.route.support.ZuulProxyTestBase;
import org.springframework.context.annotation.Bean;
......@@ -80,8 +85,6 @@ import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import com.netflix.niws.client.http.RestClient;
import lombok.SneakyThrows;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RestClientRibbonCommandIntegrationTests.TestConfig.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
"zuul.routes.other: /test/**=http://localhost:7777/local",
......@@ -285,6 +288,9 @@ public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
@RibbonClient(name = "another", configuration = ZuulProxyTestBase.AnotherRibbonClientConfiguration.class) })
static class TestConfig extends ZuulProxyTestBase.AbstractZuulProxyApplication {
@Autowired(required = false)
private Set<ZuulFallbackProvider> fallbackProviders = Collections.emptySet();
@RequestMapping("/trailing-slash")
public String trailingSlash(HttpServletRequest request) {
return request.getRequestURI();
......@@ -327,7 +333,7 @@ public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
@Bean
public RibbonCommandFactory<?> ribbonCommandFactory(
SpringClientFactory clientFactory) {
return new MyRibbonCommandFactory(clientFactory);
return new MyRibbonCommandFactory(clientFactory, fallbackProviders);
}
@Bean
......@@ -350,8 +356,9 @@ public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
private SpringClientFactory clientFactory;
public MyRibbonCommandFactory(SpringClientFactory clientFactory) {
super(clientFactory, new ZuulProperties());
public MyRibbonCommandFactory(SpringClientFactory clientFactory,
Set<ZuulFallbackProvider> fallbackProviders) {
super(clientFactory, new ZuulProperties(), fallbackProviders);
this.clientFactory = clientFactory;
}
......
/*
*
* * Copyright 2013-2016 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.filters.route.support;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.junit.Assert.assertEquals;
/**
* @author Ryan Baxter
*/
public abstract class RibbonCommandFallbackTests {
@Value("${local.server.port}")
protected int port;
@Test
public void fallback() {
String uri = "/simple/slow";
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + uri, HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("fallback", result.getBody());
}
@Test
public void noFallback() {
String uri = "/another/twolevel/slow";
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + uri, HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
System.out.println("no fallback body: " + result.getBody());
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, result.getStatusCode());
}
}
......@@ -17,11 +17,9 @@
package org.springframework.cloud.netflix.zuul.filters.route.support;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assume.assumeThat;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -29,9 +27,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.http.HttpServletRequest;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -46,13 +42,16 @@ import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
......@@ -66,14 +65,19 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assume.assumeThat;
/**
* @author Spencer Gibb
* @author Ryan Baxter
*/
public abstract class ZuulProxyTestBase {
......@@ -363,6 +367,22 @@ public abstract class ZuulProxyTestBase {
return "Hello space";
}
@RequestMapping("/slow")
public String slow() {
try {
Thread.sleep(80000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "slow";
}
@Bean
public ZuulFallbackProvider fallbackProvider() {
return new FallbackProvider();
}
@Bean
public ZuulFilter sampleFilter() {
return new ZuulFilter() {
......@@ -393,6 +413,7 @@ public abstract class ZuulProxyTestBase {
return 0;
}
};
}
@Override
......@@ -401,6 +422,53 @@ public abstract class ZuulProxyTestBase {
mapping.setRemoveSemicolonContent(false);
return mapping;
}
}
public static class FallbackProvider implements ZuulFallbackProvider {
@Override
public String getRoute() {
return "simple";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return null;
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("fallback".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_HTML);
return headers;
}
};
}
}
@Configuration
......
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