packages feed

wreq 0.5.1.0 → 0.5.2.0

raw patch · 18 files changed

+410/−143 lines, 18 filesdep ~basesetup-changed

Dependency ranges changed: base

Files

Network/Wreq.hs view
@@ -63,9 +63,13 @@     -- ** Custom Method     , customMethod     , customMethodWith+    , customHistoriedMethod+    , customHistoriedMethodWith     -- ** Custom Payload Method     , customPayloadMethod     , customPayloadMethodWith+    , customHistoriedPayloadMethod+    , customHistoriedPayloadMethodWith     -- * Incremental consumption of responses     -- ** GET     , foldGet@@ -94,6 +98,7 @@     , oauth2Bearer     , oauth2Token     , awsAuth+    , awsSessionTokenAuth     -- ** Proxy settings     , Proxy(Proxy)     , Lens.proxy@@ -132,6 +137,10 @@     , Lens.Status     , Lens.statusCode     , Lens.statusMessage+    , HistoriedResponse+    , Lens.hrFinalRequest+    , Lens.hrFinalResponse+    , Lens.hrRedirects     -- ** Link headers     , Lens.Link     , Lens.linkURL@@ -162,6 +171,7 @@ import Data.Maybe (fromMaybe) import Data.Text (Text) import Data.Text.Encoding (encodeUtf8)+import Network.HTTP.Client (HistoriedResponse) import Network.HTTP.Client.Internal (Proxy(..), Response) import Network.Wreq.Internal import Network.Wreq.Types (Options)@@ -336,6 +346,7 @@ -- | Issue a custom-method request -- -- Example:+-- -- @ -- 'customMethod' \"PATCH\" \"http:\/\/httpbin.org\/patch\" -- @@@ -364,6 +375,33 @@   where     methodBS = BC8.pack method ++-- | Issue a custom request method. Keep track of redirects and return the 'HistoriedResponse'+--+-- Example:+--+-- @+-- 'customHistoriedMethod' \"GET\" \"http:\/\/httpbin.org\/redirect\/3\"+-- @+--+-- >>> r <- customHistoriedMethod "GET" "http://httpbin.org/redirect/3"+-- >>> length (r ^. hrRedirects)+-- 3+--+-- @since 0.5.2.0+customHistoriedMethod :: String -> String -> IO (HistoriedResponse L.ByteString)+customHistoriedMethod method url = customHistoriedMethodWith method defaults url++-- | Issue a custom request method request, using the supplied 'Options'.+-- Keep track of redirects and return the 'HistoriedResponse'.+--+-- @since 0.5.2.0+customHistoriedMethodWith :: String -> Options -> String -> IO (HistoriedResponse L.ByteString)+customHistoriedMethodWith method opts url =+    runReadHistory =<< prepareMethod methodBS opts url+  where+    methodBS = BC8.pack method+ -- | Issue a custom-method request with a payload customPayloadMethod :: Postable a => String -> String -> a                     -> IO (Response L.ByteString)@@ -380,6 +418,22 @@   where     methodBS = BC8.pack method +-- | Issue a custom-method historied request with a payload+customHistoriedPayloadMethod :: Postable a => String -> String -> a+                        -> IO (HistoriedResponse L.ByteString)++customHistoriedPayloadMethod method url payload =+  customHistoriedPayloadMethodWith method defaults url payload++-- | Issue a custom-method historied request with a paylod, using the supplied 'Options'.+customHistoriedPayloadMethodWith :: Postable a => String -> Options -> String -> a+                        -> IO (HistoriedResponse L.ByteString)++customHistoriedPayloadMethodWith method opts url payload =+  runReadHistory =<< preparePayloadMethod methodBS opts url payload+  where+    methodBS = BC8.pack method+ foldGet :: (a -> S.ByteString -> IO a) -> a -> String -> IO a foldGet f z url = foldGetWith defaults f z url @@ -430,7 +484,8 @@ asJSON resp = do   let contentType = fst . S.break (==59) . fromMaybe "unknown" .                     lookup "Content-Type" . HTTP.responseHeaders $ resp-  unless ("application/json" `S.isPrefixOf` contentType) $+  unless ("application/json" `S.isPrefixOf` contentType+        || ("application/" `S.isPrefixOf` contentType && "+json" `S.isSuffixOf` contentType)) $     throwM . JSONError $ "content type of response is " ++ show contentType   case Aeson.eitherDecode' (HTTP.responseBody resp) of     Left err  -> throwM (JSONError err)@@ -525,7 +580,25 @@ --'getWith' opts \"https:\/\/dynamodb.us-west-2.amazonaws.com\" -- @ awsAuth :: AWSAuthVersion -> S.ByteString -> S.ByteString -> Auth-awsAuth = AWSAuth+awsAuth version key secret = AWSAuth version key secret Nothing++-- | AWS v4 request signature using a AWS STS Session Token.+--+-- Example (note the use of TLS):+--+-- @+--let opts = 'defaults'+--           '&' 'Lens.auth'+--           '?~' 'awsAuth AWSv4' \"key\" \"secret\" \"stsSessionToken\"+--'getWith' opts \"https:\/\/dynamodb.us-west-2.amazonaws.com\"+-- @+awsSessionTokenAuth :: AWSAuthVersion -- ^ Signature version (V4)+                    -> S.ByteString   -- ^ AWS AccessKeyId+                    -> S.ByteString   -- ^ AWS SecretAccessKey+                    -> S.ByteString   -- ^ AWS STS SessionToken+                    -> Auth+awsSessionTokenAuth version key secret sessionToken =+  AWSAuth version key secret (Just sessionToken)  -- | Proxy configuration. --
Network/Wreq/Internal.hs view
@@ -12,6 +12,7 @@     , prepareGet     , preparePost     , runRead+    , runReadHistory     , prepareHead     , runIgnore     , prepareOptions@@ -28,11 +29,11 @@ import Data.Monoid ((<>)) import Data.Text.Encoding (encodeUtf8) import Data.Version (showVersion)-import Network.HTTP.Client (BodyReader)+import Network.HTTP.Client (BodyReader, HistoriedResponse(..)) import Network.HTTP.Client.Internal (Proxy(..), Request, Response(..), addProxy) import Network.HTTP.Client.TLS (tlsManagerSettings) import Network.Wreq.Internal.Lens (setHeader)-import Network.Wreq.Internal.Types (Mgr, Req(..), Run)+import Network.Wreq.Internal.Types (Mgr, Req(..), Run, RunHistory) import Network.Wreq.Types (Auth(..), Options(..), Postable(..), Putable(..)) import Prelude hiding (head) import qualified Data.ByteString as S@@ -88,6 +89,12 @@   chunks <- HTTP.brConsume (HTTP.responseBody resp)   return resp { responseBody = L.fromChunks chunks } +readHistoriedResponse :: HistoriedResponse BodyReader -> IO (HistoriedResponse L.ByteString)+readHistoriedResponse resp = do+  let finalResp = hrFinalResponse resp+  chunks <- HTTP.brConsume (HTTP.responseBody finalResp)+  return resp { hrFinalResponse = finalResp { responseBody = L.fromChunks chunks } }+ foldResponseBody :: (a -> S.ByteString -> IO a) -> a                  -> Response BodyReader -> IO a foldResponseBody f z0 resp = go z0@@ -105,6 +112,10 @@ run emgr act req = either (HTTP.newManager >=> go) go emgr   where go mgr = HTTP.withResponse req mgr act +runHistory :: Mgr -> (HistoriedResponse BodyReader -> IO a) -> Request -> IO a+runHistory emgr act req = either (HTTP.newManager >=> go) go emgr+  where go mgr = HTTP.withResponseHistory req mgr act+ prepare :: (Request -> IO Request) -> Options -> String -> IO Request prepare modify opts url = do   signRequest =<< modify =<< frob <$> HTTP.parseUrlThrow url@@ -119,7 +130,7 @@     signRequest :: Request -> IO Request     signRequest = maybe return f $ auth opts       where-        f (AWSAuth versn key secret) = AWS.signRequest versn key secret+        f (AWSAuth versn key secret _) = AWS.signRequest versn key secret         f (OAuth1 consumerToken consumerSecret token secret) = OAuth1.signRequest consumerToken consumerSecret token secret         f _ = return @@ -139,8 +150,9 @@     f (BasicAuth user pass) = HTTP.applyBasicAuth user pass     f (OAuth2Bearer token)  = setHeader "Authorization" ("Bearer " <> token)     f (OAuth2Token token)   = setHeader "Authorization" ("token " <> token)-    -- for AWS request signature, see Internal/AWS-    f (AWSAuth _ _ _)       = id+    -- for AWS request signature implementation, see Internal/AWS+    f (AWSAuth _ _ _ mSessionToken) =+      maybe id (setHeader "X-Amz-Security-Token") mSessionToken     f (OAuth1 _ _ _ _)      = id  setProxy :: Options -> Request -> Request@@ -156,6 +168,9 @@  runRead :: Run L.ByteString runRead (Req mgr req) = run mgr readResponse req++runReadHistory :: RunHistory L.ByteString+runReadHistory (Req mgr req) = runHistory mgr readHistoriedResponse req  preparePost :: Postable a => Options -> String -> a -> IO Req preparePost opts url payload = Req (manager opts) <$>
Network/Wreq/Internal/Lens.hs view
@@ -13,6 +13,7 @@     , requestHeaders     , requestBody     , requestVersion+    , requestManagerOverride     , onRequestBodyException     , proxy     , hostAddress@@ -25,6 +26,7 @@     , seshCookies     , seshManager     , seshRun+    , seshRunHistory     -- * Useful functions     , assoc     , assoc2
Network/Wreq/Internal/Types.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE DeriveDataTypeable, DeriveFunctor, FlexibleInstances, GADTs,-    OverloadedStrings, RankNTypes, RecordWildCards #-}+    OverloadedStrings, RankNTypes, RecordWildCards, DefaultSignatures #-}  -- | -- Module      : Network.Wreq.Internal.Types@@ -38,6 +38,7 @@     -- * Sessions     , Session(..)     , Run+    , RunHistory     , Body(..)     -- * Caches     , CacheEntry(..)@@ -179,9 +180,9 @@             -- to be used only by GitHub). This is treated by whoever             -- accepts it as the equivalent of a username and             -- password.-          | AWSAuth AWSAuthVersion S.ByteString S.ByteString+          | AWSAuth AWSAuthVersion S.ByteString S.ByteString (Maybe S.ByteString)             -- ^ Amazon Web Services request signing-            -- AWSAuthVersion key secret+            -- AWSAuthVersion key secret (optional: session-token)           | OAuth1 S.ByteString S.ByteString S.ByteString S.ByteString             -- ^ OAuth1 request signing             -- OAuth1 consumerToken consumerSecret token secret@@ -209,6 +210,8 @@ -- | A type that can be converted into a POST request payload. class Postable a where     postPayload :: a -> Request -> IO Request+    default postPayload :: Putable a => a -> Request -> IO Request+    postPayload = putPayload     -- ^ Represent a value in the request body (and perhaps the     -- headers) of a POST request. @@ -288,12 +291,15 @@ -- response. type Run body = Req -> IO (Response body) +type RunHistory body = Req -> IO (HTTP.HistoriedResponse body)+ -- | A session that spans multiple requests.  This is responsible for -- cookie management and TCP connection reuse. data Session = Session {       seshCookies :: Maybe (IORef CookieJar)     , seshManager :: Manager     , seshRun :: Session -> Run Body -> Run Body+    , seshRunHistory :: Session -> RunHistory Body -> RunHistory Body     }  instance Show Session where
Network/Wreq/Lens.hs view
@@ -78,6 +78,12 @@     , responseStatus     , responseVersion +    -- * HistoriedResponse+    , HistoriedResponse+    , hrFinalResponse+    , hrFinalRequest+    , hrRedirects+     -- ** Status     , Status     , statusCode@@ -104,9 +110,10 @@ import Control.Lens (Fold, Lens, Lens', Traversal', folding) import Data.Attoparsec.ByteString (Parser, endOfInput, parseOnly) import Data.ByteString (ByteString)+import qualified Data.ByteString.Lazy as L import Data.Text (Text) import Data.Time.Clock (UTCTime)-import Network.HTTP.Client (Cookie, CookieJar, Manager, ManagerSettings, Proxy)+import Network.HTTP.Client (Cookie, CookieJar, Request, Manager, ManagerSettings, Proxy, HistoriedResponse) import Network.HTTP.Client (RequestBody, Response) import Network.HTTP.Client.MultipartFormData (Part) import Network.HTTP.Types.Header (Header, HeaderName, ResponseHeaders)@@ -389,6 +396,18 @@ -- | A lens onto all cookies set in the response. responseCookieJar :: Lens' (Response body) CookieJar responseCookieJar = TH.responseCookieJar++-- | A lens onto the final request of a historied response.+hrFinalRequest :: Lens' (HistoriedResponse body) Request+hrFinalRequest = TH.hrFinalRequest++-- | A lens onto the final response of a historied response.+hrFinalResponse :: Lens' (HistoriedResponse body) (Response body)+hrFinalResponse = TH.hrFinalResponse++-- | A lens onto the list of redirects of a historied response.+hrRedirects :: Lens' (HistoriedResponse body) [(Request, Response L.ByteString)]+hrRedirects = TH.hrRedirects  -- | A lens onto the numeric identifier of an HTTP status. statusCode :: Lens' Status Int
Network/Wreq/Lens/TH.hs view
@@ -45,6 +45,11 @@     , responseCookieJar     , responseClose' +    , HTTP.HistoriedResponse+    , hrFinalResponse+    , hrFinalRequest+    , hrRedirects+     , HTTP.Status     , statusCode     , statusMessage@@ -77,6 +82,7 @@ makeLensesWith (lensRules & lensField .~ fieldName toCamelCase) ''HTTP.Cookie makeLenses ''HTTP.Proxy makeLenses ''HTTP.Response+makeLenses ''HTTP.HistoriedResponse makeLenses ''HTTP.Status makeLenses ''Types.Link makeLenses ''Form.Part
Network/Wreq/Session.hs view
@@ -39,16 +39,25 @@ -- 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+-- session requests. The manager settings in the 'Options' parameter+-- for the 'getWith', 'postWith' and similar functions is ignored.  module Network.Wreq.Session     (     -- * Session creation       Session+    , newSession+    , newAPISession     , withSession     , withAPISession     -- ** More control-oriented session creation+    , newSessionControl     , withSessionWith     , withSessionControl+    -- ** Get information about session state+    , getSessionCookieJar     -- * HTTP verbs     , get     , post@@ -66,16 +75,18 @@     , deleteWith     , customMethodWith     , customPayloadMethodWith+    , customHistoriedMethodWith+    , customHistoriedPayloadMethodWith     -- * Extending a session     , Lens.seshRun     ) where -import Control.Lens ((&), (?~), (.~))+import Control.Lens ((&), (.~)) import Data.Foldable (forM_) import Data.IORef (newIORef, readIORef, writeIORef)-import Network.Wreq (Options, Response)+import Network.Wreq (Options, Response, HistoriedResponse) import Network.Wreq.Internal-import Network.Wreq.Internal.Types (Body(..), Req(..), Session(..))+import Network.Wreq.Internal.Types (Body(..), Req(..), Session(..), RunHistory) import Network.Wreq.Types (Postable, Putable, Run) import Prelude hiding (head) import qualified Data.ByteString.Char8 as BC8@@ -90,21 +101,42 @@ -- This session manages cookies and uses default session manager -- configuration. withSession :: (Session -> IO a) -> IO a-withSession = withSessionWith defaultManagerSettings+withSession act = newSession >>= act+{-# DEPRECATED withSession "Use newSession instead." #-} +-- | Create a 'Session'.+--+-- This session manages cookies and uses default session manager+-- configuration.+--+-- @since 0.5.2.0+newSession :: IO Session+newSession = newSessionControl (Just (HTTP.createCookieJar [])) defaultManagerSettings+ -- | Create a session. -- -- This uses the default session manager settings, but does not manage -- cookies.  It is intended for use with REST-like HTTP-based APIs, -- which typically do not use cookies. withAPISession :: (Session -> IO a) -> IO a-withAPISession = withSessionControl Nothing defaultManagerSettings+withAPISession act = newAPISession >>= act+{-# DEPRECATED withAPISession "Use newAPISession instead." #-} +-- | Create a session.+--+-- This uses the default session manager settings, but does not manage+-- cookies.  It is intended for use with REST-like HTTP-based APIs,+-- which typically do not use cookies.+--+-- @since 0.5.2.0+newAPISession :: IO Session+newAPISession = newSessionControl Nothing defaultManagerSettings+ -- | Create a session, using the given manager settings.  This session -- manages cookies. withSessionWith :: HTTP.ManagerSettings -> (Session -> IO a) -> IO a withSessionWith = withSessionControl (Just (HTTP.createCookieJar []))-{-# DEPRECATED withSessionWith "Use withSessionControl instead." #-}+{-# DEPRECATED withSessionWith "Use newSessionControl instead." #-}  -- | Create a session, using the given cookie jar and manager settings. withSessionControl :: Maybe HTTP.CookieJar@@ -113,13 +145,33 @@                -> HTTP.ManagerSettings                -> (Session -> IO a) -> IO a withSessionControl mj settings act = do-  mref <- maybe (return Nothing) (fmap Just . newIORef) mj-  mgr <- HTTP.newManager settings-  act Session { seshCookies = mref-              , seshManager = mgr-              , seshRun = runWith-              }+    sess <- newSessionControl mj settings+    act sess+{-# DEPRECATED withSessionControl "Use newSessionControl instead." #-} +-- | Create a session, using the given cookie jar and manager settings.+--+-- @since 0.5.2.0+newSessionControl ::  Maybe HTTP.CookieJar+                  -- ^ If 'Nothing' is specified, no cookie management+                  -- will be performed.+               -> HTTP.ManagerSettings+               -> IO Session+newSessionControl mj settings = do+     mref <- maybe (return Nothing) (fmap Just . newIORef) mj+     mgr <- HTTP.newManager settings+     return Session { seshCookies = mref+                     , seshManager = mgr+                     , seshRun = runWith+                     , seshRunHistory = runWithHistory+                     }++-- | Extract current 'Network.HTTP.Client.CookieJar' from a 'Session'+--+-- @since 0.5.2.0+getSessionCookieJar :: Session -> IO (Maybe HTTP.CookieJar)+getSessionCookieJar = traverse readIORef . seshCookies+ -- | 'Session'-specific version of 'Network.Wreq.get'. get :: Session -> String -> IO (Response L.ByteString) get = getWith defaults@@ -181,6 +233,15 @@   where     methodBS = BC8.pack method +-- | 'Session'-specific version of 'Network.Wreq.customHistoriedMethodWith'.+--+-- @since 0.5.2.0+customHistoriedMethodWith :: String -> Options -> Session -> String -> IO (HistoriedResponse L.ByteString)+customHistoriedMethodWith method opts sesh url =+    runHistory stringHistory sesh =<< prepareMethod methodBS opts url+  where+    methodBS = BC8.pack method+ -- | 'Session'-specific version of 'Network.Wreq.customPayloadMethodWith'. customPayloadMethodWith :: Postable a => String -> Options -> Session -> String -> a                         -> IO (Response L.ByteString)@@ -189,24 +250,47 @@   where     methodBS = BC8.pack method -runWith :: Session -> Run Body -> Run Body-runWith Session{..} act (Req _ req) = do-  req' <- case seshCookies of-            Nothing -> return (req & Lens.cookieJar .~ Nothing)-            Just ref -> (\s -> req & Lens.cookieJar ?~ s) `fmap` readIORef ref+-- | 'Session'-specific version of 'Network.Wreq.customHistoriedPayloadMethodWith'.+--+-- @since 0.5.2.0+customHistoriedPayloadMethodWith :: Postable a => String -> Options -> Session -> String -> a+                        -> IO (HistoriedResponse L.ByteString)+customHistoriedPayloadMethodWith method opts sesh url payload =+  runHistory stringHistory sesh =<< preparePayloadMethod methodBS opts url payload+  where+    methodBS = BC8.pack method+++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   resp <- act (Req (Right seshManager) req')   forM_ seshCookies $ \ref ->-    writeIORef ref (HTTP.responseCookieJar resp)+    writeIORef ref (HTTP.responseCookieJar (extract resp))   return resp +runWith :: Session -> Run Body -> Run Body+runWith = runWithGeneric id++runWithHistory :: Session -> RunHistory Body -> RunHistory Body+runWithHistory = runWithGeneric HTTP.hrFinalResponse+ type Mapping a = (Body -> a, a -> Body, Run a)+type MappingHistory a = (Body -> a, a -> Body, RunHistory a)  run :: Mapping a -> Session -> Run a run (to,from,act) sesh =   fmap (fmap to) . seshRun sesh sesh (fmap (fmap from) . act) +runHistory :: MappingHistory a -> Session -> RunHistory a+runHistory (to,from,act) sesh =+  fmap (fmap to) . seshRunHistory sesh sesh (fmap (fmap from) . act)+ string :: Mapping L.ByteString string = (\(StringBody s) -> s, StringBody, runRead) +stringHistory :: MappingHistory L.ByteString+stringHistory = (\(StringBody s) -> s, StringBody, runReadHistory)+ ignore :: Mapping ()-ignore = (\_ -> (), const NoBody, runIgnore)+ignore = (const (), const NoBody, runIgnore)
Network/Wreq/Types.hs view
@@ -41,7 +41,7 @@ import Data.Aeson (Value, encode) import Data.Int (Int8, Int16, Int32, Int64) import Data.Word (Word, Word8, Word16, Word32, Word64)-import Network.HTTP.Client (Request)+import Network.HTTP.Client (Request(method)) import Network.HTTP.Client.MultipartFormData (Part, formDataBody) import Network.Wreq.Internal.Types import qualified Data.ByteString as S@@ -53,37 +53,40 @@ import qualified Network.HTTP.Client as HTTP import qualified Network.Wreq.Internal.Lens as Lens -instance Postable Part where-    postPayload a = postPayload [a]--instance Postable [Part] where-    postPayload = formDataBody--instance Postable [(S.ByteString, S.ByteString)] where-    postPayload ps req = return $ HTTP.urlEncodedBody ps req--instance Postable (S.ByteString, S.ByteString) where-    postPayload p = postPayload [p]--instance Postable [FormParam] where-    postPayload ps = postPayload (map f ps)-      where f (a := b) = (a, renderFormValue b)+-- By default if the type is Putable, we use that as postPayload+instance Postable Part+instance Postable [Part]+instance Postable [(S.ByteString, S.ByteString)]+instance Postable (S.ByteString, S.ByteString)+instance Postable [FormParam]+instance Postable FormParam+instance Postable Payload+instance Postable S.ByteString+instance Postable L.ByteString+instance Postable Value -instance Postable FormParam where-    postPayload p = postPayload [p]+instance Putable Part where+    putPayload a = putPayload [a] -instance Postable Payload where-    postPayload = putPayload+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 -instance Postable S.ByteString where-    postPayload = putPayload+instance Putable [(S.ByteString, S.ByteString)] where+    putPayload ps req =+        -- According to doc, urlEncodedBody changes the request type to POST which is wrong; change it back+        return $ HTTP.urlEncodedBody ps req {method=method req} -instance Postable L.ByteString where-    postPayload = putPayload+instance Putable (S.ByteString, S.ByteString) where+    putPayload p = putPayload [p] -instance Postable Value where-    postPayload = putPayload+instance Putable [FormParam] where+    putPayload ps = putPayload (map f ps)+      where f (a := b) = (a, renderFormValue b) +instance Putable FormParam where+    putPayload p = putPayload [p]  instance Putable Payload where     putPayload pl =
Setup.hs view
@@ -1,6 +1,33 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -Wall #-} module Main (main) where +#ifndef MIN_VERSION_cabal_doctest+#define MIN_VERSION_cabal_doctest(x,y,z) 0+#endif++#if MIN_VERSION_cabal_doctest(1,0,0)++import Distribution.Extra.Doctest ( defaultMainWithDoctests )+main :: IO ()+main = defaultMainWithDoctests "doctests"++#else++#ifdef MIN_VERSION_Cabal+-- If the macro is defined, we have new cabal-install,+-- but for some reason we don't have cabal-doctest in package-db+--+-- Probably we are running cabal sdist, when otherwise using new-build+-- workflow+#warning You are configuring this package without cabal-doctest installed. \+         The doctests test-suite will not work as a result. \+         To fix this, install cabal-doctest before configuring.+#endif+ import Distribution.Simple  main :: IO () main = defaultMain++#endif
TODO.md view
@@ -2,8 +2,6 @@  * More advanced tutorial-style documentation -* TLS server certificate verification- * An example that spiders a Haddock package's docs to validate its   internal and external links 
changelog.md view
@@ -1,5 +1,28 @@ -*- markdown -*- +2018-01-01 0.5.2.0++* Added some HistoriedResponse support++* Deprecated withSession, added newSession (to be inline with upstream http-client)++* Added same instances for Putable as for Postable (might be merged?)++* Added getSessionCookieJar to get cookies from a Session++* Fixed customPayloadMethod to follow the method (it was sometimes POST)++2017-12-23 0.5.1.1++* Add awsSessionTokenAuth (in addition to the existing awsAuth) to+  support AWS Session Token Service (AWS STS) credentials. These look+  like regular AWS credentials but have an additional session token as+  a 3rd element. This mechanism is needed to be able to (a) use EC2+  instance profiles, (b) make calls form AWS Lambda, (c) is useful for+  delegated role access (assumeRole within and across accounts), and (d)+  enables MFA-protected access scenarios.+  See tests/AWS/IAM.hs for a test and simple example.+ 2017-01-09 0.5.1.0  * Add Session-specific version of Network.Wreq.customPayloadMethodWith
httpbin/HttpBin/Server.hs view
@@ -141,9 +141,9 @@       params = Map.foldlWithKey' step Map.empty .                rqQueryParams $ req       wibble (k,v) = (decodeUtf8 (original k), decodeUtf8 v)-      rqHeaders = headers req-      hdrs = Map.fromList . map wibble . listHeaders $ rqHeaders-      url = case getHeader "Host" rqHeaders of+      rqHdrs = headers req+      hdrs = Map.fromList . map wibble . listHeaders $ rqHdrs+      url = case getHeader "Host" rqHdrs of               Nothing   -> []               Just host -> [("url", toJSON . decodeUtf8 $                                     "http://" <> host <> rqURI req)]
tests/AWS.hs view
@@ -14,12 +14,9 @@ that you are familiar with AWS concepts and the charging model.  ** ENABLING AWS TESTS **-For now, enable AWS tests by setting the WREQ_AWS_ACCESS_KEY_ID-env variable per below.--TODO| To enable AWS tests use the `-faws` flag as part of-TODO|   $ cabal configure --enable-tests -faws ...-TODO| To capture code coverage information, add the `-fdeveloper` flag.+To enable AWS tests use the `-faws` flag as part of+  $ cabal configure --enable-tests -faws ...+To capture code coverage information, add the `-fdeveloper` flag.  ** REQUIRED CLIENT CONFIGURATION ** The tests require two environment variables:@@ -76,20 +73,6 @@  tests :: IO Test tests = do-  -- TODO - use ... configure -faws ... in the future-  --        but couldn't figure out (yet) how to get-  --        a hold of the flag value in test code.-  -- Workaround: for now, the presence of the-  --   WREQ_AWS_ACCESS_KEY_ID-  -- env variable enables the tests.-  flag <- (getEnv "WREQ_AWS_ACCESS_KEY_ID" >> return True) `E.catch`-          \(_::IOException) -> return False-  tests0 flag--tests0 :: Bool -> IO Test-tests0 False =-  return $ testGroup "aws" [] -- skip AWS tests-tests0 True = do   region <- env "us-west-2" "WREQ_AWS_REGION"   key <- BS8.pack `fmap` getEnv "WREQ_AWS_ACCESS_KEY_ID"   secret <- BS8.pack `fmap` getEnv "WREQ_AWS_SECRET_ACCESS_KEY"
tests/AWS/IAM.hs view
@@ -1,19 +1,24 @@-{-# LANGUAGE OverloadedLists, OverloadedStrings #-}+{-# LANGUAGE OverloadedLists, OverloadedStrings, DeriveGeneric #-} module AWS.IAM (tests) where  import AWS.Aeson import Control.Concurrent (threadDelay) import Control.Lens hiding ((.=))-import Data.Aeson.Encode (encode)-import Data.Aeson.Lens (key, _String, values)+import Data.Aeson (encode)+import Data.Aeson.Lens (key, _String, values, _Value)+import Data.Char (toUpper) import Data.IORef (IORef, readIORef, writeIORef) import Data.Text as T (Text, pack, unpack, split)+import Data.Text.Encoding (encodeUtf8) import Data.Text.Lazy as LT (toStrict) import Data.Text.Lazy.Encoding as E (decodeUtf8)+import GHC.Generics import Network.Wreq import Test.Framework (Test, testGroup) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit (assertBool)+import qualified Data.Aeson as A+import qualified Data.Aeson.Types as DAT  tests :: String -> String -> Options -> IORef String -> Test tests prefix region baseopts iamTestState = testGroup "iam" [@@ -111,8 +116,20 @@     elem (prefix ++ roleName) arns'  -- Security Token Service (STS)+data Cred = Cred {+    accessKeyId :: T.Text,+    secretAccessKey :: T.Text,+    sessionToken :: T.Text,+    expiration :: Int -- Unix epoch+  } deriving (Generic, Show, Eq)++instance A.FromJSON Cred where+  parseJSON = DAT.genericParseJSON $ DAT.defaultOptions {+      DAT.fieldLabelModifier = \(h:t) -> toUpper h:t+    }+ stsAssumeRole :: String -> String -> Options -> IORef String -> IO ()-stsAssumeRole _prefix region baseopts iamTestState = do+stsAssumeRole prefix region baseopts iamTestState = do   arn <- readIORef iamTestState   let opts = baseopts              & param  "Action"  .~ ["AssumeRole"]@@ -122,8 +139,48 @@              & param  "RoleSessionName" .~ ["Bob"]              & header "Accept"  .~ ["application/json"]   r <- getWith opts (stsUrl region) -- STS call (part of IAM service family)+  let v = r ^? responseBody+            . key "AssumeRoleResponse"+            . key "AssumeRoleResult"+            . key "Credentials"+            . _Value   assertBool "stsAssumeRole 200" $ r ^. responseStatus . statusCode == 200   assertBool "stsAssumeRole OK" $ r ^. responseStatus . statusMessage == "OK"++  -- Now, use the temporary credentials to call an AWS service+  let cred = conv v :: Cred+  let key' = encodeUtf8 $ accessKeyId cred+  let secret' = encodeUtf8 $ secretAccessKey cred+  let token' = encodeUtf8 $ sessionToken cred+  let baseopts2 = defaults+                  & auth ?~ awsSessionTokenAuth AWSv4 key' secret' token'+  let opts2 = baseopts2+              & param  "Action"  .~ ["ListRoles"]+              & param  "Version" .~ ["2010-05-08"]+              & header "Accept"  .~ ["application/json"]+  r2 <- getWith opts2 (iamUrl region)+  assertBool "listRoles 200" $ r2 ^. responseStatus . statusCode == 200+  assertBool "listRoles OK" $ r2 ^. responseStatus . statusMessage == "OK"+  let arns = r2 ^.. responseBody . key "ListRolesResponse" .+                                  key "ListRolesResult" .+                                  key "Roles" .+                                  values .+                                  key "Arn" . _String+  -- arns are of form: "arn:aws:iam::<acct>:role/ec2-role"+  let arns' = map (T.unpack . last . T.split (=='/')) arns+  assertBool "listRoles contains test role" $+    elem (prefix ++ roleName) arns'++  where+    conv :: DAT.FromJSON a => Maybe DAT.Value -> a+    conv v = case v of+      Nothing -> error "1"+      Just x ->+        case A.fromJSON x of+          A.Success r ->+            r+          A.Error e ->+            error $ show e  iamUrl :: String -> String iamUrl _ =
− tests/DocTests.hs
@@ -1,47 +0,0 @@--- I don't know who originally wrote this, but I picked it up from--- Edward Kmett's folds package, and subsequently modified it.--module Main where--import Build_doctest (deps)-import Control.Applicative ((<$>), (<*>))-import Control.Monad (filterM)-import Data.List (isPrefixOf, isSuffixOf)-import System.Directory--- import System.Environment (getArgs)-import System.FilePath ((</>))-import Test.DocTest (doctest)--main :: IO ()-main = do-  -- doctest chokes on the command line args that cabal test passes in-  -- args <- getArgs-  let args = []-  srcs <- getSources-  dist <- getDistDir-  doctest $ args ++ [ "-i."-            , "-i" ++ dist ++ "/build/autogen"-            , "-optP-include"-            , "-optP" ++ dist ++ "/build/autogen/cabal_macros.h"-            , "-hide-all-packages"-            ] ++ map ("-package="++) deps ++ srcs--getSources :: IO [FilePath]-getSources = filter (isSuffixOf ".hs") <$> go "Network"-  where-    go dir = do-      (dirs, files) <- getFilesAndDirectories dir-      (files ++) . concat <$> mapM go dirs--getFilesAndDirectories :: FilePath -> IO ([FilePath], [FilePath])-getFilesAndDirectories dir = do-  c <- map (dir </>) . filter (`notElem` ["..", "."]) <$>-       getDirectoryContents dir-  (,) <$> filterM doesDirectoryExist c <*> filterM doesFileExist c--getDistDir :: IO FilePath-getDistDir = do-  names <- getDirectoryContents "dist"-  return $ case filter ("dist-sandbox-" `isPrefixOf`) names of-             (d:_) -> "dist/" ++ d-             _     -> "dist"
tests/UnitTests.hs view
@@ -12,7 +12,7 @@ import Control.Exception (Exception, throwIO) import Control.Lens ((^.), (^?), (.~), (?~), (&), iso, ix, Traversal') import Control.Monad (unless, void)-import Data.Aeson+import Data.Aeson hiding (Options) import Data.Aeson.Lens (key, AsValue, _Object) import Data.ByteString (ByteString) import Data.Char (toUpper)@@ -287,7 +287,8 @@     (r ^? responseCookie "x" . cookieValue)  -cookieSession site = Session.withSession $ \s -> do+cookieSession site = do+  s <- Session.newSession   r0 <- Session.get s (site "/cookies/set?foo=bar")   assertEqual "after set foo, foo set" (Just "bar")     (r0 ^? responseCookie "foo" . cookieValue)@@ -366,7 +367,8 @@ -- Snap responds incorrectly to HEAD (by sending a response body), -- thereby killing http-client's ability to continue a session. -- https://github.com/snapframework/snap-core/issues/192-snapHeadSessionBug site = Session.withSession $ \s -> do+snapHeadSessionBug site = do+  s <- Session.newSession   basicHead (session s) site   -- will crash with (InvalidStatusLine "0")   basicGet (session s) site@@ -402,8 +404,8 @@ testWith :: [Test] -> IO () testWith tests = do   (tid, mserv) <- startServer-  Session.withSession $ \s ->-    flip E.finally (killThread tid) .+  s <- Session.newSession+  flip E.finally (killThread tid) .     defaultMain $ tests <>                   [ testGroup "plain" $ httpbinTests basic                   , testGroup "session" $ httpbinTests (session s)] <>
+ tests/doctests.hs view
@@ -0,0 +1,12 @@+module Main where++import Build_doctests (flags, pkgs, module_sources)+import Data.Foldable (traverse_)+import Test.DocTest (doctest)++main :: IO ()+main = do+    traverse_ putStrLn args+    doctest args+  where+    args = flags ++ pkgs ++ module_sources
wreq.cabal view
@@ -1,5 +1,5 @@ name:                wreq-version:             0.5.1.0+version:             0.5.2.0 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.10+cabal-version:       >=1.24 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@@ -48,6 +48,10 @@   www/*.md   www/Makefile +custom-setup+  setup-depends:+    base, Cabal, cabal-doctest >=1.0.2 && <1.1+ -- disable doctests with -f-doctest flag doctest   description: enable doctest tests@@ -118,7 +122,7 @@     time-locale-compat,     template-haskell,     text,-    time,+    time >= 1.5,     unordered-containers  -- A convenient server for testing locally, or if httpbin.org is down.@@ -207,14 +211,14 @@     vector,     wreq -test-suite doctest+test-suite doctests   type:           exitcode-stdio-1.0   hs-source-dirs: tests-  main-is:        DocTests.hs+  main-is:        doctests.hs   ghc-options:    -Wall -fwarn-tabs -threaded   if flag(developer)     ghc-options:  -Werror-  default-language: Haskell98+  default-language: Haskell2010    if !flag(doctest)     buildable: False