req-conduit 0.2.1 → 1.0.0
raw patch · 6 files changed
+80/−70 lines, 6 filesdep ~basedep ~bytestringdep ~conduit
Dependency ranges changed: base, bytestring, conduit, conduit-extra, http-client, req, resourcet, temporary
Files
- CHANGELOG.md +7/−1
- Network/HTTP/Req/Conduit.hs +46/−32
- README.md +3/−3
- httpbin-tests/Network/HTTP/Req/ConduitSpec.hs +7/−12
- req-conduit.cabal +13/−13
- weigh-bench/Main.hs +4/−9
CHANGELOG.md view
@@ -1,3 +1,9 @@+## Req Conduit 1.0.0++* This version is to be used with Req 1.0.0 and later.++* Dropped support for GHC 7.8.+ ## Req Conduit 0.2.1 * Fixed weigh benchmark.@@ -6,7 +12,7 @@ ## Req Conduit 0.2.0 -* This version is to be used with Req 0.3.0.+* This version is to be used with Req 0.3.0 and later. * Removed `req'` as it's now in Req itself.
Network/HTTP/Req/Conduit.hs view
@@ -11,11 +11,12 @@ -- Conduit helpers for streaming big request bodies. -- -- The package re-uses some pieces of code from the @http-conduit@ package,--- but not to the extent that depending on that package is reasonable.+-- but not to the extent that depending on that package becomes reasonable. -{-# LANGUAGE CPP #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-} #if __GLASGOW_HASKELL__ < 710 {-# LANGUAGE ConstraintKinds #-}@@ -26,12 +27,11 @@ ReqBodySource (..) -- * Streaming response bodies -- $streaming-response- , httpSource )+ , responseBodySource ) where import Control.Monad import Control.Monad.IO.Class (MonadIO (..))-import Control.Monad.Trans.Resource (MonadResource (..)) import Data.ByteString (ByteString) import Data.Conduit (Source, ($$+), ($$++), await, yield) import Data.IORef@@ -60,24 +60,33 @@ -- $streaming-response ----- Streaming response is a bit tricky as acquiring and releasing a resource--- (initiating a connection and then closing it in our case) in the context--- of @conduit@ streaming requires working with the--- 'Control.Monad.Trans.Resource.ResourceT' monad transformer. This does not--- play well with the framework @req@ builds.+-- The easiest way to stream response of an HTTP request is to use the+-- 'reqBr' function in conjunction with 'responseBodySource': ----- Essentially there are only two ways to make it work:+-- > {-# LANGUAGE OverloadedStrings #-}+-- >+-- > module Main (main) where+-- >+-- > import Data.Conduit ((.|), runConduitRes)+-- > import Data.Default.Class+-- > import Network.HTTP.Req+-- > import Network.HTTP.Req.Conduit+-- > import qualified Data.Conduit.Binary as CB+-- >+-- > main :: IO ()+-- > main = runReq def $ do+-- > let size = 100000 :: Int+-- > reqBr GET (https "httpbin.org" /: "bytes" /~ size) NoReqBody mempty $ \r ->+-- > runConduitRes $+-- > responseBodySource r .| CB.sinkFile "my-file.bin" ----- * Require that every 'MonadHttp' must be an instance of--- 'MonadResource'. This obviously makes the @req@ package harder to--- work with and less user-friendly. Not to mention that most of the--- time the instance won't be necessary.--- * Use the 'withReqManager' in combination with 'ReturnRequest'--- response interpretation to get both 'L.Manager' and 'L.Request' and--- then delegate the work to a custom callback.+-- This solution benefits from the fact that Req still handles all the+-- details like handling of exceptions and retrying for us. However this+-- approach is only viable when the entire pipeline can be run in 'IO' monad+-- (in the function that is the last argument of 'reqBr'). ----- We go with the second option. Here is an example of how to stream 100000--- bytes and save them to a file:+-- If you need to use a more complex monad, you'll need to deal with the+-- lower-level function 'req'': -- -- > {-# LANGUAGE FlexibleInstances #-} -- > {-# LANGUAGE OverloadedStrings #-}@@ -87,10 +96,11 @@ -- > import Control.Exception (throwIO) -- > import Control.Monad.IO.Class (MonadIO (..)) -- > import Control.Monad.Trans.Resource (ResourceT)--- > import Data.Conduit ((=$=), runConduitRes, ConduitM)+-- > import Data.Conduit -- > import Network.HTTP.Req -- > import Network.HTTP.Req.Conduit -- > import qualified Data.Conduit.Binary as CB+-- > import qualified Network.HTTP.Client as L -- > -- > instance MonadHttp (ConduitM i o (ResourceT IO)) where -- > handleHttpException = liftIO . throwIO@@ -98,19 +108,23 @@ -- > main :: IO () -- > main = runConduitRes $ do -- > let size = 100000 :: Int--- > req' GET (https "httpbin.org" /: "bytes" /~ size) NoReqBody mempty httpSource--- > =$= CB.sinkFile "my-favorite-file.bin"+-- > req' GET (https "httpbin.org" /: "bytes" /~ size) NoReqBody mempty+-- > (\request manager ->+-- > bracketP (L.responseOpen request manager) L.responseClose+-- > responseBodySource)+-- > .| CB.sinkFile "my-file.bin"+--+-- 'req'' does not open\/close connections, handle exceptions, and does not+-- perform retrying though, so you're on your own. --- | Perform an HTTP request and get the response as a 'C.Producer'.+-- | Turn @'L.Response' 'L.BodyReader'@ into a 'C.Producer'.+--+-- @since 1.0.0 -httpSource- :: MonadResource m- => L.Request -- ^ Pre-formed 'L.Request'- -> L.Manager -- ^ Manger to use+responseBodySource :: MonadIO m+ => L.Response L.BodyReader -- ^ Response with body reader -> C.Producer m ByteString -- ^ Response body as a 'C.Producer'-httpSource request manager =- C.bracketP (L.responseOpen request manager) L.responseClose- (bodyReaderSource . L.responseBody)+responseBodySource = bodyReaderSource . L.responseBody ---------------------------------------------------------------------------- -- Helpers
README.md view
@@ -7,9 +7,9 @@ [](https://travis-ci.org/mrkkrp/req-conduit) [](https://coveralls.io/github/mrkkrp/req-conduit?branch=master) -This library extends functionality of-the [`req`](https://hackage.haskell.org/package/req) package-with [`conduit`](https://hackage.haskell.org/package/conduit) helpers for+This library extends functionality of the+[`req`](https://hackage.haskell.org/package/req) package with+[`conduit`](https://hackage.haskell.org/package/conduit) helpers for streaming big request bodies in constant space. ## Contribution
httpbin-tests/Network/HTTP/Req/ConduitSpec.hs view
@@ -10,9 +10,7 @@ import Control.Exception (throwIO) import Control.Monad-import Control.Monad.IO.Class (MonadIO (..))-import Control.Monad.Trans.Resource (ResourceT)-import Data.Conduit ((=$=), runConduitRes, ConduitM)+import Data.Conduit ((.|), runConduitRes) import Data.Int (Int64) import Network.HTTP.Req import Network.HTTP.Req.Conduit@@ -42,21 +40,18 @@ it "works" $ do let tempi :: (Handle -> IO ()) -> IO () tempi f = withSystemTempFile "req-conduit" (const f)- tempi $ \h ->- runConduitRes $ do- let size :: Int- size = 10 * 1024 * 1024- req' GET (httpbin /: "stream-bytes" /~ size) NoReqBody- mempty httpSource =$= CB.sinkHandle h+ tempi $ \h -> do+ let size :: Int+ size = 10 * 1024 * 1024+ reqBr GET (httpbin /: "stream-bytes" /~ size) NoReqBody mempty $ \r ->+ runConduitRes $+ responseBodySource r .| CB.sinkHandle h ---------------------------------------------------------------------------- -- Instances instance MonadHttp IO where handleHttpException = throwIO--instance MonadHttp (ConduitM i o (ResourceT IO)) where- handleHttpException = liftIO . throwIO ---------------------------------------------------------------------------- -- Helpers
req-conduit.cabal view
@@ -1,7 +1,7 @@ name: req-conduit-version: 0.2.1+version: 1.0.0 cabal-version: >= 1.18-tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.1+tested-with: GHC==7.10.3, GHC==8.0.2, GHC==8.2.2 license: BSD3 license-file: LICENSE.md author: Mark Karpov <markkarpov92@gmail.com>, Michael Snoyman <michael@snoyman.com>@@ -25,11 +25,11 @@ default: False library- build-depends: base >= 4.7 && < 5.0+ build-depends: base >= 4.8 && < 5.0 , bytestring >= 0.2 && < 0.11- , conduit >= 0.5.5 && < 1.3+ , conduit >= 1.2.8 && < 1.3 , http-client >= 0.5 && < 0.6- , req >= 0.3 && < 0.4+ , req >= 1.0 && < 2.0 , resourcet >= 1.1 && < 1.2 , transformers >= 0.4 && < 0.6 exposed-modules: Network.HTTP.Req.Conduit@@ -44,12 +44,12 @@ other-modules: Network.HTTP.Req.ConduitSpec hs-source-dirs: httpbin-tests type: exitcode-stdio-1.0- build-depends: base >= 4.7 && < 5.0+ build-depends: base >= 4.8 && < 5.0 , bytestring >= 0.2 && < 0.11- , conduit >= 0.5.5 && < 1.3- , conduit-extra >= 1.1.10 && < 1.2+ , conduit >= 1.2.8 && < 1.3+ , conduit-extra >= 1.1.10 && < 1.3 , hspec >= 2.0 && < 3.0- , req >= 0.3 && < 0.4+ , req >= 1.0 && < 2.0 , req-conduit , resourcet >= 1.1 && < 1.2 , temporary >= 1.1 && < 1.3@@ -64,11 +64,11 @@ main-is: Main.hs hs-source-dirs: weigh-bench type: exitcode-stdio-1.0- build-depends: base >= 4.7 && < 5.0+ build-depends: base >= 4.8 && < 5.0 , bytestring >= 0.2 && < 0.11- , conduit >= 0.5.5 && < 1.3- , conduit-extra >= 1.1.10 && < 1.2- , req >= 0.3 && < 0.4+ , conduit >= 1.2.8 && < 1.3+ , conduit-extra >= 1.1.10 && < 1.3+ , req >= 1.0 && < 2.0 , req-conduit , resourcet >= 1.1 && < 1.2 , temporary >= 1.1 && < 1.3
weigh-bench/Main.hs view
@@ -7,9 +7,7 @@ import Control.Exception (throwIO) import Control.Monad-import Control.Monad.IO.Class (MonadIO (..))-import Control.Monad.Trans.Resource (ResourceT)-import Data.Conduit ((=$=), runConduitRes, ConduitM)+import Data.Conduit ((.|), runConduitRes) import Data.Int (Int64) import Network.HTTP.Req import Network.HTTP.Req.Conduit@@ -41,18 +39,15 @@ bigResponse :: Int -> IO () bigResponse size = withSystemTempFile "req-conduit" $ \_ h ->- runConduitRes $- req' GET (httpbin /: "stream-bytes" /~ size) NoReqBody- mempty httpSource =$= CB.sinkHandle h+ reqBr GET (httpbin /: "stream-bytes" /~ size) NoReqBody mempty $ \r ->+ runConduitRes $+ responseBodySource r .| CB.sinkHandle h ---------------------------------------------------------------------------- -- Instances instance MonadHttp IO where handleHttpException = throwIO--instance MonadHttp (ConduitM i o (ResourceT IO)) where- handleHttpException = liftIO . throwIO ---------------------------------------------------------------------------- -- Helpers