module Control.Monad.Http (
MonadHttp(..),
HttpT(..),
runHttpT
) where
import qualified Control.Monad.Catch as Catch
import qualified Control.Monad.Except as Except
import qualified Control.Monad.Reader as Reader
import qualified Control.Monad.Trans as Trans
import qualified Data.ByteString.Lazy as LBS
import qualified Network.HTTP.Simple as HTTPSimple
import qualified Network.HTTP.Client as HTTP
import qualified Network.HTTP.Types as HTTP
class Monad m => MonadHttp m where
performRequest :: HTTP.Request -> m (HTTP.Response LBS.ByteString)
instance MonadHttp IO where
performRequest = HTTPSimple.httpLbs
instance Catch.MonadThrow m => MonadHttp (HttpT m) where
performRequest request = check
where
check = do
response <- Reader.ask
let status = HTTP.responseStatus response
if status >= HTTP.ok200 && status < HTTP.multipleChoices300
then return response
else
let
badResponse = response { HTTP.responseBody = () }
body = LBS.toStrict . HTTP.responseBody $ response
in
Catch.throwM $ HTTP.HttpExceptionRequest request (HTTP.StatusCodeException badResponse body)
instance Trans.MonadIO m => MonadHttp (Except.ExceptT e m) where
performRequest = HTTPSimple.httpLbs
newtype HttpT m a = HttpT { unHttpT :: Reader.ReaderT (HTTP.Response LBS.ByteString) m a }
deriving (Functor, Applicative, Monad, Trans.MonadTrans, Catch.MonadThrow, Catch.MonadCatch, Trans.MonadIO, Reader.MonadReader (HTTP.Response LBS.ByteString))
runHttpT ::
HttpT m a
-> HTTP.Response LBS.ByteString
-> m a
runHttpT = Reader.runReaderT . unHttpT
instance Except.MonadError e m => Except.MonadError e (HttpT m) where
throwError = Trans.lift . Except.throwError
catchError m f = HttpT . Reader.ReaderT $ \r -> Except.catchError
(runHttpT m r)
(\e -> runHttpT (f e) r)