Skip to content

Commit

Permalink
Rework routeDispatchTries + endpoint lookup for more intuitive 404/40…
Browse files Browse the repository at this point in the history
…5 responses. (#52)

Prevent cask from responding with 405 for an undefined route.
Fixes #51.

Prior to this commit, `prepareRouteTries` created a mapping from
method-name to DispatchTrie (Map[String, DispatchTrie[…]]).

This commit instead creates a DispatchTrie[Map[String, …]],
basically an inversion of the previous result.

The updated tests in minimalApplication and minimalApplication2
have been updated to cover the differences.
  • Loading branch information
megri authored Nov 15, 2021
1 parent f35498e commit 0e7ee84
Show file tree
Hide file tree
Showing 30 changed files with 70 additions and 68 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.bloop/
.metals/
.vscode/
target/
*.iml
.idea
Expand Down
2 changes: 2 additions & 0 deletions .mill-version
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
0.9.7

2 changes: 1 addition & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class CaskMainModule(val crossScalaVersion: String) extends CaskModule {
def scalacPluginIvyDeps = T{ if (!isDotty) Agg(ivy"com.lihaoyi::acyclic:0.2.0") else Agg() }

object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"
def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
ivy"com.lihaoyi::requests::0.6.9"
Expand Down
79 changes: 38 additions & 41 deletions cask/src/cask/main/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ abstract class Main{

implicit def log: cask.util.Logger = new cask.util.Logger.Console()

def routeTries = Main.prepareRouteTries(allRoutes)
def dispatchTrie = Main.prepareDispatchTrie(allRoutes)

def defaultHandler = new BlockingHandler(
new Main.DefaultHandler(routeTries, mainDecorators, debugMode, handleNotFound, handleMethodNotAllowed, handleEndpointError)
new Main.DefaultHandler(dispatchTrie, mainDecorators, debugMode, handleNotFound, handleMethodNotAllowed, handleEndpointError)
)

def handleNotFound() = Main.defaultHandleNotFound()
Expand All @@ -72,7 +72,7 @@ abstract class Main{
}

object Main{
class DefaultHandler(routeTries: Map[String, DispatchTrie[(Routes, EndpointMetadata[_])]],
class DefaultHandler(dispatchTrie: DispatchTrie[Map[String, (Routes, EndpointMetadata[_])]],
mainDecorators: Seq[Decorator[_, _, _]],
debugMode: Boolean,
handleNotFound: () => Response.Raw,
Expand All @@ -99,35 +99,30 @@ object Main{
(r: Any) => Main.writeResponse(exchange, r.asInstanceOf[Response.Raw])
)

val dispatchTrie: DispatchTrie[(Routes, EndpointMetadata[_])] = routeTries.get(effectiveMethod) match {
case None =>
Main.writeResponse(exchange, handleMethodNotAllowed())
return
case Some(trie) => trie
}

dispatchTrie.lookup(Util.splitPath(exchange.getRequestPath).toList, Map()) match {
case None => Main.writeResponse(exchange, handleNotFound())
case Some(((routes, metadata), routeBindings, remaining)) =>
Decorator.invoke(
Request(exchange, remaining),
metadata.endpoint,
metadata.entryPoint.asInstanceOf[EntryPoint[Routes, _]],
routes,
routeBindings,
(mainDecorators ++ routes.decorators ++ metadata.decorators).toList,
Nil
) match{
case Result.Success(res) => runner(res)
case e: Result.Error =>
Main.writeResponse(
exchange,
handleError(routes, metadata, e)
)
None
case Some((methodMap, routeBindings, remaining)) =>
methodMap.get(effectiveMethod) match {
case None => Main.writeResponse(exchange, handleMethodNotAllowed())
case Some((routes, metadata)) =>
Decorator.invoke(
Request(exchange, remaining),
metadata.endpoint,
metadata.entryPoint.asInstanceOf[EntryPoint[Routes, _]],
routes,
routeBindings,
(mainDecorators ++ routes.decorators ++ metadata.decorators).toList,
Nil
) match {
case Result.Success(res) => runner(res)
case e: Result.Error =>
Main.writeResponse(
exchange,
handleError(routes, metadata, e)
)
}
}
}
// println("Completed Request: " + exchange.getRequestPath)
}catch{case e: Throwable =>
e.printStackTrace()
}
Expand All @@ -147,23 +142,25 @@ object Main{
)
}

def prepareRouteTries(allRoutes: Seq[Routes]): Map[String, DispatchTrie[(Routes, EndpointMetadata[_])]] = {
val routeList = for{
def prepareDispatchTrie(allRoutes: Seq[Routes]): DispatchTrie[Map[String, (Routes, EndpointMetadata[_])]] = {
val flattenedRoutes = for {
routes <- allRoutes
route <- routes.caskMetadata.value.map(x => x: EndpointMetadata[_])
} yield (routes, route)
metadata <- routes.caskMetadata.value
} yield {
val segments = Util.splitPath(metadata.endpoint.path)
val methodMap = metadata.endpoint.methods.map(_ -> (routes, metadata: EndpointMetadata[_])).toMap
(segments, methodMap, metadata.endpoint.subpath)
}

val allMethods: Set[String] =
routeList.flatMap(_._2.endpoint.methods).map(_.toLowerCase).toSet
val dispatchInputs = flattenedRoutes.groupBy(_._1).map { case (segments, values) =>
val methodMap = values.map(_._2).flatten.toMap
val hasSubpath = values.map(_._3).contains(true)
(segments, methodMap, hasSubpath)
}.toSeq

allMethods
.map { method =>
method -> DispatchTrie.construct[(Routes, EndpointMetadata[_])](0,
for ((route, metadata) <- routeList if metadata.endpoint.methods.contains(method))
yield (Util.splitPath(metadata.endpoint.path): collection.IndexedSeq[String], (route, metadata), metadata.endpoint.subpath)
)
}.toMap
DispatchTrie.construct(0, dispatchInputs)
}

def writeResponse(exchange: HttpServerExchange, response: Response.Raw) = {
response.data.headers.foreach{case (k, v) =>
exchange.getResponseHeaders.put(new HttpString(k), v)
Expand Down
2 changes: 1 addition & 1 deletion example/compress/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/compress2/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/compress3/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/cookies/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/decorated/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/decorated2/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/formJsonPost/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/httpMethods/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/minimalApplication/app/test/src/ExampleTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object ExampleTests extends TestSuite{

requests.post(s"$host/do-thing", data = "hello").text() ==> "olleh"

requests.get(s"$host/do-thing", check = false).statusCode ==> 404
requests.delete(s"$host/do-thing", check = false).statusCode ==> 405
}
}
}
2 changes: 1 addition & 1 deletion example/minimalApplication/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object ExampleTests extends TestSuite{

requests.post(s"$host/do-thing", data = "hello").text() ==> "olleh"

requests.get(s"$host/do-thing", check = false).statusCode ==> 404
requests.delete(s"$host/do-thing", check = false).statusCode ==> 405
}
}
}
2 changes: 1 addition & 1 deletion example/minimalApplication2/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/redirectAbort/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/scalatags/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ trait AppModule extends CrossScalaModule{
)

object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/staticFiles/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/staticFiles2/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/todo/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ trait AppModule extends CrossScalaModule{
)

object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/todoApi/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/todoDb/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ trait AppModule extends CrossScalaModule{
)

object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/twirl/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ trait AppModule extends CrossScalaModule with mill.twirllib.TwirlModule{
)

object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/variableRoutes/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/websockets/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/websockets2/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/websockets3/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down
2 changes: 1 addition & 1 deletion example/websockets4/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{
def ivyDeps = Agg[Dep](
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def testFramework = "utest.runner.Framework"

def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.10",
Expand Down

0 comments on commit 0e7ee84

Please sign in to comment.