packages feed

hexpress-0.1.0.0: src/Network/Hexpress/Server.hs

module Network.Hexpress.Server
( addCustomHeader
, setMimeType
, sendString
, sendJSON
, sendJSONLiteral
, end
, debugLog
, staticFile
, staticFileCached
, staticDir
, run
, runEnv
, runSettings
) where

import Network.Hexpress.Types
import Network.Hexpress.Request
import Data.CaseInsensitive as CI
import Network.HTTP.Types.Status
import Network.HTTP.Types.Header
import Data.ByteString.Lazy.Char8 as LB
import Data.ByteString.Char8 as SB
import Control.Monad.IO.Class
import Control.Monad.Trans.Maybe
import Data.Aeson as Aeson
import Data.Text as TXT
import Network.HTTP.Types.URI
import Network.Mime
import qualified Network.Wai.Handler.Warp as WAI
import System.FilePath
import Data.Maybe
import Control.Exception (catch, IOException)

addCustomHeader :: (SB.ByteString, SB.ByteString) -> Server ()
addCustomHeader (name, contents) = addHeader (CI.mk name, contents)

setMimeType :: SB.ByteString -> Server ()
setMimeType mtype = do
  addHeader (hContentType, mtype)

sendJSONLiteral :: Aeson.Value -> Server ()
sendJSONLiteral = sendJSON

sendJSON :: ToJSON a => a -> Server ()
sendJSON obj = do
  setMimeType (SB.pack "application/json")
  setStatus status200
  sendByteString (Aeson.encode obj)

sendString :: String -> Server ()
sendString str = do
  sendByteString $ LB.pack str

end :: Server a
end = MaybeT (return Nothing)

debugLog :: String -> Server ()
debugLog str = liftIO $ Prelude.putStrLn str

stringToPath :: String -> [TXT.Text]
stringToPath str = decodePathSegments $ SB.pack str

handleFileError :: IOException -> IO (Maybe a)
handleFileError _ = return Nothing

staticFile :: String -> SB.ByteString -> Server ()
staticFile fname mime = do
  setMimeType mime
  setStatus status200
  contents <- liftIO $ catch (LB.readFile fname >>= (\cont -> return (Just cont))) handleFileError
  case contents of Nothing       -> notFound ()
                   Just contents -> sendByteString contents

staticFileCached :: String -> SB.ByteString -> IO (Server ())
staticFileCached fname mime = do
  contents <- LB.readFile fname
  let serv = addHeader (hContentType, mime) >>
             setStatus status200 >>
             sendByteString contents
  return serv

getfname :: [TXT.Text] -> [TXT.Text] -> Maybe [TXT.Text]
getfname [] path = Just path
getfname (x:xs) [] = Nothing
getfname (x:xs) (y:ys)
  | xs == [] && x == TXT.empty = Just (y:ys)
  | x == y = getfname xs ys
  | otherwise = Nothing

staticDir :: String -> String -> Server ()
staticDir prefix location = staticDirHelper where
  prefixPath = stringToPath prefix
  locationPath = stringToPath location
  staticDirHelper = do
    path <- getPath
    let localname = getfname prefixPath path
    if localname == Nothing then notFound ()
    else do
      let fname = joinPath $ Prelude.map TXT.unpack (locationPath ++ fromJust localname)
      let mtype = defaultMimeLookup (TXT.pack fname)
      staticFile fname mtype

notFound :: a -> Server b
notFound _ = do
  setStatus status404
  end

run :: Int -> Server () -> IO ()
run port srv = do
  app <- serverToApp srv
  WAI.run port app

runEnv :: Int -> Server () -> IO ()
runEnv port srv = do
  app <- serverToApp srv
  WAI.runEnv port app

runSettings :: WAI.Settings -> Server () -> IO ()
runSettings settings srv = do
  app <- serverToApp srv
  WAI.runSettings settings app