Copyright | (c) 2015 Athan Clark |
---|---|
License | BSD-style |
Maintainer | athan.clark@gmail.com |
Stability | experimental |
Portability | GHC |
Safe Haskell | None |
Language | Haskell2010 |
This module exports most of what you'll need for sophisticated routing -
all the tools from wai-middleware-verbs
(routing for the incoming HTTP method) and
wai-middleware-content-type
(routing for the incoming Accept header, and implied file extension),
WAI itself, and
wai-transformers - some simple
type aliases wrapped around WAI's Application
and Middleware
types, allowing us
to embed monad transformer stacks for our applications.
The routing system lets you embed these complicated HTTP verb / content-type
sensative responses just as easily as a WAI Middleware
. There is enough
tooling provided to use one paradigm or the other. Note - nested-routes
does not affect the pathInfo
of the incoming Request
in any way, but merely
matches on it and passes control to the designated response.
To match a route, you have a few options - you can match against a string literal, a regular expression (via regex-compat), or an attoparsec parser. This list will most likely grow in the future, depending on demand.
There is also support for embedding security layers in your routes, in the same
nested manner. By "tagging" a set of routes with an authorization role (with auth
),
you populate a list of roles breached during any request. In the authentication
parameter in routeAuth
and routeActionAuth
, the function
keeps the session integrity in-place, while auth
lets you create your authorization
boundaries. Both are symbiotic and neccessary for establishing security, and both allow
you to tap into the monad transformer stack to do logging, STM, database queries,
etc.
To use your set of routes in a WAI application, you need to "extract" the
functionality from your route set - using the route
, routeAuth
, routeAction
,
and routeActionAuth
functions, you can create monolithic apps very easily.
But, if you would like to extract the security middleware to place before
say, a static middleware you already have in place, use the extractAuth
functions, and others for their respective purposes. This way, you can decompose
the routing system into each subject matter, and re-compose (.
) them in whichever
order you like for your application.
- type Tries x s e = (RootedPredTrie Text x, RootedPredTrie Text x, RootedPredTrie Text s, RootedPredTrie Text e)
- newtype HandlerT x sec err aux m a = HandlerT {
- runHandlerT :: StateT (Tries x sec err) m a
- execHandlerT :: Monad m => HandlerT x sec err aux m a -> m (Tries x sec err)
- type ActionT e u m a = VerbListenerT (FileExtListenerT (MiddlewareT m) m a) e u m a
- type RoutableT s e u ue m a = HandlerT (MiddlewareT m) (s, AuthScope) (e -> MiddlewareT m) (e, u, ue) m a
- type RoutableActionT s e u ue m a = HandlerT (ActionT ue u m ()) (s, AuthScope) (e -> ActionT ue u m ()) (e, u, ue) m a
- data AuthScope
- type ExtrudeSoundly cleanxs xs c r = (cleanxs ~ CatMaybes xs, ArityTypeListIso c cleanxs r, Extrude (UrlChunks xs) (RootedPredTrie Text c) (RootedPredTrie Text r))
- handle :: (Monad m, Functor m, HasResult childContent (MiddlewareT m), HasResult err (e -> MiddlewareT m), Singleton (UrlChunks xs) childContent (RootedPredTrie Text resultContent), cleanxs ~ CatMaybes xs, ArityTypeListIso childContent cleanxs resultContent) => UrlChunks xs -> childContent -> HandlerT resultContent sec err (e, u, ue) m ()
- handleAction :: (Monad m, Functor m, HasResult childContent (ActionT ue u m ()), HasResult err (e -> ActionT ue u m ()), Singleton (UrlChunks xs) childContent (RootedPredTrie Text resultContent), cleanxs ~ CatMaybes xs, ArityTypeListIso childContent cleanxs resultContent) => UrlChunks xs -> childContent -> HandlerT resultContent sec err (e, u, ue) m ()
- here :: (Monad m, Functor m, HasResult content (MiddlewareT m), HasResult err (e -> MiddlewareT m)) => content -> HandlerT content sec err (e, u, ue) m ()
- hereAction :: (Monad m, Functor m, HasResult content (ActionT ue u m ()), HasResult err (e -> ActionT ue u m ())) => content -> HandlerT content sec err (e, u, ue) m ()
- handleAny :: (Monad m, Functor m, HasResult content (MiddlewareT m), HasResult err (e -> MiddlewareT m)) => content -> HandlerT content sec err (e, u, ue) m ()
- handleAnyAction :: (Monad m, Functor m, HasResult content (ActionT ue u m ()), HasResult err (e -> ActionT ue u m ())) => content -> HandlerT content sec err (e, u, ue) m ()
- parent :: (Monad m, Functor m, cleanxs ~ CatMaybes xs, ExtrudeSoundly cleanxs xs childContent resultContent, ExtrudeSoundly cleanxs xs childSec resultSec, ExtrudeSoundly cleanxs xs childErr resultErr) => UrlChunks xs -> HandlerT childContent childSec childErr aux m () -> HandlerT resultContent resultSec resultErr aux m ()
- auth :: (Monad m, Functor m) => sec -> err -> AuthScope -> HandlerT content (sec, AuthScope) err aux m ()
- notFound :: (Monad m, Functor m, HasResult content (MiddlewareT m), HasResult err (e -> MiddlewareT m)) => content -> HandlerT content sec err (e, u, ue) m ()
- notFoundAction :: (Monad m, Functor m, HasResult content (ActionT ue u m ()), HasResult err (e -> ActionT ue u m ())) => content -> HandlerT content sec err (e, u, ue) m ()
- action :: MonadIO m => ActionT e u m () -> MiddlewareT m
- route :: (Functor m, Monad m, MonadIO m) => HandlerT (MiddlewareT m) sec err aux m () -> MiddlewareT m
- routeAuth :: (Functor m, Monad m, MonadIO m) => (Request -> [sec] -> m (Response -> Response, Maybe e)) -> RoutableT sec e u ue m () -> MiddlewareT m
- routeAction :: (Functor m, Monad m, MonadIO m) => RoutableActionT sec e u ue m () -> MiddlewareT m
- routeActionAuth :: (Functor m, Monad m, MonadIO m) => (Request -> [sec] -> m (Response -> Response, Maybe e)) -> RoutableActionT sec e u ue m () -> MiddlewareT m
- extractContent :: (Functor m, Monad m, MonadIO m) => HandlerT (MiddlewareT m) sec err aux m a -> MiddlewareT m
- extractNotFound :: (Functor m, Monad m, MonadIO m) => HandlerT (MiddlewareT m) sec err aux m a -> MiddlewareT m
- extractAuthSym :: (Functor m, Monad m) => HandlerT x (sec, AuthScope) err aux m a -> Request -> m [sec]
- extractAuth :: (Functor m, Monad m, MonadIO m) => (Request -> [sec] -> m (Response -> Response, Maybe e)) -> HandlerT x (sec, AuthScope) (e -> MiddlewareT m) aux m a -> MiddlewareT m
- extractNearestVia :: (Functor m, Monad m, MonadIO m) => (HandlerT (MiddlewareT m) sec err aux m a -> m (RootedPredTrie Text (MiddlewareT m))) -> HandlerT (MiddlewareT m) sec err aux m a -> MiddlewareT m
- actionToMiddleware :: MonadIO m => RoutableActionT sec e u ue m () -> RoutableT sec e u ue m ()
Types
type Tries x s e = (RootedPredTrie Text x, RootedPredTrie Text x, RootedPredTrie Text s, RootedPredTrie Text e) Source
newtype HandlerT x sec err aux m a Source
HandlerT | |
|
Monad m => MonadState (Tries x sec err) (HandlerT k x sec err aux m) Source | |
MonadTrans (HandlerT k x sec err aux) Source | |
Monad m => Monad (HandlerT k x sec err aux m) Source | |
Functor m => Functor (HandlerT k x sec err aux m) Source | |
Monad m => Applicative (HandlerT k x sec err aux m) Source | |
MonadIO m => MonadIO (HandlerT k x sec err aux m) Source |
execHandlerT :: Monad m => HandlerT x sec err aux m a -> m (Tries x sec err) Source
type RoutableT s e u ue m a = HandlerT (MiddlewareT m) (s, AuthScope) (e -> MiddlewareT m) (e, u, ue) m a Source
type RoutableActionT s e u ue m a = HandlerT (ActionT ue u m ()) (s, AuthScope) (e -> ActionT ue u m ()) (e, u, ue) m a Source
Designate the scope of security to the set of routes - either only the adjacent routes, or the adjacent and the parent container node (root node if not declared).
type ExtrudeSoundly cleanxs xs c r = (cleanxs ~ CatMaybes xs, ArityTypeListIso c cleanxs r, Extrude (UrlChunks xs) (RootedPredTrie Text c) (RootedPredTrie Text r)) Source
Combinators
handle :: (Monad m, Functor m, HasResult childContent (MiddlewareT m), HasResult err (e -> MiddlewareT m), Singleton (UrlChunks xs) childContent (RootedPredTrie Text resultContent), cleanxs ~ CatMaybes xs, ArityTypeListIso childContent cleanxs resultContent) => UrlChunks xs -> childContent -> HandlerT resultContent sec err (e, u, ue) m () Source
Embed a MiddlewareT
into a set of routes.
handleAction :: (Monad m, Functor m, HasResult childContent (ActionT ue u m ()), HasResult err (e -> ActionT ue u m ()), Singleton (UrlChunks xs) childContent (RootedPredTrie Text resultContent), cleanxs ~ CatMaybes xs, ArityTypeListIso childContent cleanxs resultContent) => UrlChunks xs -> childContent -> HandlerT resultContent sec err (e, u, ue) m () Source
Embed an ActionT
into a set of routes directly, without first converting
it to a MiddlewareT
.
here :: (Monad m, Functor m, HasResult content (MiddlewareT m), HasResult err (e -> MiddlewareT m)) => content -> HandlerT content sec err (e, u, ue) m () Source
Create a handle for the present route - an alias for h -> handle o (Just h)
.
hereAction :: (Monad m, Functor m, HasResult content (ActionT ue u m ()), HasResult err (e -> ActionT ue u m ())) => content -> HandlerT content sec err (e, u, ue) m () Source
handleAny :: (Monad m, Functor m, HasResult content (MiddlewareT m), HasResult err (e -> MiddlewareT m)) => content -> HandlerT content sec err (e, u, ue) m () Source
Match against any route, as a last resort against all failing handle
s.
handleAnyAction :: (Monad m, Functor m, HasResult content (ActionT ue u m ()), HasResult err (e -> ActionT ue u m ())) => content -> HandlerT content sec err (e, u, ue) m () Source
parent :: (Monad m, Functor m, cleanxs ~ CatMaybes xs, ExtrudeSoundly cleanxs xs childContent resultContent, ExtrudeSoundly cleanxs xs childSec resultSec, ExtrudeSoundly cleanxs xs childErr resultErr) => UrlChunks xs -> HandlerT childContent childSec childErr aux m () -> HandlerT resultContent resultSec resultErr aux m () Source
Prepend a path to an existing set of routes.
auth :: (Monad m, Functor m) => sec -> err -> AuthScope -> HandlerT content (sec, AuthScope) err aux m () Source
Sets the security role and error handler for a set of routes, optionally including its parent route.
notFound :: (Monad m, Functor m, HasResult content (MiddlewareT m), HasResult err (e -> MiddlewareT m)) => content -> HandlerT content sec err (e, u, ue) m () Source
Embed a MiddlewareT
as a not-found handler into a set of routes.
notFoundAction :: (Monad m, Functor m, HasResult content (ActionT ue u m ()), HasResult err (e -> ActionT ue u m ())) => content -> HandlerT content sec err (e, u, ue) m () Source
Embed an ActionT
as a not-found handler into a set of routes, without first converting
it to a MiddlewareT
.
action :: MonadIO m => ActionT e u m () -> MiddlewareT m Source
Turn an ActionT
into a MiddlewareT
- could be used to make middleware-based
route sets cooperate with the content-type and verb combinators.
Routing
:: (Functor m, Monad m, MonadIO m) | |
=> HandlerT (MiddlewareT m) sec err aux m () | Assembled |
-> MiddlewareT m |
Turns a HandlerT
containing MiddlewareT
s into a MiddlewareT
.
:: (Functor m, Monad m, MonadIO m) | |
=> (Request -> [sec] -> m (Response -> Response, Maybe e)) | authorize |
-> RoutableT sec e u ue m () | Assembled |
-> MiddlewareT m |
Given a security verification function that returns a method to updating the session,
turn a set of routes containing MiddlewareT
s into a MiddlewareT
, where a session
is secured before responding.
routeAction :: (Functor m, Monad m, MonadIO m) => RoutableActionT sec e u ue m () -> MiddlewareT m Source
Exactly like route
, except specialized to route sets that contain ActionT
s -
essentially fmap
ing action
to each element.
:: (Functor m, Monad m, MonadIO m) | |
=> (Request -> [sec] -> m (Response -> Response, Maybe e)) | authorize |
-> RoutableActionT sec e u ue m () | Assembled |
-> MiddlewareT m |
Exactly like routeAuth
, but specialized for ActionT
.
Extraction
extractContent :: (Functor m, Monad m, MonadIO m) => HandlerT (MiddlewareT m) sec err aux m a -> MiddlewareT m Source
Extracts only the normal handle
(content) routes into
a MiddlewareT
, disregarding security and not-found responses.
extractNotFound :: (Functor m, Monad m, MonadIO m) => HandlerT (MiddlewareT m) sec err aux m a -> MiddlewareT m Source
Extracts only the notFound
responses into a MiddlewareT
.
extractAuthSym :: (Functor m, Monad m) => HandlerT x (sec, AuthScope) err aux m a -> Request -> m [sec] Source
Find the security tokens / authorization roles affiliated with a request for a set of routes.
extractAuth :: (Functor m, Monad m, MonadIO m) => (Request -> [sec] -> m (Response -> Response, Maybe e)) -> HandlerT x (sec, AuthScope) (e -> MiddlewareT m) aux m a -> MiddlewareT m Source
Extracts only the security handling logic into a MiddlewareT
.
extractNearestVia :: (Functor m, Monad m, MonadIO m) => (HandlerT (MiddlewareT m) sec err aux m a -> m (RootedPredTrie Text (MiddlewareT m))) -> HandlerT (MiddlewareT m) sec err aux m a -> MiddlewareT m Source
Given a way to draw out a special-purpose trie from our route set, route to the responses based on a furthest-reached method.
actionToMiddleware :: MonadIO m => RoutableActionT sec e u ue m () -> RoutableT sec e u ue m () Source
Turns a HandlerT
containing ActionT
s into a HandlerT
containing MiddlewareT
s.