wreq 0.5.2.0 → 0.5.2.1
raw patch · 7 files changed
+70/−38 lines, 7 filesdep +cryptonitedep +memorydep −byteabledep −cryptohashdep ~base
Dependencies added: cryptonite, memory
Dependencies removed: byteable, cryptohash
Dependency ranges changed: base
Files
- Network/Wreq.hs +4/−0
- Network/Wreq/Internal/AWS.hs +24/−14
- Network/Wreq/Session.hs +9/−7
- Network/Wreq/Types.hs +1/−1
- changelog.md +6/−0
- wreq.cabal +4/−4
- www/tutorial.md +22/−12
Network/Wreq.hs view
@@ -678,6 +678,10 @@ -- The \"magical\" type conversion on the right-hand side of ':=' -- above is due to the 'FormValue' class. This package provides -- sensible instances for the standard string and number types.+-- You may need to explicitly add types to the values (e.g. :: String)+-- in order to evade ambigous type errors.+--+-- >>> r <- post "http://httpbin.org/post" ["num" := (31337 :: Int), "str" := ("foo" :: String)] -- -- The 'Aeson.Value' type gives a JSON request body with a -- @Content-Type@ of @application/json@. Any instance of
Network/Wreq/Internal/AWS.hs view
@@ -8,9 +8,9 @@ import Control.Applicative ((<$>)) import Control.Lens ((%~), (^.), (&), to)-import Crypto.MAC (hmac, hmacGetDigest)+import Crypto.MAC.HMAC (HMAC (..), hmac, hmacGetDigest) import Data.ByteString.Base16 as HEX (encode)-import Data.Byteable (toBytes)+import Data.ByteArray (convert) import Data.Char (toLower) import Data.List (sort) import Data.Monoid ((<>))@@ -21,9 +21,9 @@ import Network.HTTP.Types (parseSimpleQuery, urlEncode) import Network.Wreq.Internal.Lens import Network.Wreq.Internal.Types (AWSAuthVersion(..))-import qualified Crypto.Hash as CT (HMAC, SHA256)-import qualified Crypto.Hash.SHA256 as SHA256 (hash, hashlazy)+import qualified Crypto.Hash as CT (Digest, SHA256, hash, hashlazy) import qualified Data.ByteString.Char8 as S+import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.CaseInsensitive as CI (original) import qualified Data.HashSet as HashSet import qualified Network.HTTP.Client as HTTP@@ -44,6 +44,17 @@ Request -> IO Request signRequest AWSv4 = signRequestV4 +hexSha256Hash :: S.ByteString -> S.ByteString+hexSha256Hash dta =+ let digest = CT.hash dta :: CT.Digest CT.SHA256+ in S.pack (show digest)++hexSha256HashLazy :: L.ByteString -> S.ByteString+hexSha256HashLazy dta =+ let digest = CT.hashlazy dta :: CT.Digest CT.SHA256+ in S.pack (show digest)++ signRequestV4 :: S.ByteString -> S.ByteString -> Request -> IO Request signRequestV4 key secret request = do !ts <- timestamp -- YYYYMMDDT242424Z, UTC based@@ -55,7 +66,7 @@ date = S.takeWhile (/= 'T') ts -- YYYYMMDD hashedPayload | request ^. method `elem` ["POST", "PUT"] = payloadHash req- | otherwise = HEX.encode $ SHA256.hash ""+ | otherwise = hexSha256Hash "" -- add common v4 signing headers, service specific headers, and -- drop tmp header and Runscope-Bucket-Auth header (if present). req = request & requestHeaders %~@@ -87,7 +98,7 @@ "AWS4-HMAC-SHA256" , ts , dateScope- , HEX.encode $ SHA256.hash canonicalReq+ , hexSha256Hash canonicalReq ] -- task 3, steps 1 and 2 let signature = ("AWS4" <> secret) &@@ -113,16 +124,15 @@ timestamp = render <$> getCurrentTime where render = S.pack . formatTime defaultTimeLocale "%Y%m%dT%H%M%SZ" . utcToLocalTime utc -- UTC printable: YYYYMMDDTHHMMSSZ- hmac' s k = toBytes (hmacGetDigest h)- where h = hmac k s :: (CT.HMAC CT.SHA256)+ hmac' :: S.ByteString -> S.ByteString -> S.ByteString+ hmac' s k = convert (hmacGetDigest h)+ where h = hmac k s :: (HMAC CT.SHA256) payloadHash :: Request -> S.ByteString payloadHash req = case HTTP.requestBody req of- HTTP.RequestBodyBS bs ->- HEX.encode $ SHA256.hash bs- HTTP.RequestBodyLBS lbs ->- HEX.encode $ SHA256.hashlazy lbs+ HTTP.RequestBodyBS bs -> hexSha256Hash bs+ HTTP.RequestBodyLBS lbs -> hexSha256HashLazy lbs _ -> error "addTmpPayloadHashHeader: unexpected request body type" -- Per AWS documentation at:@@ -179,5 +189,5 @@ | otherwise = hostname where p1 "-" = "." p1 other = other- p2 "--" = "-"- p2 other = other+ p2 "--" = "-"+ p2 other = other
Network/Wreq/Session.hs view
@@ -18,7 +18,7 @@ -- -- * Transparent cookie management. Any cookies set by the server -- persist from one request to the next. (Bypass this overhead--- using 'withAPISession'.)+-- using 'newAPISession'.) -- -- -- This module is designed to be used alongside the "Network.Wreq"@@ -28,19 +28,20 @@ -- import "Network.Wreq" -- import qualified "Network.Wreq.Session" as Sess ----- main = Sess.'withSession' $ \\sess ->+-- main = do+-- sess <- Sess.'newSession' -- Sess.'get' sess \"http:\/\/httpbin.org\/get\" -- @ ----- We create a 'Session' using 'withSession', then pass the session to+-- We create a 'Session' using 'newSession', then pass the session to -- subsequent functions. When talking to a REST-like service that does--- not use cookies, it is more efficient to use 'withAPISession'.+-- not use cookies, it is more efficient to use 'newAPISession'. -- -- Note the use of qualified import statements in the examples above, -- so that we can refer unambiguously to the 'Session'-specific -- implementation of HTTP GET. ----- One 'Network.HTTP.Client.Manager' (possibly set with 'withSessionControl') is used for all+-- One 'Network.HTTP.Client.Manager' (possibly set with 'newSessionControl') is used for all -- session requests. The manager settings in the 'Options' parameter -- for the 'getWith', 'postWith' and similar functions is ignored. @@ -94,6 +95,7 @@ import qualified Network.HTTP.Client as HTTP import qualified Network.Wreq.Internal.Lens as Lens import qualified Network.Wreq.Lens as Lens+import Data.Traversable as T -- | Create a 'Session', passing it to the given function. The -- 'Session' will no longer be valid after that function returns.@@ -170,7 +172,7 @@ -- -- @since 0.5.2.0 getSessionCookieJar :: Session -> IO (Maybe HTTP.CookieJar)-getSessionCookieJar = traverse readIORef . seshCookies+getSessionCookieJar = T.traverse readIORef . seshCookies -- | 'Session'-specific version of 'Network.Wreq.get'. get :: Session -> String -> IO (Response L.ByteString)@@ -263,7 +265,7 @@ runWithGeneric :: (resp -> Response b) -> Session -> (Req -> IO resp) -> Req -> IO resp runWithGeneric extract Session{..} act (Req _ req) = do- req' <- (\c -> req & Lens.cookieJar .~ c) <$> traverse readIORef seshCookies+ req' <- (\c -> req & Lens.cookieJar .~ c) `fmap` T.traverse readIORef seshCookies resp <- act (Req (Right seshManager) req') forM_ seshCookies $ \ref -> writeIORef ref (HTTP.responseCookieJar (extract resp))
Network/Wreq/Types.hs view
@@ -71,7 +71,7 @@ instance Putable [Part] where putPayload p req = -- According to doc, formDataBody changes the request type to POST which is wrong; change it back- (\r -> r{method=method req}) <$> formDataBody p req+ (\r -> r{method=method req}) `fmap` formDataBody p req instance Putable [(S.ByteString, S.ByteString)] where putPayload ps req =
changelog.md view
@@ -1,5 +1,11 @@ -*- markdown -*- +2018-03-01 0.5.2.1++* Fixed some building issues with older versions++* Removed dependency on cryptohash, using cryptonite instead+ 2018-01-01 0.5.2.0 * Added some HistoriedResponse support
wreq.cabal view
@@ -1,5 +1,5 @@ name: wreq-version: 0.5.2.0+version: 0.5.2.1 synopsis: An easy-to-use HTTP client library. description: .@@ -36,7 +36,7 @@ copyright: 2014 Bryan O'Sullivan category: Web build-type: Custom-cabal-version: >=1.24+cabal-version: >=1.10 tested-with: GHC==8.2.1, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2 extra-source-files: README.md@@ -105,11 +105,10 @@ authenticate-oauth >= 1.5, base >= 4.5 && < 5, base16-bytestring,- byteable, bytestring >= 0.9, case-insensitive, containers,- cryptohash,+ cryptonite, exceptions >= 0.5, ghc-prim, hashable,@@ -118,6 +117,7 @@ http-types >= 0.8, lens >= 4.5, lens-aeson,+ memory, mime-types, time-locale-compat, template-haskell,
www/tutorial.md view
@@ -435,7 +435,11 @@ ~~~~ {.haskell} ghci> r <- get "http://httpbin.org/basic-auth/user/pass"-*** Exception: StatusCodeException (Status {statusCode = 401, {-...-}+*** Exception: HttpExceptionRequest Request { ... }+ (StatusCodeException (Response {+ responseStatus = Status {statusCode = 401, {-...-} }+ , {- ... -}+ }), "..." ) ~~~~ If we then supply a username and password, our request will succeed.@@ -516,7 +520,11 @@ ~~~~ {.haskell} h> r <- get "http://httpbin.org/wibblesticks"-*** Exception: StatusCodeException (Status {statusCode = 404, {-...-}+*** Exception: HttpExceptionRequest Request { ... }+ (StatusCodeException (Response {+ responseStatus = Status {statusCode = 404, {-...-} }+ , {- ... -}+ }), "..." ) ~~~~ Here's a simple example of how we can respond to one kind of error: a@@ -526,17 +534,19 @@ ~~~~ {.haskell} import Control.Exception as E import Control.Lens-import Network.HTTP.Client+import Network.HTTP.Client (HttpException (HttpExceptionRequest),+ HttpExceptionContent (StatusCodeException)) import Network.Wreq getAuth url myauth = get url `E.catch` handler where- handler e@(StatusCodeException s _ _)- | s ^. statusCode == 401 = getWith authopts authurl- | otherwise = throwIO e- where authopts = defaults & auth .~ myauth- -- switch to TLS when we use auth- authurl = "https" ++ dropWhile (/=':') url+ handler e@(HttpExceptionRequest _ (StatusCodeException r _))+ | r ^. responseStatus . statusCode == 401 = getWith authopts authurl+ | otherwise = throwIO e+ handler e = throwIO e+ authopts = defaults & auth ?~ myauth+ -- switch to TLS when we use auth+ authurl = "https" ++ dropWhile (/=':') url ~~~~ (A "real world" version would remember which URLs required@@ -573,7 +583,8 @@ import qualified Network.Wreq.Session as S main :: IO ()-main = S.withSession $ \sess -> do+main = do+ sess <- S.newSession -- First request: tell the server to set a cookie S.get sess "http://httpbin.org/cookies/set?name=hi" @@ -589,8 +600,7 @@ module qualified, and we'll identify its functions by prefixing them with "`S.`". -* To create a `Session`, we use `S.withSession`. It calls our code- with `sess`, the `Session` value we'll use.+* To create a `Session`, we use `S.newSession`. * Instead of `get` and `post`, we call the `Session`-specific versions, [`S.get`](http://hackage.haskell.org/package/wreq/docs/Network-Wreq-Session.html#v:get) and [`S.post`](http://hackage.haskell.org/package/wreq/docs/Network-Wreq-Session.html#v:post), and pass `sess` to each of them.