req 1.0.0 → 1.1.0
raw patch · 7 files changed
+99/−50 lines, 7 filesdep ~aeson
Dependency ranges changed: aeson
Files
- CHANGELOG.md +7/−0
- LICENSE.md +1/−1
- Network/HTTP/Req.hs +50/−35
- README.md +8/−4
- httpbin-tests/Network/HTTP/ReqSpec.hs +1/−1
- pure-tests/Network/HTTP/ReqSpec.hs +15/−0
- req.cabal +17/−9
CHANGELOG.md view
@@ -1,3 +1,10 @@+## Req 1.1.0++* Added `customAuth` and `attachHeader` to facilitate creation of custom+ authentication options.++* Added `basicProxyAuth` authentication option.+ ## Req 1.0.0 * Added the `reqBr` function allowing to consume `Response BodyReader`
LICENSE.md view
@@ -1,4 +1,4 @@-Copyright © 2016–2017 Mark Karpov+Copyright © 2016–2018 Mark Karpov All rights reserved.
Network/HTTP/Req.hs view
@@ -1,6 +1,6 @@ -- | -- Module : Network.HTTP.Req--- Copyright : © 2016–2017 Mark Karpov+-- Copyright : © 2016–2018 Mark Karpov -- License : BSD 3 clause -- -- Maintainer : Mark Karpov <markkarpov92@gmail.com>@@ -18,8 +18,8 @@ -- -- === About the library ----- Req is an easy-to-use, type-safe, expandable, high-level HTTP library--- that just works without any fooling around.+-- Req is an easy-to-use, type-safe, expandable, high-level HTTP client+-- library that just works without any fooling around. -- -- What does the phrase “easy-to-use” mean? It means that the library is -- designed to be beginner-friendly so it's simple to add to your monad@@ -170,6 +170,7 @@ , QueryParam (..) -- *** Headers , header+ , attachHeader -- *** Cookies -- $cookies , cookieJar@@ -177,9 +178,11 @@ -- $authentication , basicAuth , basicAuthUnsafe+ , basicProxyAuth , oAuth1 , oAuth2Bearer , oAuth2Token+ , customAuth -- *** Other , port , decompress@@ -311,41 +314,39 @@ -- > -- > module Main (main) where -- >--- > import Control.Exception (throwIO) -- > import Control.Monad+-- > import Control.Monad.IO.Class -- > import Data.Aeson+-- > import Data.Default.Class -- > import Data.Maybe (fromJust) -- > import Data.Monoid ((<>)) -- > import Data.Text (Text) -- > import GHC.Generics -- > import Network.HTTP.Req -- > import qualified Data.ByteString.Char8 as B--- >--- > instance MonadHttp IO where--- > handleHttpException = throwIO -- -- We will be making requests against the <https://httpbin.org> service. -- -- Make a GET request, grab 5 random bytes: -- -- > main :: IO ()--- > main = do+-- > main = runReq def $ do -- > let n :: Int -- > n = 5 -- > bs <- req GET (https "httpbin.org" /: "bytes" /~ n) NoReqBody bsResponse mempty--- > B.putStrLn (responseBody bs)+-- > liftIO $ B.putStrLn (responseBody bs) -- -- The same, but now we use a query parameter named @\"seed\"@ to control -- seed of the generator: -- -- > main :: IO ()--- > main = do+-- > main = runReq def $ do -- > let n, seed :: Int -- > n = 5 -- > seed = 100 -- > bs <- req GET (https "httpbin.org" /: "bytes" /~ n) NoReqBody bsResponse $ -- > "seed" =: seed--- > B.putStrLn (responseBody bs)+-- > liftIO $ B.putStrLn (responseBody bs) -- -- POST JSON data and get some info about the POST request: --@@ -358,27 +359,27 @@ -- > instance FromJSON MyData -- > -- > main :: IO ()--- > main = do+-- > main = runReq def $ do -- > let myData = MyData -- > { size = 6 -- > , color = "Green" } -- > v <- req POST (https "httpbin.org" /: "post") (ReqBodyJson myData) jsonResponse mempty--- > print (responseBody v :: Value)+-- > liftIO $ print (responseBody v :: Value) -- -- Sending URL-encoded body: -- -- > main :: IO ()--- > main = do+-- > main = runReq def $ do -- > let params = -- > "foo" =: ("bar" :: Text) <> -- > queryFlag "baz" -- > response <- req POST (https "httpbin.org" /: "post") (ReqBodyUrlEnc params) jsonResponse mempty--- > print (responseBody response :: Value)+-- > liftIO $ print (responseBody response :: Value) -- -- Using various optional parameters and URL that is not known in advance: -- -- > main :: IO ()--- > main = do+-- > main = runReq def $ do -- > -- This is an example of what to do when URL is given dynamically. Of -- > -- course in a real application you may not want to use 'fromJust'. -- > let (url, options) = fromJust (parseUrlHttps "https://httpbin.org/get?foo=bar")@@ -388,7 +389,7 @@ -- > basicAuth "username" "password" <> -- > options <> -- contains the ?foo=bar part -- > port 443 -- here you can put any port of course--- > print (responseBody response :: Value)+-- > liftIO $ print (responseBody response :: Value) req :: ( MonadHttp m@@ -1012,17 +1013,15 @@ -- -- @since 0.2.0 ----- ==== __Example__+-- ==== __Examples__ ----- > import Control.Exception (throwIO)--- > import qualified Network.HTTP.Client.MultipartFormData as LM+-- > import Control.Monad.IO.Class+-- > import Data.Default.Class -- > import Network.HTTP.Req--- >--- > instance MonadHttp IO where--- > handleHttpException = throwIO+-- > import qualified Network.HTTP.Client.MultipartFormData as LM -- > -- > main :: IO ()--- > main = do+-- > main = runReq def $ do -- > body <- -- > reqBodyMultipart -- > [ LM.partBS "title" "My Image"@@ -1033,7 +1032,7 @@ -- > body -- > bsResponse -- > mempty--- > print $ responseBody response+-- > liftIO $ print (responseBody response) data ReqBodyMultipart = ReqBodyMultipart ByteString LI.RequestBody @@ -1147,12 +1146,6 @@ withRequest :: (L.Request -> L.Request) -> Option scheme withRequest f = Option (Endo (second f)) Nothing --- | A helper to create an 'Option' that adds a finalizer (an IO-enabled--- request transformation that is applied after all other modifications).--asFinalizer :: (L.Request -> IO L.Request) -> Option scheme-asFinalizer = Option mempty . pure- instance RequestComponent (Option scheme) where getRequestMod (Option f _) = Endo $ \x -> let (qparams, x') = appEndo f ([], x)@@ -1233,6 +1226,8 @@ -- | A non-public helper that attaches a header with given name and content -- to a 'L.Request'.+--+-- @since 1.1.0 attachHeader :: ByteString -> ByteString -> L.Request -> L.Request attachHeader name value x =@@ -1287,9 +1282,20 @@ :: ByteString -- ^ Username -> ByteString -- ^ Password -> Option scheme -- ^ Auth 'Option'-basicAuthUnsafe username password = asFinalizer+basicAuthUnsafe username password = customAuth (pure . L.applyBasicAuth username password) +-- | The 'Option' set basic proxy authentication header.+--+-- @since 1.1.0++basicProxyAuth+ :: ByteString -- ^ Username+ -> ByteString -- ^ Password+ -> Option scheme -- ^ Auth 'Option'+basicProxyAuth username password =+ withRequest (L.applyBasicProxyAuth username password)+ -- | The 'Option' adds OAuth1 authentication. -- -- @since 0.2.0@@ -1301,7 +1307,7 @@ -> ByteString -- ^ OAuth token secret -> Option scheme -- ^ Auth 'Option' oAuth1 consumerToken consumerSecret token tokenSecret =- asFinalizer (OAuth.signOAuth app creds)+ customAuth (OAuth.signOAuth app creds) where app = OAuth.newOAuth { OAuth.oauthConsumerKey = consumerToken@@ -1320,7 +1326,7 @@ oAuth2Bearer :: ByteString -- ^ Token -> Option 'Https -- ^ Auth 'Option'-oAuth2Bearer token = asFinalizer+oAuth2Bearer token = customAuth (pure . attachHeader "Authorization" ("Bearer " <> token)) -- | The 'Option' adds a not-quite-standard OAuth2 bearer token (that seems@@ -1336,8 +1342,17 @@ oAuth2Token :: ByteString -- ^ Token -> Option 'Https -- ^ Auth 'Option'-oAuth2Token token = asFinalizer+oAuth2Token token = customAuth (pure . attachHeader "Authorization" ("token " <> token))++-- | A helper to create custom authentication 'Option's. The given+-- 'IO'-enabled request transformation is applied after all other+-- modifications when constructing a request. Use wisely.+--+-- @since 1.1.0++customAuth :: (L.Request -> IO L.Request) -> Option scheme+customAuth = Option mempty . pure ---------------------------------------------------------------------------- -- Request—Optional parameters—Other
README.md view
@@ -5,11 +5,11 @@ [](http://stackage.org/nightly/package/req) [](http://stackage.org/lts/package/req) [](https://travis-ci.org/mrkkrp/req)-[](https://coveralls.io/github/mrkkrp/req?branch=master) * [Motivation and Req vs other libraries](#motivation-and-req-vs-other-libraries) * [Unsolved problems](#unsolved-problems) * [Related packages](#related-packages)+* [Blog posts](#blog-posts) * [Contribution](#contribution) * [License](#license) @@ -40,8 +40,8 @@ liftIO $ print (responseBody r :: Value) ``` -Req is an easy-to-use, type-safe, expandable, high-level HTTP library that-just works without any fooling around.+Req is an easy-to-use, type-safe, expandable, high-level HTTP client library+that just works without any fooling around. What does the phrase “easy-to-use” mean? It means that the library is designed to be beginner-friendly so it's simple to add to your monad stack,@@ -193,6 +193,10 @@ If you happen to have written a package that adds new features to Req, please submit a PR to include it in this list. +## Blog posts++* [Req 1.0.0, HTTP client, and streaming](https://markkarpov.com/post/req-1.0.0-http-client-and-streaming.html)+ ## Contribution Issues, bugs, and questions may be reported in [the GitHub issue tracker for@@ -202,6 +206,6 @@ ## License -Copyright © 2016–2017 Mark Karpov+Copyright © 2016–2018 Mark Karpov Distributed under BSD 3 clause license.
httpbin-tests/Network/HTTP/ReqSpec.hs view
@@ -210,7 +210,7 @@ NoReqBody ignoreResponse mempty responseStatusCode r `shouldBe` status - forM_ [101..102] checkStatusCode+ -- forM_ [101..102] checkStatusCode forM_ [200..208] checkStatusCode -- forM_ [300..308] checkStatusCode forM_ [400..431] checkStatusCode
pure-tests/Network/HTTP/ReqSpec.hs view
@@ -37,6 +37,7 @@ import qualified Data.Text.Encoding as T import qualified Network.HTTP.Client as L import qualified Network.HTTP.Types as Y+import qualified Network.HTTP.Types.Header as Y spec :: Spec spec = do@@ -238,6 +239,12 @@ (oAuth2Bearer token0 <> oAuth2Bearer token1) lookup "Authorization" (L.requestHeaders request) `shouldBe` pure ("Bearer " <> token0)+ describe "ProxyAuthorization" $ do+ it "sets Authorization header to correct value" $+ property $ \username password -> do+ request <- req_ GET url NoReqBody (basicProxyAuth username password)+ lookup "Proxy-Authorization" (L.requestHeaders request) `shouldBe`+ pure (basicProxyAuthHeader username password) describe "oAuth2Token" $ do it "sets Authorization header to correct value" $ property $ \token -> do@@ -447,3 +454,11 @@ basicAuthHeader username password = fromJust . lookup Y.hAuthorization . L.requestHeaders $ L.applyBasicAuth username password L.defaultRequest++-- | Get "Proxy-Authorization" basic proxy auth header given username and+-- password.++basicProxyAuthHeader :: ByteString -> ByteString -> ByteString+basicProxyAuthHeader username password =+ fromJust . lookup Y.hProxyAuthorization . L.requestHeaders $+ L.applyBasicProxyAuth username password L.defaultRequest
req.cabal view
@@ -1,7 +1,7 @@ name: req-version: 1.0.0-cabal-version: >= 1.18-tested-with: GHC==7.10.3, GHC==8.0.2, GHC==8.2.2+version: 1.1.0+cabal-version: 1.18+tested-with: GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.2 license: BSD3 license-file: LICENSE.md author: Mark Karpov <markkarpov92@gmail.com>@@ -9,9 +9,9 @@ homepage: https://github.com/mrkkrp/req bug-reports: https://github.com/mrkkrp/req/issues category: Network, Web-synopsis: Easy-to-use, type-safe, expandable, high-level HTTP library+synopsis: Easy-to-use, type-safe, expandable, high-level HTTP client library build-type: Simple-description: Easy-to-use, type-safe, expandable, high-level HTTP library.+description: Easy-to-use, type-safe, expandable, high-level HTTP client library. extra-doc-files: CHANGELOG.md , README.md data-files: httpbin-data/utf8.html@@ -27,7 +27,7 @@ default: False library- build-depends: aeson >= 0.9 && < 1.3+ build-depends: aeson >= 0.9 && < 1.4 , authenticate-oauth >= 1.5 && < 1.7 , base >= 4.8 && < 5.0 , blaze-builder >= 0.3 && < 0.5@@ -53,6 +53,12 @@ ghc-options: -O0 -Wall -Werror else ghc-options: -O2 -Wall+ if flag(dev) && impl(ghc >= 8.0)+ ghc-options: -Wcompat+ -Wincomplete-record-updates+ -Wincomplete-uni-patterns+ -Wnoncanonical-monad-instances+ -Wnoncanonical-monadfail-instances default-language: Haskell2010 test-suite pure-tests@@ -61,7 +67,7 @@ hs-source-dirs: pure-tests type: exitcode-stdio-1.0 build-depends: QuickCheck >= 2.7 && < 3.0- , aeson >= 0.9 && < 1.3+ , aeson >= 0.9 && < 1.4 , base >= 4.8 && < 5.0 , blaze-builder >= 0.3 && < 0.5 , bytestring >= 0.10.8 && < 0.11@@ -75,6 +81,7 @@ , req , text >= 0.2 && < 1.3 , time >= 1.2 && < 1.9+ build-tools: hspec-discover >= 2.0 && < 3.0 if flag(dev) ghc-options: -O0 -Wall -Werror else@@ -87,7 +94,7 @@ hs-source-dirs: httpbin-tests type: exitcode-stdio-1.0 build-depends: QuickCheck >= 2.7 && < 3.0- , aeson >= 0.9 && < 1.3+ , aeson >= 0.9 && < 1.4 , base >= 4.8 && < 5.0 , bytestring >= 0.10.8 && < 0.11 , data-default-class@@ -98,7 +105,8 @@ , mtl >= 2.0 && < 3.0 , req , text >= 0.2 && < 1.3- , unordered-containers >= 0.2.5 && < 0.2.9+ , unordered-containers >= 0.2.5 && < 0.2.10+ build-tools: hspec-discover >= 2.0 && < 3.0 if flag(dev) ghc-options: -O0 -Wall -Werror else