http-client 0.6.2 → 0.6.3
raw patch · 5 files changed
+45/−30 lines, 5 files
Files
- ChangeLog.md +7/−0
- Network/HTTP/Client/Body.hs +26/−17
- Network/HTTP/Client/Response.hs +4/−5
- http-client.cabal +1/−1
- test-nonet/Network/HTTP/Client/BodySpec.hs +7/−7
ChangeLog.md view
@@ -1,5 +1,12 @@ # Changelog for http-client +## 0.6.3++* Detect response body termination before reading an extra null chunk+ when possible. This allows connections to be reused in some corner+ cases. See+ [#395](https://github.com/snoyberg/http-client/issues/395)+ ## 0.6.2 * Add `shouldStripHeaderOnRedirect` option to `Request` [#300](https://github.com/snoyberg/http-client/issues/300)
Network/HTTP/Client/Body.hs view
@@ -8,7 +8,6 @@ , brConsume , brEmpty , constBodyReader- , brAddCleanup , brReadSome , brRead ) where@@ -61,12 +60,6 @@ [] -> ([], S.empty) x:xs -> (xs, x) -brAddCleanup :: IO () -> BodyReader -> BodyReader-brAddCleanup cleanup brRead' = do- bs <- brRead'- when (S.null bs) cleanup- return bs- -- | Strictly consume all remaining chunks of data from the stream. -- -- Since 0.1.0@@ -111,16 +104,25 @@ Nothing -> start Just popper -> goPopper popper -makeUnlimitedReader :: Connection -> IO BodyReader-makeUnlimitedReader Connection {..} = do+makeUnlimitedReader+ :: IO () -- ^ cleanup+ -> Connection+ -> IO BodyReader+makeUnlimitedReader cleanup Connection {..} = do icomplete <- newIORef False return $ do bs <- connectionRead- when (S.null bs) $ writeIORef icomplete True+ when (S.null bs) $ do+ writeIORef icomplete True+ cleanup return bs -makeLengthReader :: Int -> Connection -> IO BodyReader-makeLengthReader count0 Connection {..} = do+makeLengthReader+ :: IO () -- ^ cleanup+ -> Int+ -> Connection+ -> IO BodyReader+makeLengthReader cleanup count0 Connection {..} = do icount <- newIORef count0 return $ do count <- readIORef icount@@ -134,20 +136,27 @@ let (x, y) = S.splitAt count bs connectionUnread y writeIORef icount (-1)+ cleanup return x EQ -> do writeIORef icount (-1)+ cleanup return bs GT -> do writeIORef icount (count - S.length bs) return bs -makeChunkedReader :: Bool -- ^ raw- -> Connection- -> IO BodyReader-makeChunkedReader raw conn@Connection {..} = do+makeChunkedReader+ :: IO () -- ^ cleanup+ -> Bool -- ^ raw+ -> Connection+ -> IO BodyReader+makeChunkedReader cleanup raw conn@Connection {..} = do icount <- newIORef 0- return $ go icount+ return $ do+ bs <- go icount+ when (S.null bs) cleanup+ pure bs where go icount = do count0 <- readIORef icount
Network/HTTP/Client/Response.hs view
@@ -107,15 +107,14 @@ else do body1 <- if isChunked- then makeChunkedReader rawBody conn+ then makeChunkedReader (cleanup True) rawBody conn else case mcl of- Just len -> makeLengthReader len conn- Nothing -> makeUnlimitedReader conn- body2 <- if needsGunzip req hs+ Just len -> makeLengthReader (cleanup True) len conn+ Nothing -> makeUnlimitedReader (cleanup True) conn+ if needsGunzip req hs then makeGzipReader body1 else return body1- return $ brAddCleanup (cleanup True) body2 return Response { responseStatus = s
http-client.cabal view
@@ -1,5 +1,5 @@ name: http-client-version: 0.6.2+version: 0.6.3 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-nonet/Network/HTTP/Client/BodySpec.hs view
@@ -22,7 +22,7 @@ (conn, _, input) <- dummyConnection [ "5\r\nhello\r\n6\r\n world\r\n0\r\nnot consumed" ]- reader <- makeChunkedReader False conn+ reader <- makeChunkedReader (return ()) False conn body <- brConsume reader S.concat body `shouldBe` "hello world" input' <- input@@ -32,7 +32,7 @@ it "chunked, pieces" $ do (conn, _, input) <- dummyConnection $ map S.singleton $ S.unpack "5\r\nhello\r\n6\r\n world\r\n0\r\nnot consumed"- reader <- makeChunkedReader False conn+ reader <- makeChunkedReader (return ()) False conn body <- brConsume reader S.concat body `shouldBe` "hello world" input' <- input@@ -43,7 +43,7 @@ (conn, _, input) <- dummyConnection [ "5\r\nhello\r\n6\r\n world\r\n0\r\nnot consumed" ]- reader <- makeChunkedReader True conn+ reader <- makeChunkedReader (return ()) True conn body <- brConsume reader S.concat body `shouldBe` "5\r\nhello\r\n6\r\n world\r\n0\r\n" input' <- input@@ -53,7 +53,7 @@ it "chunked, pieces, raw" $ do (conn, _, input) <- dummyConnection $ map S.singleton $ S.unpack "5\r\nhello\r\n6\r\n world\r\n0\r\nnot consumed"- reader <- makeChunkedReader True conn+ reader <- makeChunkedReader (return ()) True conn body <- brConsume reader S.concat body `shouldBe` "5\r\nhello\r\n6\r\n world\r\n0\r\n" input' <- input@@ -64,7 +64,7 @@ (conn, _, input) <- dummyConnection [ "hello world done" ]- reader <- makeLengthReader 11 conn+ reader <- makeLengthReader (return ()) 11 conn body <- brConsume reader S.concat body `shouldBe` "hello world" input' <- input@@ -74,7 +74,7 @@ it "length, pieces" $ do (conn, _, input) <- dummyConnection $ map S.singleton $ S.unpack "hello world done"- reader <- makeLengthReader 11 conn+ reader <- makeLengthReader (return ()) 11 conn body <- brConsume reader S.concat body `shouldBe` "hello world" input' <- input@@ -85,7 +85,7 @@ let orig = L.fromChunks $ replicate 5000 "Hello world!" origZ = compress orig (conn, _, input) <- dummyConnection $ L.toChunks origZ ++ ["ignored"]- reader' <- makeLengthReader (fromIntegral $ L.length origZ) conn+ reader' <- makeLengthReader (return ()) (fromIntegral $ L.length origZ) conn reader <- makeGzipReader reader' body <- brConsume reader L.fromChunks body `shouldBe` orig