summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2018-08-18 10:16:27 +0800
committerLi Haoyi <haoyi.sg@gmail.com>2018-08-18 10:16:27 +0800
commit4b0cfdf0eeca46cfccbf9fe42af42f383932c427 (patch)
treeeafc563d53cc6d840d88a9848a88fa67f03cbb9e
parentf8bb6f693b8450f8e049396fd0e7d032ac7acb23 (diff)
downloadcask-4b0cfdf0eeca46cfccbf9fe42af42f383932c427.tar.gz
cask-4b0cfdf0eeca46cfccbf9fe42af42f383932c427.tar.bz2
cask-4b0cfdf0eeca46cfccbf9fe42af42f383932c427.zip
0.1.10.1.1
-rw-r--r--build.sc2
-rw-r--r--cask/src/cask/decorators/compress.scala4
-rw-r--r--cask/src/cask/endpoints/FormEndpoint.scala18
-rw-r--r--cask/src/cask/endpoints/JsonEndpoint.scala10
-rw-r--r--cask/src/cask/endpoints/ParamReader.scala16
-rw-r--r--cask/src/cask/endpoints/StaticEndpoints.scala10
-rw-r--r--cask/src/cask/endpoints/WebEndpoints.scala16
-rw-r--r--cask/src/cask/endpoints/WebSocketEndpoint.scala15
-rw-r--r--cask/src/cask/internal/DispatchTrie.scala13
-rw-r--r--cask/src/cask/main/Decorators.scala10
-rw-r--r--cask/src/cask/main/Main.scala4
-rw-r--r--cask/src/cask/main/Routes.scala4
-rw-r--r--cask/src/cask/model/ParamContext.scala5
-rw-r--r--cask/src/cask/model/Params.scala16
-rw-r--r--cask/src/cask/model/Response.scala98
-rw-r--r--cask/src/cask/package.scala8
-rw-r--r--cask/test/src/test/cask/FailureTests.scala5
-rw-r--r--docs/pages/1 - Cask: a Scala HTTP micro-framework.md6
-rw-r--r--example/compress/build.sc2
-rw-r--r--example/compress2/build.sc2
-rw-r--r--example/compress3/build.sc2
-rw-r--r--example/cookies/build.sc2
-rw-r--r--example/decorated/app/src/Decorated.scala4
-rw-r--r--example/decorated/build.sc2
-rw-r--r--example/decorated2/app/src/Decorated2.scala4
-rw-r--r--example/decorated2/build.sc2
-rw-r--r--example/endpoints/app/src/Endpoints.scala2
-rw-r--r--example/endpoints/build.sc2
-rw-r--r--example/formJsonPost/build.sc2
-rw-r--r--example/httpMethods/build.sc2
-rw-r--r--example/minimalApplication/build.sc2
-rw-r--r--example/minimalApplication2/build.sc2
-rw-r--r--example/redirectAbort/build.sc2
-rw-r--r--example/scalatags/build.sc2
-rw-r--r--example/staticFiles/build.sc2
-rw-r--r--example/todo/app/src/TodoServer.scala2
-rw-r--r--example/todo/build.sc2
-rw-r--r--example/todoApi/build.sc2
-rw-r--r--example/todoDb/app/src/TodoMvcDb.scala2
-rw-r--r--example/todoDb/build.sc2
-rw-r--r--example/variableRoutes/app/src/VariableRoutes.scala4
-rw-r--r--example/variableRoutes/build.sc2
-rw-r--r--example/websockets/build.sc2
43 files changed, 139 insertions, 177 deletions
diff --git a/build.sc b/build.sc
index 9ea4c7b..b368baa 100644
--- a/build.sc
+++ b/build.sc
@@ -58,7 +58,7 @@ object cask extends ScalaModule with PublishModule {
}
object example extends Module{
trait LocalModule extends ScalaModule{
- def ivyDeps = super.ivyDeps().filter(_ != ivy"com.lihaoyi::cask:0.1.0")
+ def ivyDeps = super.ivyDeps().filter(_ != ivy"com.lihaoyi::cask:0.1.1")
override def millSourcePath = super.millSourcePath / "app"
def moduleDeps = Seq(cask)
diff --git a/cask/src/cask/decorators/compress.scala b/cask/src/cask/decorators/compress.scala
index 22bd29e..75e2bed 100644
--- a/cask/src/cask/decorators/compress.scala
+++ b/cask/src/cask/decorators/compress.scala
@@ -3,11 +3,11 @@ import java.io.{ByteArrayOutputStream, OutputStream}
import java.util.zip.{DeflaterOutputStream, GZIPOutputStream}
import cask.internal.Router
-import cask.model.{ParamContext, Response}
+import cask.model.{Request, Response}
import collection.JavaConverters._
class compress extends cask.Decorator{
- def wrapFunction(ctx: ParamContext, delegate: Delegate) = {
+ def wrapFunction(ctx: Request, delegate: Delegate) = {
val acceptEncodings = ctx.exchange.getRequestHeaders.get("Accept-Encoding").asScala.flatMap(_.split(", "))
delegate(Map()).map{ v =>
val (newData, newHeaders) = if (acceptEncodings.exists(_.toLowerCase == "gzip")) {
diff --git a/cask/src/cask/endpoints/FormEndpoint.scala b/cask/src/cask/endpoints/FormEndpoint.scala
index a952a2a..cc74093 100644
--- a/cask/src/cask/endpoints/FormEndpoint.scala
+++ b/cask/src/cask/endpoints/FormEndpoint.scala
@@ -7,40 +7,40 @@ import io.undertow.server.handlers.form.FormParserFactory
import collection.JavaConverters._
-sealed trait FormReader[T] extends Router.ArgReader[Seq[FormEntry], T, ParamContext]
+sealed trait FormReader[T] extends Router.ArgReader[Seq[FormEntry], T, Request]
object FormReader{
implicit def paramFormReader[T: QueryParamReader] = new FormReader[T]{
def arity = implicitly[QueryParamReader[T]].arity
- def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = {
+ def read(ctx: Request, label: String, input: Seq[FormEntry]) = {
implicitly[QueryParamReader[T]].read(ctx, label, if (input == null) null else input.map(_.valueOrFileName))
}
}
implicit def formEntryReader = new FormReader[FormEntry]{
def arity = 1
- def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input.head
+ def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.head
}
implicit def formEntriesReader = new FormReader[Seq[FormEntry]]{
def arity = 1
- def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input
+ def read(ctx: Request, label: String, input: Seq[FormEntry]) = input
}
implicit def formValueReader = new FormReader[FormValue]{
def arity = 1
- def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input.head.asInstanceOf[FormValue]
+ def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.head.asInstanceOf[FormValue]
}
implicit def formValuesReader = new FormReader[Seq[FormValue]]{
def arity = 1
- def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input.map(_.asInstanceOf[FormValue])
+ def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.map(_.asInstanceOf[FormValue])
}
implicit def formFileReader = new FormReader[FormFile]{
def arity = 1
- def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input.head.asInstanceOf[FormFile]
+ def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.head.asInstanceOf[FormFile]
}
implicit def formFilesReader = new FormReader[Seq[FormFile]]{
def arity = 1
- def read(ctx: ParamContext, label: String, input: Seq[FormEntry]) = input.map(_.asInstanceOf[FormFile])
+ def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.map(_.asInstanceOf[FormFile])
}
}
class postForm(val path: String, override val subpath: Boolean = false) extends Endpoint {
@@ -49,7 +49,7 @@ class postForm(val path: String, override val subpath: Boolean = false) extends
val methods = Seq("post")
type Input = Seq[FormEntry]
type InputParser[T] = FormReader[T]
- def wrapFunction(ctx: ParamContext,
+ def wrapFunction(ctx: Request,
delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = {
try {
val formData = FormParserFactory.builder().build().createParser(ctx.exchange).parseBlocking()
diff --git a/cask/src/cask/endpoints/JsonEndpoint.scala b/cask/src/cask/endpoints/JsonEndpoint.scala
index f91b888..212597c 100644
--- a/cask/src/cask/endpoints/JsonEndpoint.scala
+++ b/cask/src/cask/endpoints/JsonEndpoint.scala
@@ -5,15 +5,15 @@ import java.io.ByteArrayOutputStream
import cask.internal.{Router, Util}
import cask.internal.Router.EntryPoint
import cask.main.{Endpoint, HttpDecorator, Routes}
-import cask.model.{ParamContext, Response}
+import cask.model.{Request, Response}
-sealed trait JsReader[T] extends Router.ArgReader[ujson.Js.Value, T, cask.model.ParamContext]
+sealed trait JsReader[T] extends Router.ArgReader[ujson.Js.Value, T, cask.model.Request]
object JsReader{
implicit def defaultJsReader[T: upickle.default.Reader] = new JsReader[T]{
def arity = 1
- def read(ctx: cask.model.ParamContext, label: String, input: ujson.Js.Value): T = {
+ def read(ctx: cask.model.Request, label: String, input: ujson.Js.Value): T = {
implicitly[upickle.default.Reader[T]].apply(input)
}
}
@@ -21,7 +21,7 @@ object JsReader{
implicit def paramReader[T: ParamReader] = new JsReader[T] {
override def arity = 0
- override def read(ctx: cask.model.ParamContext, label: String, v: ujson.Js.Value) = {
+ override def read(ctx: cask.model.Request, label: String, v: ujson.Js.Value) = {
implicitly[ParamReader[T]].read(ctx, label, Nil)
}
}
@@ -31,7 +31,7 @@ class postJson(val path: String, override val subpath: Boolean = false) extends
val methods = Seq("post")
type Input = ujson.Js.Value
type InputParser[T] = JsReader[T]
- def wrapFunction(ctx: ParamContext,
+ def wrapFunction(ctx: Request,
delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = {
val obj = for{
str <-
diff --git a/cask/src/cask/endpoints/ParamReader.scala b/cask/src/cask/endpoints/ParamReader.scala
index f1ec0bf..e43f482 100644
--- a/cask/src/cask/endpoints/ParamReader.scala
+++ b/cask/src/cask/endpoints/ParamReader.scala
@@ -1,22 +1,28 @@
package cask.endpoints
import cask.internal.Router
-import cask.model.ParamContext
+import cask.model.{Cookie, Request}
import io.undertow.server.HttpServerExchange
import io.undertow.server.handlers.form.{FormData, FormParserFactory}
-abstract class ParamReader[T] extends Router.ArgReader[Unit, T, cask.model.ParamContext]{
+abstract class ParamReader[T] extends Router.ArgReader[Unit, T, cask.model.Request]{
def arity: Int
- def read(ctx: cask.model.ParamContext, label: String, v: Unit): T
+ def read(ctx: cask.model.Request, label: String, v: Unit): T
}
object ParamReader{
- class NilParam[T](f: (ParamContext, String) => T) extends ParamReader[T]{
+ class NilParam[T](f: (Request, String) => T) extends ParamReader[T]{
def arity = 0
- def read(ctx: cask.model.ParamContext, label: String, v: Unit): T = f(ctx, label)
+ def read(ctx: cask.model.Request, label: String, v: Unit): T = f(ctx, label)
}
implicit object HttpExchangeParam extends NilParam[HttpServerExchange]((ctx, label) => ctx.exchange)
implicit object FormDataParam extends NilParam[FormData]((ctx, label) =>
FormParserFactory.builder().build().createParser(ctx.exchange).parseBlocking()
)
+
+ implicit object RequestParam extends NilParam[Request]((ctx, label) => ctx)
+
+ implicit object CookieParam extends NilParam[Cookie]((ctx, label) =>
+ Cookie.fromUndertow(ctx.exchange.getRequestCookies().get(label))
+ )
}
diff --git a/cask/src/cask/endpoints/StaticEndpoints.scala b/cask/src/cask/endpoints/StaticEndpoints.scala
index 1d66b2c..15eae0d 100644
--- a/cask/src/cask/endpoints/StaticEndpoints.scala
+++ b/cask/src/cask/endpoints/StaticEndpoints.scala
@@ -1,7 +1,7 @@
package cask.endpoints
import cask.main.Endpoint
-import cask.model.ParamContext
+import cask.model.Request
class staticFiles(val path: String) extends Endpoint{
type Output = String
@@ -9,8 +9,8 @@ class staticFiles(val path: String) extends Endpoint{
type Input = Seq[String]
type InputParser[T] = QueryParamReader[T]
override def subpath = true
- def wrapFunction(ctx: ParamContext, delegate: Delegate): Returned = {
- delegate(Map()).map(t => cask.model.StaticFile(t + "/" + ctx.remaining.mkString("/")))
+ def wrapFunction(ctx: Request, delegate: Delegate): Returned = {
+ delegate(Map()).map(t => cask.model.StaticFile(t + "/" + ctx.remainingPathSegments.mkString("/")))
}
def wrapPathSegment(s: String): Input = Seq(s)
@@ -22,9 +22,9 @@ class staticResources(val path: String, resourceRoot: ClassLoader = getClass.get
type Input = Seq[String]
type InputParser[T] = QueryParamReader[T]
override def subpath = true
- def wrapFunction(ctx: ParamContext, delegate: Delegate): Returned = {
+ def wrapFunction(ctx: Request, delegate: Delegate): Returned = {
delegate(Map()).map(t =>
- cask.model.StaticResource(t + "/" + ctx.remaining.mkString("/"), resourceRoot)
+ cask.model.StaticResource(t + "/" + ctx.remainingPathSegments.mkString("/"), resourceRoot)
)
}
diff --git a/cask/src/cask/endpoints/WebEndpoints.scala b/cask/src/cask/endpoints/WebEndpoints.scala
index 41c3113..02aeec4 100644
--- a/cask/src/cask/endpoints/WebEndpoints.scala
+++ b/cask/src/cask/endpoints/WebEndpoints.scala
@@ -2,7 +2,7 @@ package cask.endpoints
import cask.internal.Router
import cask.main.{Endpoint, HttpDecorator}
-import cask.model.{ParamContext, Response}
+import cask.model.{Request, Response}
import collection.JavaConverters._
@@ -11,7 +11,7 @@ trait WebEndpoint extends Endpoint with HttpDecorator{
type Output = Response
type Input = Seq[String]
type InputParser[T] = QueryParamReader[T]
- def wrapFunction(ctx: ParamContext,
+ def wrapFunction(ctx: Request,
delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = {
delegate(
ctx.exchange.getQueryParameters
@@ -34,14 +34,14 @@ class put(val path: String, override val subpath: Boolean = false) extends WebEn
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, cask.model.ParamContext]{
+ extends Router.ArgReader[Seq[String], T, cask.model.Request]{
def arity: Int
- def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]): T
+ def read(ctx: cask.model.Request, label: String, v: Seq[String]): T
}
object QueryParamReader{
class SimpleParam[T](f: String => T) extends QueryParamReader[T]{
def arity = 1
- def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]): T = f(v.head)
+ def read(ctx: cask.model.Request, label: String, v: Seq[String]): T = f(v.head)
}
implicit object StringParam extends SimpleParam[String](x => x)
@@ -54,20 +54,20 @@ object QueryParamReader{
implicit object FloatParam extends SimpleParam[Float](_.toFloat)
implicit def SeqParam[T: QueryParamReader] = new QueryParamReader[Seq[T]]{
def arity = 1
- def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]): Seq[T] = {
+ def read(ctx: cask.model.Request, label: String, v: Seq[String]): Seq[T] = {
v.map(x => implicitly[QueryParamReader[T]].read(ctx, label, Seq(x)))
}
}
implicit def OptionParam[T: QueryParamReader] = new QueryParamReader[Option[T]]{
def arity = 1
- def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]): Option[T] = {
+ def read(ctx: cask.model.Request, label: String, v: Seq[String]): Option[T] = {
v.headOption.map(x => implicitly[QueryParamReader[T]].read(ctx, label, Seq(x)))
}
}
implicit def paramReader[T: ParamReader] = new QueryParamReader[T] {
override def arity = 0
- override def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]) = {
+ override def read(ctx: cask.model.Request, label: String, v: Seq[String]) = {
implicitly[ParamReader[T]].read(ctx, label, v)
}
}
diff --git a/cask/src/cask/endpoints/WebSocketEndpoint.scala b/cask/src/cask/endpoints/WebSocketEndpoint.scala
index 89c05b9..8c6bc16 100644
--- a/cask/src/cask/endpoints/WebSocketEndpoint.scala
+++ b/cask/src/cask/endpoints/WebSocketEndpoint.scala
@@ -1,25 +1,22 @@
package cask.endpoints
import cask.internal.Router
-import cask.model.{ParamContext, Subpath}
+import cask.model.Request
import io.undertow.server.HttpServerExchange
import io.undertow.websockets.WebSocketConnectionCallback
-trait WebsocketParam[T] extends Router.ArgReader[Seq[String], T, cask.model.ParamContext]
+trait WebsocketParam[T] extends Router.ArgReader[Seq[String], T, cask.model.Request]
object WebsocketParam{
- class NilParam[T](f: (ParamContext, String) => T) extends WebsocketParam[T]{
+ class NilParam[T](f: (Request, String) => T) extends WebsocketParam[T]{
def arity = 0
- def read(ctx: ParamContext, label: String, v: Seq[String]): T = f(ctx, label)
+ def read(ctx: Request, label: String, v: Seq[String]): T = f(ctx, label)
}
implicit object HttpExchangeParam extends NilParam[HttpServerExchange](
(ctx, label) => ctx.exchange
)
- implicit object SubpathParam extends NilParam[Subpath](
- (ctx, label) => new Subpath(ctx.remaining)
- )
class SimpleParam[T](f: String => T) extends WebsocketParam[T]{
def arity = 1
- def read(ctx: cask.model.ParamContext, label: String, v: Seq[String]): T = f(v.head)
+ def read(ctx: cask.model.Request, label: String, v: Seq[String]): T = f(v.head)
}
implicit object StringParam extends SimpleParam[String](x => x)
@@ -44,7 +41,7 @@ class websocket(val path: String, override val subpath: Boolean = false) extends
type Input = Seq[String]
type InputParser[T] = WebsocketParam[T]
type Returned = Router.Result[WebsocketResult]
- def wrapFunction(ctx: ParamContext, delegate: Delegate): Returned = delegate(Map())
+ def wrapFunction(ctx: Request, delegate: Delegate): Returned = delegate(Map())
def wrapPathSegment(s: String): Input = Seq(s)
diff --git a/cask/src/cask/internal/DispatchTrie.scala b/cask/src/cask/internal/DispatchTrie.scala
index 57d8d9d..952c39b 100644
--- a/cask/src/cask/internal/DispatchTrie.scala
+++ b/cask/src/cask/internal/DispatchTrie.scala
@@ -36,15 +36,20 @@ object DispatchTrie{
}else{
DispatchTrie[T](
current = terminals.headOption.map(x => x._2 -> x._3),
- children = continuations.map{ case (k, vs) =>
- if (!k.startsWith("::")) (k, construct(index + 1, vs))
- else (k, DispatchTrie(Some(vs.head._2 -> vs.head._3), Map()))
- }.toMap
+ children = continuations.map{ case (k, vs) => (k, construct(index + 1, vs))}.toMap
)
}
}
}
+/**
+ * A simple Trie that can be compiled from a list of endpoints, to allow
+ * endpoint lookup in O(length-of-request-path) time. Lookup returns the
+ * [[T]] this trie contains, as well as a map of bound wildcards (path
+ * segments starting with `:`) and any remaining un-used path segments
+ * (only when `current._2 == true`, indicating this route allows trailing
+ * segments)
+ */
case class DispatchTrie[T](current: Option[(T, Boolean)],
children: Map[String, DispatchTrie[T]]){
final def lookup(remainingInput: List[String],
diff --git a/cask/src/cask/main/Decorators.scala b/cask/src/cask/main/Decorators.scala
index 239cab4..0515232 100644
--- a/cask/src/cask/main/Decorators.scala
+++ b/cask/src/cask/main/Decorators.scala
@@ -2,7 +2,7 @@ package cask.main
import cask.internal.Router
import cask.internal.Router.ArgReader
-import cask.model.{ParamContext, Response}
+import cask.model.{Request, Response}
trait Endpoint extends BaseEndpoint with HttpDecorator
@@ -44,11 +44,11 @@ trait BaseEndpoint extends BaseDecorator{
trait BaseDecorator{
type Input
- type InputParser[T] <: ArgReader[Input, T, ParamContext]
+ type InputParser[T] <: ArgReader[Input, T, Request]
type Output
type Delegate = Map[String, Input] => Router.Result[Output]
type Returned <: Router.Result[Any]
- def wrapFunction(ctx: ParamContext, delegate: Delegate): Returned
+ def wrapFunction(ctx: Request, delegate: Delegate): Returned
def getParamParser[T](implicit p: InputParser[T]) = p
}
trait HttpDecorator extends BaseDecorator{
@@ -73,10 +73,10 @@ trait Decorator extends HttpDecorator {
type InputParser[T] = NoOpParser[Input, T]
}
-class NoOpParser[Input, T] extends ArgReader[Input, T, ParamContext] {
+class NoOpParser[Input, T] extends ArgReader[Input, T, Request] {
def arity = 1
- def read(ctx: ParamContext, label: String, input: Input) = input.asInstanceOf[T]
+ def read(ctx: Request, label: String, input: Input) = input.asInstanceOf[T]
}
object NoOpParser{
implicit def instance[Input, T] = new NoOpParser[Input, T]
diff --git a/cask/src/cask/main/Main.scala b/cask/src/cask/main/Main.scala
index 70fe206..46e1a65 100644
--- a/cask/src/cask/main/Main.scala
+++ b/cask/src/cask/main/Main.scala
@@ -74,7 +74,7 @@ abstract class BaseMain{
case None =>
writeResponse(exchange, handleNotFound())
case Some(((routes, metadata), extBindings, remaining)) =>
- val ctx = ParamContext(exchange, remaining)
+ val ctx = Request(exchange, remaining)
def rec(remaining: List[Decorator],
bindings: List[Map[String, Any]]): Router.Result[Any] = try {
remaining match {
@@ -84,7 +84,7 @@ abstract class BaseMain{
case Nil =>
metadata.endpoint.wrapFunction(ctx, epBindings =>
metadata.entryPoint
- .asInstanceOf[EntryPoint[cask.main.Routes, cask.model.ParamContext]]
+ .asInstanceOf[EntryPoint[cask.main.Routes, cask.model.Request]]
.invoke(routes, ctx, (epBindings ++ extBindings.mapValues(metadata.endpoint.wrapPathSegment)) :: bindings.reverse)
.asInstanceOf[Router.Result[Nothing]]
)
diff --git a/cask/src/cask/main/Routes.scala b/cask/src/cask/main/Routes.scala
index aaec832..1a74b87 100644
--- a/cask/src/cask/main/Routes.scala
+++ b/cask/src/cask/main/Routes.scala
@@ -1,7 +1,7 @@
package cask.main
import cask.internal.Router.EntryPoint
-import cask.model.ParamContext
+import cask.model.Request
import scala.reflect.macros.blackbox.Context
import language.experimental.macros
@@ -43,7 +43,7 @@ object Routes{
m.asInstanceOf[MethodSymbol],
weakTypeOf[T],
q"${annotObjectSyms.head}.convertToResultType",
- tq"cask.ParamContext",
+ tq"cask.Request",
annotObjectSyms.map(annotObjectSym => q"$annotObjectSym.getParamParser"),
annotObjectSyms.map(annotObjectSym => tq"$annotObjectSym.Input")
diff --git a/cask/src/cask/model/ParamContext.scala b/cask/src/cask/model/ParamContext.scala
deleted file mode 100644
index 43da260..0000000
--- a/cask/src/cask/model/ParamContext.scala
+++ /dev/null
@@ -1,5 +0,0 @@
-package cask.model
-
-import io.undertow.server.HttpServerExchange
-
-case class ParamContext(exchange: HttpServerExchange, remaining: Seq[String])
diff --git a/cask/src/cask/model/Params.scala b/cask/src/cask/model/Params.scala
index bd10161..270c190 100644
--- a/cask/src/cask/model/Params.scala
+++ b/cask/src/cask/model/Params.scala
@@ -2,18 +2,11 @@ package cask.model
import java.io.{ByteArrayOutputStream, InputStream}
-import cask.endpoints.ParamReader.NilParam
import cask.internal.Util
import io.undertow.server.HttpServerExchange
import io.undertow.server.handlers.CookieImpl
-import io.undertow.websockets.spi.WebSocketHttpExchange
-class Subpath(val value: Seq[String])
-object Subpath{
- implicit object SubpathParam extends NilParam[Subpath]((ctx, label) => new Subpath(ctx.remaining))
-}
-
-case class Request(exchange: HttpServerExchange){
+case class Request(exchange: HttpServerExchange, remainingPathSegments: Seq[String]){
import collection.JavaConverters._
lazy val cookies: Map[String, Cookie] = {
exchange.getRequestCookies.asScala.mapValues(Cookie.fromUndertow).toMap
@@ -33,13 +26,8 @@ case class Request(exchange: HttpServerExchange){
.toMap
}
}
-object Request{
- implicit object RequestParam extends NilParam[Request]((ctx, label) => new Request(ctx.exchange))
-}
object Cookie{
- implicit object CookieParam extends NilParam[Cookie]((ctx, label) =>
- Cookie.fromUndertow(ctx.exchange.getRequestCookies().get(label))
- )
+
def fromUndertow(from: io.undertow.server.handlers.Cookie): Cookie = {
Cookie(
from.getName,
diff --git a/cask/src/cask/model/Response.scala b/cask/src/cask/model/Response.scala
index 9ac5664..59b44c9 100644
--- a/cask/src/cask/model/Response.scala
+++ b/cask/src/cask/model/Response.scala
@@ -4,22 +4,24 @@ import java.io.{InputStream, OutputStream, OutputStreamWriter}
import cask.internal.Util
-
-trait Response{
- def data: Response.Data
- def statusCode: Int
- def headers: Seq[(String, String)]
- def cookies: Seq[Cookie]
-}
+/**
+ * The basic response returned by a HTTP endpoint.
+ *
+ * Note that [[data]] by default can take in a wide range of types: strings,
+ * bytes, uPickle JSON-convertable types or arbitrary input streams. You can
+ * also construct your own implementations of `Response.Data`.
+ */
+case class Response(
+ data: Response.Data,
+ statusCode: Int,
+ headers: Seq[(String, String)],
+ cookies: Seq[Cookie]
+)
object Response{
def apply(data: Data,
statusCode: Int = 200,
headers: Seq[(String, String)] = Nil,
- cookies: Seq[Cookie] = Nil) = Simple(data, statusCode, headers, cookies)
- case class Simple(data: Data,
- statusCode: Int = 200,
- headers: Seq[(String, String)] = Nil,
- cookies: Seq[Cookie] = Nil) extends Response
+ cookies: Seq[Cookie] = Nil) = new Response(data, statusCode, headers, cookies)
implicit def dataResponse[T](t: T)(implicit c: T => Data) = Response(t)
trait Data{
@@ -43,59 +45,33 @@ object Response{
}
}
}
-case class Redirect(url: String) extends Response{
- override def data = ""
-
- override def statusCode = 301
-
- override def headers = Seq("Location" -> url)
-
- override def cookies = Nil
+object Redirect{
+ def apply(url: String) = Response("", 301, Seq("Location" -> url), Nil)
}
-case class Abort(code: Int) extends Response {
- override def data = ""
-
- override def statusCode = code
-
- override def headers = Nil
-
- override def cookies = Nil
+object Abort{
+ def apply(code: Int) = Response("", code, Nil, Nil)
}
-
-case class StaticFile(path: String) extends Response {
- val relPath = java.nio.file.Paths.get(path)
- val (data0, statusCode0) =
- if (java.nio.file.Files.exists(relPath) && java.nio.file.Files.isRegularFile(relPath)){
- (java.nio.file.Files.newInputStream(relPath): Response.Data, 200)
- }else{
- ("": Response.Data, 404)
- }
- override def data = data0
-
- override def statusCode = statusCode0
-
- override def headers = Nil
-
- override def cookies = Nil
+object StaticFile{
+ def apply(path: String) = {
+ val relPath = java.nio.file.Paths.get(path)
+ val (data0, statusCode0) =
+ if (java.nio.file.Files.exists(relPath) && java.nio.file.Files.isRegularFile(relPath)){
+ (java.nio.file.Files.newInputStream(relPath): Response.Data, 200)
+ }else{
+ ("": Response.Data, 404)
+ }
+ Response(data0, statusCode0, Nil, Nil)
+ }
}
-
-case class StaticResource(path: String, resourceRoot: ClassLoader) extends Response {
- val relPath = java.nio.file.Paths.get(path)
- val (data0, statusCode0) = resourceRoot.getResourceAsStream(path) match{
- case null => ("": Response.Data, 404)
- case res => (res: Response.Data, 200)
+object StaticResource{
+ def apply(path: String, resourceRoot: ClassLoader) = {
+ val relPath = java.nio.file.Paths.get(path)
+ val (data0, statusCode0) = resourceRoot.getResourceAsStream(path) match{
+ case null => ("": Response.Data, 404)
+ case res => (res: Response.Data, 200)
+ }
+ Response(data0, statusCode0, Nil, Nil)
}
-
- override def data = data0
-
- override def statusCode = statusCode0
-
- override def headers = Nil
-
- override def cookies = Nil
}
-
-
-
diff --git a/cask/src/cask/package.scala b/cask/src/cask/package.scala
index 06d9738..cd1a8e5 100644
--- a/cask/src/cask/package.scala
+++ b/cask/src/cask/package.scala
@@ -2,10 +2,10 @@ package object cask {
// 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
+ val StaticFile = model.StaticFile
+ val StaticResource = model.StaticResource
type FormEntry = model.FormEntry
val FormEntry = model.FormEntry
type FormValue = model.FormValue
@@ -14,12 +14,8 @@ package object cask {
val FormFile = model.FormFile
type Cookie = model.Cookie
val Cookie = model.Cookie
- type Subpath = model.Subpath
- val Subpath = model.Subpath
type Request = model.Request
val Request = model.Request
- type ParamContext = model.ParamContext
- val ParamContext = model.ParamContext
// endpoints
type websocket = endpoints.websocket
diff --git a/cask/test/src/test/cask/FailureTests.scala b/cask/test/src/test/cask/FailureTests.scala
index fed56e5..d24d52c 100644
--- a/cask/test/src/test/cask/FailureTests.scala
+++ b/cask/test/src/test/cask/FailureTests.scala
@@ -1,12 +1,11 @@
package test.cask
-import cask.internal.Router
-import cask.model.{ParamContext, Response}
+import cask.model.Request
import utest._
object FailureTests extends TestSuite {
class myDecorator extends cask.Decorator {
- def wrapFunction(ctx: ParamContext, delegate: Delegate): Returned = {
+ def wrapFunction(ctx: Request, delegate: Delegate): Returned = {
delegate(Map("extra" -> 31337))
}
}
diff --git a/docs/pages/1 - Cask: a Scala HTTP micro-framework.md b/docs/pages/1 - Cask: a Scala HTTP micro-framework.md
index 4ed5841..dd98dd2 100644
--- a/docs/pages/1 - Cask: a Scala HTTP micro-framework.md
+++ b/docs/pages/1 - Cask: a Scala HTTP micro-framework.md
@@ -81,10 +81,10 @@ via the following coordinates:
```scala
// Mill
-ivy"com.lihaoyi::cask:0.1.0"
+ivy"com.lihaoyi::cask:0.1.1"
// SBT
-"com.lihaoyi" %% "cask" % "0.1.0"
+"com.lihaoyi" %% "cask" % "0.1.1"
```
The `./cask` command is just a wrapper around the
@@ -233,7 +233,7 @@ You can write extra decorator annotations that stack on top of the existing
done by implementing the `cask.Decorator` interface and it's `getRawParams`
function. `getRawParams`:
-- Receives a `ParamContext`, which basically gives you full access to the
+- Receives a `Request`, which basically gives you full access to the
underlying undertow HTTP connection so you can pick out whatever data you
would like
diff --git a/example/compress/build.sc b/example/compress/build.sc
index 2166763..a08b2c6 100644
--- a/example/compress/build.sc
+++ b/example/compress/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/compress2/build.sc b/example/compress2/build.sc
index 2166763..a08b2c6 100644
--- a/example/compress2/build.sc
+++ b/example/compress2/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/compress3/build.sc b/example/compress3/build.sc
index 2166763..a08b2c6 100644
--- a/example/compress3/build.sc
+++ b/example/compress3/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/cookies/build.sc b/example/cookies/build.sc
index 2166763..a08b2c6 100644
--- a/example/cookies/build.sc
+++ b/example/cookies/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/decorated/app/src/Decorated.scala b/example/decorated/app/src/Decorated.scala
index 77f9133..f037c6a 100644
--- a/example/decorated/app/src/Decorated.scala
+++ b/example/decorated/app/src/Decorated.scala
@@ -4,12 +4,12 @@ object Decorated extends cask.MainRoutes{
override def toString = "[haoyi]"
}
class loggedIn extends cask.Decorator {
- def wrapFunction(ctx: cask.ParamContext, delegate: Delegate): Returned = {
+ def wrapFunction(ctx: cask.Request, delegate: Delegate): Returned = {
delegate(Map("user" -> new User()))
}
}
class withExtra extends cask.Decorator {
- def wrapFunction(ctx: cask.ParamContext, delegate: Delegate): Returned = {
+ def wrapFunction(ctx: cask.Request, delegate: Delegate): Returned = {
delegate(Map("extra" -> 31337))
}
}
diff --git a/example/decorated/build.sc b/example/decorated/build.sc
index 2166763..a08b2c6 100644
--- a/example/decorated/build.sc
+++ b/example/decorated/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/decorated2/app/src/Decorated2.scala b/example/decorated2/app/src/Decorated2.scala
index 014965e..20526b7 100644
--- a/example/decorated2/app/src/Decorated2.scala
+++ b/example/decorated2/app/src/Decorated2.scala
@@ -4,12 +4,12 @@ object Decorated2 extends cask.MainRoutes{
override def toString = "[haoyi]"
}
class loggedIn extends cask.Decorator {
- def wrapFunction(ctx: cask.ParamContext, delegate: Delegate): Returned = {
+ def wrapFunction(ctx: cask.Request, delegate: Delegate): Returned = {
delegate(Map("user" -> new User()))
}
}
class withExtra extends cask.Decorator {
- def wrapFunction(ctx: cask.ParamContext, delegate: Delegate): Returned = {
+ def wrapFunction(ctx: cask.Request, delegate: Delegate): Returned = {
delegate(Map("extra" -> 31337))
}
}
diff --git a/example/decorated2/build.sc b/example/decorated2/build.sc
index 2166763..a08b2c6 100644
--- a/example/decorated2/build.sc
+++ b/example/decorated2/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/endpoints/app/src/Endpoints.scala b/example/endpoints/app/src/Endpoints.scala
index 5450029..b478c80 100644
--- a/example/endpoints/app/src/Endpoints.scala
+++ b/example/endpoints/app/src/Endpoints.scala
@@ -2,7 +2,7 @@ package app
class custom(val path: String, val methods: Seq[String]) extends cask.Endpoint{
type Output = Int
- def wrapFunction(ctx: cask.ParamContext, delegate: Delegate): Returned = {
+ def wrapFunction(ctx: cask.Request, delegate: Delegate): Returned = {
delegate(Map()).map{num =>
cask.Response("Echo " + num, statusCode = num)
}
diff --git a/example/endpoints/build.sc b/example/endpoints/build.sc
index 2166763..a08b2c6 100644
--- a/example/endpoints/build.sc
+++ b/example/endpoints/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/formJsonPost/build.sc b/example/formJsonPost/build.sc
index 2166763..a08b2c6 100644
--- a/example/formJsonPost/build.sc
+++ b/example/formJsonPost/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/httpMethods/build.sc b/example/httpMethods/build.sc
index 2166763..a08b2c6 100644
--- a/example/httpMethods/build.sc
+++ b/example/httpMethods/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/minimalApplication/build.sc b/example/minimalApplication/build.sc
index 2166763..a08b2c6 100644
--- a/example/minimalApplication/build.sc
+++ b/example/minimalApplication/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/minimalApplication2/build.sc b/example/minimalApplication2/build.sc
index 2166763..a08b2c6 100644
--- a/example/minimalApplication2/build.sc
+++ b/example/minimalApplication2/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/redirectAbort/build.sc b/example/redirectAbort/build.sc
index 2166763..a08b2c6 100644
--- a/example/redirectAbort/build.sc
+++ b/example/redirectAbort/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/scalatags/build.sc b/example/scalatags/build.sc
index d517c6b..86141e0 100644
--- a/example/scalatags/build.sc
+++ b/example/scalatags/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
ivy"com.lihaoyi::scalatags:0.6.7",
)
diff --git a/example/staticFiles/build.sc b/example/staticFiles/build.sc
index 00a7ee3..54d1fb5 100644
--- a/example/staticFiles/build.sc
+++ b/example/staticFiles/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
def forkWorkingDir = build.millSourcePath
diff --git a/example/todo/app/src/TodoServer.scala b/example/todo/app/src/TodoServer.scala
index 1a768d1..4adb55c 100644
--- a/example/todo/app/src/TodoServer.scala
+++ b/example/todo/app/src/TodoServer.scala
@@ -17,7 +17,7 @@ object TodoServer extends cask.MainRoutes{
class transactional extends cask.Decorator{
class TransactionFailed(val value: Router.Result.Error) extends Exception
- def wrapFunction(pctx: cask.ParamContext, delegate: Delegate): Returned = {
+ def wrapFunction(pctx: cask.Request, delegate: Delegate): Returned = {
try ctx.transaction(
delegate(Map()) match{
case Router.Result.Success(t) => Router.Result.Success(t)
diff --git a/example/todo/build.sc b/example/todo/build.sc
index ca0716a..0f01a14 100644
--- a/example/todo/build.sc
+++ b/example/todo/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
ivy"org.xerial:sqlite-jdbc:3.18.0",
ivy"io.getquill::quill-jdbc:2.5.4",
ivy"com.lihaoyi::scalatags:0.6.7",
diff --git a/example/todoApi/build.sc b/example/todoApi/build.sc
index 2166763..a08b2c6 100644
--- a/example/todoApi/build.sc
+++ b/example/todoApi/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/todoDb/app/src/TodoMvcDb.scala b/example/todoDb/app/src/TodoMvcDb.scala
index c566b60..7ce3c50 100644
--- a/example/todoDb/app/src/TodoMvcDb.scala
+++ b/example/todoDb/app/src/TodoMvcDb.scala
@@ -16,7 +16,7 @@ object TodoMvcDb extends cask.MainRoutes{
class transactional extends cask.Decorator{
class TransactionFailed(val value: Router.Result.Error) extends Exception
- def wrapFunction(pctx: cask.ParamContext, delegate: Delegate): Returned = {
+ def wrapFunction(pctx: cask.Request, delegate: Delegate): Returned = {
try ctx.transaction(
delegate(Map()) match{
case Router.Result.Success(t) => Router.Result.Success(t)
diff --git a/example/todoDb/build.sc b/example/todoDb/build.sc
index 046e2e1..5bfc599 100644
--- a/example/todoDb/build.sc
+++ b/example/todoDb/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
ivy"org.xerial:sqlite-jdbc:3.18.0",
ivy"io.getquill::quill-jdbc:2.5.4"
)
diff --git a/example/variableRoutes/app/src/VariableRoutes.scala b/example/variableRoutes/app/src/VariableRoutes.scala
index 760ab15..a1c8a4f 100644
--- a/example/variableRoutes/app/src/VariableRoutes.scala
+++ b/example/variableRoutes/app/src/VariableRoutes.scala
@@ -11,8 +11,8 @@ object VariableRoutes extends cask.MainRoutes{
}
@cask.get("/path", subpath = true)
- def showSubpath(subPath: cask.Subpath) = {
- s"Subpath ${subPath.value}"
+ def showSubpath(request: cask.Request) = {
+ s"Subpath ${request.remainingPathSegments}"
}
initialize()
diff --git a/example/variableRoutes/build.sc b/example/variableRoutes/build.sc
index 2166763..a08b2c6 100644
--- a/example/variableRoutes/build.sc
+++ b/example/variableRoutes/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{
diff --git a/example/websockets/build.sc b/example/websockets/build.sc
index 96991eb..aa9fa17 100644
--- a/example/websockets/build.sc
+++ b/example/websockets/build.sc
@@ -4,7 +4,7 @@ import mill._, scalalib._
trait AppModule extends ScalaModule{
def scalaVersion = "2.12.6"
def ivyDeps = Agg(
- ivy"com.lihaoyi::cask:0.1.0",
+ ivy"com.lihaoyi::cask:0.1.1",
)
object test extends Tests{