snap-0.3.0: src/Snap/Extension/Server.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-|
This module provides replacements for the 'httpServe' and 'quickHttpServe'
functions exported by 'Snap.Http.Server'. By taking a 'Initializer' as an
argument, these functions simplify the glue code that is needed to use Snap
Extensions.
-}
module Snap.Extension.Server
( ConfigExtend
, httpServe
, quickHttpServe
, defaultConfig
, getReloadHandler
, setReloadHandler
, module Snap.Http.Server.Config
) where
import Control.Exception (SomeException)
import Control.Monad
import Control.Monad.CatchIO
import Data.ByteString (ByteString)
import Data.Maybe
import Data.Monoid
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Prelude hiding (catch)
import Snap.Extension
import Snap.Http.Server (simpleHttpServe)
import qualified Snap.Http.Server.Config as C
import Snap.Http.Server.Config hiding ( defaultConfig
, completeConfig
, getOther
, setOther
)
import Snap.Util.GZip
import Snap.Types
import System.IO
------------------------------------------------------------------------------
-- | 'ConfigExtend' is similar to the 'Config' exported by 'Snap.Http.Server',
-- but is augmented with a @reloadHandler@ field which can be accessed using
-- 'getReloadHandler' and 'setReloadHandler'.
type ConfigExtend s = Config
(SnapExtend s) (IO [(ByteString, Maybe ByteString)] -> SnapExtend s ())
------------------------------------------------------------------------------
getReloadHandler :: ConfigExtend s -> Maybe
(IO [(ByteString, Maybe ByteString)] -> SnapExtend s ())
getReloadHandler = C.getOther
------------------------------------------------------------------------------
setReloadHandler :: (IO [(ByteString, Maybe ByteString)] -> SnapExtend s ())
-> ConfigExtend s
-> ConfigExtend s
setReloadHandler = C.setOther
------------------------------------------------------------------------------
-- | These are the default values for all the fields in 'ConfigExtend'.
--
-- > hostname = "localhost"
-- > address = "0.0.0.0"
-- > port = 8000
-- > accessLog = "log/access.log"
-- > errorLog = "log/error.log"
-- > locale = "en_US"
-- > compression = True
-- > verbose = True
-- > errorHandler = prints the error message
-- > reloadHandler = prints the result of each reload handler (error/success)
--
defaultConfig :: ConfigExtend s
defaultConfig = setReloadHandler handler C.defaultConfig
where
handler = path "admin/reload" . defaultReloadHandler
------------------------------------------------------------------------------
-- | Completes a partial 'Config' by filling in the unspecified values with
-- the default values from 'defaultConfig'.
completeConfig :: ConfigExtend s -> ConfigExtend s
completeConfig c = case getListen c' of
[] -> addListen (ListenHttp "0.0.0.0" 8000) c'
_ -> c'
where c' = mappend defaultConfig c
------------------------------------------------------------------------------
-- | Starts serving HTTP requests using the given handler, with settings from
-- the 'ConfigExtend' passed in. This function never returns; to shut down
-- the HTTP server, kill the controlling thread.
httpServe :: ConfigExtend s
-- ^ Any configuration options which override the defaults
-> Initializer s
-- ^ The 'Initializer' function for the application's monad
-> SnapExtend s ()
-- ^ The application to be served
-> IO ()
httpServe config initializer handler = do
(snap, cleanup) <- runInitializerWithReloadAction
verbose
initializer
(catch500 handler)
reloader
let site = compress $ snap
mapM_ printListen $ C.getListen config
_ <- try $ serve $ site :: IO (Either SomeException ())
putStr "\n"
cleanup
output "Shutting down..."
where
conf = completeConfig config
verbose = fromJust $ getVerbose conf
output = when verbose . hPutStrLn stderr
reloader = fromJust $ getReloadHandler conf
compress = if fromJust $ getCompression conf then withCompression else id
catch500 = flip catch $ fromJust $ getErrorHandler conf
serve = simpleHttpServe config
listenToString (C.ListenHttp host port) =
concat ["http://", fromUTF8 host, ":", show port, "/"]
listenToString (C.ListenHttps host port _ _) =
concat ["https://", fromUTF8 host, ":", show port, "/"]
printListen l = output $ "Listening on " ++ listenToString l
------------------------------------------------------------------------------
-- | Starts serving HTTP using the given handler. The configuration is read
-- from the options given on the command-line, as returned by
-- 'commandLineConfig'.
quickHttpServe :: Initializer s
-- ^ The 'Initializer' function for the application's monad
-> SnapExtend s ()
-- ^ The application to be served
-> IO ()
quickHttpServe r m = commandLineConfig emptyConfig >>= \c -> httpServe c r m
------------------------------------------------------------------------------
fromUTF8 :: ByteString -> String
fromUTF8 = T.unpack . T.decodeUtf8