diff --git a/akka-http-tests/src/test/scala/akka/http/server/BasicRouteSpecs.scala b/akka-http-tests/src/test/scala/akka/http/server/BasicRouteSpecs.scala index 3d84d38f70..315a3be1fe 100644 --- a/akka-http-tests/src/test/scala/akka/http/server/BasicRouteSpecs.scala +++ b/akka-http-tests/src/test/scala/akka/http/server/BasicRouteSpecs.scala @@ -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 + } + } + } } diff --git a/akka-http-tests/src/test/scala/akka/http/server/directives/ExecutionDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/server/directives/ExecutionDirectivesSpec.scala new file mode 100644 index 0000000000..dca4bf3172 --- /dev/null +++ b/akka-http-tests/src/test/scala/akka/http/server/directives/ExecutionDirectivesSpec.scala @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +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!!!" + } +} diff --git a/akka-http/src/main/scala/akka/http/server/directives/ExecutionDirectives.scala b/akka-http/src/main/scala/akka/http/server/directives/ExecutionDirectives.scala index a476c5f0ce..23b89d8dda 100644 --- a/akka-http/src/main/scala/akka/http/server/directives/ExecutionDirectives.scala +++ b/akka-http/src/main/scala/akka/http/server/directives/ExecutionDirectives.scala @@ -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 _) + } } /**