diff --git a/build.sc b/build.sc index 3d0b375ae0..dfe03805a6 100644 --- a/build.sc +++ b/build.sc @@ -51,7 +51,7 @@ object cask extends ScalaModule with PublishModule { def testFrameworks = Seq("utest.runner.Framework") def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ivy"org.xerial:sqlite-jdbc:3.18.0", ivy"io.getquill::quill-jdbc:2.5.4" ) diff --git a/cask/src/cask/endpoints/WebEndpoints.scala b/cask/src/cask/endpoints/WebEndpoints.scala index 9fe28a0184..ab3b4808b1 100644 --- a/cask/src/cask/endpoints/WebEndpoints.scala +++ b/cask/src/cask/endpoints/WebEndpoints.scala @@ -13,12 +13,16 @@ trait WebEndpoint extends Endpoint{ type InputParser[T] = QueryParamReader[T] def wrapFunction(ctx: Request, delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = { - delegate( - ctx.exchange.getQueryParameters - .asScala - .map{case (k, vs) => (k, vs.asScala.toArray.toSeq)} - .toMap - ) + + val b = Map.newBuilder[String, Seq[String]] + val queryParams = ctx.exchange.getQueryParameters + for(k <- queryParams.keySet().iterator().asScala){ + val deque = queryParams.get(k) + val arr = new Array[String](deque.size) + deque.toArray(arr) + b += (k -> (arr: Seq[String])) + } + delegate(b.result()) } def wrapPathSegment(s: String) = Seq(s) } diff --git a/cask/src/cask/internal/Router.scala b/cask/src/cask/internal/Router.scala index 9fa9b60166..64af4cda78 100644 --- a/cask/src/cask/internal/Router.scala +++ b/cask/src/cask/internal/Router.scala @@ -2,6 +2,7 @@ package cask.internal import language.experimental.macros import scala.annotation.StaticAnnotation +import scala.collection.mutable import scala.reflect.macros.blackbox.Context /** @@ -44,14 +45,25 @@ object Router{ argSignatures: Seq[Seq[ArgSig[_, T, _, C]]], doc: Option[String], invoke0: (T, C, Seq[Map[String, Any]], Seq[Seq[ArgSig[Any, _, _, C]]]) => Result[Any]){ + + val firstArgs = argSignatures.head + .map(x => x.name -> x) + .toMap[String, Router.ArgSig[_, T, _, C]] + def invoke(target: T, ctx: C, paramLists: Seq[Map[String, Any]]): Result[Any] = { - val unknown = paramLists.head.keySet -- argSignatures.head.map(_.name).toSet - val missing = argSignatures.head.filter(as => - as.reads.arity != 0 && !paramLists.head.contains(as.name) && as.default.isEmpty - ) + val missing = mutable.Buffer.empty[Router.ArgSig[_, T, _, C]] + + val unknown = paramLists.head.keys.filter(!firstArgs.contains(_)) + + for(k <- firstArgs.keys) { + if (!paramLists.head.contains(k)) { + val as = firstArgs(k) + if (as.reads.arity != 0 && as.default.isEmpty) missing.append(as) + } + } if (missing.nonEmpty || unknown.nonEmpty) Result.Error.MismatchedArguments(missing, unknown.toSeq) else { @@ -313,9 +325,9 @@ class Router[C <: Context](val c: C) { ${method.name.toString}, ${argSigs.toList}, ${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)" + }}, ( $baseArgSym: $curCls, $ctxSymbol: $ctx, diff --git a/cask/src/cask/internal/Util.scala b/cask/src/cask/internal/Util.scala index 7da343eb13..431944a2f4 100644 --- a/cask/src/cask/internal/Util.scala +++ b/cask/src/cask/internal/Util.scala @@ -54,8 +54,34 @@ object Util { def pluralize(s: String, n: Int) = { if (n == 1) s else s + "s" } - def splitPath(p: String) = { - p.dropWhile(_ == '/').reverse.dropWhile(_ == '/').reverse.split('/').filter(_.nonEmpty) + + /** + * Splits a string into path segments; automatically removes all + * leading/trailing slashes, and ignores empty path segments. + * + * Written imperatively for performance since it's used all over the place. + */ + def splitPath(p: String): IndexedSeq[String] = { + val pLength = p.length + var i = 0 + while(i < pLength && p(i) == '/') i += 1 + var segmentStart = i + val out = mutable.ArrayBuffer.empty[String] + + def complete() = { + if (i != segmentStart) { + val s = p.substring(segmentStart, i) + out += s + } + segmentStart = i + 1 + } + + while(i < pLength){ + if (p(i) == '/') complete() + i += 1 + } + complete() + out } def stackTraceString(e: Throwable) = { diff --git a/cask/src/cask/main/Main.scala b/cask/src/cask/main/Main.scala index 5c0b120cdf..e12683fbce 100644 --- a/cask/src/cask/main/Main.scala +++ b/cask/src/cask/main/Main.scala @@ -78,26 +78,33 @@ abstract class BaseMain{ writeResponseHandler(r).handleRequest(exchange) } ) - } else { - exchange.getRequestMethod.toString.toLowerCase() -> ((r: Any) => writeResponse(exchange, r.asInstanceOf[Response])) - } + } else ( + exchange.getRequestMethod.toString.toLowerCase(), + (r: Any) => writeResponse(exchange, r.asInstanceOf[Response]) + ) routeTries(effectiveMethod).lookup(Util.splitPath(exchange.getRequestPath).toList, Map()) match { - case None => - writeResponse(exchange, handleNotFound()) - case Some(((routes, metadata), extBindings, remaining)) => + case None => writeResponse(exchange, handleNotFound()) + case Some(((routes, metadata), routeBindings, remaining)) => val ctx = Request(exchange, remaining) def rec(remaining: List[Decorator], bindings: List[Map[String, Any]]): Router.Result[Any] = try { remaining match { case head :: rest => - head.wrapFunction(ctx, args => rec(rest, args :: bindings).asInstanceOf[Router.Result[head.Output]]) + head.wrapFunction( + ctx, + args => rec(rest, args :: bindings).asInstanceOf[Router.Result[head.Output]] + ) case Nil => - metadata.endpoint.wrapFunction(ctx, epBindings => + metadata.endpoint.wrapFunction(ctx, endpointBindings => metadata.entryPoint .asInstanceOf[EntryPoint[cask.main.Routes, cask.model.Request]] - .invoke(routes, ctx, (epBindings ++ extBindings.mapValues(metadata.endpoint.wrapPathSegment)) :: bindings.reverse) + .invoke( + routes, ctx, + (endpointBindings ++ routeBindings.mapValues(metadata.endpoint.wrapPathSegment)) + :: bindings.reverse + ) .asInstanceOf[Router.Result[Nothing]] ) } diff --git a/cask/test/src/test/cask/UtilTests.scala b/cask/test/src/test/cask/UtilTests.scala new file mode 100644 index 0000000000..7b34434415 --- /dev/null +++ b/cask/test/src/test/cask/UtilTests.scala @@ -0,0 +1,22 @@ +package test.cask + +import utest._ + +object UtilTests extends TestSuite { + val tests = Tests{ + 'splitPath - { + cask.internal.Util.splitPath("") ==> Seq() + cask.internal.Util.splitPath("/") ==> Seq() + cask.internal.Util.splitPath("////") ==> Seq() + + cask.internal.Util.splitPath("abc") ==> Seq("abc") + cask.internal.Util.splitPath("/abc/") ==> Seq("abc") + cask.internal.Util.splitPath("//abc") ==> Seq("abc") + cask.internal.Util.splitPath("abc//") ==> Seq("abc") + + cask.internal.Util.splitPath("abc//def") ==> Seq("abc", "def") + cask.internal.Util.splitPath("//abc//def//") ==> Seq("abc", "def") + } + } +} + diff --git a/example/compress/build.sc b/example/compress/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/compress/build.sc +++ b/example/compress/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/compress2/build.sc b/example/compress2/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/compress2/build.sc +++ b/example/compress2/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/compress3/build.sc b/example/compress3/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/compress3/build.sc +++ b/example/compress3/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/cookies/build.sc b/example/cookies/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/cookies/build.sc +++ b/example/cookies/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/decorated/build.sc b/example/decorated/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/decorated/build.sc +++ b/example/decorated/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/decorated2/build.sc b/example/decorated2/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/decorated2/build.sc +++ b/example/decorated2/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/endpoints/build.sc b/example/endpoints/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/endpoints/build.sc +++ b/example/endpoints/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/formJsonPost/build.sc b/example/formJsonPost/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/formJsonPost/build.sc +++ b/example/formJsonPost/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/httpMethods/build.sc b/example/httpMethods/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/httpMethods/build.sc +++ b/example/httpMethods/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/minimalApplication/build.sc b/example/minimalApplication/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/minimalApplication/build.sc +++ b/example/minimalApplication/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/minimalApplication2/build.sc b/example/minimalApplication2/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/minimalApplication2/build.sc +++ b/example/minimalApplication2/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/redirectAbort/build.sc b/example/redirectAbort/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/redirectAbort/build.sc +++ b/example/redirectAbort/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/scalatags/build.sc b/example/scalatags/build.sc index 12e8ff9ca1..cf8b34c441 100644 --- a/example/scalatags/build.sc +++ b/example/scalatags/build.sc @@ -13,7 +13,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/staticFiles/build.sc b/example/staticFiles/build.sc index 4f4716dcd4..4040676eef 100644 --- a/example/staticFiles/build.sc +++ b/example/staticFiles/build.sc @@ -14,7 +14,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) def forkWorkingDir = build.millSourcePath diff --git a/example/todo/build.sc b/example/todo/build.sc index 37e5253fc3..a71a287ee5 100644 --- a/example/todo/build.sc +++ b/example/todo/build.sc @@ -15,7 +15,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/todoApi/build.sc b/example/todoApi/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/todoApi/build.sc +++ b/example/todoApi/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/todoDb/build.sc b/example/todoDb/build.sc index 52ac761383..77e580e323 100644 --- a/example/todoDb/build.sc +++ b/example/todoDb/build.sc @@ -14,7 +14,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/twirl/build.sc b/example/twirl/build.sc index 55ea56292b..646bea7834 100644 --- a/example/twirl/build.sc +++ b/example/twirl/build.sc @@ -17,7 +17,7 @@ trait AppModule extends ScalaModule with mill.twirllib.TwirlModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/variableRoutes/build.sc b/example/variableRoutes/build.sc index e5faf4bea0..03421a3212 100644 --- a/example/variableRoutes/build.sc +++ b/example/variableRoutes/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ) } } \ No newline at end of file diff --git a/example/websockets/build.sc b/example/websockets/build.sc index ef595b14ba..909c6b192d 100644 --- a/example/websockets/build.sc +++ b/example/websockets/build.sc @@ -12,7 +12,7 @@ trait AppModule extends ScalaModule{ def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.6.3", - ivy"com.lihaoyi::requests::0.1.3", + ivy"com.lihaoyi::requests::0.1.5", ivy"org.asynchttpclient:async-http-client:2.5.2" ) }