{-# LANGUAGE FlexibleInstances, PatternGuards, ScopedTypeVariables, TypeSynonymInstances #-}
module Happstack.Server.Routing
(
http
, https
, methodM
, methodOnly
, methodSP
, method
, MatchMethod(..)
, dir
, dirs
, nullDir
, trailingSlash
, noTrailingSlash
, anyPath
, path
, uriRest
, host
, withHost
, guardRq
) where
import Control.Monad (MonadPlus(mzero), unless)
import qualified Data.ByteString.Char8 as B
import Happstack.Server.Monads (ServerMonad(..))
import Happstack.Server.Types (Request(..), Method(..), FromReqURI(..), getHeader, rqURL)
import System.FilePath (makeRelative, splitDirectories)
class MatchMethod m where
matchMethod :: m -> Method -> Bool
instance MatchMethod Method where
matchMethod :: Method -> Method -> Bool
matchMethod Method
m = (Method -> Bool) -> Method -> Bool
forall m. MatchMethod m => m -> Method -> Bool
matchMethod (Method -> Method -> Bool
forall a. Eq a => a -> a -> Bool
== Method
m)
instance MatchMethod [Method] where
matchMethod :: [Method] -> Method -> Bool
matchMethod [Method]
ms Method
m = (Method -> Bool) -> [Method] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Method -> Method -> Bool
forall m. MatchMethod m => m -> Method -> Bool
`matchMethod` Method
m) [Method]
ms
instance MatchMethod (Method -> Bool) where
matchMethod :: (Method -> Bool) -> Method -> Bool
matchMethod Method -> Bool
f Method
HEAD = Method -> Bool
f Method
HEAD Bool -> Bool -> Bool
|| Method -> Bool
f Method
GET
matchMethod Method -> Bool
f Method
m = Method -> Bool
f Method
m
instance MatchMethod () where
matchMethod :: () -> Method -> Bool
matchMethod () Method
_ = Bool
True
guardRq :: (ServerMonad m, MonadPlus m) => (Request -> Bool) -> m ()
guardRq :: (Request -> Bool) -> m ()
guardRq Request -> Bool
f = do
Request
rq <- m Request
forall (m :: * -> *). ServerMonad m => m Request
askRq
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Request -> Bool
f Request
rq) m ()
forall (m :: * -> *) a. MonadPlus m => m a
mzero
http :: (ServerMonad m, MonadPlus m) => m ()
http :: m ()
http = (Request -> Bool) -> m ()
forall (m :: * -> *).
(ServerMonad m, MonadPlus m) =>
(Request -> Bool) -> m ()
guardRq (Bool -> Bool
not (Bool -> Bool) -> (Request -> Bool) -> Request -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> Bool
rqSecure)
https :: (ServerMonad m, MonadPlus m) => m ()
https :: m ()
https = (Request -> Bool) -> m ()
forall (m :: * -> *).
(ServerMonad m, MonadPlus m) =>
(Request -> Bool) -> m ()
guardRq Request -> Bool
rqSecure
method :: (ServerMonad m, MonadPlus m, MatchMethod method) => method -> m ()
method :: method -> m ()
method method
meth = (Request -> Bool) -> m ()
forall (m :: * -> *).
(ServerMonad m, MonadPlus m) =>
(Request -> Bool) -> m ()
guardRq ((Request -> Bool) -> m ()) -> (Request -> Bool) -> m ()
forall a b. (a -> b) -> a -> b
$ \Request
rq -> method -> Method -> Bool
forall m. MatchMethod m => m -> Method -> Bool
matchMethod method
meth (Request -> Method
rqMethod Request
rq)
methodM :: (ServerMonad m, MonadPlus m, MatchMethod method) => method -> m ()
methodM :: method -> m ()
methodM method
meth = method -> m ()
forall (m :: * -> *) method.
(ServerMonad m, MonadPlus m, MatchMethod method) =>
method -> m ()
methodOnly method
meth m () -> m () -> m ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> m ()
forall (m :: * -> *). (ServerMonad m, MonadPlus m) => m ()
nullDir
methodOnly :: (ServerMonad m, MonadPlus m, MatchMethod method) => method -> m ()
methodOnly :: method -> m ()
methodOnly = method -> m ()
forall (m :: * -> *) method.
(ServerMonad m, MonadPlus m, MatchMethod method) =>
method -> m ()
method
{-# DEPRECATED methodOnly "this function is just an alias for method now" #-}
{-# DEPRECATED methodSP "use method instead." #-}
methodSP :: (ServerMonad m, MonadPlus m, MatchMethod method) => method -> m b-> m b
methodSP :: method -> m b -> m b
methodSP method
m m b
handle = method -> m ()
forall (m :: * -> *) method.
(ServerMonad m, MonadPlus m, MatchMethod method) =>
method -> m ()
methodM method
m m () -> m b -> m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> m b
handle
nullDir :: (ServerMonad m, MonadPlus m) => m ()
nullDir :: m ()
nullDir = (Request -> Bool) -> m ()
forall (m :: * -> *).
(ServerMonad m, MonadPlus m) =>
(Request -> Bool) -> m ()
guardRq ((Request -> Bool) -> m ()) -> (Request -> Bool) -> m ()
forall a b. (a -> b) -> a -> b
$ \Request
rq -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (Request -> [String]
rqPaths Request
rq)
dir :: (ServerMonad m, MonadPlus m) => String -> m a -> m a
dir :: String -> m a -> m a
dir String
staticPath m a
handle =
do
Request
rq <- m Request
forall (m :: * -> *). ServerMonad m => m Request
askRq
case Request -> [String]
rqPaths Request
rq of
(String
p:[String]
xs) | String
p String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
staticPath -> (Request -> Request) -> m a -> m a
forall (m :: * -> *) a.
ServerMonad m =>
(Request -> Request) -> m a -> m a
localRq (\Request
newRq -> Request
newRq{rqPaths :: [String]
rqPaths = [String]
xs}) m a
handle
[String]
_ -> m a
forall (m :: * -> *) a. MonadPlus m => m a
mzero
dirs :: (ServerMonad m, MonadPlus m) => FilePath -> m a -> m a
dirs :: String -> m a -> m a
dirs String
fp m a
m =
do let parts :: [String]
parts = String -> [String]
splitDirectories (String -> String -> String
makeRelative String
"/" String
fp)
(String -> m a -> m a) -> m a -> [String] -> m a
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr String -> m a -> m a
forall (m :: * -> *) a.
(ServerMonad m, MonadPlus m) =>
String -> m a -> m a
dir m a
m [String]
parts
host :: (ServerMonad m, MonadPlus m) => String -> m a -> m a
host :: String -> m a -> m a
host String
desiredHost m a
handle =
do Request
rq <- m Request
forall (m :: * -> *). ServerMonad m => m Request
askRq
case String -> Request -> Maybe ByteString
forall r. HasHeaders r => String -> r -> Maybe ByteString
getHeader String
"host" Request
rq of
(Just ByteString
hostBS) | String
desiredHost String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString -> String
B.unpack ByteString
hostBS -> m a
handle
Maybe ByteString
_ -> m a
forall (m :: * -> *) a. MonadPlus m => m a
mzero
withHost :: (ServerMonad m, MonadPlus m) => (String -> m a) -> m a
withHost :: (String -> m a) -> m a
withHost String -> m a
handle =
do Request
rq <- m Request
forall (m :: * -> *). ServerMonad m => m Request
askRq
case String -> Request -> Maybe ByteString
forall r. HasHeaders r => String -> r -> Maybe ByteString
getHeader String
"host" Request
rq of
(Just ByteString
hostBS) -> String -> m a
handle (ByteString -> String
B.unpack ByteString
hostBS)
Maybe ByteString
_ -> m a
forall (m :: * -> *) a. MonadPlus m => m a
mzero
path :: (FromReqURI a, MonadPlus m, ServerMonad m) => (a -> m b) -> m b
path :: (a -> m b) -> m b
path a -> m b
handle = do
Request
rq <- m Request
forall (m :: * -> *). ServerMonad m => m Request
askRq
case Request -> [String]
rqPaths Request
rq of
(String
p:[String]
xs) | Just a
a <- String -> Maybe a
forall a. FromReqURI a => String -> Maybe a
fromReqURI String
p
-> (Request -> Request) -> m b -> m b
forall (m :: * -> *) a.
ServerMonad m =>
(Request -> Request) -> m a -> m a
localRq (\Request
newRq -> Request
newRq{rqPaths :: [String]
rqPaths = [String]
xs}) (a -> m b
handle a
a)
[String]
_ -> m b
forall (m :: * -> *) a. MonadPlus m => m a
mzero
uriRest :: (ServerMonad m) => (String -> m a) -> m a
uriRest :: (String -> m a) -> m a
uriRest String -> m a
handle = m Request
forall (m :: * -> *). ServerMonad m => m Request
askRq m Request -> (Request -> m a) -> m a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> m a
handle (String -> m a) -> (Request -> String) -> Request -> m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> String
rqURL
anyPath :: (ServerMonad m, MonadPlus m) => m r -> m r
anyPath :: m r -> m r
anyPath m r
x = (String -> m r) -> m r
forall a (m :: * -> *) b.
(FromReqURI a, MonadPlus m, ServerMonad m) =>
(a -> m b) -> m b
path ((String -> m r) -> m r) -> (String -> m r) -> m r
forall a b. (a -> b) -> a -> b
$ (\(String
_::String) -> m r
x)
trailingSlash :: (ServerMonad m, MonadPlus m) => m ()
trailingSlash :: m ()
trailingSlash = (Request -> Bool) -> m ()
forall (m :: * -> *).
(ServerMonad m, MonadPlus m) =>
(Request -> Bool) -> m ()
guardRq ((Request -> Bool) -> m ()) -> (Request -> Bool) -> m ()
forall a b. (a -> b) -> a -> b
$ \Request
rq -> (String -> Char
forall a. [a] -> a
last (Request -> String
rqUri Request
rq)) Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'/'
noTrailingSlash :: (ServerMonad m, MonadPlus m) => m ()
noTrailingSlash :: m ()
noTrailingSlash = (Request -> Bool) -> m ()
forall (m :: * -> *).
(ServerMonad m, MonadPlus m) =>
(Request -> Bool) -> m ()
guardRq ((Request -> Bool) -> m ()) -> (Request -> Bool) -> m ()
forall a b. (a -> b) -> a -> b
$ \Request
rq -> (String -> Char
forall a. [a] -> a
last (Request -> String
rqUri Request
rq)) Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'/'