http-client 0.7.11 → 0.7.12
raw patch · 6 files changed
+49/−11 lines, 6 filesdep ~base
Dependency ranges changed: base
Files
- ChangeLog.md +4/−0
- Network/HTTP/Client.hs +3/−3
- Network/HTTP/Client/Connection.hs +6/−6
- Network/HTTP/Client/Response.hs +8/−1
- http-client.cabal +1/−1
- test/Network/HTTP/ClientSpec.hs +27/−0
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for http-client +## 0.7.12++* Fix premature connection closing due to weak reference lifetimes [#490](https://github.com/snoyberg/http-client/pull/490)+ ## 0.7.11 * Allow making requests to raw IPv6 hosts [#477](https://github.com/snoyberg/http-client/pull/477)
Network/HTTP/Client.hs view
@@ -349,9 +349,9 @@ -- > -- Create the request -- > let requestObject = object ["name" .= "Michael", "age" .= 30] -- > let requestObject = object--- > [ "name" .= ("Michael" :: Text)--- > , "age" .= (30 :: Int)--- > ]+-- > [ "name" .= ("Michael" :: Text)+-- > , "age" .= (30 :: Int)+-- > ] -- > -- > initialRequest <- parseRequest "http://httpbin.org/post" -- > let request = initialRequest { method = "POST", requestBody = RequestBodyLBS $ encode requestObject }
Network/HTTP/Client/Connection.hs view
@@ -210,7 +210,7 @@ -- Pick up an IP using an approximation of the happy-eyeballs algorithm: -- https://datatracker.ietf.org/doc/html/rfc8305--- +-- firstSuccessful :: [NS.AddrInfo] -> (NS.AddrInfo -> IO a) -> IO a firstSuccessful [] _ = error "getAddrInfo returned empty list" firstSuccessful addresses cb = do@@ -226,12 +226,12 @@ z <- forConcurrently (zip addresses [0..]) $ \(addr, n) -> do when (n > 0) $ threadDelay $ n * connectionAttemptDelay tryAddress addr- - case listToMaybe (reverse z) of - Just e@(Left _) -> tryPutMVar result e - _ -> error $ "tryAddresses invariant violated: " <> show addresses++ case listToMaybe (reverse z) of+ Just e@(Left _) -> tryPutMVar result e+ _ -> error $ "tryAddresses invariant violated: " ++ show addresses where tryAddress addr = do r :: Either E.IOException a <- E.try $! cb addr for_ r $ \_ -> tryPutMVar result r- pure r + pure r
Network/HTTP/Client/Response.hs view
@@ -97,7 +97,14 @@ -- should we put this connection back into the connection manager? toPut = Just "close" /= lookup "connection" hs && version > W.HttpVersion 1 0- cleanup bodyConsumed = managedRelease mconn $ if toPut && bodyConsumed then Reuse else DontReuse+ cleanup bodyConsumed = do+ managedRelease mconn $ if toPut && bodyConsumed then Reuse else DontReuse+ -- Keep alive the `Managed Connection` until we're done with it, to prevent an early+ -- collection.+ -- Reasoning: as long as someone holds a reference to the explicit cleanup,+ -- we shouldn't perform an implicit cleanup.+ keepAlive mconn+ body <- -- RFC 2616 section 4.4_1 defines responses that must not include a body
http-client.cabal view
@@ -1,5 +1,5 @@ name: http-client-version: 0.7.11+version: 0.7.12 synopsis: An HTTP client engine description: Hackage documentation generation is not reliable. For up to date documentation, please see: <http://www.stackage.org/package/http-client>. homepage: https://github.com/snoyberg/http-client
test/Network/HTTP/ClientSpec.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} module Network.HTTP.ClientSpec where +import Control.Concurrent (threadDelay) import qualified Data.ByteString.Char8 as BS import Network.HTTP.Client import Network.HTTP.Client.Internal@@ -10,6 +11,7 @@ import Test.Hspec import Control.Applicative ((<$>)) import Data.ByteString.Lazy.Char8 () -- orphan instance+import System.Mem (performGC) main :: IO () main = hspec spec@@ -21,6 +23,31 @@ man <- newManager defaultManagerSettings res <- httpLbs req man responseStatus res `shouldBe` status200++ -- Test the failure condition described in https://github.com/snoyberg/http-client/issues/489+ it "keeps connection alive long enough" $ do+ req <- parseUrlThrow "http://httpbin.org/"+ man <- newManager defaultManagerSettings+ res <- responseOpen req man+ responseStatus res `shouldBe` status200+ let+ getChunk = responseBody res+ drainAll = do+ chunk <- getChunk+ if BS.null chunk then pure () else drainAll++ -- The returned `BodyReader` used to not contain a reference to the `Managed Connection`,+ -- only to the extracted connection and to the release action. Therefore, triggering a GC+ -- would close the connection even though we were not done reading.+ performGC+ -- Not ideal, but weak finalizers run on a separate thread, so it's racing with our drain+ -- call+ threadDelay 500000++ drainAll+ -- Calling `responseClose res` here prevents the early collection from happening in this+ -- test, but in a larger production application that did involve a `responseClose`, it still+ -- occurred. describe "method in URL" $ do it "success" $ do