Commit 834dfe22 by Spencer Gibb

Handle ClientException.SERVER_THROTTLED as 503

fixes gh-406
parent 1b190a32
......@@ -17,6 +17,7 @@
package org.springframework.cloud.netflix.zuul.filters.post;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.apachecommons.CommonsLog;
......@@ -59,19 +60,28 @@ public class SendErrorFilter extends ZuulFilter {
public Object run() {
try {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
int statusCode = (Integer) ctx.get("error.status_code");
request.setAttribute("javax.servlet.error.status_code", statusCode);
if (ctx.containsKey("error.exception")) {
Object e = ctx.get("error.exception");
log.warn("Error during filtering", Throwable.class.cast(e));
ctx.getRequest().setAttribute("javax.servlet.error.exception", e);
request.setAttribute("javax.servlet.error.exception", e);
}
ctx.getRequest().setAttribute("javax.servlet.error.status_code", statusCode);
RequestDispatcher dispatcher = ctx.getRequest().getRequestDispatcher(
if (ctx.containsKey("error.message")) {
String message = (String) ctx.get("error.message");
request.setAttribute("javax.servlet.error.message", message);
}
RequestDispatcher dispatcher = request.getRequestDispatcher(
this.errorPath);
if (dispatcher != null) {
ctx.set(SEND_ERROR_FILTER_RAN, true);
if (!ctx.getResponse().isCommitted()) {
dispatcher.forward(ctx.getRequest(), ctx.getResponse());
dispatcher.forward(request, ctx.getResponse());
}
}
}
......
......@@ -79,6 +79,7 @@ public class RibbonRoutingFilter extends ZuulFilter {
}
catch (ZuulException ex) {
context.set("error.status_code", ex.nStatusCode);
context.set("error.message", ex.errorCause);
context.set("error.exception", ex);
}
catch (Exception ex) {
......@@ -127,13 +128,14 @@ public class RibbonRoutingFilter extends ZuulFilter {
}
catch (HystrixRuntimeException ex) {
info.put("status", "500");
if (ex.getFallbackException() != null
&& ex.getFallbackException().getCause() != null
&& ex.getFallbackException().getCause() instanceof ClientException) {
ClientException cause = (ClientException) ex.getFallbackException()
.getCause();
throw new ZuulException(cause, "Forwarding error", 500,
cause.getErrorType().toString());
ClientException clientException = findClientException(ex);
if (clientException != null) {
int statusCode = 500;
if (clientException.getErrorType() == ClientException.ErrorType.SERVER_THROTTLED) {
statusCode = 503;
}
throw new ZuulException(clientException, "Forwarding error", statusCode,
clientException.getErrorType().toString());
}
throw new ZuulException(ex, "Forwarding error", 500,
ex.getFailureType().toString());
......@@ -141,6 +143,19 @@ public class RibbonRoutingFilter extends ZuulFilter {
}
protected ClientException findClientException(HystrixRuntimeException ex) {
if (ex.getCause() != null
&& ex.getCause() instanceof ClientException) {
return (ClientException) ex.getCause();
}
if (ex.getFallbackException() != null
&& ex.getFallbackException().getCause() != null
&& ex.getFallbackException().getCause() instanceof ClientException) {
return (ClientException) ex.getFallbackException().getCause();
}
return null;
}
private InputStream getRequestBody(HttpServletRequest request) {
InputStream requestEntity = null;
// ApacheHttpClient4Handler does not support body in delete requests
......
......@@ -16,12 +16,16 @@
package org.springframework.cloud.netflix.zuul;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import com.netflix.client.ClientException;
import com.netflix.client.http.HttpRequest;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import com.netflix.zuul.exception.ZuulException;
import com.netflix.niws.client.http.RestClient;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -44,9 +48,12 @@ import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
......@@ -85,6 +92,14 @@ public class SampleZuulProxyApplicationTests extends ZuulProxyTestBase {
}
@Test
public void ribbonCommandServiceUnavailable() {
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/simple/throwexception/503",
HttpMethod.GET, new HttpEntity<>((Void) null), String.class);
assertEquals(HttpStatus.SERVICE_UNAVAILABLE, result.getStatusCode());
}
@Test
public void ribbonCommandBadHost() {
ResponseEntity<String> result = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/badhost/1",
......@@ -133,18 +148,41 @@ class SampleZuulProxyApplication extends ZuulProxyTestBase.AbstractZuulProxyAppl
}
@Override
@SuppressWarnings("deprecation")
@SneakyThrows
public RestClientRibbonCommand create(RibbonCommandContext context) {
String uri = context.getUri();
if (uri.startsWith("/throwexception/")) {
String code = uri.replace("/throwexception/", "");
throw new ZuulException(new RuntimeException(), Integer.parseInt(code),
"test error");
RestClient restClient = getClientFactory().getClient(context.getServiceId(),
RestClient.class);
return new MyCommand(Integer.parseInt(code),
context.getServiceId(), restClient, getVerb(context.getVerb()),
context.getUri(), context.getRetryable(), context.getHeaders(),
context.getParams(), context.getRequestEntity());
}
return super.create(context);
}
}
static class MyCommand extends RestClientRibbonCommand {
private int errorCode;
public MyCommand(int errorCode, String commandKey, RestClient restClient, HttpRequest.Verb verb, String uri, Boolean retryable, MultiValueMap<String, String> headers, MultiValueMap<String, String> params, InputStream requestEntity) throws URISyntaxException {
super(commandKey, restClient, verb, uri, retryable, headers, params, requestEntity);
this.errorCode = errorCode;
}
@Override
protected ClientHttpResponse forward() throws Exception {
if (errorCode == 503) {
throw new ClientException(ClientException.ErrorType.SERVER_THROTTLED);
}
return new MockClientHttpResponse((byte[])null, HttpStatus.valueOf(this.errorCode));
}
}
// Load balancer with fixed server list for "simple" pointing to localhost
@Configuration
static class BadHostRibbonClientConfiguration {
......
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