RouteTable

io.eezo.http.RouteTable
See theRouteTable companion object
final class RouteTable(mounted: Seq[Route], val identify: Request => Request)

The routes an application serves, in the order they are matched.

Seq order is the entire contract: the table never sorts, and ++ concatenates. Specificity sorting was rejected because it is a rule a reader cannot see by reading the generated file top to bottom, and that file is what people debug routing with. Rails, Phoenix and Play are all declaration ordered for the same reason. The sbt plugin owns emit order.

identify is who the table says is behind a request it serves, RouteTable.naming over the identify of every declaration mounted into it: the generator writes that call, and a table built by hand names nobody. It exists for the socket upgrade and for nothing else. A guarded page is named by the guard's own wrapper, inside dispatch and inside Csrf.protect with it, so a public page's handler reads nobody however the visitor signed in; an upgrade has no such wrapper on a route nobody guarded, and the one thing that holds every declaration at once is this table.

Attributes

Companion
object
Source
Route.scala
Graph
Supertypes
class Object
trait Matchable
class Any

Members list

Value members

Concrete methods

def ++(other: RouteTable): RouteTable

Concatenation. Order is preserved, so the receiver's routes keep winning.

Concatenation. Order is preserved, so the receiver's routes keep winning.

The two namings are composed rather than one of them chosen, because the framework routes an application appends, HttpApp.serve's table ++ RouteTable(frameworkRoutes), name nobody and must not cost the application the naming its own declarations composed.

Attributes

Source
Route.scala
def dispatch(request: Request): Response

Runs the first HTTP route whose method and pattern both match.

Runs the first HTTP route whose method and pattern both match.

Matching and running are two steps, matching then running, because the server has to know which route matched before it reads the session cookie: an API route reads none.

Attributes

Source
Route.scala
def dispatchWs(path: String): Option[(Ws, Map[String, String])]

Finds the first WebSocket route whose pattern matches, if any.

Finds the first WebSocket route whose pattern matches, if any.

The WebSocket half of dispatch: no method to disagree on, since an upgrade is always a GET, so one pass with no Allow bookkeeping is the whole job. The single caller, HttpServer's WebSocket creator, decides the 404 itself: Jetty requires that decision to complete a Callback rather than throw.

Attributes

Source
Route.scala
def shadowed: Seq[(Route, Route)]

Every pair where an earlier route swallows a later one, in table order.

Every pair where an earlier route swallows a later one, in table order.

Pure, and deliberately not a log call: a warning in this constructor would fire in every test that builds a shadowing table on purpose, and once more for each intermediate ++ produces. HttpServer.start is the single site that emits, because the assembled table it serves is the only one where shadowing is a defect rather than a step.

Never an error. First-match order is what lets a handwritten /widgets/new beat a derived /widgets/:id, so shadowing is legal by construction; only the duplicate the constructor rejects, two routes of the same provenance on one method and path, is a bug with no reading under which it was meant. Both kinds are checked against their own kind, since dispatch and dispatchWs walk separate partitions and an HTTP route cannot swallow an upgrade.

Attributes

Source
Route.scala

Concrete fields

val httpRoutes: Seq[Http]

The two kinds, split once. HttpServer.run reads wsRoutes to build its single WebSocket mapping and hands httpRoutes to the Jetty handler, so neither walks past a route it cannot use.

The two kinds, split once. HttpServer.run reads wsRoutes to build its single WebSocket mapping and hands httpRoutes to the Jetty handler, so neither walks past a route it cannot use.

Attributes

Source
Route.scala

Attributes

Source
Route.scala
val overridden: Seq[Route]

The derived routes a handwritten route on the same method and path replaced.

The derived routes a handwritten route on the same method and path replaced.

Decision 18: a handwritten route wins over a derived one. A GET /posts written under app/ next to a case class Post ... derives Resource is the ordinary way to take over one page of a resource and keep the other six, so it must boot rather than fail. The loser is dropped from routes outright rather than left sitting behind the winner, which is what keeps it out of the boot listing as well as out of dispatch.

Pure, and deliberately not a log call, for the reason shadowed gives: HttpServer.start is the one site where the assembled table is the table the application actually serves.

Attributes

Source
Route.scala
val routes: Seq[Route]

The routes served, in the order they are matched.

The routes served, in the order they are matched.

Attributes

Source
Route.scala
val wsRoutes: Seq[Ws]

Attributes

Source
Route.scala