packages feed

http-client 0.4.25 → 0.4.26

raw patch · 6 files changed

+76/−14 lines, 6 filesdep ~basePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Network.HTTP.Client.Internal: ConnectionClosed :: ConnectionClosed
+ Network.HTTP.Client.Internal: data ConnectionClosed

Files

ChangeLog.md view
@@ -1,3 +1,7 @@+## 0.4.26++* Make sure we never read from or write to closed socket [#170](https://github.com/snoyberg/http-client/pull/170)+ ## 0.4.25  * Don't error out when response body flushing fails [#169](https://github.com/snoyberg/http-client/issues/169)
Network/HTTP/Client/Connection.hs view
@@ -85,15 +85,39 @@                -> IO Connection makeConnection r w c = do     istack <- newIORef []++    -- it is necessary to make sure we never read from or write to+    -- already closed connection.+    closedVar <- newIORef False+     _ <- mkWeakIORef istack c     return $! Connection-        { connectionRead = join $ atomicModifyIORef istack $ \stack ->-            case stack of-                x:xs -> (xs, return x)-                [] -> ([], r)-        , connectionUnread = \x -> atomicModifyIORef istack $ \stack -> (x:stack, ())-        , connectionWrite = w-        , connectionClose = c+        { connectionRead = do+            closed <- readIORef closedVar+            when closed $+              throwIO ConnectionClosed+            join $ atomicModifyIORef istack $ \stack ->+              case stack of+                  x:xs -> (xs, return x)+                  [] -> ([], r)++        , connectionUnread = \x -> do+            closed <- readIORef closedVar+            when closed $+              throwIO ConnectionClosed+            atomicModifyIORef istack $ \stack -> (x:stack, ())++        , connectionWrite = \x -> do+            closed <- readIORef closedVar+            when closed $+              throwIO ConnectionClosed+            w x++        , connectionClose = do+            closed <- readIORef closedVar+            unless closed $+              c+            writeIORef closedVar True         }  socketConnection :: Socket -> Int -> IO Connection
Network/HTTP/Client/Core.hs view
@@ -218,12 +218,10 @@                 -- instead just close the connection.                 let maxFlush = 1024                 lbs <- brReadSome (responseBody res) maxFlush-                    -- Ignore IO exceptions at this point. A server may-                    -- terminate the connection immediately in some cases, such-                    -- as when using withResponseHistory (where the response-                    -- body may be flushed before getting this far). See:+                    -- The connection may already be closed, e.g.+                    -- when using withResponseHistory. See                     -- https://github.com/snoyberg/http-client/issues/169-                    `catch` \(_ :: IOException) -> return L.empty+                    `catch` \(_ :: ConnectionClosed) -> return L.empty                 responseClose res                  -- And now perform the actual redirect
Network/HTTP/Client/Types.hs view
@@ -7,6 +7,7 @@     ( BodyReader     , Connection (..)     , StatusHeaders (..)+    , ConnectionClosed (..)     , HttpException (..)     , Cookie (..)     , CookieJar (..)@@ -75,11 +76,19 @@     , connectionWrite :: S.ByteString -> IO ()       -- ^ Send data to server     , connectionClose :: IO ()+      -- ^ Close connection. Any successive operation on the connection+      -- (exept closing) should fail with `ConnectionClosed` exception.+      -- It is allowed to close connection multiple times.     }     deriving T.Typeable  data StatusHeaders = StatusHeaders Status HttpVersion RequestHeaders     deriving (Show, Eq, Ord, T.Typeable)++data ConnectionClosed = ConnectionClosed+  deriving (Eq, Show)++instance Exception ConnectionClosed  data HttpException = StatusCodeException Status ResponseHeaders CookieJar                    | InvalidUrlException String String
http-client.cabal view
@@ -1,5 +1,5 @@ name:                http-client-version:             0.4.25+version:             0.4.26 synopsis:            An HTTP client engine, intended as a base layer for more user-friendly packages. 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-nonet/Network/HTTP/ClientSpec.hs view
@@ -1,9 +1,11 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-} module Network.HTTP.ClientSpec where  import           Control.Concurrent        (forkIO, threadDelay) import           Control.Concurrent.Async  (withAsync)-import           Control.Exception         (bracket)+import qualified Control.Concurrent.Async  as Async+import           Control.Exception         (bracket, catch, IOException) import           Control.Monad             (forever, replicateM_, void) import           Network.HTTP.Client import           Network.HTTP.Types        (status413)@@ -33,6 +35,20 @@             N.appWrite ad "hello\r\n"             threadDelay 10000 +redirectCloseServer :: (Int -> IO a) -> IO a+redirectCloseServer inner = bracket+    (N.bindRandomPortTCP "*4")+    (sClose . snd)+    $ \(port, lsocket) -> withAsync+        (N.runTCPServer (N.serverSettingsTCPSocket lsocket) app)+        (const $ inner port)+  where+    app ad = do+      Async.race_+          (forever (N.appRead ad))+          (N.appWrite ad "HTTP/1.1 301 Redirect\r\nLocation: /\r\nConnection: close\r\n\r\nhello")+      N.appCloseConnection ad+ bad100Server :: Bool -- ^ include extra headers?              -> (Int -> IO a) -> IO a bad100Server extraHeaders inner = bracket@@ -175,3 +191,14 @@         withManager defaultManagerSettings $ \man -> do             res <- getChunkedResponse port' man             responseBody res `shouldBe` "Wikipedia in\r\n\r\nchunks."++    it "withResponseHistory and redirect" $ redirectCloseServer $ \port -> do+        -- see https://github.com/snoyberg/http-client/issues/169+        req' <- parseUrl $ "http://127.0.0.1:" ++ show port+        let req = req' {redirectCount = 1}+        withManager defaultManagerSettings $ \man -> do+          withResponseHistory req man (const $ return ())+            `shouldThrow` \e ->+              case e of+                TooManyRedirects _ -> True+                _ -> False