diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2018-07-25 16:17:50 +0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2018-07-25 16:30:07 +0800 |
commit | c4476471f4ff4b38c518b5478996bc178a129e6b (patch) | |
tree | 63078ea3da9b866bd1edab7a6a4db2a9c9f13ab1 /cask/src/cask/main | |
parent | 603dfa8946f8c78580568613cd268ad05c6c38f6 (diff) | |
download | cask-c4476471f4ff4b38c518b5478996bc178a129e6b.tar.gz cask-c4476471f4ff4b38c518b5478996bc178a129e6b.tar.bz2 cask-c4476471f4ff4b38c518b5478996bc178a129e6b.zip |
Split up `cask` package into subpackages
Diffstat (limited to 'cask/src/cask/main')
-rw-r--r-- | cask/src/cask/main/Main.scala | 86 | ||||
-rw-r--r-- | cask/src/cask/main/Routes.scala | 60 |
2 files changed, 146 insertions, 0 deletions
diff --git a/cask/src/cask/main/Main.scala b/cask/src/cask/main/Main.scala new file mode 100644 index 0000000..77bac94 --- /dev/null +++ b/cask/src/cask/main/Main.scala @@ -0,0 +1,86 @@ +package cask.main + +import cask.model.{BaseResponse, Response, Status} +import cask.Cookie +import cask.internal.Router.EntryPoint +import cask.internal.{DispatchTrie, Router, Util} +import io.undertow.Undertow +import io.undertow.server.{HttpHandler, HttpServerExchange} +import io.undertow.server.handlers.BlockingHandler +import io.undertow.util.HttpString + + +class MainRoutes extends BaseMain with Routes{ + def allRoutes = Seq(this) +} +class Main(servers0: Routes*) extends BaseMain{ + def allRoutes = servers0.toSeq +} +abstract class BaseMain{ + def allRoutes: Seq[Routes] + val port: Int = 8080 + val host: String = "localhost" + + lazy val routeList = for{ + routes <- allRoutes + route <- routes.caskMetadata.value.map(x => x: Routes.EndpointMetadata[_]) + } yield (routes, route) + + lazy val routeTrie = DispatchTrie.construct[(Routes, Routes.EndpointMetadata[_])](0, + for((route, metadata) <- routeList) + yield (Util.splitPath(metadata.metadata.path): IndexedSeq[String], (route, metadata), metadata.metadata.subpath) + ) + + def handleError(statusCode: Int): Response = { + Response( + s"Error $statusCode: ${Status.codesToStatus(statusCode).reason}", + statusCode = statusCode + ) + } + + def writeResponse(exchange: HttpServerExchange, response: BaseResponse) = { + response.headers.foreach{case (k, v) => + exchange.getResponseHeaders.put(new HttpString(k), v) + } + response.cookies.foreach(c => exchange.setResponseCookie(Cookie.toUndertow(c))) + + exchange.setStatusCode(response.statusCode) + response.data.write(exchange.getOutputStream) + } + + lazy val defaultHandler = new HttpHandler() { + def handleRequest(exchange: HttpServerExchange): Unit = { + routeTrie.lookup(Util.splitPath(exchange.getRequestPath).toList, Map()) match{ + case None => writeResponse(exchange, handleError(404)) + case Some(((routes, metadata), bindings, remaining)) => + val result = metadata.metadata.handle( + exchange, remaining, bindings, routes, + metadata.entryPoint.asInstanceOf[EntryPoint[metadata.metadata.InputType, cask.main.Routes, (HttpServerExchange, Seq[String])]] + ) + + result match{ + case Router.Result.Success(response) => writeResponse(exchange, response) + case Router.Result.Error.Exception(e) => + println(e) + e.printStackTrace() + writeResponse(exchange, handleError(500)) + case err: Router.Result.Error => + println(err) + writeResponse(exchange, handleError(400)) + } + + + } + } + } + + def main(args: Array[String]): Unit = { + val server = Undertow.builder + .addHttpListener(port, host) + .setHandler(new BlockingHandler(defaultHandler)) + .build + server.start() + } +} + + diff --git a/cask/src/cask/main/Routes.scala b/cask/src/cask/main/Routes.scala new file mode 100644 index 0000000..fdd39bd --- /dev/null +++ b/cask/src/cask/main/Routes.scala @@ -0,0 +1,60 @@ +package cask.main + +import cask.endpoints.Endpoint +import cask.internal.Router.EntryPoint +import io.undertow.server.HttpServerExchange + +import scala.reflect.macros.blackbox.Context +import language.experimental.macros + +object Routes{ + case class EndpointMetadata[T](metadata: Endpoint[_], + entryPoint: EntryPoint[_, T, (HttpServerExchange, Seq[String])]) + case class RoutesEndpointsMetadata[T](value: EndpointMetadata[T]*) + object RoutesEndpointsMetadata{ + implicit def initialize[T] = macro initializeImpl[T] + implicit def initializeImpl[T: c.WeakTypeTag](c: Context): c.Expr[RoutesEndpointsMetadata[T]] = { + import c.universe._ + val router = new cask.internal.Router[c.type](c) + + val routeParts = for{ + m <- c.weakTypeOf[T].members + annot <- m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[Endpoint[_]]) + } yield { + val annotObject = q"new ${annot.tree.tpe}(..${annot.tree.children.tail})" + val annotObjectSym = c.universe.TermName(c.freshName("annotObject")) + val route = router.extractMethod( + m.asInstanceOf[MethodSymbol], + weakTypeOf[T], + (t: router.c.universe.Tree) => q"$annotObjectSym.wrapMethodOutput($t)", + c.weakTypeOf[(io.undertow.server.HttpServerExchange, Seq[String])], + q"$annotObjectSym.parseMethodInput", + tq"$annotObjectSym.InputType" + ) + + + q"""{ + val $annotObjectSym = $annotObject + cask.main.Routes.EndpointMetadata( + $annotObjectSym, + $route + ) + }""" + } + + c.Expr[RoutesEndpointsMetadata[T]](q"""cask.main.Routes.RoutesEndpointsMetadata(..$routeParts)""") + } + } +} + +trait Routes{ + private[this] var metadata0: Routes.RoutesEndpointsMetadata[this.type] = null + def caskMetadata = + if (metadata0 != null) metadata0 + else throw new Exception("Routes not yet initialize") + + protected[this] def initialize()(implicit routes: Routes.RoutesEndpointsMetadata[this.type]): Unit = { + metadata0 = routes + } +} + |