Merge pull request #16326 from spray/w/16289-fix-handleExceptions

=htp #16289 in `handleException` also catch strict exceptions during route building or execution
This commit is contained in:
Konrad Malawski 2014-11-19 10:25:53 +01:00
commit 849eda2337
3 changed files with 126 additions and 5 deletions

View file

@ -134,4 +134,36 @@ class BasicRouteSpecs extends RoutingSpec {
expect(dynamicRoute, "xxxx")
}
}
case object MyException extends RuntimeException
"Route sealing" should {
"catch route execution exceptions" in {
Get("/abc") ~> ScalaRoutingDSL.sealRoute {
get { ctx
throw MyException
}
} ~> check {
status shouldEqual StatusCodes.InternalServerError
}
}
"catch route building exceptions" in {
Get("/abc") ~> ScalaRoutingDSL.sealRoute {
get {
throw MyException
}
} ~> check {
status shouldEqual StatusCodes.InternalServerError
}
}
"convert all rejections to responses" in {
object MyRejection extends Rejection
Get("/abc") ~> ScalaRoutingDSL.sealRoute {
get {
reject(MyRejection)
}
} ~> check {
status shouldEqual StatusCodes.InternalServerError
}
}
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.server
package directives
import akka.http.model.StatusCodes
import scala.concurrent.Future
class ExecutionDirectivesSpec extends RoutingSpec {
object MyException extends RuntimeException
val handler =
ExceptionHandler {
case MyException complete(500, "Pling! Plong! Something went wrong!!!")
}
"The `handleExceptions` directive" should {
"handle an exception strictly thrown in the inner route with the supplied exception handler" in {
exceptionShouldBeHandled {
handleExceptions(handler) { ctx
throw MyException
}
}
}
"handle an Future.failed RouteResult with the supplied exception handler" in {
exceptionShouldBeHandled {
handleExceptions(handler) { ctx
Future.failed(MyException)
}
}
}
"handle an eventually failed Future[RouteResult] with the supplied exception handler" in {
exceptionShouldBeHandled {
handleExceptions(handler) { ctx
Future {
Thread.sleep(100)
throw MyException
}
}
}
}
"handle an exception happening during route building" in {
exceptionShouldBeHandled {
get {
handleExceptions(handler) {
throw MyException
}
}
}
}
"not interfere with alternative routes" in {
Get("/abc") ~>
get {
handleExceptions(handler)(reject) ~ { ctx
throw MyException
}
} ~> check {
status shouldEqual StatusCodes.InternalServerError
responseAs[String] shouldEqual "There was an internal server error."
}
}
"not handle other exceptions" in {
Get("/abc") ~>
get {
handleExceptions(handler) {
throw new RuntimeException
}
} ~> check {
status shouldEqual StatusCodes.InternalServerError
responseAs[String] shouldEqual "There was an internal server error."
}
}
}
def exceptionShouldBeHandled(route: Route) =
Get("/abc") ~> route ~> check {
status shouldEqual StatusCodes.InternalServerError
responseAs[String] shouldEqual "Pling! Plong! Something went wrong!!!"
}
}

View file

@ -8,6 +8,9 @@ package directives
import akka.http.util.FastFuture
import FastFuture._
import scala.concurrent.Future
import scala.util.control.NonFatal
trait ExecutionDirectives {
import BasicDirectives._
@ -16,11 +19,15 @@ trait ExecutionDirectives {
* [[akka.http.server.ExceptionHandler]].
*/
def handleExceptions(handler: ExceptionHandler): Directive0 =
extractRequestContext flatMap { ctx
import ctx.executionContext
mapRouteResultFuture {
_.fast.recoverWith(handler andThen (_(ctx.withContentNegotiationDisabled)))
}
Directive { innerRouteBuilder
ctx
import ctx.executionContext
def handleException: PartialFunction[Throwable, Future[RouteResult]] =
handler andThen (_(ctx.withContentNegotiationDisabled))
try innerRouteBuilder(())(ctx).fast.recoverWith(handleException)
catch {
case NonFatal(e) handleException.applyOrElse[Throwable, Future[RouteResult]](e, throw _)
}
}
/**