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 +4/−0
- Network/HTTP/Client/Connection.hs +31/−7
- Network/HTTP/Client/Core.hs +3/−5
- Network/HTTP/Client/Types.hs +9/−0
- http-client.cabal +1/−1
- test-nonet/Network/HTTP/ClientSpec.hs +28/−1
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