| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Ecluse.Core.Server.Route
Contents
Description
A route: one record saying everything there is to say about one URL the proxy serves.
A Route carries its method condition, its path template (literal segments and named
captures that parse themselves), what to do when it matches, and its documentation.
An ecosystem's routing table is then simply a list of these values
(Ecluse.Core.Registry.Npm.Route is npm's), and routerOf folds that list into the
mount's router: first match wins, no match is the deny-by-default 404.
There is no route sum. A classified-route type would have to be matched again to decide
what to do about it, and again to document it, and each of those matches is somewhere the
three can fall out of step. Here the pattern, the action, and the documentation are the
same value, so they cannot disagree, and the manifest renders RouteSpec
projections of the very records the router runs.
What stays a named function
The engine owns the structure: literal matching, capture arity, ordering, exact
consumption. It does not infer an ecosystem's semantics. A Capture carries its own
segment parser and a Route its own builder, so the security-critical leaf logic (the
component-safety gate, an ecosystem's scoped-name decoding, a version parse, the
cross-capture path-confusion check) stays in named, reviewed, separately-tested functions
that the record references, rather than being regenerated from a generic template.
Synopsis
- data Route v = Route {
- routeName :: RouteName
- routeMethod :: MethodMatch
- routeSegs :: [PatternSeg v]
- routeBuild :: Method -> [v] -> Maybe (ResponseAction response)
- routeSummary :: Text
- routeDescription :: Text
- routeRequest :: Maybe RequestSpec
- routeContract :: ResponseContract response
- newtype RouteName = RouteName {
- unRouteName :: Text
- data PatternSeg v
- data Capture v = Capture {
- capName :: Text
- capDescription :: Text
- capConsume :: [Text] -> Maybe (v, [Text])
- data MethodMatch
- routerOf :: RouteAction -> [Route v] -> MountRouter
- matchRoute :: [Route v] -> Method -> [Text] -> Maybe (Route v, RouteAction)
A route
One route, whole: how it matches, what it does, and what it means.
Generic over the ecosystem's capture-value type v, which is the only thing about a
route that is not shared (npm's captures yield a parsed package or an artifact name;
another registry's would yield its own).
Constructors
| Route | |
Fields
| |
A route's name within its ecosystem ("packument", "tarball"). Not qualified: the
route already lives in its ecosystem's table, and the manifest adds the namespace when it
needs a globally unique identifier.
Constructors
| RouteName | |
Fields
| |
Instances
| Show RouteName Source # | |
| Eq RouteName Source # | |
| Ord RouteName Source # | |
data PatternSeg v Source #
One segment of a path template: a fixed segment matched verbatim, or a named capture that consumes one or more leading segments and yields a value.
A named path capture: how it parses (the security-critical leaf) and how it
documents. capConsume may consume more than one segment (an ecosystem whose
identifier spans a decoded '/' needs this) and returns the unconsumed tail, so
captures thread left to right; Nothing fails the match, and the request falls through to
the next route or to the deny-by-default catch-all.
Constructors
| Capture | |
Fields
| |
data MethodMatch Source #
The method condition on a route: the read methods (GET and HEAD), or the one
client write (PUT).
Any other method matches no route and therefore denies (deny by default): the front door
answers only the methods it was taught, so a DELETE or POST over a package path is a
404 rather than being read as a package request. This also keeps the documented method
honest: the manifest says GET for a read, and only a GET (or its bodiless HEAD) is
served.
Kept as a small closed vocabulary rather than a bare predicate so the manifest can still name the documented method.
Constructors
| MethodPut | The write method ( |
| MethodRead | The read methods ( |
Instances
| Show MethodMatch Source # | |
Defined in Ecluse.Core.Server.Route Methods showsPrec :: Int -> MethodMatch -> ShowS # show :: MethodMatch -> String # showList :: [MethodMatch] -> ShowS # | |
| Eq MethodMatch Source # | |
Defined in Ecluse.Core.Server.Route | |
Routing a request
routerOf :: RouteAction -> [Route v] -> MountRouter Source #
Fold an ecosystem's route table into its mount's router: the first route that claims
the request decides what is done with it, and a request no route claims is the
deny-by-default 404 in the mount's own error surface.
Deny-by-default is structural here: routerOf has no other way to answer. There is no
catch-all branch to forget. The 404 Answer a mount supplies for a path no route
claims is its deny-by-default surface (npm's {"error": "not found"}).
matchRoute :: [Route v] -> Method -> [Text] -> Maybe (Route v, RouteAction) Source #
The route that claims a request, and the action it names: the first whose method
condition holds, whose segments are consumed exactly, and whose builder accepts the
captures. Nothing when none does.
Exported beside routerOf because it is what makes a routing table testable with no
server: feed it a method and segments and assert which route won (by its routeName), or
that none did. The action itself is a closure and is exercised through the serve path.