diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java index 6823e89f..b6cb2eb8 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java @@ -98,8 +98,6 @@ public abstract class SpringCloudCircuitBreakerFilterFactory return cb.run(chain.filter(exchange).doOnSuccess(v -> { if (statuses.contains(exchange.getResponse().getStatusCode())) { HttpStatus status = exchange.getResponse().getStatusCode(); - exchange.getResponse().setStatusCode(null); - reset(exchange); throw new CircuitBreakerStatusCodeException(status); } }), t -> { @@ -107,6 +105,9 @@ public abstract class SpringCloudCircuitBreakerFilterFactory return Mono.error(t); } + exchange.getResponse().setStatusCode(null); + reset(exchange); + // TODO: copied from RouteToRequestUrlFilter URI uri = exchange.getRequest().getURI(); // TODO: assume always? @@ -121,7 +122,7 @@ public abstract class SpringCloudCircuitBreakerFilterFactory ServerHttpRequest request = exchange.getRequest().mutate().uri(requestUrl).build(); return getDispatcherHandler().handle(exchange.mutate().request(request).build()); - }).onErrorResume(t -> handleErrorWithoutFallback(t)); + }).onErrorResume(t -> handleErrorWithoutFallback(t, config.isResumeWithoutError())); } @Override @@ -132,7 +133,7 @@ public abstract class SpringCloudCircuitBreakerFilterFactory }; } - protected abstract Mono handleErrorWithoutFallback(Throwable t); + protected abstract Mono handleErrorWithoutFallback(Throwable t, boolean resumeWithoutError); private void addExceptionDetails(Throwable t, ServerWebExchange exchange) { ofNullable(t).ifPresent( @@ -154,6 +155,8 @@ public abstract class SpringCloudCircuitBreakerFilterFactory private Set statusCodes = new HashSet<>(); + private boolean resumeWithoutError = false; + @Override public void setRouteId(String routeId) { this.routeId = routeId; @@ -206,6 +209,14 @@ public abstract class SpringCloudCircuitBreakerFilterFactory return this; } + public boolean isResumeWithoutError() { + return resumeWithoutError; + } + + public void setResumeWithoutError(boolean resumeWithoutError) { + this.resumeWithoutError = resumeWithoutError; + } + } public class CircuitBreakerStatusCodeException extends HttpStatusCodeException { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerResilience4JFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerResilience4JFilterFactory.java index 55c04c35..aebb2598 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerResilience4JFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerResilience4JFilterFactory.java @@ -38,13 +38,16 @@ public class SpringCloudCircuitBreakerResilience4JFilterFactory extends SpringCl } @Override - protected Mono handleErrorWithoutFallback(Throwable t) { + protected Mono handleErrorWithoutFallback(Throwable t, boolean resumeWithoutError) { if (java.util.concurrent.TimeoutException.class.isInstance(t)) { return Mono.error(new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, t.getMessage(), t)); } if (CallNotPermittedException.class.isInstance(t)) { return Mono.error(new ServiceUnavailableException()); } + if (resumeWithoutError) { + return Mono.empty(); + } return Mono.error(t); } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java index a821b3f7..385068f8 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java @@ -103,4 +103,16 @@ public abstract class SpringCloudCircuitBreakerFilterFactoryTests extends BaseWe .isOk().expectHeader().valueEquals(ROUTE_ID_HEADER, "circuitbreaker_fallback_test_statuscode"); } + @Test + public void filterStatusCodeResumeWithoutError() { + testClient.get().uri("/status/500").header("Host", "www.circuitbreakerresumewithouterror.org").exchange().expectStatus() + .isEqualTo(500); + + testClient.get().uri("/status/404").header("Host", "www.circuitbreakerresumewithouterror.org").exchange().expectStatus() + .isEqualTo(404); + + testClient.get().uri("/status/200").header("Host", "www.circuitbreakerresumewithouterror.org").exchange().expectStatus() + .isOk().expectHeader().valueEquals(ROUTE_ID_HEADER, "circuitbreaker_resume_without_error"); + } + } diff --git a/spring-cloud-gateway-server/src/test/resources/application.yml b/spring-cloud-gateway-server/src/test/resources/application.yml index 840ff6e3..64af423a 100644 --- a/spring-cloud-gateway-server/src/test/resources/application.yml +++ b/spring-cloud-gateway-server/src/test/resources/application.yml @@ -109,6 +109,20 @@ spring: - "NOT_FOUND" fallbackUri: forward:/statusCodeFallbackController + # ===================================== + - id: circuitbreaker_resume_without_error + uri: ${test.uri} + predicates: + - Host=**.circuitbreakerresumewithouterror.org + filters: + - name: CircuitBreaker + args: + name: resumewithouterror + statusCodes: + - 500 + - "NOT_FOUND" + resume-without-error: true + # ===================================== - id: change_uri_test uri: ${test.uri}