{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
module Network.HTTP.Client.Conduit
(
withResponse
, responseOpen
, responseClose
, acquireResponse
, defaultManagerSettings
, newManager
, newManagerSettings
, module Network.HTTP.Client
, httpLbs
, httpNoBody
, requestBodySource
, requestBodySourceChunked
, bodyReaderSource
) where
import Control.Monad (unless)
import Control.Monad.IO.Unlift (MonadIO, liftIO, MonadUnliftIO, withRunInIO)
import Control.Monad.Reader (MonadReader (..))
import Data.Acquire (Acquire, mkAcquire, with)
import Data.ByteString (ByteString)
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import Data.Conduit (ConduitM, ($$+),
await, yield, ($$++))
import Data.Int (Int64)
import Data.IORef (newIORef, readIORef, writeIORef)
import Network.HTTP.Client hiding (closeManager,
defaultManagerSettings, httpLbs,
newManager, responseClose,
responseOpen,
withResponse, BodyReader, brRead, brConsume, httpNoBody)
import qualified Network.HTTP.Client as H
import Network.HTTP.Client.TLS (tlsManagerSettings)
withResponse :: (MonadUnliftIO m, MonadIO n, MonadReader env m, HasHttpManager env)
=> Request
-> (Response (ConduitM i ByteString n ()) -> m a)
-> m a
withResponse req f = do
env <- ask
withRunInIO $ \run -> with (acquireResponse req env) (run . f)
acquireResponse :: (MonadIO n, MonadReader env m, HasHttpManager env)
=> Request
-> m (Acquire (Response (ConduitM i ByteString n ())))
acquireResponse req = do
env <- ask
let man = getHttpManager env
return $ do
res <- mkAcquire (H.responseOpen req man) H.responseClose
return $ fmap bodyReaderSource res
defaultManagerSettings :: ManagerSettings
defaultManagerSettings = tlsManagerSettings
newManager :: MonadIO m => m Manager
newManager = newManagerSettings defaultManagerSettings
newManagerSettings :: MonadIO m => ManagerSettings -> m Manager
newManagerSettings = liftIO . H.newManager
responseOpen :: (MonadIO m, MonadIO n, MonadReader env m, HasHttpManager env)
=> Request
-> m (Response (ConduitM i ByteString n ()))
responseOpen req = do
env <- ask
liftIO $ fmap bodyReaderSource `fmap` H.responseOpen req (getHttpManager env)
responseClose :: MonadIO m => Response body -> m ()
responseClose = liftIO . H.responseClose
bodyReaderSource :: MonadIO m
=> H.BodyReader
-> ConduitM i ByteString m ()
bodyReaderSource br =
loop
where
loop = do
bs <- liftIO $ H.brRead br
unless (S.null bs) $ do
yield bs
loop
requestBodySource :: Int64 -> ConduitM () ByteString IO () -> RequestBody
requestBodySource size = RequestBodyStream size . srcToPopperIO
requestBodySourceChunked :: ConduitM () ByteString IO () -> RequestBody
requestBodySourceChunked = RequestBodyStreamChunked . srcToPopperIO
srcToPopperIO :: ConduitM () ByteString IO () -> GivesPopper ()
srcToPopperIO src f = do
(rsrc0, ()) <- src $$+ return ()
irsrc <- newIORef rsrc0
let popper :: IO ByteString
popper = do
rsrc <- readIORef irsrc
(rsrc', mres) <- rsrc $$++ await
writeIORef irsrc rsrc'
case mres of
Nothing -> return S.empty
Just bs
| S.null bs -> popper
| otherwise -> return bs
f popper
httpLbs :: (MonadIO m, HasHttpManager env, MonadReader env m)
=> Request
-> m (Response L.ByteString)
httpLbs req = do
env <- ask
let man = getHttpManager env
liftIO $ H.httpLbs req man
httpNoBody :: (MonadIO m, HasHttpManager env, MonadReader env m)
=> Request
-> m (Response ())
httpNoBody req = do
env <- ask
let man = getHttpManager env
liftIO $ H.httpNoBody req man