!htp simplify implicit resolution by getting rid of RoutingSetup

This commit is contained in:
Johannes Rudolph 2015-07-24 17:08:54 +02:00
parent 3b7d308545
commit e22118acd7
6 changed files with 87 additions and 98 deletions

View file

@ -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
}
}

View file

@ -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)
}

View file

@ -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?!")
}
}
}
}

View file

@ -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)
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
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)
}

View file

@ -1,77 +0,0 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
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)
}