salvia-1.0.0: src/Network/Salvia/Handler/Parser.hs
{-# LANGUAGE FlexibleContexts #-}
module Network.Salvia.Handler.Parser
( hRequestParser
, hResponseParser
, hParser
, readNonEmptyLines
)
where
import Control.Applicative
import Control.Monad.State hiding (sequence)
import Data.Traversable
import Network.Protocol.Http
import Network.Salvia.Interface
import Network.Salvia.Handler.Error
import Prelude hiding (sequence)
import System.IO
-- import System.Timeout
-- | Like the `hParser' but always parses `HTTP` `Requests`s.
hRequestParser
:: (HandleM m, RawHttpM Request m, HttpM Request m, MonadIO m)
=> Int -- ^ Timeout in milliseconds.
-> (String -> m a) -- ^ The fail handler.
-> m a -- ^ The success handler.
-> m (Maybe a)
hRequestParser = hParser pt parseRequest
where pt x = do request (put x)
rawRequest (put x)
-- | Like the `hParser' but always parses `HTTP` `Response`s.
hResponseParser
:: (HandleM m, RawHttpM Response m, HttpM Response m, MonadIO m)
=> Int -- ^ Timeout in milliseconds.
-> (String -> m a) -- ^ The fail handler.
-> m a -- ^ The success handler.
-> m (Maybe a)
hResponseParser = hParser pt parseResponse
where pt x = do response (put x)
rawResponse (put x)
{- |
The 'hParser' handler is used to parse the raw `HTTP` message into the
'Message' data type. This handler is generally used as (one of) the first
handlers in a client or server environment. The first handler argument is
executed when the message is invalid, possibly due to parser errors, and is
parametrized with the error string. The second handler argument is executed
when the message is valid. When the message could not be parsed within the time
specified with the first argument the function silently returns.
-}
hParser
:: (HandleM m, MonadIO m)
=> (Http d -> m b) -- ^ What to do with message.
-> (String -> Either String (Http d)) -- ^ Custom message parser.
-> Int -- ^ Timeout in milliseconds.
-> (String -> m a) -- ^ The fail handler.
-> m a -- ^ The success handler.
-> m (Maybe a)
hParser action parse _ onfail onsuccess =
do h <- handle
mmsg <-
liftM join
. flip catchIO Nothing
. fmap Just
-- . timeout (t * 1000)
$ do hSetBuffering h (BlockBuffering (Just (64*1024)))
Just <$> readNonEmptyLines h
let hndl = (onfail . show) `either` (\x -> action x >> onsuccess)
sequence (hndl . parse <$> mmsg)
-- Read all lines until the first empty line.
readNonEmptyLines :: Handle -> IO String
readNonEmptyLines h = ($"") <$> f
where
f = do l <- hGetLine h
let lf = showChar '\n'
if null l || l == "\r"
then return lf
else ((showString l . lf) .) <$> f