diff --git a/riptide-failsafe/README.md b/riptide-failsafe/README.md
index c2cd87318..596b300e2 100644
--- a/riptide-failsafe/README.md
+++ b/riptide-failsafe/README.md
@@ -52,8 +52,8 @@ The failsafe plugin will not perform retries nor apply circuit breakers unless t
Http.builder()
.plugin(new FailsafePlugin(Executors.newScheduledThreadPool(20))
.withRetryPolicy(new RetryPolicy()
- .retryOn(SocketTimeoutException.class)
.withDelay(25, TimeUnit.MILLISECONDS)
+ .withDelay(new RetryAfterDelayFunction(clock))
.withMaxRetries(4))
.withCircuitBreaker(new CircuitBreaker()
.withFailureThreshold(3, 10)
@@ -63,8 +63,32 @@ Http.builder()
```
Please visit the [Failsafe readme](https://github.com/jhalterman/failsafe#readme) in order to see possible
-configurations. Make sure you **check out
-[zalando/failsafe-actuator](https://github.com/zalando/failsafe-actuator)** for a seemless integration of
+configurations.
+
+**Beware** when using `retryOn` to retry conditionally on certain exception types.
+You'll need to register `RetryException` in order for the `retry()` route to work:
+
+```java
+new RetryPolicy()
+ .retryOn(SocketTimeoutException.class)
+ .retryOn(RetryException.class);
+```
+
+As of Failsafe version 1.1.0, it's now supported to dynamically compute delays using a custom function.
+Riptide: Failsafe offers a special implementation that understands
+[`Retry-After` (RFC 7231, section 7.1.3)](https://tools.ietf.org/html/rfc7231#section-7.1.3):
+
+```java
+Http.builder()
+ .plugin(new FailsafePlugin(Executors.newScheduledThreadPool(20))
+ .withRetryPolicy(new RetryPolicy()
+ .withDelay(25, TimeUnit.MILLISECONDS)
+ .withDelay(new RetryAfterDelayFunction(clock)))
+ .build();
+```
+
+Make sure you **check out
+[zalando/failsafe-actuator](https://github.com/zalando/failsafe-actuator)** for a seamless integration of
Failsafe and Spring Boot:
```java
@@ -74,7 +98,6 @@ private CircuitBreaker breaker;
Http.builder()
.plugin(new FailsafePlugin(Executors.newScheduledThreadPool(20))
.withRetryPolicy(new RetryPolicy()
- .retryOn(SocketTimeoutException.class)
.withDelay(25, TimeUnit.MILLISECONDS)
.withMaxRetries(4))
.withCircuitBreaker(breaker))
diff --git a/riptide-failsafe/pom.xml b/riptide-failsafe/pom.xml
index d9d25963c..44d871027 100644
--- a/riptide-failsafe/pom.xml
+++ b/riptide-failsafe/pom.xml
@@ -16,7 +16,7 @@
Client side response routing
- 1.0.4
+ 1.1.0
diff --git a/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/FailsafePlugin.java b/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/FailsafePlugin.java
index 9b54eaeea..b44a0db00 100644
--- a/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/FailsafePlugin.java
+++ b/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/FailsafePlugin.java
@@ -4,7 +4,6 @@
import net.jodah.failsafe.Failsafe;
import net.jodah.failsafe.RetryPolicy;
import org.springframework.http.client.ClientHttpResponse;
-import org.zalando.riptide.CancelableCompletableFuture;
import org.zalando.riptide.Plugin;
import org.zalando.riptide.RequestArguments;
import org.zalando.riptide.RequestExecution;
@@ -36,11 +35,7 @@ public FailsafePlugin(final ScheduledExecutorService scheduler) {
}
public FailsafePlugin withRetryPolicy(final RetryPolicy retryPolicy) {
- return new FailsafePlugin(scheduler, withRetryExceptionSupport(retryPolicy), circuitBreaker);
- }
-
- private RetryPolicy withRetryExceptionSupport(final RetryPolicy retryPolicy) {
- return new RetryPolicy(retryPolicy).retryOn(RetryException.class);
+ return new FailsafePlugin(scheduler, retryPolicy, circuitBreaker);
}
public FailsafePlugin withCircuitBreaker(final CircuitBreaker circuitBreaker) {
diff --git a/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/RetryAfterDelayFunction.java b/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/RetryAfterDelayFunction.java
new file mode 100644
index 000000000..78ba52a05
--- /dev/null
+++ b/riptide-failsafe/src/main/java/org/zalando/riptide/failsafe/RetryAfterDelayFunction.java
@@ -0,0 +1,90 @@
+package org.zalando.riptide.failsafe;
+
+import lombok.extern.slf4j.Slf4j;
+import net.jodah.failsafe.ExecutionContext;
+import net.jodah.failsafe.RetryPolicy.DelayFunction;
+import net.jodah.failsafe.util.Duration;
+import org.zalando.riptide.HttpResponseException;
+
+import javax.annotation.Nullable;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+import static java.lang.Long.parseLong;
+import static java.time.Duration.between;
+import static java.time.Instant.now;
+import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
+
+/**
+ * @see RFC 7231, section 7.1.3: Retry-After
+ */
+@Slf4j
+public final class RetryAfterDelayFunction implements DelayFunction