summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2018-07-25 16:17:50 +0800
committerLi Haoyi <haoyi.sg@gmail.com>2018-07-25 16:30:07 +0800
commitc4476471f4ff4b38c518b5478996bc178a129e6b (patch)
tree63078ea3da9b866bd1edab7a6a4db2a9c9f13ab1
parent603dfa8946f8c78580568613cd268ad05c6c38f6 (diff)
downloadcask-c4476471f4ff4b38c518b5478996bc178a129e6b.tar.gz
cask-c4476471f4ff4b38c518b5478996bc178a129e6b.tar.bz2
cask-c4476471f4ff4b38c518b5478996bc178a129e6b.zip
Split up `cask` package into subpackages
-rw-r--r--cask/src/cask/Endpoints.scala71
-rw-r--r--cask/src/cask/Status.scala286
-rw-r--r--cask/src/cask/endpoints/Endpoint.scala19
-rw-r--r--cask/src/cask/endpoints/FormEndpoint.scala (renamed from cask/src/cask/FormEndpoint.scala)8
-rw-r--r--cask/src/cask/endpoints/JsonEndpoint.scala (renamed from cask/src/cask/JsonEndpoint.scala)7
-rw-r--r--cask/src/cask/endpoints/ParamReader.scala27
-rw-r--r--cask/src/cask/endpoints/StaticEndpoints.scala30
-rw-r--r--cask/src/cask/endpoints/WebEndpoints.scala (renamed from cask/src/cask/QueryParamReader.scala)53
-rw-r--r--cask/src/cask/internal/DispatchTrie.scala (renamed from cask/src/cask/DispatchTrie.scala)6
-rw-r--r--cask/src/cask/internal/Router.scala (renamed from cask/src/cask/Router.scala)40
-rw-r--r--cask/src/cask/internal/Util.scala (renamed from cask/src/cask/Util.scala)2
-rw-r--r--cask/src/cask/main/Main.scala (renamed from cask/src/cask/Main.scala)19
-rw-r--r--cask/src/cask/main/Routes.scala (renamed from cask/src/cask/Routes.scala)54
-rw-r--r--cask/src/cask/model/Cookie.scala (renamed from cask/src/cask/Cookie.scala)2
-rw-r--r--cask/src/cask/model/FormValue.scala (renamed from cask/src/cask/FormValue.scala)2
-rw-r--r--cask/src/cask/model/Params.scala4
-rw-r--r--cask/src/cask/model/Response.scala81
-rw-r--r--cask/src/cask/model/Status.scala288
-rw-r--r--cask/src/cask/package.scala37
-rw-r--r--cask/test/src/test/cask/DispatchTrieTests.scala2
-rw-r--r--cask/test/src/test/cask/RedirectAbort.scala4
21 files changed, 564 insertions, 478 deletions
diff --git a/cask/src/cask/Endpoints.scala b/cask/src/cask/Endpoints.scala
deleted file mode 100644
index b0bc207..0000000
--- a/cask/src/cask/Endpoints.scala
+++ /dev/null
@@ -1,71 +0,0 @@
-package cask
-
-import cask.Router.EntryPoint
-import io.undertow.server.HttpServerExchange
-
-import collection.JavaConverters._
-
-trait Endpoint[R]{
- type InputType
- val path: String
- def subpath: Boolean = false
- def wrapMethodOutput(t: R): Any
- def handle(exchange: HttpServerExchange,
- remaining: Seq[String],
- bindings: Map[String, String],
- routes: Routes,
- entryPoint: EntryPoint[InputType, Routes, (HttpServerExchange, Seq[String])]): Router.Result[BaseResponse]
-}
-trait WebEndpoint extends Endpoint[Response]{
- type InputType = Seq[String]
- def wrapMethodOutput(t: Response) = t
- def parseMethodInput[T](implicit p: QueryParamReader[T]) = p
- def handle(exchange: HttpServerExchange,
- remaining: Seq[String],
- bindings: Map[String, String],
- routes: Routes,
- entryPoint: EntryPoint[Seq[String], Routes, (HttpServerExchange, Seq[String])]): Router.Result[Response] = {
- val allBindings =
- bindings.map{case (k, v) => (k, Seq(v))} ++
- exchange.getQueryParameters
- .asScala
- .toSeq
- .map{case (k, vs) => (k, vs.asScala.toSeq)}
-
- entryPoint.invoke(routes, (exchange, remaining), allBindings)
- .asInstanceOf[Router.Result[Response]]
- }
-}
-class get(val path: String, override val subpath: Boolean = false) extends WebEndpoint
-class post(val path: String, override val subpath: Boolean = false) extends WebEndpoint
-class put(val path: String, override val subpath: Boolean = false) extends WebEndpoint
-class route(val path: String, val methods: Seq[String], override val subpath: Boolean = false) extends WebEndpoint
-class static(val path: String) extends Endpoint[String] {
- type InputType = Seq[String]
- override def subpath = true
- def wrapOutput(t: String) = t
- def parseMethodInput[T](implicit p: QueryParamReader[T]) = p
- def wrapMethodOutput(t: String) = t
-
- def handle(exchange: HttpServerExchange,
- remaining: Seq[String],
- bindings: Map[String, String],
- routes: Routes,
- entryPoint: EntryPoint[Seq[String], Routes, (HttpServerExchange, Seq[String])]): Router.Result[Response] = {
- entryPoint.invoke(routes, (exchange, remaining), Map()).asInstanceOf[Router.Result[String]] match{
- case Router.Result.Success(s) =>
- val relPath = java.nio.file.Paths.get(
- s + "/" + remaining.mkString("/")
- )
- 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 => e
-
- }
-
- }
-} \ No newline at end of file
diff --git a/cask/src/cask/Status.scala b/cask/src/cask/Status.scala
deleted file mode 100644
index f3f5434..0000000
--- a/cask/src/cask/Status.scala
+++ /dev/null
@@ -1,286 +0,0 @@
-package cask
-
-sealed trait Status {
- val code: Int
- val reason: String
-}
-
-object Status {
- val codesToStatus: Map[Int, Status] = Map(
- 100 -> Continue,
- 101 -> SwitchingProtocols,
- 200 -> OK,
- 201 -> Created,
- 202 -> Accepted,
- 203 -> NonAuthoritativeInformation,
- 204 -> NoContent,
- 205 -> ResetContent,
- 206 -> PartialContent,
- 300 -> MultipleChoices,
- 301 -> MovedPermanently,
- 302 -> Found,
- 303 -> SeeOther,
- 304 -> NotModified,
- 305 -> UseProxy,
- 307 -> TemporaryRedirect,
- 308 -> PermanentRedirect,
- 400 -> BadRequest,
- 401 -> Unauthorized,
- 402 -> PaymentRequired,
- 403 -> Forbidden,
- 404 -> NotFound,
- 405 -> MethodNotAllowed,
- 406 -> NotAcceptable,
- 407 -> ProxyAuthenticationRequired,
- 408 -> RequestTimeout,
- 409 -> Conflict,
- 410 -> Gone,
- 411 -> LengthRequired,
- 412 -> PreconditionFailed,
- 413 -> RequestEntityTooLarge,
- 414 -> RequestURITooLong,
- 415 -> UnsupportedMediaType,
- 416 -> RequestedRangeNotSatisfiable,
- 417 -> ExpectationFailed,
- 418 -> Teapot,
- 420 -> EnhanceYourCalm,
- 429 -> TooManyRequests,
- 451 -> UnavailableForLegalReasons,
- 500 -> InternalServerError,
- 501 -> NotImplemented,
- 502 -> BadGateway,
- 503 -> ServiceUnavailable,
- 504 -> GatewayTimeout,
- 505 -> HTTPVersionNotSupported
- )
-
- val statusToCodes: Map[String, Int] =
- codesToStatus.map { case (code, status) => status.reason -> code }
-}
-
-case class Unknown(code: Int, reason: String) extends Status
-
-case object Continue extends Status {
- val code = 100
- val reason: String = "Continue"
-}
-
-case object SwitchingProtocols extends Status {
- val code = 101
- val reason: String = "Switching Protocols"
-}
-
-case object OK extends Status {
- val code = 200
- val reason: String = "OK"
-}
-
-case object Created extends Status {
- val code = 201
- val reason: String = "Created"
-}
-
-case object Accepted extends Status {
- val code = 202
- val reason: String = "Accepted"
-}
-
-case object NonAuthoritativeInformation extends Status {
- val code = 203
- val reason: String = "Non-Authoritative Information"
-}
-
-case object NoContent extends Status {
- val code = 204
- val reason: String = "No Content"
-}
-
-case object ResetContent extends Status {
- val code = 205
- val reason: String = "Reset Content"
-}
-
-case object PartialContent extends Status {
- val code = 206
- val reason: String = "Partial Content"
-}
-
-case object MultipleChoices extends Status {
- val code = 300
- val reason: String = "Multiple Choices"
-}
-
-case object MovedPermanently extends Status {
- val code = 301
- val reason: String = "Moved Permanently"
-}
-
-case object Found extends Status {
- val code = 302
- val reason: String = "Found"
-}
-
-case object SeeOther extends Status {
- val code = 303
- val reason: String = "See Other"
-}
-
-case object NotModified extends Status {
- val code = 304
- val reason: String = "Not Modified"
-}
-
-case object UseProxy extends Status {
- val code = 305
- val reason: String = "Use Proxy"
-}
-
-case object TemporaryRedirect extends Status {
- val code = 307
- val reason: String = "Temporary Redirect"
-}
-
-case object PermanentRedirect extends Status {
- val code = 308
- val reason: String = "Permanent Redirect"
-}
-
-case object BadRequest extends Status {
- val code = 400
- val reason: String = "Bad Request"
-}
-
-case object Unauthorized extends Status {
- val code = 401
- val reason: String = "Unauthorized"
-}
-
-case object PaymentRequired extends Status {
- val code = 402
- val reason: String = "Payment Required"
-}
-
-case object Forbidden extends Status {
- val code = 403
- val reason: String = "Forbidden"
-}
-
-case object NotFound extends Status {
- val code = 404
- val reason: String = "Not Found"
-}
-
-case object MethodNotAllowed extends Status {
- val code = 405
- val reason: String = "Method Not Allowed"
-}
-
-case object NotAcceptable extends Status {
- val code = 406
- val reason: String = "Not Acceptable"
-}
-
-case object ProxyAuthenticationRequired extends Status {
- val code = 407
- val reason: String = "Proxy Authentication Required"
-}
-
-case object RequestTimeout extends Status {
- val code = 408
- val reason: String = "Request Time-out"
-}
-
-case object Conflict extends Status {
- val code = 409
- val reason: String = "Conflict"
-}
-
-case object Gone extends Status {
- val code = 410
- val reason: String = "Gone"
-}
-
-case object LengthRequired extends Status {
- val code = 411
- val reason: String = "Length Required"
-}
-
-case object PreconditionFailed extends Status {
- val code = 412
- val reason: String = "Precondition Failed"
-}
-
-case object RequestEntityTooLarge extends Status {
- val code = 413
- val reason: String = "Request Entity Too Large"
-}
-
-case object RequestURITooLong extends Status {
- val code = 414
- val reason: String = "Request-URI Too Large"
-}
-
-case object UnsupportedMediaType extends Status {
- val code = 415
- val reason: String = "Unsupported Media Type"
-}
-
-case object RequestedRangeNotSatisfiable extends Status {
- val code = 416
- val reason: String = "Requested range not satisfiable"
-}
-
-case object ExpectationFailed extends Status {
- val code = 417
- val reason: String = "Expectation Failed"
-}
-
-case object Teapot extends Status {
- val code = 418
- val reason: String = "I'm a teapot"
-}
-
-case object EnhanceYourCalm extends Status {
- val code = 420
- val reason: String = "Enhance Your Calm"
-}
-
-case object TooManyRequests extends Status {
- val code = 429
- val reason: String = "Too Many Requests"
-}
-
-case object UnavailableForLegalReasons extends Status {
- val code = 451
- val reason: String = "Unavailable For Legal Reasons"
-}
-
-case object InternalServerError extends Status {
- val code = 500
- val reason: String = "Internal Server Error"
-}
-
-case object NotImplemented extends Status {
- val code = 501
- val reason: String = "Not Implemented"
-}
-
-case object BadGateway extends Status {
- val code = 502
- val reason: String = "Bad Gateway"
-}
-
-case object ServiceUnavailable extends Status {
- val code = 503
- val reason: String = "Service Unavailable"
-}
-
-case object GatewayTimeout extends Status {
- val code = 504
- val reason: String = "Gateway Time-out"
-}
-
-case object HTTPVersionNotSupported extends Status {
- val code = 505
- val reason: String = "HTTP Version not supported"
-}
diff --git a/cask/src/cask/endpoints/Endpoint.scala b/cask/src/cask/endpoints/Endpoint.scala
new file mode 100644
index 0000000..5d69faa
--- /dev/null
+++ b/cask/src/cask/endpoints/Endpoint.scala
@@ -0,0 +1,19 @@
+package cask.endpoints
+
+import cask.internal.Router
+import cask.internal.Router.EntryPoint
+import cask.main.Routes
+import cask.model.BaseResponse
+import io.undertow.server.HttpServerExchange
+
+trait Endpoint[R]{
+ type InputType
+ val path: String
+ def subpath: Boolean = false
+ def wrapMethodOutput(t: R): Any
+ def handle(exchange: HttpServerExchange,
+ remaining: Seq[String],
+ bindings: Map[String, String],
+ routes: Routes,
+ entryPoint: EntryPoint[InputType, Routes, (HttpServerExchange, Seq[String])]): Router.Result[BaseResponse]
+} \ No newline at end of file
diff --git a/cask/src/cask/FormEndpoint.scala b/cask/src/cask/endpoints/FormEndpoint.scala
index 0d6f2f2..29265b9 100644
--- a/cask/src/cask/FormEndpoint.scala
+++ b/cask/src/cask/endpoints/FormEndpoint.scala
@@ -1,8 +1,12 @@
-package cask
+package cask.endpoints
-import cask.Router.EntryPoint
+import cask.internal.Router.EntryPoint
+import cask.internal.Router
+import cask.main.Routes
+import cask.model.{FormValue, Response}
import io.undertow.server.HttpServerExchange
import io.undertow.server.handlers.form.FormParserFactory
+
import collection.JavaConverters._
sealed trait FormReader[T] extends Router.ArgReader[Seq[FormValue], T, (HttpServerExchange, Seq[String])]
diff --git a/cask/src/cask/JsonEndpoint.scala b/cask/src/cask/endpoints/JsonEndpoint.scala
index 58fd596..ca66f67 100644
--- a/cask/src/cask/JsonEndpoint.scala
+++ b/cask/src/cask/endpoints/JsonEndpoint.scala
@@ -1,6 +1,9 @@
-package cask
+package cask.endpoints
-import cask.Router.EntryPoint
+import cask.internal.Router
+import cask.internal.Router.EntryPoint
+import cask.main.Routes
+import cask.model.Response
import io.undertow.server.HttpServerExchange
diff --git a/cask/src/cask/endpoints/ParamReader.scala b/cask/src/cask/endpoints/ParamReader.scala
new file mode 100644
index 0000000..a9d9a7f
--- /dev/null
+++ b/cask/src/cask/endpoints/ParamReader.scala
@@ -0,0 +1,27 @@
+package cask.endpoints
+
+import cask.Cookie
+import cask.internal.Router
+import io.undertow.server.HttpServerExchange
+import io.undertow.server.handlers.form.{FormData, FormParserFactory}
+
+abstract class ParamReader[T]
+ extends Router.ArgReader[Seq[String], T, (HttpServerExchange, Seq[String])]{
+ def arity: Int
+ def read(ctx: (HttpServerExchange, Seq[String]), v: Seq[String]): T
+}
+object ParamReader{
+ class NilParam[T](f: (HttpServerExchange, Seq[String]) => T) extends ParamReader[T]{
+ def arity = 0
+ def read(ctx: (HttpServerExchange, Seq[String]), v: Seq[String]): T = f(ctx._1, ctx._2)
+ }
+ implicit object HttpExchangeParam extends NilParam[HttpServerExchange]((server, remaining) => server)
+ implicit object SubpathParam extends NilParam[cask.model.Subpath]((server, remaining) => new cask.model.Subpath(remaining))
+ implicit object CookieParam extends NilParam[cask.model.Cookies]((server, remaining) => {
+ import collection.JavaConverters._
+ new cask.model.Cookies(server.getRequestCookies.asScala.toMap.map{case (k, v) => (k, Cookie.fromUndertow(v))})
+ })
+ implicit object FormDataParam extends NilParam[FormData]((server, remaining) =>
+ FormParserFactory.builder().build().createParser(server).parseBlocking()
+ )
+}
diff --git a/cask/src/cask/endpoints/StaticEndpoints.scala b/cask/src/cask/endpoints/StaticEndpoints.scala
new file mode 100644
index 0000000..ed964b0
--- /dev/null
+++ b/cask/src/cask/endpoints/StaticEndpoints.scala
@@ -0,0 +1,30 @@
+package cask.endpoints
+
+import cask.internal.Router
+import cask.internal.Router.EntryPoint
+import cask.main.Routes
+import cask.model.BaseResponse
+import io.undertow.server.HttpServerExchange
+
+class static(val path: String) extends Endpoint[String] {
+ type InputType = Seq[String]
+ override def subpath = true
+ def wrapOutput(t: String) = t
+ def parseMethodInput[T](implicit p: QueryParamReader[T]) = p
+ def wrapMethodOutput(t: String) = t
+
+ def handle(exchange: HttpServerExchange,
+ remaining: Seq[String],
+ bindings: Map[String, String],
+ routes: Routes,
+ entryPoint: EntryPoint[Seq[String], Routes, (HttpServerExchange, Seq[String])]): Router.Result[BaseResponse] = {
+ entryPoint.invoke(routes, (exchange, remaining), Map()).asInstanceOf[Router.Result[String]] match{
+ case Router.Result.Success(s) =>
+ Router.Result.Success(cask.model.Static(s + "/" + remaining.mkString("/")))
+
+ case e: Router.Result.Error => e
+
+ }
+
+ }
+}
diff --git a/cask/src/cask/QueryParamReader.scala b/cask/src/cask/endpoints/WebEndpoints.scala
index 1d34b14..af2ed0a 100644
--- a/cask/src/cask/QueryParamReader.scala
+++ b/cask/src/cask/endpoints/WebEndpoints.scala
@@ -1,29 +1,39 @@
-package cask
+package cask.endpoints
+import cask.internal.Router
+import cask.internal.Router.EntryPoint
+import cask.main.Routes
+import cask.model.BaseResponse
import io.undertow.server.HttpServerExchange
-import io.undertow.server.handlers.form.{FormData, FormParserFactory}
+import collection.JavaConverters._
-abstract class ParamReader[T]
- extends Router.ArgReader[Seq[String], T, (HttpServerExchange, Seq[String])]{
- def arity: Int
- def read(ctx: (HttpServerExchange, Seq[String]), v: Seq[String]): T
-}
-object ParamReader{
- class NilParam[T](f: (HttpServerExchange, Seq[String]) => T) extends ParamReader[T]{
- def arity = 0
- def read(ctx: (HttpServerExchange, Seq[String]), v: Seq[String]): T = f(ctx._1, ctx._2)
+
+trait WebEndpoint extends Endpoint[BaseResponse]{
+ type InputType = Seq[String]
+ def wrapMethodOutput(t: BaseResponse) = t
+ def parseMethodInput[T](implicit p: QueryParamReader[T]) = p
+ def handle(exchange: HttpServerExchange,
+ remaining: Seq[String],
+ bindings: Map[String, String],
+ routes: Routes,
+ entryPoint: EntryPoint[Seq[String], Routes, (HttpServerExchange, Seq[String])]): Router.Result[BaseResponse] = {
+ val allBindings =
+ bindings.map{case (k, v) => (k, Seq(v))} ++
+ exchange.getQueryParameters
+ .asScala
+ .toSeq
+ .map{case (k, vs) => (k, vs.asScala.toArray.toSeq)}
+
+ entryPoint.invoke(routes, (exchange, remaining), allBindings)
+ .asInstanceOf[Router.Result[BaseResponse]]
}
- implicit object HttpExchangeParam extends NilParam[HttpServerExchange]((server, remaining) => server)
- implicit object SubpathParam extends NilParam[Subpath]((server, remaining) => new Subpath(remaining))
- implicit object CookieParam extends NilParam[Cookies]((server, remaining) => {
- import collection.JavaConverters._
- new Cookies(server.getRequestCookies.asScala.toMap.map{case (k, v) => (k, Cookie.fromUndertow(v))})
- })
- implicit object FormDataParam extends NilParam[FormData]((server, remaining) =>
- FormParserFactory.builder().build().createParser(server).parseBlocking()
- )
}
+class get(val path: String, override val subpath: Boolean = false) extends WebEndpoint
+class post(val path: String, override val subpath: Boolean = false) extends WebEndpoint
+class put(val path: String, override val subpath: Boolean = false) extends WebEndpoint
+class route(val path: String, val methods: Seq[String], override val subpath: Boolean = false) extends WebEndpoint
+
abstract class QueryParamReader[T]
extends Router.ArgReader[Seq[String], T, (HttpServerExchange, Seq[String])]{
def arity: Int
@@ -58,6 +68,3 @@ object QueryParamReader{
}
}
-
-class Subpath(val value: Seq[String])
-class Cookies(val value: Map[String, Cookie]) \ No newline at end of file
diff --git a/cask/src/cask/DispatchTrie.scala b/cask/src/cask/internal/DispatchTrie.scala
index d986c8b..57d8d9d 100644
--- a/cask/src/cask/DispatchTrie.scala
+++ b/cask/src/cask/internal/DispatchTrie.scala
@@ -1,4 +1,4 @@
-package cask
+package cask.internal
import collection.mutable
object DispatchTrie{
def construct[T](index: Int,
@@ -21,7 +21,7 @@ object DispatchTrie{
if (terminals.length > 1){
throw new Exception(
"More than one endpoint has the same path: " +
- terminals.map(_._1.map(_.mkString("/"))).mkString(", ")
+ terminals.map(_._1.map(_.mkString("/"))).mkString(", ")
)
} else if(wildcards.size >= 1 && continuations.size > 1) {
throw new Exception(
@@ -49,7 +49,7 @@ case class DispatchTrie[T](current: Option[(T, Boolean)],
children: Map[String, DispatchTrie[T]]){
final def lookup(remainingInput: List[String],
bindings: Map[String, String])
- : Option[(T, Map[String, String], Seq[String])] = {
+ : Option[(T, Map[String, String], Seq[String])] = {
remainingInput match{
case Nil =>
current.map(x => (x._1, bindings, Nil))
diff --git a/cask/src/cask/Router.scala b/cask/src/cask/internal/Router.scala
index 90d5245..f6a4405 100644
--- a/cask/src/cask/Router.scala
+++ b/cask/src/cask/internal/Router.scala
@@ -1,4 +1,4 @@
-package cask
+package cask.internal
import io.undertow.server.HttpServerExchange
@@ -67,10 +67,10 @@ object Router{
}
def read[I, C]
- (dict: Map[String, I],
- default: => Option[Any],
- arg: ArgSig[I, _, _, C],
- thunk: I => Any): FailMaybe = {
+ (dict: Map[String, I],
+ default: => Option[Any],
+ arg: ArgSig[I, _, _, C],
+ thunk: I => Any): FailMaybe = {
arg.reads.arity match{
case 0 =>
tryEither(
@@ -160,10 +160,10 @@ object Router{
}
def makeReadCall[I, C]
- (dict: Map[String, I],
- ctx: C,
- default: => Option[Any],
- arg: ArgSig[I, _, _, C]) = {
+ (dict: Map[String, I],
+ ctx: C,
+ default: => Option[Any],
+ arg: ArgSig[I, _, _, C]) = {
read[I, C](dict, default, arg, arg.reads.read(ctx, _))
}
@@ -267,7 +267,7 @@ class Router[C <: Context](val c: C) {
}
val argSig = q"""
- cask.Router.ArgSig[$annotDeserializeType, $curCls, $docUnwrappedType, $ctx](
+ cask.internal.Router.ArgSig[$annotDeserializeType, $curCls, $docUnwrappedType, $ctx](
${arg.name.toString},
${docUnwrappedType.toString + (if(vararg) "*" else "")},
$docTree,
@@ -278,7 +278,7 @@ class Router[C <: Context](val c: C) {
val reader =
if(vararg) c.abort(meth.pos, "Varargs are not supported in cask routes")
else q"""
- cask.Router.makeReadCall(
+ cask.internal.Router.makeReadCall(
$argListSymbol,
ctx,
$default,
@@ -305,23 +305,23 @@ class Router[C <: Context](val c: C) {
if (meth.paramLists.isEmpty) q"$baseArgSym.${meth.name.toTermName}"
else q"$baseArgSym.${meth.name.toTermName}(..$argNameCasts)"
val res = q"""
- cask.Router.EntryPoint[$annotDeserializeType, $curCls, $ctx](
+ cask.internal.Router.EntryPoint[$annotDeserializeType, $curCls, $ctx](
${meth.name.toString},
scala.Seq(..$argSigs),
${methodDoc match{
- case None => q"scala.None"
- case Some(s) => q"scala.Some($s)"
- }},
+ case None => q"scala.None"
+ case Some(s) => q"scala.Some($s)"
+ }},
${varargs.contains(true)},
($baseArgSym: $curCls, ctx: $ctx, $argListSymbol: Map[String, $annotDeserializeType]) =>
- cask.Router.validate(Seq(..$readArgs)) match{
- case cask.Router.Result.Success(Seq(..$argNames)) =>
- cask.Router.Result.Success(
+ cask.internal.Router.validate(Seq(..$readArgs)) match{
+ case cask.internal.Router.Result.Success(Seq(..$argNames)) =>
+ cask.internal.Router.Result.Success(
${wrapOutput(methCall)}
)
- case x: cask.Router.Result.Error => x
+ case x: cask.internal.Router.Result.Error => x
}
- ).asInstanceOf[cask.Router.EntryPoint[Any, $curCls, $ctx]]
+ ).asInstanceOf[cask.internal.Router.EntryPoint[Any, $curCls, $ctx]]
"""
c.internal.transform(res){(t, a) =>
diff --git a/cask/src/cask/Util.scala b/cask/src/cask/internal/Util.scala
index 8614639..84c8d52 100644
--- a/cask/src/cask/Util.scala
+++ b/cask/src/cask/internal/Util.scala
@@ -1,4 +1,4 @@
-package cask
+package cask.internal
object Util {
def splitPath(p: String) =
diff --git a/cask/src/cask/Main.scala b/cask/src/cask/main/Main.scala
index 188c24c..77bac94 100644
--- a/cask/src/cask/Main.scala
+++ b/cask/src/cask/main/Main.scala
@@ -1,13 +1,14 @@
-package cask
-import cask.Router.EntryPoint
-import java.io.OutputStream
-import java.nio.ByteBuffer
+package cask.main
-import cask.Routes.RoutesEndpointsMetadata
+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.handlers.BlockingHandler
import io.undertow.server.{HttpHandler, HttpServerExchange}
-import io.undertow.util.{Headers, HttpString}
+import io.undertow.server.handlers.BlockingHandler
+import io.undertow.util.HttpString
+
class MainRoutes extends BaseMain with Routes{
def allRoutes = Seq(this)
@@ -27,7 +28,7 @@ abstract class BaseMain{
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)
+ yield (Util.splitPath(metadata.metadata.path): IndexedSeq[String], (route, metadata), metadata.metadata.subpath)
)
def handleError(statusCode: Int): Response = {
@@ -54,7 +55,7 @@ abstract class BaseMain{
case Some(((routes, metadata), bindings, remaining)) =>
val result = metadata.metadata.handle(
exchange, remaining, bindings, routes,
- metadata.entryPoint.asInstanceOf[EntryPoint[metadata.metadata.InputType, cask.Routes, (HttpServerExchange, Seq[String])]]
+ metadata.entryPoint.asInstanceOf[EntryPoint[metadata.metadata.InputType, cask.main.Routes, (HttpServerExchange, Seq[String])]]
)
result match{
diff --git a/cask/src/cask/Routes.scala b/cask/src/cask/main/Routes.scala
index e539312..fdd39bd 100644
--- a/cask/src/cask/Routes.scala
+++ b/cask/src/cask/main/Routes.scala
@@ -1,51 +1,11 @@
-package cask
-import language.experimental.macros
-import java.io.{InputStream, OutputStream, OutputStreamWriter, StringWriter}
+package cask.main
-import cask.Router.EntryPoint
+import cask.endpoints.Endpoint
+import cask.internal.Router.EntryPoint
import io.undertow.server.HttpServerExchange
import scala.reflect.macros.blackbox.Context
-
-case class Request(cookies: Map[String, Cookie],
- data: InputStream,
- queryParams: Map[String, Seq[String]],
- headers: Map[String, Seq[String]])
-
-case class Response(data: Response.Data,
- statusCode: Int = 200,
- headers: Seq[(String, String)] = Nil,
- cookies: Seq[Cookie] = Nil) extends BaseResponse
-
-trait BaseResponse{
- def data: Response.Data
- def statusCode: Int
- def headers: Seq[(String, String)]
- def cookies: Seq[Cookie]
-}
-object Response{
- implicit def dataResponse[T](t: T)(implicit c: T => Data) = Response(t)
- trait Data{
- def write(out: OutputStream): Unit
- }
- object Data{
- implicit class StringData(s: String) extends Data{
- def write(out: OutputStream) = out.write(s.getBytes)
- }
- 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)
- }
- implicit def JsonResponse[T: upickle.default.Writer](t: T) = new Data{
- def write(out: OutputStream) = implicitly[upickle.default.Writer[T]].write(
- new ujson.BaseRenderer(new OutputStreamWriter(out)),
- t
- )
- }
- }
-}
+import language.experimental.macros
object Routes{
case class EndpointMetadata[T](metadata: Endpoint[_],
@@ -55,7 +15,7 @@ object Routes{
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.Router[c.type](c)
+ val router = new cask.internal.Router[c.type](c)
val routeParts = for{
m <- c.weakTypeOf[T].members
@@ -75,14 +35,14 @@ object Routes{
q"""{
val $annotObjectSym = $annotObject
- cask.Routes.EndpointMetadata(
+ cask.main.Routes.EndpointMetadata(
$annotObjectSym,
$route
)
}"""
}
- c.Expr[RoutesEndpointsMetadata[T]](q"""cask.Routes.RoutesEndpointsMetadata(..$routeParts)""")
+ c.Expr[RoutesEndpointsMetadata[T]](q"""cask.main.Routes.RoutesEndpointsMetadata(..$routeParts)""")
}
}
}
diff --git a/cask/src/cask/Cookie.scala b/cask/src/cask/model/Cookie.scala
index 8d1d239..119ea66 100644
--- a/cask/src/cask/Cookie.scala
+++ b/cask/src/cask/model/Cookie.scala
@@ -1,4 +1,4 @@
-package cask
+package cask.model
import io.undertow.server.handlers.CookieImpl
diff --git a/cask/src/cask/FormValue.scala b/cask/src/cask/model/FormValue.scala
index 60a2d39..01c75a4 100644
--- a/cask/src/cask/FormValue.scala
+++ b/cask/src/cask/model/FormValue.scala
@@ -1,4 +1,4 @@
-package cask
+package cask.model
object FormValue{
def fromUndertow(from: io.undertow.server.handlers.form.FormData.FormValue) = {
diff --git a/cask/src/cask/model/Params.scala b/cask/src/cask/model/Params.scala
new file mode 100644
index 0000000..f64fd87
--- /dev/null
+++ b/cask/src/cask/model/Params.scala
@@ -0,0 +1,4 @@
+package cask.model
+
+class Subpath(val value: Seq[String])
+class Cookies(val value: Map[String, Cookie]) \ No newline at end of file
diff --git a/cask/src/cask/model/Response.scala b/cask/src/cask/model/Response.scala
new file mode 100644
index 0000000..b3bb22b
--- /dev/null
+++ b/cask/src/cask/model/Response.scala
@@ -0,0 +1,81 @@
+package cask.model
+
+import java.io.{InputStream, OutputStream, OutputStreamWriter}
+
+
+trait BaseResponse{
+ def data: BaseResponse.Data
+ def statusCode: Int
+ def headers: Seq[(String, String)]
+ def cookies: Seq[Cookie]
+}
+object BaseResponse{
+ implicit def dataResponse[T](t: T)(implicit c: T => Data) = Response(t)
+ trait Data{
+ def write(out: OutputStream): Unit
+ }
+ object Data{
+ implicit class StringData(s: String) extends Data{
+ def write(out: OutputStream) = out.write(s.getBytes)
+ }
+ 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)
+ }
+ implicit def JsonResponse[T: upickle.default.Writer](t: T) = new Data{
+ def write(out: OutputStream) = implicitly[upickle.default.Writer[T]].write(
+ new ujson.BaseRenderer(new OutputStreamWriter(out)),
+ t
+ )
+ }
+ }
+}
+case class Redirect(url: String) extends BaseResponse{
+ override def data = ""
+
+ override def statusCode = 301
+
+ override def headers = Seq("Location" -> url)
+
+ override def cookies = Nil
+}
+case class Abort(code: Int) extends BaseResponse {
+ override def data = ""
+
+ override def statusCode = code
+
+ override def headers = Nil
+
+ override def cookies = Nil
+}
+
+case class Static(path: String) extends BaseResponse {
+ val relPath = java.nio.file.Paths.get(path)
+ val (data0: BaseResponse.Data, statusCode0) =
+ if (java.nio.file.Files.exists(relPath) && java.nio.file.Files.isRegularFile(relPath)){
+ (java.nio.file.Files.newInputStream(relPath), 200)
+ }else{
+ ("", 404)
+ }
+ override def data = data0
+
+ override def statusCode = statusCode0
+
+ override def headers = Nil
+
+ override def cookies = Nil
+}
+
+
+case class Request(cookies: Map[String, Cookie],
+ data: InputStream,
+ queryParams: Map[String, Seq[String]],
+ headers: Map[String, Seq[String]])
+
+case class Response(data: BaseResponse.Data,
+ statusCode: Int = 200,
+ headers: Seq[(String, String)] = Nil,
+ cookies: Seq[Cookie] = Nil) extends BaseResponse
+
diff --git a/cask/src/cask/model/Status.scala b/cask/src/cask/model/Status.scala
new file mode 100644
index 0000000..26bb60f
--- /dev/null
+++ b/cask/src/cask/model/Status.scala
@@ -0,0 +1,288 @@
+package cask.model
+
+sealed trait Status {
+ val code: Int
+ val reason: String
+}
+
+object Status {
+ val codesToStatus: Map[Int, Status] = Map(
+ 100 -> Continue,
+ 101 -> SwitchingProtocols,
+ 200 -> OK,
+ 201 -> Created,
+ 202 -> Accepted,
+ 203 -> NonAuthoritativeInformation,
+ 204 -> NoContent,
+ 205 -> ResetContent,
+ 206 -> PartialContent,
+ 300 -> MultipleChoices,
+ 301 -> MovedPermanently,
+ 302 -> Found,
+ 303 -> SeeOther,
+ 304 -> NotModified,
+ 305 -> UseProxy,
+ 307 -> TemporaryRedirect,
+ 308 -> PermanentRedirect,
+ 400 -> BadRequest,
+ 401 -> Unauthorized,
+ 402 -> PaymentRequired,
+ 403 -> Forbidden,
+ 404 -> NotFound,
+ 405 -> MethodNotAllowed,
+ 406 -> NotAcceptable,
+ 407 -> ProxyAuthenticationRequired,
+ 408 -> RequestTimeout,
+ 409 -> Conflict,
+ 410 -> Gone,
+ 411 -> LengthRequired,
+ 412 -> PreconditionFailed,
+ 413 -> RequestEntityTooLarge,
+ 414 -> RequestURITooLong,
+ 415 -> UnsupportedMediaType,
+ 416 -> RequestedRangeNotSatisfiable,
+ 417 -> ExpectationFailed,
+ 418 -> Teapot,
+ 420 -> EnhanceYourCalm,
+ 429 -> TooManyRequests,
+ 451 -> UnavailableForLegalReasons,
+ 500 -> InternalServerError,
+ 501 -> NotImplemented,
+ 502 -> BadGateway,
+ 503 -> ServiceUnavailable,
+ 504 -> GatewayTimeout,
+ 505 -> HTTPVersionNotSupported
+ )
+
+ val statusToCodes: Map[String, Int] =
+ codesToStatus.map { case (code, status) => status.reason -> code }
+
+
+ case class Unknown(code: Int, reason: String) extends Status
+
+ case object Continue extends Status {
+ val code = 100
+ val reason: String = "Continue"
+ }
+
+ case object SwitchingProtocols extends Status {
+ val code = 101
+ val reason: String = "Switching Protocols"
+ }
+
+ case object OK extends Status {
+ val code = 200
+ val reason: String = "OK"
+ }
+
+ case object Created extends Status {
+ val code = 201
+ val reason: String = "Created"
+ }
+
+ case object Accepted extends Status {
+ val code = 202
+ val reason: String = "Accepted"
+ }
+
+ case object NonAuthoritativeInformation extends Status {
+ val code = 203
+ val reason: String = "Non-Authoritative Information"
+ }
+
+ case object NoContent extends Status {
+ val code = 204
+ val reason: String = "No Content"
+ }
+
+ case object ResetContent extends Status {
+ val code = 205
+ val reason: String = "Reset Content"
+ }
+
+ case object PartialContent extends Status {
+ val code = 206
+ val reason: String = "Partial Content"
+ }
+
+ case object MultipleChoices extends Status {
+ val code = 300
+ val reason: String = "Multiple Choices"
+ }
+
+ case object MovedPermanently extends Status {
+ val code = 301
+ val reason: String = "Moved Permanently"
+ }
+
+ case object Found extends Status {
+ val code = 302
+ val reason: String = "Found"
+ }
+
+ case object SeeOther extends Status {
+ val code = 303
+ val reason: String = "See Other"
+ }
+
+ case object NotModified extends Status {
+ val code = 304
+ val reason: String = "Not Modified"
+ }
+
+ case object UseProxy extends Status {
+ val code = 305
+ val reason: String = "Use Proxy"
+ }
+
+ case object TemporaryRedirect extends Status {
+ val code = 307
+ val reason: String = "Temporary Redirect"
+ }
+
+ case object PermanentRedirect extends Status {
+ val code = 308
+ val reason: String = "Permanent Redirect"
+ }
+
+ case object BadRequest extends Status {
+ val code = 400
+ val reason: String = "Bad Request"
+ }
+
+ case object Unauthorized extends Status {
+ val code = 401
+ val reason: String = "Unauthorized"
+ }
+
+ case object PaymentRequired extends Status {
+ val code = 402
+ val reason: String = "Payment Required"
+ }
+
+ case object Forbidden extends Status {
+ val code = 403
+ val reason: String = "Forbidden"
+ }
+
+ case object NotFound extends Status {
+ val code = 404
+ val reason: String = "Not Found"
+ }
+
+ case object MethodNotAllowed extends Status {
+ val code = 405
+ val reason: String = "Method Not Allowed"
+ }
+
+ case object NotAcceptable extends Status {
+ val code = 406
+ val reason: String = "Not Acceptable"
+ }
+
+ case object ProxyAuthenticationRequired extends Status {
+ val code = 407
+ val reason: String = "Proxy Authentication Required"
+ }
+
+ case object RequestTimeout extends Status {
+ val code = 408
+ val reason: String = "Request Time-out"
+ }
+
+ case object Conflict extends Status {
+ val code = 409
+ val reason: String = "Conflict"
+ }
+
+ case object Gone extends Status {
+ val code = 410
+ val reason: String = "Gone"
+ }
+
+ case object LengthRequired extends Status {
+ val code = 411
+ val reason: String = "Length Required"
+ }
+
+ case object PreconditionFailed extends Status {
+ val code = 412
+ val reason: String = "Precondition Failed"
+ }
+
+ case object RequestEntityTooLarge extends Status {
+ val code = 413
+ val reason: String = "Request Entity Too Large"
+ }
+
+ case object RequestURITooLong extends Status {
+ val code = 414
+ val reason: String = "Request-URI Too Large"
+ }
+
+ case object UnsupportedMediaType extends Status {
+ val code = 415
+ val reason: String = "Unsupported Media Type"
+ }
+
+ case object RequestedRangeNotSatisfiable extends Status {
+ val code = 416
+ val reason: String = "Requested range not satisfiable"
+ }
+
+ case object ExpectationFailed extends Status {
+ val code = 417
+ val reason: String = "Expectation Failed"
+ }
+
+ case object Teapot extends Status {
+ val code = 418
+ val reason: String = "I'm a teapot"
+ }
+
+ case object EnhanceYourCalm extends Status {
+ val code = 420
+ val reason: String = "Enhance Your Calm"
+ }
+
+ case object TooManyRequests extends Status {
+ val code = 429
+ val reason: String = "Too Many Requests"
+ }
+
+ case object UnavailableForLegalReasons extends Status {
+ val code = 451
+ val reason: String = "Unavailable For Legal Reasons"
+ }
+
+ case object InternalServerError extends Status {
+ val code = 500
+ val reason: String = "Internal Server Error"
+ }
+
+ case object NotImplemented extends Status {
+ val code = 501
+ val reason: String = "Not Implemented"
+ }
+
+ case object BadGateway extends Status {
+ val code = 502
+ val reason: String = "Bad Gateway"
+ }
+
+ case object ServiceUnavailable extends Status {
+ val code = 503
+ val reason: String = "Service Unavailable"
+ }
+
+ case object GatewayTimeout extends Status {
+ val code = 504
+ val reason: String = "Gateway Time-out"
+ }
+
+ case object HTTPVersionNotSupported extends Status {
+ val code = 505
+ val reason: String = "HTTP Version not supported"
+ }
+
+}
diff --git a/cask/src/cask/package.scala b/cask/src/cask/package.scala
index f46dc8d..fddb1e0 100644
--- a/cask/src/cask/package.scala
+++ b/cask/src/cask/package.scala
@@ -1,11 +1,30 @@
package object cask {
- def redirect(url: String) = Response(
- "",
- 301,
- headers = Seq("Location" -> url)
- )
- def abort(code: Int) = Response(
- "",
- code
- )
+ // model
+ type Response = model.Response
+ val Response = model.Response
+ type Abort = model.Abort
+ val Abort = model.Abort
+ type Redirect = model.Redirect
+ val Redirect = model.Redirect
+ type FormValue = model.FormValue
+ val FormValue = model.FormValue
+ type Cookie = model.Cookie
+ val Cookie = model.Cookie
+ type Cookies = model.Cookies
+ type Subpath = model.Subpath
+
+ // endpoints
+ type get = endpoints.get
+ type post = endpoints.post
+ type put = endpoints.put
+ type route = endpoints.route
+ type static = endpoints.static
+ type postJson = endpoints.postJson
+ type postForm = endpoints.postForm
+
+ // main
+ type MainRoutes = main.MainRoutes
+ type Routes = main.Routes
+ type Main = main.Main
+
}
diff --git a/cask/test/src/test/cask/DispatchTrieTests.scala b/cask/test/src/test/cask/DispatchTrieTests.scala
index fdd5f27..068d7d0 100644
--- a/cask/test/src/test/cask/DispatchTrieTests.scala
+++ b/cask/test/src/test/cask/DispatchTrieTests.scala
@@ -1,5 +1,5 @@
package test.cask
-import cask.DispatchTrie
+import cask.internal.DispatchTrie
import utest._
object DispatchTrieTests extends TestSuite {
diff --git a/cask/test/src/test/cask/RedirectAbort.scala b/cask/test/src/test/cask/RedirectAbort.scala
index 6a557d6..f2aa811 100644
--- a/cask/test/src/test/cask/RedirectAbort.scala
+++ b/cask/test/src/test/cask/RedirectAbort.scala
@@ -3,12 +3,12 @@ package test.cask
object RedirectAbort extends cask.MainRoutes{
@cask.get("/")
def index() = {
- cask.redirect("/login")
+ cask.Redirect("/login")
}
@cask.get("/login")
def login() = {
- cask.abort(401)
+ cask.Abort(401)
}
initialize()