diff --git a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTest.scala b/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTest.scala index d0e01fed6b..5ff2a08608 100644 --- a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTest.scala +++ b/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTest.scala @@ -6,12 +6,12 @@ package akka.http.scaladsl.testkit import com.typesafe.config.{ ConfigFactory, Config } import scala.collection.immutable -import scala.concurrent.{ Await, Future } +import scala.concurrent.{ ExecutionContext, Await, Future } import scala.concurrent.duration._ import scala.util.DynamicVariable import scala.reflect.ClassTag import akka.actor.ActorSystem -import akka.stream.ActorMaterializer +import akka.stream.{ Materializer, ActorMaterializer } import akka.http.scaladsl.client.RequestBuilding import akka.http.scaladsl.util.FastFuture import akka.http.scaladsl.server._ @@ -115,8 +115,14 @@ trait RouteTest extends RequestBuilding with RouteTestResultComponent with Marsh type Out = HttpRequest def apply(request: HttpRequest, f: HttpRequest ⇒ HttpRequest) = f(request) } - implicit def injectIntoRoute(implicit timeout: RouteTestTimeout, setup: RoutingSetup, - defaultHostInfo: DefaultHostInfo) = + implicit def injectIntoRoute(implicit timeout: RouteTestTimeout, + defaultHostInfo: DefaultHostInfo, + routingSettings: RoutingSettings, + executionContext: ExecutionContext, + materializer: Materializer, + routingLog: RoutingLog, + rejectionHandler: RejectionHandler = RejectionHandler.default, + exceptionHandler: ExceptionHandler = null) = new TildeArrow[RequestContext, Future[RouteResult]] { type Out = RouteTestResult def apply(request: HttpRequest, route: Route): Out = { @@ -125,12 +131,12 @@ trait RouteTest extends RequestBuilding with RouteTestResultComponent with Marsh request.withEffectiveUri( securedConnection = defaultHostInfo.securedConnection, defaultHostHeader = defaultHostInfo.host) - val ctx = new RequestContextImpl(effectiveRequest, setup.routingLog.requestLog(effectiveRequest), setup.settings) - val sealedExceptionHandler = setup.exceptionHandler.seal(setup.settings) + val ctx = new RequestContextImpl(effectiveRequest, routingLog.requestLog(effectiveRequest), routingSettings) + val sealedExceptionHandler = ExceptionHandler.seal(exceptionHandler) val semiSealedRoute = // sealed for exceptions but not for rejections Directives.handleExceptions(sealedExceptionHandler) { route } val deferrableRouteResult = semiSealedRoute(ctx) - deferrableRouteResult.fast.foreach(routeTestResult.handleResult)(setup.executor) + deferrableRouteResult.fast.foreach(routeTestResult.handleResult)(executionContext) routeTestResult } } diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/ExceptionHandler.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/ExceptionHandler.scala index 950b87edc5..3b9ac54dae 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/ExceptionHandler.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/ExceptionHandler.scala @@ -48,4 +48,11 @@ object ExceptionHandler { ctx.complete(InternalServerError) } } + + /** + * Creates a sealed ExceptionHandler from the given one. Returns the default handler if the given one + * is `null`. + */ + def seal(handler: ExceptionHandler)(implicit settings: RoutingSettings): ExceptionHandler = + if (handler ne null) handler.seal(settings) else ExceptionHandler.default(settings) } diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala index 01e00769fb..825584003b 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala @@ -4,7 +4,9 @@ package akka.http.scaladsl.server -import scala.concurrent.Future +import akka.stream.Materializer + +import scala.concurrent.{ ExecutionContext, Future } import akka.stream.scaladsl.Flow import akka.http.scaladsl.model.{ HttpRequest, HttpResponse } import akka.http.scaladsl.util.FastFuture._ @@ -19,10 +21,11 @@ object Route { /** * "Seals" a route by wrapping it with exception handling and rejection conversion. */ - def seal(route: Route)(implicit setup: RoutingSetup): Route = { + def seal(route: Route)(implicit routingSettings: RoutingSettings, + rejectionHandler: RejectionHandler = RejectionHandler.default, + exceptionHandler: ExceptionHandler = null): Route = { import directives.ExecutionDirectives._ - import setup._ - handleExceptions(exceptionHandler.seal(setup.settings)) { + handleExceptions(ExceptionHandler.seal(exceptionHandler)) { handleRejections(rejectionHandler.seal) { route } @@ -34,19 +37,35 @@ object Route { * * This conversion is also implicitly available through [[RouteResult.route2HandlerFlow]]. */ - def handlerFlow(route: Route)(implicit setup: RoutingSetup): Flow[HttpRequest, HttpResponse, Unit] = + def handlerFlow(route: Route)(implicit routingSettings: RoutingSettings, + materializer: Materializer, + routingLog: RoutingLog, + executionContext: ExecutionContext = null, + rejectionHandler: RejectionHandler = RejectionHandler.default, + exceptionHandler: ExceptionHandler = null): Flow[HttpRequest, HttpResponse, Unit] = Flow[HttpRequest].mapAsync(1)(asyncHandler(route)) /** * Turns a `Route` into an async handler function. */ - def asyncHandler(route: Route)(implicit setup: RoutingSetup): HttpRequest ⇒ Future[HttpResponse] = { - import setup._ - val sealedRoute = seal(route) - request ⇒ - sealedRoute(new RequestContextImpl(request, routingLog.requestLog(request), setup.settings)).fast.map { - case RouteResult.Complete(response) ⇒ response - case RouteResult.Rejected(rejected) ⇒ throw new IllegalStateException(s"Unhandled rejections '$rejected', unsealed RejectionHandler?!") - } + def asyncHandler(route: Route)(implicit routingSettings: RoutingSettings, + materializer: Materializer, + routingLog: RoutingLog, + executionContext: ExecutionContext = null, + rejectionHandler: RejectionHandler = RejectionHandler.default, + exceptionHandler: ExceptionHandler = null): HttpRequest ⇒ Future[HttpResponse] = { + val effectiveEC = if (executionContext ne null) executionContext else materializer.executionContext + + { + implicit val executionContext = effectiveEC // overrides parameter + + val sealedRoute = seal(route) + request ⇒ + sealedRoute(new RequestContextImpl(request, routingLog.requestLog(request), routingSettings)).fast + .map { + case RouteResult.Complete(response) ⇒ response + case RouteResult.Rejected(rejected) ⇒ throw new IllegalStateException(s"Unhandled rejections '$rejected', unsealed RejectionHandler?!") + } + } } } diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/RouteResult.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/RouteResult.scala index 832e9345f2..ec4af4849e 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/RouteResult.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/RouteResult.scala @@ -5,6 +5,8 @@ package akka.http.scaladsl.server import scala.collection.immutable +import scala.concurrent.ExecutionContext +import akka.stream.Materializer import akka.stream.scaladsl.Flow import akka.http.scaladsl.model.{ HttpRequest, HttpResponse } @@ -20,6 +22,11 @@ object RouteResult { final case class Complete(response: HttpResponse) extends RouteResult final case class Rejected(rejections: immutable.Seq[Rejection]) extends RouteResult - implicit def route2HandlerFlow(route: Route)(implicit setup: RoutingSetup): Flow[HttpRequest, HttpResponse, Unit] = + implicit def route2HandlerFlow(route: Route)(implicit routingSettings: RoutingSettings, + materializer: Materializer, + routingLog: RoutingLog, + executionContext: ExecutionContext = null, + rejectionHandler: RejectionHandler = RejectionHandler.default, + exceptionHandler: ExceptionHandler = null): Flow[HttpRequest, HttpResponse, Unit] = Route.handlerFlow(route) } diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/RoutingLog.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/RoutingLog.scala new file mode 100644 index 0000000000..d3950167eb --- /dev/null +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/RoutingLog.scala @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package akka.http.scaladsl.server + +import akka.event.LoggingAdapter +import akka.actor.{ ActorSystem, ActorContext } +import akka.http.scaladsl.model.HttpRequest + +trait RoutingLog { + def log: LoggingAdapter + def requestLog(request: HttpRequest): LoggingAdapter +} + +object RoutingLog extends LowerPriorityRoutingLogImplicits { + def apply(defaultLog: LoggingAdapter): RoutingLog = + new RoutingLog { + def log = defaultLog + def requestLog(request: HttpRequest) = defaultLog + } + + implicit def fromActorContext(implicit ac: ActorContext): RoutingLog = RoutingLog(ac.system.log) +} +sealed abstract class LowerPriorityRoutingLogImplicits { + implicit def fromActorSystem(implicit system: ActorSystem): RoutingLog = RoutingLog(system.log) +} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/RoutingSetup.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/RoutingSetup.scala deleted file mode 100644 index 71250c96ed..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/RoutingSetup.scala +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2009-2014 Typesafe Inc. - */ - -package akka.http.scaladsl.server - -import scala.concurrent.ExecutionContext -import akka.event.LoggingAdapter -import akka.actor.{ ActorSystem, ActorContext } -import akka.stream.Materializer -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.HttpRequest - -/** - * Provides a ``RoutingSetup`` for a given connection. - */ -trait RoutingSetupProvider { - def apply(connection: Http.IncomingConnection): RoutingSetup -} -object RoutingSetupProvider { - def apply(f: Http.IncomingConnection ⇒ RoutingSetup): RoutingSetupProvider = - new RoutingSetupProvider { - def apply(connection: Http.IncomingConnection) = f(connection) - } - - implicit def default(implicit setup: RoutingSetup) = RoutingSetupProvider(_ ⇒ setup) -} - -/** - * Provides all dependencies required for route execution. - */ -class RoutingSetup( - val settings: RoutingSettings, - val exceptionHandler: ExceptionHandler, - val rejectionHandler: RejectionHandler, - val executionContext: ExecutionContext, - val materializer: Materializer, - val routingLog: RoutingLog) { - - // enable `import setup._` to properly bring implicits in scope - implicit def executor: ExecutionContext = executionContext - implicit def implicitMaterializer: Materializer = materializer -} - -object RoutingSetup { - implicit def apply(implicit routingSettings: RoutingSettings, - exceptionHandler: ExceptionHandler = null, - rejectionHandler: RejectionHandler = null, - executionContext: ExecutionContext = null, - materializer: Materializer, - routingLog: RoutingLog): RoutingSetup = - new RoutingSetup( - routingSettings, - if (exceptionHandler ne null) exceptionHandler else ExceptionHandler.default(routingSettings), - if (rejectionHandler ne null) rejectionHandler else RejectionHandler.default, - if (executionContext ne null) executionContext else materializer.executionContext, - materializer, - routingLog) -} - -trait RoutingLog { - def log: LoggingAdapter - def requestLog(request: HttpRequest): LoggingAdapter -} - -object RoutingLog extends LowerPriorityRoutingLogImplicits { - def apply(defaultLog: LoggingAdapter): RoutingLog = - new RoutingLog { - def log = defaultLog - def requestLog(request: HttpRequest) = defaultLog - } - - implicit def fromActorContext(implicit ac: ActorContext): RoutingLog = RoutingLog(ac.system.log) -} -sealed abstract class LowerPriorityRoutingLogImplicits { - implicit def fromActorSystem(implicit system: ActorSystem): RoutingLog = RoutingLog(system.log) -} \ No newline at end of file