{-# 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