withRequestTimeout

Signature

def withRequestTimeout(timeout: Duration): Directive0
def withRequestTimeout(timeout: Duration, handler: HttpRequest ⇒ HttpResponse): Directive0
def withRequestTimeout(timeout: Duration, handler: Option[HttpRequest ⇒ HttpResponse]): Directive0

Description

This directive enables “late” (during request processing) control over the Request timeout feature in Akka HTTP.

The timeout can be either loosened or made more tight using this directive, however one should be aware that it is inherently racy (which may especially show with very tight timeouts) since a timeout may already have been triggered when this directive executes.

In case of pipelined HTTP requests (multiple requests being accepted on the same connection before sending the first response) a the request timeout failure of the n-th request will shut down the connection causing the already enqueued requests to be dropped. This is by-design, as the request timeout feature serves as a “safety net” in case of programming errors (e.g. a Future that never completes thus potentially blocking the entire connection forever) or malicious attacks on the server.

Optionally, a timeout handler may be provided in which is called when a time-out is triggered and must produce an HttpResponseHttpResponse that will be sent back to the client instead of the “too late” response (in case it’d ever arrive). See also withRequestTimeoutResponse if only looking to customise the timeout response without changing the timeout itself.

Warning

Please note that setting the timeout from within a directive is inherently racy (as the “point in time from which we’re measuring the timeout” is already in the past (the moment we started handling the request), so if the existing timeout already was triggered before your directive had the chance to change it, an timeout may still be logged.

It is recommended to use a larger statically configured timeout (think of it as a “safety net” against programming errors or malicious attackers) and if needed tighten it using the directives – not the other way around.

For more information about various timeouts in Akka HTTP see Akka HTTP Timeouts.

Example

Scala
val route =
  path("timeout") {
    withRequestTimeout(1.seconds) { // modifies the global akka.http.server.request-timeout for this request
      val response: Future[String] = slowFuture() // very slow
      complete(response)
    }
  }

// check
runRoute(route, "timeout").status should ===(StatusCodes.ServiceUnavailable) // the timeout response
Java
final Duration timeout = Duration.create(1, TimeUnit.SECONDS);
CompletionStage<String> slowFuture = new CompletableFuture<>();

final Route route = path("timeout", () ->
        withRequestTimeout(timeout, () -> {
            return completeOKWithFutureString(slowFuture); // very slow
        })
);

// test:
StatusCode statusCode = runRoute(system, materializer, route, "timeout").get().status();
assert (StatusCodes.SERVICE_UNAVAILABLE.equals(statusCode));

With setting the handler at the same time:

Scala
val timeoutResponse = HttpResponse(
  StatusCodes.EnhanceYourCalm,
  entity = "Unable to serve response within time limit, please enhance your calm.")

val route =
  path("timeout") {
    // updates timeout and handler at
    withRequestTimeout(1.milli, request => timeoutResponse) {
      val response: Future[String] = slowFuture() // very slow
      complete(response)
    }
  }

// check
runRoute(route, "timeout").status should ===(StatusCodes.EnhanceYourCalm) // the timeout response
Java
final Duration timeout = Duration.create(1, TimeUnit.MILLISECONDS);
CompletionStage<String> slowFuture = new CompletableFuture<>();

HttpResponse enhanceYourCalmResponse = HttpResponse.create()
        .withStatus(StatusCodes.ENHANCE_YOUR_CALM)
        .withEntity("Unable to serve response within time limit, please enhance your calm.");

final Route route = path("timeout", () ->
        withRequestTimeout(timeout, (request) -> enhanceYourCalmResponse, () -> {
            return completeOKWithFutureString(slowFuture); // very slow
        })
);

// test:
StatusCode statusCode = runRoute(system, materializer, route, "timeout").get().status();
assert (StatusCodes.ENHANCE_YOUR_CALM.equals(statusCode));
Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.