salvia-0.0.4: src/Network/Salvia/Core/Handler.hs
{-# LANGUAGE FlexibleInstances #-}
module Network.Salvia.Core.Handler (
SendAction
, SendQueue
, Context (..)
, makeContext
, Handler
, ResourceHandler
, UriHandler
, putRequest, putResponse, putQueue
, modRequest, modResponse, modQueue
, withRequest, withResponse
) where
import Control.Applicative (Applicative, pure, (<*>))
import Control.Monad.State (StateT, ap, modify, gets)
import Network.Socket (SockAddr)
import System.IO
import Network.Salvia.Core.Config
import Network.Protocol.Http (Message, emptyRequest, emptyResponse)
import Network.Protocol.Uri (URI)
-------- single request/response context --------------------------------------
type SendAction = Handle -> IO ()
type SendQueue = [SendAction]
data Context =
Context {
-- | The HTTP server configuration.
config :: HttpdConfig
-- | The HTTP request header.
, request :: Message
-- | The HTTP response header.
, response :: Message
-- | The socket handle for the connection with the client.
, sock :: Handle
-- | The client addres.
, address :: SockAddr
-- | The queue of send actions.
, queue :: SendQueue
}
-- Create and empty server context.
makeContext :: HttpdConfig -> SockAddr -> Handle -> Context
makeContext c a s = Context {
config = c
, request = emptyRequest
, response = emptyResponse -- 200 OK, by default.
, sock = s
, address = a
, queue = []
}
-------- request handlers -----------------------------------------------------
-- HTTP handler types.
type Handler a = StateT Context IO a
type ResourceHandler a = FilePath -> Handler a
type UriHandler a = URI -> Handler a
-- Make handlers applicative.
instance Applicative (StateT Context IO) where
pure = return
(<*>) = ap
-- Domain specific setters and modifiers.
putRequest :: Message -> Handler ()
putRequest r = modify (\m -> m { request = r })
modRequest :: (Message -> Message) -> Handler ()
modRequest f = gets request >>= putRequest . f
putResponse :: Message -> Handler ()
putResponse r = modify (\m -> m { response = r })
modResponse :: (Message -> Message) -> Handler ()
modResponse f = gets response >>= putResponse . f
putQueue :: SendQueue -> Handler ()
putQueue q = modify (\m -> m { queue = q })
modQueue :: (SendQueue -> SendQueue) -> Handler ()
modQueue f = gets queue >>= putQueue . f
-- Execute handlers in a modified environment, but restore the original state.
-- This is comparable to the `local' function from the Reader monad.
withModified :: (Monad m) => m d -> (t -> m a) -> (d -> m b) -> t -> m c -> m c
withModified get m put f h =
do { o <- get ; m f ; r <- h ; put o ; return r }
withRequest :: (Message -> Message) -> Handler a -> Handler a
withRequest = withModified (gets request) modRequest putRequest
withResponse :: (Message -> Message) -> Handler a -> Handler a
withResponse = withModified (gets response) modResponse putResponse