http-conduit 2.2.4 → 2.3.0
raw patch · 6 files changed
+249/−274 lines, 6 filesdep +unliftiodep +unliftio-coredep +voiddep −exceptionsdep −lifted-basedep −monad-controldep ~conduitdep ~conduit-extradep ~resourcetPVP ok
version bump matches the API change (PVP)
Dependencies added: unliftio, unliftio-core, void
Dependencies removed: exceptions, lifted-base, monad-control
Dependency ranges changed: conduit, conduit-extra, resourcet
API changes (from Hackage documentation)
- Network.HTTP.Client.Conduit: withManager :: MonadIO m => (ReaderT Manager m a) -> m a
- Network.HTTP.Client.Conduit: withManagerSettings :: MonadIO m => ManagerSettings -> (ReaderT Manager m a) -> m a
- Network.HTTP.Conduit: conduitManagerSettings :: ManagerSettings
- Network.HTTP.Conduit: withManager :: (MonadIO m, MonadBaseControl IO m) => (Manager -> ResourceT m a) -> m a
- Network.HTTP.Conduit: withManagerSettings :: (MonadIO m, MonadBaseControl IO m) => ManagerSettings -> (Manager -> ResourceT m a) -> m a
- Network.HTTP.Client.Conduit: bodyReaderSource :: MonadIO m => BodyReader -> Producer m ByteString
+ Network.HTTP.Client.Conduit: bodyReaderSource :: MonadIO m => BodyReader -> ConduitM i ByteString m ()
- Network.HTTP.Client.Conduit: requestBodySource :: Int64 -> Source IO ByteString -> RequestBody
+ Network.HTTP.Client.Conduit: requestBodySource :: Int64 -> ConduitM () ByteString IO () -> RequestBody
- Network.HTTP.Client.Conduit: requestBodySourceChunked :: Source IO ByteString -> RequestBody
+ Network.HTTP.Client.Conduit: requestBodySourceChunked :: ConduitM () ByteString IO () -> RequestBody
- Network.HTTP.Client.Conduit: withResponse :: (MonadBaseControl IO m, MonadIO n, MonadReader env m, HasHttpManager env) => Request -> (Response (ConduitM i ByteString n ()) -> m a) -> m a
+ Network.HTTP.Client.Conduit: withResponse :: (MonadUnliftIO m, MonadIO n, MonadReader env m, HasHttpManager env) => Request -> (Response (ConduitM i ByteString n ()) -> m a) -> m a
- Network.HTTP.Conduit: http :: MonadResource m => Request -> Manager -> m (Response (ResumableSource m ByteString))
+ Network.HTTP.Conduit: http :: MonadResource m => Request -> Manager -> m (Response (ConduitM i ByteString m ()))
- Network.HTTP.Conduit: lbsResponse :: Monad m => Response (ResumableSource m ByteString) -> m (Response ByteString)
+ Network.HTTP.Conduit: lbsResponse :: Monad m => Response (ConduitM () ByteString m ()) -> m (Response ByteString)
- Network.HTTP.Conduit: requestBodySource :: Int64 -> Source (ResourceT IO) ByteString -> RequestBody
+ Network.HTTP.Conduit: requestBodySource :: Int64 -> ConduitM () ByteString (ResourceT IO) () -> RequestBody
- Network.HTTP.Conduit: requestBodySourceChunked :: Source (ResourceT IO) ByteString -> RequestBody
+ Network.HTTP.Conduit: requestBodySourceChunked :: ConduitM () ByteString (ResourceT IO) () -> RequestBody
- Network.HTTP.Conduit: requestBodySourceChunkedIO :: Source IO ByteString -> RequestBody
+ Network.HTTP.Conduit: requestBodySourceChunkedIO :: ConduitM () ByteString IO () -> RequestBody
- Network.HTTP.Conduit: requestBodySourceIO :: Int64 -> Source IO ByteString -> RequestBody
+ Network.HTTP.Conduit: requestBodySourceIO :: Int64 -> ConduitM () ByteString IO () -> RequestBody
- Network.HTTP.Conduit: responseBody :: Response body -> body
+ Network.HTTP.Conduit: responseBody :: () => Response body -> body
- Network.HTTP.Conduit: responseCookieJar :: Response body -> CookieJar
+ Network.HTTP.Conduit: responseCookieJar :: () => Response body -> CookieJar
- Network.HTTP.Conduit: responseHeaders :: Response body -> ResponseHeaders
+ Network.HTTP.Conduit: responseHeaders :: () => Response body -> ResponseHeaders
- Network.HTTP.Conduit: responseStatus :: Response body -> Status
+ Network.HTTP.Conduit: responseStatus :: () => Response body -> Status
- Network.HTTP.Conduit: responseVersion :: Response body -> HttpVersion
+ Network.HTTP.Conduit: responseVersion :: () => Response body -> HttpVersion
- Network.HTTP.Simple: httpSink :: (MonadIO m, MonadMask m) => Request -> (Response () -> Sink ByteString m a) -> m a
+ Network.HTTP.Simple: httpSink :: MonadUnliftIO m => Request -> (Response () -> ConduitM ByteString Void m a) -> m a
- Network.HTTP.Simple: setRequestBodySource :: Int64 -> Source IO ByteString -> Request -> Request
+ Network.HTTP.Simple: setRequestBodySource :: Int64 -> ConduitM () ByteString IO () -> Request -> Request
- Network.HTTP.Simple: withResponse :: (MonadIO m, MonadMask m, MonadIO n) => Request -> (Response (ConduitM i ByteString n ()) -> m a) -> m a
+ Network.HTTP.Simple: withResponse :: (MonadUnliftIO m, MonadIO n) => Request -> (Response (ConduitM i ByteString n ()) -> m a) -> m a
Files
- ChangeLog.md +11/−0
- Network/HTTP/Client/Conduit.hs +12/−27
- Network/HTTP/Conduit.hs +14/−41
- Network/HTTP/Simple.hs +30/−24
- http-conduit.cabal +9/−8
- test/main.hs +173/−174
ChangeLog.md view
@@ -1,3 +1,14 @@+## 2.3.0++* conduit 1.3 support+ * NOTE: Even for older versions of conduit, this includes dropping+ support for finalizers+* `http` returns a `Source` instead of a `ResumableSource` (due to lack of+ finalizers)+* Drop monad-control for unliftio+* Removed some deprecated functions: `withManager`, `withManagerSettings`,+ `conduitManagerSettings`+ ## 2.2.4 * Add `httpBS` to `Network.HTTP.Simple`
Network/HTTP/Client/Conduit.hs view
@@ -19,8 +19,6 @@ -- * Manager helpers , defaultManagerSettings , newManager- , withManager- , withManagerSettings , newManagerSettings -- * General HTTP client interface , module Network.HTTP.Client@@ -33,21 +31,20 @@ ) where import Control.Monad (unless)-import Control.Monad.IO.Class (MonadIO, liftIO)-import Control.Monad.Reader (MonadReader (..), ReaderT (..))-import Control.Monad.Trans.Control (MonadBaseControl)+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, Producer, Source,- await, yield, ($$+), ($$++))+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, withManager,+ responseOpen, withResponse, BodyReader, brRead, brConsume, httpNoBody) import qualified Network.HTTP.Client as H import Network.HTTP.Client.TLS (tlsManagerSettings)@@ -56,18 +53,18 @@ -- -- * Response body is represented as a @Producer@. ----- * Generalized to any instance of @MonadBaseControl@, not just @IO@.+-- * Generalized to any instance of @MonadUnliftIO@, not just @IO@. -- -- * The @Manager@ is contained by a @MonadReader@ context. -- -- Since 2.1.0-withResponse :: (MonadBaseControl IO m, MonadIO n, MonadReader env m, HasHttpManager env)+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- with (acquireResponse req env) f+ withRunInIO $ \run -> with (acquireResponse req env) (run . f) -- | An @Acquire@ for getting a @Response@. --@@ -100,18 +97,6 @@ newManagerSettings :: MonadIO m => ManagerSettings -> m Manager newManagerSettings = liftIO . H.newManager --- | Get a new manager with 'defaultManagerSettings' and construct a @ReaderT@ containing it.------ Since 2.1.0-withManager :: MonadIO m => (ReaderT Manager m a) -> m a-withManager = withManagerSettings defaultManagerSettings---- | Get a new manager with the given settings and construct a @ReaderT@ containing it.------ Since 2.1.0-withManagerSettings :: MonadIO m => ManagerSettings -> (ReaderT Manager m a) -> m a-withManagerSettings settings (ReaderT inner) = newManagerSettings settings >>= inner- -- | Conduit-powered version of 'H.responseOpen'. -- -- See 'withResponse' for the differences with 'H.responseOpen'.@@ -132,7 +117,7 @@ bodyReaderSource :: MonadIO m => H.BodyReader- -> Producer m ByteString+ -> ConduitM i ByteString m () bodyReaderSource br = loop where@@ -142,13 +127,13 @@ yield bs loop -requestBodySource :: Int64 -> Source IO ByteString -> RequestBody+requestBodySource :: Int64 -> ConduitM () ByteString IO () -> RequestBody requestBodySource size = RequestBodyStream size . srcToPopperIO -requestBodySourceChunked :: Source IO ByteString -> RequestBody+requestBodySourceChunked :: ConduitM () ByteString IO () -> RequestBody requestBodySourceChunked = RequestBodyStreamChunked . srcToPopperIO -srcToPopperIO :: Source IO ByteString -> GivesPopper ()+srcToPopperIO :: ConduitM () ByteString IO () -> GivesPopper () srcToPopperIO src f = do (rsrc0, ()) <- src $$+ return () irsrc <- newIORef rsrc0
Network/HTTP/Conduit.hs view
@@ -181,11 +181,8 @@ , Manager , newManager , closeManager- , withManager- , withManagerSettings -- ** Settings , ManagerSettings- , conduitManagerSettings , tlsManagerSettings , mkManagerSettings , managerConnCount@@ -225,13 +222,12 @@ import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L-import Data.Conduit (ResumableSource, ($$+-), await, ($$++), ($$+), Source, addCleanup)-import qualified Data.Conduit.Internal as CI+import Data.Conduit import qualified Data.Conduit.List as CL import Data.IORef (readIORef, writeIORef, newIORef) import Data.Int (Int64) import Control.Applicative as A ((<$>))-import Control.Monad.IO.Class (MonadIO (liftIO))+import Control.Monad.IO.Unlift (MonadIO (liftIO)) import Control.Monad.Trans.Resource import qualified Network.HTTP.Client as Client (httpLbs, responseOpen, responseClose)@@ -301,31 +297,14 @@ req <- liftIO $ parseUrlThrow url responseBody A.<$> httpLbs (setConnectionClose req) man -conduitManagerSettings :: ManagerSettings-conduitManagerSettings = tlsManagerSettings-{-# DEPRECATED conduitManagerSettings "Use tlsManagerSettings" #-}--withManager :: (MonadIO m, MonadBaseControl IO m)- => (Manager -> ResourceT m a)- -> m a-withManager = withManagerSettings tlsManagerSettings-{-# DEPRECATED withManager "Please use newManager tlsManagerSettings" #-}--withManagerSettings :: (MonadIO m, MonadBaseControl IO m)- => ManagerSettings- -> (Manager -> ResourceT m a)- -> m a-withManagerSettings set f = liftIO (newManager set) >>= runResourceT . f-{-# DEPRECATED withManagerSettings "Please use newManager" #-}- setConnectionClose :: Request -> Request setConnectionClose req = req{requestHeaders = ("Connection", "close") : requestHeaders req} lbsResponse :: Monad m- => Response (ResumableSource m S.ByteString)+ => Response (ConduitM () S.ByteString m ()) -> m (Response L.ByteString) lbsResponse res = do- bss <- responseBody res $$+- CL.consume+ bss <- runConduit $ responseBody res .| CL.consume return res { responseBody = L.fromChunks bss }@@ -333,27 +312,21 @@ http :: MonadResource m => Request -> Manager- -> m (Response (ResumableSource m S.ByteString))+ -> m (Response (ConduitM i S.ByteString m ())) http req man = do (key, res) <- allocate (Client.responseOpen req man) Client.responseClose-#if MIN_VERSION_conduit(1, 2, 0)- let rsrc = CI.ResumableSource- (flip CI.unConduitM CI.Done $ addCleanup (const $ release key) $ HCC.bodyReaderSource $ responseBody res)- (release key)-#else- let rsrc = CI.ResumableSource- (addCleanup (const $ release key) $ HCC.bodyReaderSource $ responseBody res)- (release key)-#endif- return res { responseBody = rsrc }+ return res { responseBody = do+ HCC.bodyReaderSource $ responseBody res+ release key+ } -requestBodySource :: Int64 -> Source (ResourceT IO) S.ByteString -> RequestBody+requestBodySource :: Int64 -> ConduitM () S.ByteString (ResourceT IO) () -> RequestBody requestBodySource size = RequestBodyStream size . srcToPopper -requestBodySourceChunked :: Source (ResourceT IO) S.ByteString -> RequestBody+requestBodySourceChunked :: ConduitM () S.ByteString (ResourceT IO) () -> RequestBody requestBodySourceChunked = RequestBodyStreamChunked . srcToPopper -srcToPopper :: Source (ResourceT IO) S.ByteString -> HCC.GivesPopper ()+srcToPopper :: ConduitM () S.ByteString (ResourceT IO) () -> HCC.GivesPopper () srcToPopper src f = runResourceT $ do (rsrc0, ()) <- src $$+ return () irsrc <- liftIO $ newIORef rsrc0@@ -370,8 +343,8 @@ | otherwise -> return bs liftIO $ f popper -requestBodySourceIO :: Int64 -> Source IO S.ByteString -> RequestBody+requestBodySourceIO :: Int64 -> ConduitM () S.ByteString IO () -> RequestBody requestBodySourceIO = HCC.requestBodySource -requestBodySourceChunkedIO :: Source IO S.ByteString -> RequestBody+requestBodySourceChunkedIO :: ConduitM () S.ByteString IO () -> RequestBody requestBodySourceChunkedIO = HCC.requestBodySourceChunked
Network/HTTP/Simple.hs view
@@ -79,7 +79,7 @@ import qualified Network.HTTP.Client.TLS as H import Network.HTTP.Client.Conduit (bodyReaderSource) import qualified Network.HTTP.Client.Conduit as HC-import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.IO.Unlift (MonadIO, liftIO, MonadUnliftIO, withRunInIO) import Data.Aeson (FromJSON (..), Value) import Data.Aeson.Parser (json') import qualified Data.Aeson.Types as A@@ -88,11 +88,13 @@ import Control.Exception (throwIO, Exception) import Data.Typeable (Typeable) import qualified Data.Conduit as C+import Data.Conduit (runConduit, (.|), ConduitM) import qualified Data.Conduit.Attoparsec as C-import qualified Control.Monad.Catch as Catch import qualified Network.HTTP.Types as H import Data.Int (Int64) import Control.Monad.Trans.Resource (MonadResource)+import qualified Control.Exception as E (bracket)+import Data.Void (Void) -- | Perform an HTTP request and return the body as a @ByteString@. --@@ -161,17 +163,19 @@ -- | Perform an HTTP request and consume the body with the given 'C.Sink' -- -- @since 2.1.10-httpSink :: (MonadIO m, Catch.MonadMask m)+httpSink :: MonadUnliftIO m => H.Request- -> (H.Response () -> C.Sink S.ByteString m a)+ -> (H.Response () -> ConduitM S.ByteString Void m a) -> m a-httpSink req sink = do- man <- liftIO H.getGlobalManager- Catch.bracket- (liftIO $ H.responseOpen req man)- (liftIO . H.responseClose)- (\res -> bodyReaderSource (getResponseBody res)- C.$$ sink (fmap (const ()) res))+httpSink req sink = withRunInIO $ \run -> do+ man <- H.getGlobalManager+ E.bracket+ (H.responseOpen req man)+ H.responseClose+ $ \res -> run+ $ runConduit+ $ bodyReaderSource (getResponseBody res)+ .| sink (fmap (const ()) res) -- | Perform an HTTP request, and get the response body as a Source. --@@ -221,16 +225,16 @@ -- value. -- -- @since 2.2.3-withResponse :: (MonadIO m, Catch.MonadMask m, MonadIO n)+withResponse :: (MonadUnliftIO m, MonadIO n) => H.Request -> (H.Response (C.ConduitM i S.ByteString n ()) -> m a) -> m a-withResponse req withRes = do- man <- liftIO H.getGlobalManager- Catch.bracket- (liftIO (H.responseOpen req man))- (liftIO . H.responseClose)- (withRes . fmap bodyReaderSource)+withResponse req withRes = withRunInIO $ \run -> do+ man <- H.getGlobalManager+ E.bracket+ (H.responseOpen req man)+ H.responseClose+ (run . withRes . fmap bodyReaderSource) -- | Alternate spelling of 'httpLBS' --@@ -294,7 +298,11 @@ ++ (map (name, ) vals) } --- | Set the request headers, wiping out any previously set headers+-- | Set the request headers, wiping out __all__ previously set headers. This+-- means if you use 'setRequestHeaders' to set some headers and also use one of+-- the other setters that modifies the @content-type@ header (such as+-- 'setRequestBodyJSON'), be sure that 'setRequestHeaders' is evaluated+-- __first__. -- -- @since 2.1.10 setRequestHeaders :: [(H.HeaderName, S.ByteString)] -> H.Request -> H.Request@@ -355,7 +363,7 @@ -- -- @since 2.1.10 setRequestBodySource :: Int64 -- ^ length of source- -> C.Source IO S.ByteString+ -> ConduitM () S.ByteString IO () -> H.Request -> H.Request setRequestBodySource len src req = req { H.requestBody = HC.requestBodySource len src }@@ -371,10 +379,8 @@ -- | Set the request body as URL encoded data ----- /Note/: This will not modify the request method. For that, please use--- 'requestMethod'. You likely don't want the default of @GET@.------ This also sets the @content-type@ to @application/x-www-form-urlencoded@+-- /Note/: This will change the request method to @POST@ and set the @content-type@+-- to @application/x-www-form-urlencoded@ -- -- @since 2.1.10 setRequestBodyURLEncoded :: [(S.ByteString, S.ByteString)] -> H.Request -> H.Request
http-conduit.cabal view
@@ -1,5 +1,5 @@ name: http-conduit-version: 2.2.4+version: 2.3.0 license: BSD3 license-file: LICENSE author: Michael Snoyman <michael@snoyman.com>@@ -25,16 +25,17 @@ , aeson >= 0.8 , bytestring >= 0.9.1.4 , transformers >= 0.2- , resourcet >= 1.1 && < 1.2- , conduit >= 0.5.5 && < 1.3- , conduit-extra >= 1.1.5+ , resourcet >= 1.1+ , conduit >= 1.2+ , conduit-extra >= 1.1 , http-types >= 0.7- , lifted-base >= 0.1 , http-client >= 0.5 && < 0.6 , http-client-tls >= 0.3 && < 0.4- , monad-control , mtl- , exceptions >= 0.6+ , unliftio-core++ if !impl(ghc>=7.9)+ build-depends: void >= 0.5.5 exposed-modules: Network.HTTP.Conduit Network.HTTP.Client.Conduit Network.HTTP.Simple@@ -62,7 +63,7 @@ , conduit >= 1.1 , utf8-string , case-insensitive- , lifted-base+ , unliftio , network , wai >= 3.0 && < 3.3 , warp >= 3.0.0.2 && < 3.3
test/main.hs view
@@ -12,15 +12,15 @@ import System.IO.Temp (withSystemTempFile) import qualified Network.Wai as Wai import Network.Wai.Handler.Warp (runSettings, defaultSettings, setPort, setBeforeMainLoop, Settings, setTimeout)-import Network.HTTP.Conduit hiding (port, withManager, withManagerSettings)+import Network.HTTP.Conduit hiding (port) import qualified Network.HTTP.Conduit as NHC import Network.HTTP.Client.MultipartFormData import Control.Concurrent (forkIO, killThread, putMVar, takeMVar, newEmptyMVar, threadDelay) import Network.HTTP.Types-import Control.Exception.Lifted (try, SomeException, bracket, onException, IOException)+import UnliftIO.Exception (try, SomeException, bracket, onException, IOException) import qualified Data.IORef as I import qualified Control.Exception as E (catch)-import Network.Socket (sClose)+import qualified Network.Socket as NS import qualified Network.BSD import CookieTest (cookieTest) #if MIN_VERSION_conduit(1,1,0)@@ -32,7 +32,7 @@ #endif import qualified Data.Conduit.Network import System.IO.Unsafe (unsafePerformIO)-import Data.Conduit (($$), ($$+-), yield, Flush (Chunk, Flush), await)+import Data.Conduit ((.|), yield, Flush (Chunk, Flush), await, runConduit) import Control.Monad (void, forever) import Control.Monad.IO.Class (liftIO) import Data.ByteString.UTF8 (fromString)@@ -53,14 +53,7 @@ import qualified Data.Aeson as A import qualified Network.HTTP.Simple as Simple import Data.Monoid (mempty)-import Control.Monad.Trans.Resource (ResourceT, runResourceT)---- I'm too lazy to rewrite code below-withManager :: (Manager -> ResourceT IO a) -> IO a-withManager = withManagerSettings tlsManagerSettings--withManagerSettings :: ManagerSettings -> (Manager -> ResourceT IO a) -> IO a-withManagerSettings set f = newManager set >>= (runResourceT . f)+import Control.Monad.Trans.Resource (runResourceT) past :: UTCTime past = UTCTime (ModifiedJulianDay 56200) (secondsToDiffTime 0)@@ -122,7 +115,7 @@ case esocket of Left (_ :: IOException) -> getPort Right socket -> do- sClose socket+ NS.close socket return port withApp :: (Wai.Request -> IO Wai.Response) -> (Int -> IO ()) -> IO ()@@ -187,42 +180,42 @@ describe "httpLbs" $ do it "preserves 'set-cookie' headers" $ withApp app $ \port -> do request <- parseUrlThrow $ concat ["http://127.0.0.1:", show port, "/cookies"]- withManager $ \manager -> do- response <- httpLbs request manager- let setCookie = mk (fromString "Set-Cookie")- (setCookieHeaders, _) = partition ((== setCookie) . fst) (NHC.responseHeaders response)- liftIO $ assertBool "response contains a 'set-cookie' header" $ length setCookieHeaders > 0+ manager <- newManager tlsManagerSettings+ response <- httpLbs request manager+ let setCookie = mk (fromString "Set-Cookie")+ (setCookieHeaders, _) = partition ((== setCookie) . fst) (NHC.responseHeaders response)+ assertBool "response contains a 'set-cookie' header" $ length setCookieHeaders > 0 it "redirects set cookies" $ withApp app $ \port -> do request <- parseUrlThrow $ concat ["http://127.0.0.1:", show port, "/cookie_redir1"]- withManager $ \manager -> do- response <- httpLbs request manager- liftIO $ (responseBody response) @?= "nom-nom-nom"+ manager <- newManager tlsManagerSettings+ response <- httpLbs request manager+ (responseBody response) @?= "nom-nom-nom" it "user-defined cookie jar works" $ withApp app $ \port -> do request <- parseUrlThrow $ concat ["http://127.0.0.1:", show port, "/dump_cookies"]- withManager $ \manager -> do- response <- httpLbs (request {redirectCount = 1, cookieJar = Just cookie_jar}) manager- liftIO $ (responseBody response) @?= "key=value"+ manager <- newManager tlsManagerSettings+ response <- httpLbs (request {redirectCount = 1, cookieJar = Just cookie_jar}) manager+ (responseBody response) @?= "key=value" it "user-defined cookie jar is not ignored when redirection is disabled" $ withApp app $ \port -> do request <- parseUrlThrow $ concat ["http://127.0.0.1:", show port, "/dump_cookies"]- withManager $ \manager -> do- response <- httpLbs (request {redirectCount = 0, cookieJar = Just cookie_jar}) manager- liftIO $ (responseBody response) @?= "key=value"+ manager <- newManager tlsManagerSettings+ response <- httpLbs (request {redirectCount = 0, cookieJar = Just cookie_jar}) manager+ (responseBody response) @?= "key=value" it "cookie jar is available in response" $ withApp app $ \port -> do request <- parseUrlThrow $ concat ["http://127.0.0.1:", show port, "/cookies"]- withManager $ \manager -> do- response <- httpLbs (request {cookieJar = Just Data.Monoid.mempty}) manager- liftIO $ (length $ destroyCookieJar $ responseCookieJar response) @?= 1+ manager <- newManager tlsManagerSettings+ response <- httpLbs (request {cookieJar = Just Data.Monoid.mempty}) manager+ (length $ destroyCookieJar $ responseCookieJar response) @?= 1 it "Cookie header isn't touched when no cookie jar supplied" $ withApp app $ \port -> do request <- parseUrlThrow $ concat ["http://127.0.0.1:", show port, "/dump_cookies"]- withManager $ \manager -> do- let request_headers = (mk "Cookie", "key2=value2") : filter ((/= mk "Cookie") . fst) (NHC.requestHeaders request)- response <- httpLbs (request {NHC.requestHeaders = request_headers, cookieJar = Nothing}) manager- liftIO $ (responseBody response) @?= "key2=value2"+ manager <- newManager tlsManagerSettings+ let request_headers = (mk "Cookie", "key2=value2") : filter ((/= mk "Cookie") . fst) (NHC.requestHeaders request)+ response <- httpLbs (request {NHC.requestHeaders = request_headers, cookieJar = Nothing}) manager+ (responseBody response) @?= "key2=value2" it "Response cookie jar is nothing when request cookie jar is nothing" $ withApp app $ \port -> do request <- parseUrlThrow $ concat ["http://127.0.0.1:", show port, "/cookies"]- withManager $ \manager -> do- response <- httpLbs (request {cookieJar = Nothing}) manager- liftIO $ (responseCookieJar response) @?= mempty+ manager <- newManager tlsManagerSettings+ response <- httpLbs (request {cookieJar = Nothing}) manager+ (responseCookieJar response) @?= mempty it "TLS" $ withAppTls app $ \port -> do request <- parseUrlThrow $ "https://127.0.0.1:" ++ show port let set = mkManagerSettings@@ -230,14 +223,16 @@ { settingDisableCertificateValidation = True } Nothing- response <- withManagerSettings set $ httpLbs request+ manager <- newManager set+ response <- httpLbs request manager responseBody response @?= "homepage" describe "manager" $ do it "closes all connections" $ withApp app $ \port1 -> withApp app $ \port2 -> do --FIXME clearSocketsList- withManager $ \manager -> do- let Just req1 = parseUrlThrow $ "http://127.0.0.1:" ++ show port1- let Just req2 = parseUrlThrow $ "http://127.0.0.1:" ++ show port2+ manager <- newManager tlsManagerSettings+ let Just req1 = parseUrlThrow $ "http://127.0.0.1:" ++ show port1+ let Just req2 = parseUrlThrow $ "http://127.0.0.1:" ++ show port2+ runResourceT $ do _res1a <- http req1 manager _res1b <- http req1 manager _res2 <- http req2 manager@@ -245,120 +240,122 @@ --FIXME requireAllSocketsClosed describe "http" $ do it "response body" $ withApp app $ \port -> do- withManager $ \manager -> do- req <- liftIO $ parseUrlThrow $ "http://127.0.0.1:" ++ show port+ manager <- newManager tlsManagerSettings+ req <- parseUrlThrow $ "http://127.0.0.1:" ++ show port+ runResourceT $ do res1 <- http req manager- bss <- responseBody res1 $$+- CL.consume+ bss <- runConduit $ responseBody res1 .| CL.consume res2 <- httpLbs req manager liftIO $ L.fromChunks bss `shouldBe` responseBody res2 describe "DOS protection" $ do it "overlong headers" $ overLongHeaders $ \port -> do- withManager $ \manager -> do- let Just req1 = parseUrlThrow $ "http://127.0.0.1:" ++ show port- res1 <- try $ http req1 manager- case res1 of- Left e -> liftIO $ show (e :: SomeException) @?= show (HttpExceptionRequest req1 OverlongHeaders)- _ -> error "Shouldn't have worked"+ manager <- newManager tlsManagerSettings+ let Just req1 = parseUrlThrow $ "http://127.0.0.1:" ++ show port+ res1 <- try $ runResourceT $ http req1 manager+ case res1 of+ Left e -> show (e :: SomeException) @?= show (HttpExceptionRequest req1 OverlongHeaders)+ _ -> error "Shouldn't have worked" it "not overlong headers" $ notOverLongHeaders $ \port -> do- withManager $ \manager -> do- let Just req1 = parseUrlThrow $ "http://127.0.0.1:" ++ show port- _ <- httpLbs req1 manager- return ()+ manager <- newManager tlsManagerSettings+ let Just req1 = parseUrlThrow $ "http://127.0.0.1:" ++ show port+ _ <- httpLbs req1 manager+ return () describe "redirects" $ do it "doesn't double escape" $ redir $ \port -> do- withManager $ \manager -> do- let go (encoded, final) = do- let Just req1 = parseUrlThrow $ concat ["http://127.0.0.1:", show port, "/redir/", encoded]- res <- httpLbs req1 manager- liftIO $ Network.HTTP.Conduit.responseStatus res @?= status200- liftIO $ responseBody res @?= L.fromChunks [TE.encodeUtf8 final]- mapM_ go- [ ("hello world%2F", "hello world/")- , ("%D7%A9%D7%9C%D7%95%D7%9D", "שלום")- , ("simple", "simple")- , ("hello%20world", "hello world")- , ("hello%20world%3f%23", "hello world?#")- ]+ manager <- newManager tlsManagerSettings+ let go (encoded, final) = do+ let Just req1 = parseUrlThrow $ concat ["http://127.0.0.1:", show port, "/redir/", encoded]+ res <- httpLbs req1 manager+ liftIO $ Network.HTTP.Conduit.responseStatus res @?= status200+ liftIO $ responseBody res @?= L.fromChunks [TE.encodeUtf8 final]+ mapM_ go+ [ ("hello world%2F", "hello world/")+ , ("%D7%A9%D7%9C%D7%95%D7%9D", "שלום")+ , ("simple", "simple")+ , ("hello%20world", "hello world")+ , ("hello%20world%3f%23", "hello world?#")+ ] it "TooManyRedirects: redirect request body is preserved" $ withApp app $ \port -> do let Just req = parseUrlThrow $ concat ["http://127.0.0.1:", show port, "/infredir/0"] let go (res, i) = liftIO $ responseBody res @?= (L8.pack $ show i)- E.catch (withManager $ \manager -> do- void $ http req{redirectCount=5} manager) $ \e ->+ manager <- newManager tlsManagerSettings+ E.catch (void $ runResourceT $ http req{redirectCount=5} manager)+ $ \e -> case e of HttpExceptionRequest _ (TooManyRedirects redirs) -> mapM_ go (zip redirs [5,4..0 :: Int]) _ -> error $ show e describe "chunked request body" $ do it "works" $ echo $ \port -> do- withManager $ \manager -> do- let go bss = do- let Just req1 = parseUrlThrow $ "POST http://127.0.0.1:" ++ show port- src = sourceList bss- lbs = L.fromChunks bss- res <- httpLbs req1- { requestBody = requestBodySourceChunked src- } manager- liftIO $ Network.HTTP.Conduit.responseStatus res @?= status200- let ts = S.concat . L.toChunks- liftIO $ ts (responseBody res) @?= ts lbs- mapM_ go- [ ["hello", "world"]- , replicate 500 "foo\003\n\r"- ]+ manager <- newManager tlsManagerSettings+ let go bss = do+ let Just req1 = parseUrlThrow $ "POST http://127.0.0.1:" ++ show port+ src = sourceList bss+ lbs = L.fromChunks bss+ res <- httpLbs req1+ { requestBody = requestBodySourceChunked src+ } manager+ liftIO $ Network.HTTP.Conduit.responseStatus res @?= status200+ let ts = S.concat . L.toChunks+ liftIO $ ts (responseBody res) @?= ts lbs+ mapM_ go+ [ ["hello", "world"]+ , replicate 500 "foo\003\n\r"+ ] describe "no status message" $ do it "works" $ noStatusMessage $ \port -> do req <- parseUrlThrow $ "http://127.0.0.1:" ++ show port- withManager $ \manager -> do- res <- httpLbs req manager- liftIO $ do- Network.HTTP.Conduit.responseStatus res `shouldBe` status200- responseBody res `shouldBe` "foo"+ manager <- newManager tlsManagerSettings+ res <- httpLbs req manager+ liftIO $ do+ Network.HTTP.Conduit.responseStatus res `shouldBe` status200+ responseBody res `shouldBe` "foo" describe "response body too short" $ do it "throws an exception" $ wrongLength $ \port -> do req <- parseUrlThrow $ "http://127.0.0.1:" ++ show port- withManager $ \manager -> do- eres <- try $ httpLbs req manager- liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres- `shouldBe` Left (show $ HttpExceptionRequest req $ ResponseBodyTooShort 50 18)+ manager <- newManager tlsManagerSettings+ eres <- try $ httpLbs req manager+ liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres+ `shouldBe` Left (show $ HttpExceptionRequest req $ ResponseBodyTooShort 50 18) describe "chunked response body" $ do it "no chunk terminator" $ wrongLengthChunk1 $ \port -> do req <- parseUrlThrow $ "http://127.0.0.1:" ++ show port- withManager $ \manager -> do- eres <- try $ httpLbs req manager- liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres- `shouldBe` Left (show (HttpExceptionRequest req IncompleteHeaders))+ manager <- newManager tlsManagerSettings+ eres <- try $ httpLbs req manager+ liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres+ `shouldBe` Left (show (HttpExceptionRequest req IncompleteHeaders)) it "incomplete chunk" $ wrongLengthChunk2 $ \port -> do req <- parseUrlThrow $ "http://127.0.0.1:" ++ show port- withManager $ \manager -> do- eres <- try $ httpLbs req manager- liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres- `shouldBe` Left (show (HttpExceptionRequest req InvalidChunkHeaders))+ manager <- newManager tlsManagerSettings+ eres <- try $ httpLbs req manager+ liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres+ `shouldBe` Left (show (HttpExceptionRequest req InvalidChunkHeaders)) it "invalid chunk" $ invalidChunk $ \port -> do req <- parseUrlThrow $ "http://127.0.0.1:" ++ show port- withManager $ \manager -> do- eres <- try $ httpLbs req manager- liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres- `shouldBe` Left (show (HttpExceptionRequest req InvalidChunkHeaders))+ manager <- newManager tlsManagerSettings+ eres <- try $ httpLbs req manager+ liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres+ `shouldBe` Left (show (HttpExceptionRequest req InvalidChunkHeaders)) it "missing header" $ rawApp "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nabcd\r\n\r\n\r\n" $ \port -> do req <- parseUrlThrow $ "http://127.0.0.1:" ++ show port- withManager $ \manager -> do- eres <- try $ httpLbs req manager- liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres- `shouldBe` Left (show (HttpExceptionRequest req InvalidChunkHeaders))+ manager <- newManager tlsManagerSettings+ eres <- try $ httpLbs req manager+ liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres+ `shouldBe` Left (show (HttpExceptionRequest req InvalidChunkHeaders)) it "junk header" $ rawApp "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nabcd\r\njunk\r\n\r\n" $ \port -> do req <- parseUrlThrow $ "http://127.0.0.1:" ++ show port- withManager $ \manager -> do- eres <- try $ httpLbs req manager- liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres- `shouldBe` Left (show (HttpExceptionRequest req InvalidChunkHeaders))+ manager <- newManager tlsManagerSettings+ eres <- try $ httpLbs req manager+ liftIO $ either (Left . (show :: HttpException -> String)) (Right . id) eres+ `shouldBe` Left (show (HttpExceptionRequest req InvalidChunkHeaders)) describe "redirect" $ do it "ignores large response bodies" $ do@@ -366,7 +363,8 @@ case pathInfo req of ["foo"] -> return $ responseLBS status200 [] "Hello World!" _ -> return $ responseSource status301 [("location", S8.pack $ "http://127.0.0.1:" ++ show port ++ "/foo")] $ forever $ yield $ Chunk $ fromByteString "hello\n"- withApp' app' $ \port -> withManager $ \manager -> do+ manager <- newManager tlsManagerSettings+ withApp' app' $ \port -> do req <- liftIO $ parseUrlThrow $ "http://127.0.0.1:" ++ show port res <- httpLbs req manager liftIO $ do@@ -397,9 +395,10 @@ describe "HTTP/1.0" $ do it "BaseHTTP" $ do let baseHTTP app' = do- _ <- appSource app' $$ await- yield "HTTP/1.0 200 OK\r\n\r\nThis is it!" $$ appSink app'- withCApp baseHTTP $ \port -> withManager $ \manager -> do+ _ <- runConduit $ appSource app' .| await+ runConduit $ yield "HTTP/1.0 200 OK\r\n\r\nThis is it!" .| appSink app'+ manager <- newManager tlsManagerSettings+ withCApp baseHTTP $ \port -> do req <- liftIO $ parseUrlThrow $ "http://127.0.0.1:" ++ show port res1 <- httpLbs req manager res2 <- httpLbs req manager@@ -410,41 +409,42 @@ entry <- Network.BSD.getHostByName "127.0.0.1" req' <- parseUrlThrow $ "http://example.com:" ++ show port let req = req' { hostAddress = Just $ Network.BSD.hostAddress entry }- res <- withManager $ httpLbs req+ manager <- newManager tlsManagerSettings+ res <- httpLbs req manager responseBody res @?= "homepage for example.com" describe "managerResponseTimeout" $ do it "works" $ withApp app $ \port -> do req1 <- parseUrlThrow $ "http://localhost:" ++ show port let req2 = req1 { responseTimeout = responseTimeoutMicro 5000000 }- withManagerSettings tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro 1 } $ \man -> do- eres1 <- try $ httpLbs req1 { NHC.path = "/delayed" } man- case eres1 of- Left (HttpExceptionRequest _ ConnectionTimeout{}) -> return ()- _ -> error "Did not time out"- _ <- httpLbs req2 man- return ()+ man <- newManager tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro 1 }+ eres1 <- try $ httpLbs req1 { NHC.path = "/delayed" } man+ case eres1 of+ Left (HttpExceptionRequest _ ConnectionTimeout{}) -> return ()+ _ -> error "Did not time out"+ _ <- httpLbs req2 man+ return () describe "delayed body" $ do it "works" $ withApp app $ \port -> do req <- parseUrlThrow $ "http://localhost:" ++ show port ++ "/delayed"- withManager $ \man -> do- _ <- http req man- return ()+ man <- newManager tlsManagerSettings+ _ <- runResourceT $ http req man+ return () it "reuse/connection close tries again" $ do withAppSettings (setTimeout 1) (const app) $ \port -> do req <- parseUrlThrow $ "http://localhost:" ++ show port- withManager $ \man -> do- res1 <- httpLbs req man- liftIO $ threadDelay 3000000- res2 <- httpLbs req man- let f res = res- { NHC.responseHeaders = filter (not . isDate) (NHC.responseHeaders res)- }- isDate ("date", _) = True- isDate _ = False- liftIO $ f res2 `shouldBe` f res1+ man <- newManager tlsManagerSettings+ res1 <- httpLbs req man+ threadDelay 3000000+ res2 <- httpLbs req man+ let f res = res+ { NHC.responseHeaders = filter (not . isDate) (NHC.responseHeaders res)+ }+ isDate ("date", _) = True+ isDate _ = False+ f res2 `shouldBe` f res1 it "setQueryString" $ do ref <- I.newIORef undefined@@ -457,10 +457,9 @@ , (TE.encodeUtf8 "שלום", Just "hola") , ("noval", Nothing) ]- withManager $ \man -> do- req <- parseUrlThrow $ "http://localhost:" ++ show port- _ <- httpLbs (setQueryString qs req) man- return ()+ man <- newManager tlsManagerSettings+ req <- parseUrlThrow $ "http://localhost:" ++ show port+ _ <- httpLbs (setQueryString qs req) man res <- I.readIORef ref res `shouldBe` qs @@ -471,24 +470,24 @@ responseBody value `shouldBe` jsonValue it "RequestBodyIO" $ echo $ \port -> do- withManager $ \manager -> do- let go bss = withSystemTempFile "request-body-io" $ \tmpfp tmph -> do- liftIO $ do- mapM_ (S.hPutStr tmph) bss- hClose tmph+ manager <- newManager tlsManagerSettings+ let go bss = withSystemTempFile "request-body-io" $ \tmpfp tmph -> do+ liftIO $ do+ mapM_ (S.hPutStr tmph) bss+ hClose tmph - let Just req1 = parseUrlThrow $ "POST http://127.0.0.1:" ++ show port- lbs = L.fromChunks bss- res <- httpLbs req1- { requestBody = RequestBodyIO (streamFile tmpfp)- } manager- liftIO $ Network.HTTP.Conduit.responseStatus res @?= status200- let ts = S.concat . L.toChunks- liftIO $ ts (responseBody res) @?= ts lbs- mapM_ go- [ ["hello", "world"]- , replicate 500 "foo\003\n\r"- ]+ let Just req1 = parseUrlThrow $ "POST http://127.0.0.1:" ++ show port+ lbs = L.fromChunks bss+ res <- httpLbs req1+ { requestBody = RequestBodyIO (streamFile tmpfp)+ } manager+ liftIO $ Network.HTTP.Conduit.responseStatus res @?= status200+ let ts = S.concat . L.toChunks+ liftIO $ ts (responseBody res) @?= ts lbs+ mapM_ go+ [ ["hello", "world"]+ , replicate 500 "foo\003\n\r"+ ] withCApp :: (Data.Conduit.Network.AppData -> IO ()) -> (Int -> IO ()) -> IO () withCApp app' f = do@@ -509,14 +508,14 @@ overLongHeaders :: (Int -> IO ()) -> IO () overLongHeaders =- withCApp $ \app' -> src $$ appSink app'+ withCApp $ \app' -> runConduit $ src .| appSink app' where src = sourceList $ "HTTP/1.0 200 OK\r\nfoo: " : repeat "bar" notOverLongHeaders :: (Int -> IO ()) -> IO () notOverLongHeaders = withCApp $ \app' -> do- appSource app' $$ CL.drop 1- src $$ appSink app'+ runConduit $ appSource app' .| CL.drop 1+ runConduit $ src .| appSink app' where src = sourceList $ [S.concat $ "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 16384\r\n\r\n" : ( take 16384 $ repeat "x")] @@ -556,20 +555,20 @@ echo :: (Int -> IO ()) -> IO () echo = withApp $ \req -> do- bss <- sourceRequestBody req $$ CL.consume+ bss <- runConduit $ sourceRequestBody req .| CL.consume return $ responseLBS status200 [] $ L.fromChunks bss noStatusMessage :: (Int -> IO ()) -> IO () noStatusMessage =- withCApp $ \app' -> src $$ appSink app'+ withCApp $ \app' -> runConduit $ src .| appSink app' where src = yield "HTTP/1.0 200\r\nContent-Length: 3\r\n\r\nfoo: barbazbin" wrongLength :: (Int -> IO ()) -> IO () wrongLength = withCApp $ \app' -> do- _ <- appSource app' $$ await- src $$ appSink app'+ _ <- runConduit $ appSource app' .| await+ runConduit $ src .| appSink app' where src = do yield "HTTP/1.0 200 OK\r\nContent-Length: 50\r\n\r\n"@@ -578,32 +577,32 @@ wrongLengthChunk1 :: (Int -> IO ()) -> IO () wrongLengthChunk1 = withCApp $ \app' -> do- _ <- appSource app' $$ await- src $$ appSink app'+ _ <- runConduit $ appSource app' .| await+ runConduit $ src .| appSink app' where src = yield "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nWiki\r\n" wrongLengthChunk2 :: (Int -> IO ()) -> IO () wrongLengthChunk2 = withCApp $ \app' -> do- _ <- appSource app' $$ await- src $$ appSink app'+ _ <- runConduit $ appSource app' .| await+ runConduit $ src .| appSink app' where src = yield "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nWiki\r\n5\r\npedia\r\nE\r\nin\r\n\r\nch\r\n" invalidChunk :: (Int -> IO ()) -> IO () invalidChunk = withCApp $ \app' -> do- _ <- appSource app' $$ await- src $$ appSink app'+ _ <- runConduit $ appSource app' .| await+ runConduit $ src .| appSink app' where src = yield "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nabcd\r\ngarbage\r\nef\r\n0\r\n\r\n" rawApp :: S8.ByteString -> (Int -> IO ()) -> IO () rawApp bs = withCApp $ \app' -> do- _ <- appSource app' $$ await- src $$ appSink app'+ _ <- runConduit $ appSource app' .| await+ runConduit $ src .| appSink app' where src = yield bs