akka-http mapResponse ignores some responses

Question about akka-http server. I have a top-level mapResponse which wraps all other routes. This mapResponse wraps all responses in the json. And recently I found that sometimes my server returns 503 error with plain-text explanation. I found that the reason is akka.http.server.request-timeout config value - it was too small. But why I didn’t catch these responses with the top-level mapResponse ? I found that this 503 response is created in HttpServerBluePrint.TimeoutAccessImpl.apply and it somehow ignores my mapResponse .

Here is the minimal reproduction code. I wrap all my routes in logAndReturn200 function. I expect that all my routes will return StatusCodes.OK . But /bar returns timeout error.

val logAndReturn200 = mapResponse { response =>
  println(response)
  HttpResponse(StatusCodes.OK)
}

val route = {
  logAndReturn200 {
    get {
      path("foo") {
        complete("OK")
      } ~
      path("bar") {
        val promise = Promise[Unit]()
        actorSystem.scheduler.scheduleOnce(10.seconds) {
          promise.success(())
        }
        onSuccess(promise.future) {
          complete("OK")
        }
      }
    }
  }
}

val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)

I think that is because if you throw an exception, that will not be a response to map yet, but will end up in the default exception handler wrapping the route and that will generate the response. That means that you would have to wrap/seal the route with an exception handler before you map the response.

An alternative would be to use mapRouteResultFuture instead, however there is a recently discovered bug related to that akka/akka-http#2173 which has a fix merged that will end up in the next version of Akka HTTP (10.1.5)

I tried Route.seal but nothing changed. And I don’t understand how exception or rejection handling related to my question. I didn’t throw any exception, I just added a timeout to one of my routes and want to use mapResponse for it.

My bad, I was under the misconception the timeout-handling would throw an exception in the routing tree if the timeout hits.

How it actually works is that it completes the request with an explicit response, byt default this is a ServiceUnavailable but you can provide an alternative timeout response as an extra parameter to withRequestTimeout and there is an additional directive, withRequestTimeoutResponse which allows for a request-dependent timeout response rather than a generic one.

The reason you won’t have the timeout response passing through the regular directives is that it is implemented on a lower level (and is present also if you use Akka HTTP without the routing DSL), so the entire timeout response part will sort of side-step the route that was executing and timed out.

2 Likes

Thanks, withRequestTimeoutResponse works! But, it’s a bit unobvious and surprising behavior. In my example, I wrap all my routes in mapResponse. And I thought that it would mean something like: “all your responses will be post-processed with this”. But in reality, some special cases will be ignored. And there is nothing about it in mapResponse docs.
Also, there is no help with this from the API. I mean that without remembering this special case I have no chances to be forced to think about it.

I agree it is not intiutitive, I’m not sure it is doable to improve it though.

I created https://github.com/akka/akka-http/issues/2184 to track this. Please chime in there if you have some ideas how this could be done.

1 Like