From 3f61791c57b450de84a6599e2338b1afcf172a05 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sat, 21 Jul 2018 22:32:38 +0800 Subject: WIP moving logic into annotations themselves --- cask/src/cask/Annotations.scala | 69 ++++++++++++++++++++++++++++++++++------- cask/src/cask/Main.scala | 22 +++++-------- cask/src/cask/Router.scala | 14 ++++++--- cask/src/cask/Routes.scala | 8 +++-- 4 files changed, 81 insertions(+), 32 deletions(-) diff --git a/cask/src/cask/Annotations.scala b/cask/src/cask/Annotations.scala index e77d98c..fa0e25e 100644 --- a/cask/src/cask/Annotations.scala +++ b/cask/src/cask/Annotations.scala @@ -1,20 +1,67 @@ package cask -import scala.annotation.StaticAnnotation +import cask.Router.EntryPoint +import io.undertow.server.HttpServerExchange +import collection.JavaConverters._ - -trait AnnotationBase{ - def wrapMethodOutput(t: Response): Any +trait EndpointAnnotation[R]{ + val path: String + def wrapMethodOutput(t: R): Any def parseMethodInput[T](implicit p: ParamReader[T]) = p + + def handle(exchange: HttpServerExchange, + bindings: Map[String, String], + routes: Routes, + entryPoint: EntryPoint[Routes, HttpServerExchange]): Router.Result[Response] } -trait RouteBase extends AnnotationBase{ - val path: String +trait RouteBase extends EndpointAnnotation[Response]{ def wrapMethodOutput(t: Response) = t + def handle(exchange: HttpServerExchange, + bindings: Map[String, String], + routes: Routes, + entryPoint: EntryPoint[Routes, HttpServerExchange]): Router.Result[Response] = { + val allBindings = + bindings.toSeq ++ + exchange.getQueryParameters + .asScala + .toSeq + .flatMap{case (k, vs) => vs.asScala.map((k, _))} + + entryPoint.invoke(routes, exchange, allBindings.map{case (k, v) => (k, Some(v))}) + .asInstanceOf[Router.Result[Response]] + } } -class get(val path: String) extends StaticAnnotation with RouteBase -class post(val path: String) extends StaticAnnotation with RouteBase -class put(val path: String) extends StaticAnnotation with RouteBase -class route(val path: String, val methods: Seq[String]) extends StaticAnnotation with RouteBase -class static(val path: String) extends StaticAnnotation{ +class get(val path: String) extends RouteBase +class post(val path: String) extends RouteBase +class put(val path: String) extends RouteBase +class route(val path: String, val methods: Seq[String]) extends RouteBase +class static(val path: String) extends EndpointAnnotation[String] { def wrapOutput(t: String) = t + + def wrapMethodOutput(t: String) = t + + def handle(exchange: HttpServerExchange, + bindings: Map[String, String], + routes: Routes, + entryPoint: EntryPoint[Routes, HttpServerExchange]): Router.Result[Response] = { + entryPoint.invoke(routes, exchange, Nil).asInstanceOf[Router.Result[String]] match{ + case Router.Result.Success(s) => + println("XXX STATIC") + val relPath = java.nio.file.Paths.get( + s + Util.splitPath(exchange.getRequestPath).drop(Util.splitPath(path).length).mkString("/") + ) + println("Y") + if (java.nio.file.Files.exists(relPath) && java.nio.file.Files.isRegularFile(relPath)){ + Router.Result.Success(Response(java.nio.file.Files.newInputStream(relPath))) + }else{ + Router.Result.Success(Response("", 404)) + } + + case e: Router.Result.Error => + println("XXX") + e + + } + + } } diff --git a/cask/src/cask/Main.scala b/cask/src/cask/Main.scala index d16172c..dcbc807 100644 --- a/cask/src/cask/Main.scala +++ b/cask/src/cask/Main.scala @@ -3,6 +3,7 @@ import cask.Router.EntryPoint import java.io.OutputStream import java.nio.ByteBuffer +import cask.Routes.Metadata import io.undertow.Undertow import io.undertow.server.handlers.BlockingHandler import io.undertow.server.{HttpHandler, HttpServerExchange} @@ -24,9 +25,9 @@ abstract class BaseMain{ route <- routes.caskMetadata.value.map(x => x: Routes.RouteMetadata[_]) } yield (routes, route) - lazy val routeTrie = DispatchTrie.construct[(Routes, Router.EntryPoint[_, HttpServerExchange])](0, + lazy val routeTrie = DispatchTrie.construct[(Routes, Routes.RouteMetadata[_])](0, for((route, metadata) <- routeList) - yield (Util.splitPath(metadata.metadata.path): IndexedSeq[String], (route, metadata.entryPoint)) + yield (Util.splitPath(metadata.metadata.path): IndexedSeq[String], (route, metadata)) ) def handleError(statusCode: Int): Response = { @@ -49,18 +50,11 @@ abstract class BaseMain{ def handleRequest(exchange: HttpServerExchange): Unit = { routeTrie.lookup(Util.splitPath(exchange.getRequestPath).toList, Map()) match{ case None => writeResponse(exchange, handleError(404)) - case Some(((routes, entrypoint), bindings)) => - import collection.JavaConverters._ - val allBindings = - bindings.toSeq ++ - exchange.getQueryParameters - .asScala - .toSeq - .flatMap{case (k, vs) => vs.asScala.map((k, _))} - - val result = entrypoint - .asInstanceOf[EntryPoint[routes.type, HttpServerExchange]] - .invoke(routes, exchange, allBindings.map{case (k, v) => (k, Some(v))}) + case Some(((routes, metadata), bindings)) => + val result = metadata.metadata.handle( + exchange, bindings, routes, + metadata.entryPoint.asInstanceOf[EntryPoint[Routes, HttpServerExchange]] + ) result match{ case Router.Result.Success(response: Response) => writeResponse(exchange, response) diff --git a/cask/src/cask/Router.scala b/cask/src/cask/Router.scala index 65192a1..e2a6e9e 100644 --- a/cask/src/cask/Router.scala +++ b/cask/src/cask/Router.scala @@ -42,6 +42,7 @@ object Router{ else if (s.startsWith("-")) s.drop(1) else s } + /** * What is known about a single endpoint for our routes. It has a [[name]], * [[argSignatures]] for each argument, and a macro-generated [[invoke0]] @@ -52,10 +53,10 @@ object Router{ * calling a Scala method. */ case class EntryPoint[T, C](name: String, - argSignatures: Seq[ArgSig[T, _, C]], - doc: Option[String], - varargs: Boolean, - invoke0: (T, C, Map[String, Seq[String]], Seq[String]) => Result[Any]){ + argSignatures: Seq[ArgSig[T, _, C]], + doc: Option[String], + varargs: Boolean, + invoke0: (T, C, Map[String, Seq[String]], Seq[String]) => Result[Any]){ def invoke(target: T, ctx: C, groupedArgs: Seq[(String, Option[String])]): Result[Any] = { var remainingArgSignatures = argSignatures.toList.filter(_.reads.arity > 0) @@ -373,6 +374,9 @@ class Router [C <: Context](val c: C) { }.unzip + val methCall = + if (meth.paramLists.isEmpty) q"$baseArgSym.${meth.name.toTermName}" + else q"$baseArgSym.${meth.name.toTermName}(..$argNameCasts)" val res = q""" cask.Router.EntryPoint[$curCls, $ctx]( ${meth.name.toString}, @@ -386,7 +390,7 @@ class Router [C <: Context](val c: C) { cask.Router.validate(Seq(..$readArgs)) match{ case cask.Router.Result.Success(List(..$argNames)) => cask.Router.Result.Success( - ${wrapOutput(q"$baseArgSym.${meth.name.toTermName}(..$argNameCasts)")} + ${wrapOutput(methCall)} ) case x: cask.Router.Result.Error => x } diff --git a/cask/src/cask/Routes.scala b/cask/src/cask/Routes.scala index 5ce8f6c..3ab5906 100644 --- a/cask/src/cask/Routes.scala +++ b/cask/src/cask/Routes.scala @@ -6,6 +6,7 @@ import cask.Router.EntryPoint import io.undertow.server.HttpServerExchange import scala.reflect.macros.blackbox.Context +import java.io.InputStream case class Response(data: Response.Data, @@ -23,11 +24,14 @@ object Response{ implicit class BytesData(b: Array[Byte]) extends Data{ def write(out: OutputStream) = out.write(b) } + implicit class StreamData(b: InputStream) extends Data{ + def write(out: OutputStream) = b.transferTo(out) + } } } object Routes{ - case class RouteMetadata[T](metadata: RouteBase, + case class RouteMetadata[T](metadata: EndpointAnnotation[_], entryPoint: EntryPoint[T, HttpServerExchange]) case class Metadata[T](value: RouteMetadata[T]*) object Metadata{ @@ -38,7 +42,7 @@ object Routes{ val routeParts = for{ m <- c.weakTypeOf[T].members - annot <- m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[AnnotationBase]) + annot <- m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[EndpointAnnotation[_]]) } yield { val annotObject = q"new ${annot.tree.tpe}(..${annot.tree.children.tail})" val annotObjectSym = c.universe.TermName(c.freshName("annotObject")) -- cgit v1.2.3