/server/libs/akka-utils/src/main/scala/cool/graph/akkautil/http/ServerExecutor.scala

https://github.com/prisma/graphcool-framework · Scala · 56 lines · 40 code · 10 blank · 6 comment · 0 complexity · b369b94ca142fb91aa10a55b6f61b922 MD5 · raw file

  1. package cool.graph.akkautil.http
  2. import akka.actor.ActorSystem
  3. import akka.http.scaladsl.Http
  4. import akka.http.scaladsl.Http.ServerBinding
  5. import akka.http.scaladsl.server.Directives._
  6. import akka.http.scaladsl.server.Route
  7. import akka.stream.ActorMaterializer
  8. import ch.megard.akka.http.cors.scaladsl.CorsDirectives
  9. import ch.megard.akka.http.cors.scaladsl.CorsDirectives._
  10. import scala.concurrent.duration.{Duration, _}
  11. import scala.concurrent.{Await, Future}
  12. /**
  13. * Class that knows how to start and stop servers. Takes one or more servers.
  14. * In case that more than one server is given, the ServerExecutor combines all given servers into one server
  15. * by collecting all their routes. Evaluation order is strictly linear.
  16. */
  17. case class ServerExecutor(port: Int, servers: Server*)(implicit system: ActorSystem, materializer: ActorMaterializer) {
  18. import system.dispatcher
  19. val routes: Route = {
  20. handleRejections(CorsDirectives.corsRejectionHandler) {
  21. cors() {
  22. val routes = servers.map(_.routes) :+ statusRoute
  23. routes.reduceLeft(_ ~ _)
  24. }
  25. }
  26. }
  27. def statusRoute: Route = (get & path("status")) {
  28. val checks = Future.sequence(servers.map(_.healthCheck))
  29. onSuccess(checks) { _ =>
  30. complete("OK")
  31. }
  32. }
  33. lazy val serverBinding: Future[ServerBinding] = {
  34. val binding = Http().bindAndHandle(Route.handlerFlow(routes), "0.0.0.0", port)
  35. binding.onSuccess { case b => println(s"Server running on :${b.localAddress.getPort}") }
  36. binding
  37. }
  38. def start: Future[_] = Future.sequence[Any, Seq](servers.map(_.onStart) :+ serverBinding)
  39. def stop: Future[_] = Future.sequence[Any, Seq](servers.map(_.onStop) :+ serverBinding.map(_.unbind))
  40. // Starts the server and blocks the calling thread until the underlying actor system terminates.
  41. def startBlocking(duration: Duration = 15.seconds): Unit = {
  42. start
  43. Await.result(system.whenTerminated, Duration.Inf)
  44. }
  45. def stopBlocking(duration: Duration = 15.seconds): Unit = Await.result(stop, duration)
  46. }