http-client 0.4.27.1 → 0.4.28
raw patch · 8 files changed
+126/−50 lines, 8 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Network.HTTP.Client: RequestBodyIO :: (IO RequestBody) -> RequestBody
+ Network.HTTP.Client.Internal: RequestBodyIO :: (IO RequestBody) -> RequestBody
+ Network.HTTP.Client.Internal: [requestManagerOverride] :: Request -> Maybe Manager
- Network.HTTP.Client.Internal: Request :: Method -> Bool -> ByteString -> Int -> ByteString -> ByteString -> RequestHeaders -> RequestBody -> Maybe Proxy -> Maybe HostAddress -> Bool -> (ByteString -> Bool) -> Int -> (Status -> ResponseHeaders -> CookieJar -> Maybe SomeException) -> Maybe Int -> (Maybe Int -> HttpException -> IO (ConnRelease, Connection, ManagedConn) -> IO (Maybe Int, (ConnRelease, Connection, ManagedConn))) -> Maybe CookieJar -> HttpVersion -> (SomeException -> IO ()) -> Request
+ Network.HTTP.Client.Internal: Request :: Method -> Bool -> ByteString -> Int -> ByteString -> ByteString -> RequestHeaders -> RequestBody -> Maybe Proxy -> Maybe HostAddress -> Bool -> (ByteString -> Bool) -> Int -> (Status -> ResponseHeaders -> CookieJar -> Maybe SomeException) -> Maybe Int -> (Maybe Int -> HttpException -> IO (ConnRelease, Connection, ManagedConn) -> IO (Maybe Int, (ConnRelease, Connection, ManagedConn))) -> Maybe CookieJar -> HttpVersion -> (SomeException -> IO ()) -> Maybe Manager -> Request
Files
- ChangeLog.md +6/−0
- Network/HTTP/Client.hs +14/−1
- Network/HTTP/Client/Core.hs +3/−1
- Network/HTTP/Client/Request.hs +66/−46
- Network/HTTP/Client/Types.hs +12/−0
- README.md +7/−0
- http-client.cabal +1/−1
- test/Network/HTTP/ClientSpec.hs +17/−1
ChangeLog.md view
@@ -1,3 +1,9 @@+## 0.4.28++* Add support for including request method in URL+* `requestManagerOverride`+* `RequestBodyIO`+ ## 0.4.27.1 * Incorrect idle connection count in HTTP manager [#185](https://github.com/snoyberg/http-client/issues/185)
Network/HTTP/Client.hs view
@@ -3,7 +3,20 @@ {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}--- | This is the main entry point for using http-client. Used by itself, this+-- |+--+-- = Simpler API+--+-- The API below is rather low-level. The @Network.HTTP.Simple@ module (from+-- the @http-conduit@ package) provides a higher-level API with built-in+-- support for things like JSON request and response bodies. For most users,+-- this will be an easier place to start. You can read the tutorial at:+--+-- https://github.com/commercialhaskell/jump/blob/master/doc/http-client.md+--+-- = Lower-level API+--+-- This is the main entry point for using http-client. Used by itself, this -- module provides low-level access for streaming request and response bodies, -- and only non-secure HTTP connections. Helper packages such as http-conduit -- provided higher level streaming approaches, while other helper packages like
Network/HTTP/Client/Core.hs view
@@ -163,13 +163,15 @@ -- -- Since 0.1.0 responseOpen :: Request -> Manager -> IO (Response BodyReader)-responseOpen req0 manager = handle addTlsHostPort $ mWrapIOException manager $ do+responseOpen req0 manager' = handle addTlsHostPort $ mWrapIOException manager $ do (req, res) <- if redirectCount req0 == 0 then httpRaw' req0 manager else go (redirectCount req0) req0 maybe (return res) throwIO =<< applyCheckStatus req (checkStatus req) res where+ manager = fromMaybe manager' (requestManagerOverride req0)+ addTlsHostPort (TlsException e) = throwIO $ TlsExceptionHostPort e (host req0) (port req0) addTlsHostPort e = throwIO e
Network/HTTP/Client/Request.hs view
@@ -64,6 +64,7 @@ import Data.IORef import System.IO (withBinaryFile, hTell, hFileSize, Handle, IOMode (ReadMode))+import Control.Monad (liftM) -- | Convert a URL into a 'Request'. --@@ -73,15 +74,33 @@ -- Since this function uses 'MonadThrow', the return monad can be anything that is -- an instance of 'MonadThrow', such as 'IO' or 'Maybe'. --+-- You can place the request method at the beginning of the URL separated by a+-- space, e.g.:+--+-- @@@+-- parseUrl "POST http://httpbin.org/post"+-- @@@+--+-- Note that the request method must be provided as all capital letters.+-- -- Since 0.1.0 parseUrl :: MonadThrow m => String -> m Request-parseUrl s =+parseUrl s' = case parseURI (encode s) of- Just uri -> setUri def uri+ Just uri -> liftM setMethod (setUri def uri) Nothing -> throwM $ InvalidUrlException s "Invalid URL" where encode = escapeURIString isAllowedInURI+ (mmethod, s) =+ case break (== ' ') s' of+ (x, ' ':y) | all (\c -> 'A' <= c && c <= 'Z') x -> (Just x, y)+ _ -> (Nothing, s') + setMethod req =+ case mmethod of+ Nothing -> req+ Just m -> req { method = S8.pack m }+ -- | Add a 'URI' to the request. If it is absolute (includes a host name), add -- it as per 'setUri'; if it is relative, merge it with the existing request. setUriRelative :: MonadThrow m => Request -> URI -> m Request@@ -240,6 +259,7 @@ case E.fromException se of Just (_ :: IOException) -> return () Nothing -> throwIO se+ , requestManagerOverride = Nothing } instance IsString Request where@@ -318,50 +338,49 @@ && decompress req (fromMaybe "" $ lookup "content-type" hs') requestBuilder :: Request -> Connection -> IO (Maybe (IO ()))-requestBuilder req Connection {..}- | expectContinue = flushHeaders >> return (Just (checkBadSend sendLater))- | otherwise = sendNow >> return Nothing+requestBuilder req Connection {..} = do+ (contentLength, sendNow, sendLater) <- toTriple (requestBody req)+ if expectContinue+ then flushHeaders contentLength >> return (Just (checkBadSend sendLater))+ else sendNow >> return Nothing where expectContinue = Just "100-continue" == lookup "Expect" (requestHeaders req) checkBadSend f = f `E.catch` onRequestBodyException req writeBuilder = toByteStringIO connectionWrite- writeHeadersWith = writeBuilder . (builder `mappend`)- flushHeaders = writeHeadersWith flush-- (contentLength, sendNow, sendLater) =- case requestBody req of- RequestBodyLBS lbs ->- let body = fromLazyByteString lbs- now = checkBadSend $ writeHeadersWith body- later = writeBuilder body- in (Just (L.length lbs), now, later)-- RequestBodyBS bs ->- let body = fromByteString bs- now = checkBadSend $ writeHeadersWith body- later = writeBuilder body- in (Just (fromIntegral $ S.length bs), now, later)-- RequestBodyBuilder len body ->- let now = checkBadSend $ writeHeadersWith body- later = writeBuilder body- in (Just len, now, later)-- -- See https://github.com/snoyberg/http-client/issues/74 for usage- -- of flush here.- RequestBodyStream len stream ->- let body = writeStream False stream- -- Don't check for a bad send on the headers themselves.- -- Ideally, we'd do the same thing for the other request body- -- types, but it would also introduce a performance hit since- -- we couldn't merge request headers and bodies together.- now = flushHeaders >> checkBadSend body- in (Just len, now, body)+ writeHeadersWith contentLength = writeBuilder . (builder contentLength `mappend`)+ flushHeaders contentLength = writeHeadersWith contentLength flush - RequestBodyStreamChunked stream ->- let body = writeStream True stream- now = flushHeaders >> checkBadSend body- in (Nothing, now, body)+ toTriple (RequestBodyLBS lbs) = do+ let body = fromLazyByteString lbs+ len = Just $ L.length lbs+ now = checkBadSend $ writeHeadersWith len body+ later = writeBuilder body+ return (len, now, later)+ toTriple (RequestBodyBS bs) = do+ let body = fromByteString bs+ len = Just $ fromIntegral $ S.length bs+ now = checkBadSend $ writeHeadersWith len body+ later = writeBuilder body+ return (len, now, later)+ toTriple (RequestBodyBuilder len body) = do+ let now = checkBadSend $ writeHeadersWith (Just len) body+ later = writeBuilder body+ return (Just len, now, later)+ toTriple (RequestBodyStream len stream) = do+ -- See https://github.com/snoyberg/http-client/issues/74 for usage+ -- of flush here.+ let body = writeStream False stream+ -- Don't check for a bad send on the headers themselves.+ -- Ideally, we'd do the same thing for the other request body+ -- types, but it would also introduce a performance hit since+ -- we couldn't merge request headers and bodies together.+ now = flushHeaders (Just len) >> checkBadSend body+ return (Just len, now, body)+ toTriple (RequestBodyStreamChunked stream) = do+ let body = writeStream True stream+ now = flushHeaders Nothing >> checkBadSend body+ return (Nothing, now, body)+ toTriple (RequestBodyIO mbody) = mbody >>= toTriple writeStream isChunked withStream = withStream loop@@ -413,14 +432,15 @@ Nothing -> ("Host", hh) : x Just{} -> x - headerPairs :: W.RequestHeaders- headerPairs = hostHeader+ headerPairs :: Maybe Int64 -> W.RequestHeaders+ headerPairs contentLength+ = hostHeader $ acceptEncodingHeader $ contentLengthHeader contentLength $ requestHeaders req - builder :: Builder- builder =+ builder :: Maybe Int64 -> Builder+ builder contentLength = fromByteString (method req) <> fromByteString " " <> requestHostname@@ -441,7 +461,7 @@ <> foldr (\a b -> headerPairToBuilder a <> b) (fromByteString "\r\n")- headerPairs+ (headerPairs contentLength) headerPairToBuilder (k, v) = fromByteString (CI.original k)
Network/HTTP/Client/Types.hs view
@@ -222,6 +222,11 @@ | RequestBodyBuilder Int64 Builder | RequestBodyStream Int64 (GivesPopper ()) | RequestBodyStreamChunked (GivesPopper ())+ | RequestBodyIO (IO RequestBody)+ -- ^ Allows creation of a @RequestBody@ inside the @IO@ monad, which is+ -- useful for making easier APIs (like @setRequestBodyFile@).+ --+ -- @since 0.4.28 deriving T.Typeable -- | --@@ -446,6 +451,13 @@ -- Default: ignore @IOException@s, rethrow all other exceptions. -- -- Since: 0.4.6++ , requestManagerOverride :: Maybe Manager+ -- ^ A 'Manager' value that should override whatever @Manager@ value was+ -- passed in to the HTTP request function manually. This is useful when+ -- dealing with implicit global managers, such as in @Network.HTTP.Simple@+ --+ -- @since 0.4.28 } deriving T.Typeable
README.md view
@@ -1,9 +1,16 @@ http-client =========== +Full tutorial docs are available at:+https://github.com/commercialhaskell/jump/blob/master/doc/http-client.md+ An HTTP client engine, intended as a base layer for more user-friendly packages. This codebase has been refactored from [http-conduit](http://www.stackage.org/package/http-conduit).++Note that, if you want to make HTTPS secure connections, you should use+[http-client-tls](https://www.stackage.org/package/http-client-tls) in addition+to this library. Below is a series of cookbook recipes. A number of recipes exist elsewhere, including `Network.HTTP.Client` and `Network.HTTP.Conduit`. The goal is to
http-client.cabal view
@@ -1,5 +1,5 @@ name: http-client-version: 0.4.27.1+version: 0.4.28 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/Network/HTTP/ClientSpec.hs view
@@ -4,7 +4,7 @@ import Control.Exception (toException) import Network (withSocketsDo) import Network.HTTP.Client-import Network.HTTP.Types (status200)+import Network.HTTP.Types (status200, status405) import Test.Hspec import Data.ByteString.Lazy.Char8 () -- orphan instance @@ -18,6 +18,22 @@ man <- newManager defaultManagerSettings res <- httpLbs req man responseStatus res `shouldBe` status200++ describe "method in URL" $ do+ it "success" $ withSocketsDo $ do+ req <- parseUrl "POST http://httpbin.org/post"+ man <- newManager defaultManagerSettings+ res <- httpLbs req man+ responseStatus res `shouldBe` status200++ it "failure" $ withSocketsDo $ do+ req' <- parseUrl "PUT http://httpbin.org/post"+ let req = req'+ { checkStatus = \_ _ _ -> Nothing+ }+ man <- newManager defaultManagerSettings+ res <- httpLbs req man+ responseStatus res `shouldBe` status405 it "managerModifyRequest" $ do let modify req = return req { port = 80 }