req 3.12.0 → 3.13.4
raw patch · 6 files changed
Files
- CHANGELOG.md +31/−0
- Network/HTTP/Req.hs +71/−75
- Setup.hs +0/−6
- httpbin-tests/Network/HTTP/ReqSpec.hs +74/−42
- pure-tests/Network/HTTP/ReqSpec.hs +18/−18
- req.cabal +42/−42
CHANGELOG.md view
@@ -1,3 +1,34 @@+## Req 3.13.4++* Fixed empty ciphersuite list when compiling against `tls < 2.0.6`+ (see [PR 175](https://github.com/mrkkrp/req/pull/175)). As a side effect, now+ compatible with older versions of `crypton-connection` (>= 0.3).++## Req 3.13.3++* Works with `crypton-connection-0.4` and newer.++## Req 3.13.2++* Disable the problematic `httpbin-tests` test suite by default. Only enable+ it when the `dev` flag is enabled. In that case it is expected that an+ httpbin server is run locally at `localhost:1234`.++## Req 3.13.1++* Switched the non-pure test suite to use https://httpbun.org instead of+ https://httpbin.org since the latter proved to be highly unreliable+ lately.++* Switched from `connection` to `crypton-connection`.++* Builds with GHC 9.6.1.++## Req 3.13.0++* Add `headerRedacted` function to add header fields, which will be with+ redacted values on print.+ ## Req 3.12.0 * Add `isStatusCodeException` function.
Network/HTTP/Req.hs view
@@ -1,20 +1,9 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveLift #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RoleAnnotations #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskellQuotes #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-}@@ -161,6 +150,7 @@ -- *** Headers header, attachHeader,+ headerRedacted, -- *** Cookies -- $cookies@@ -213,69 +203,72 @@ ) where -import qualified Blaze.ByteString.Builder as BB+import Blaze.ByteString.Builder qualified as BB import Control.Applicative import Control.Arrow (first, second) import Control.Exception hiding (Handler (..), TypeError)+import Control.Monad (guard, void, (>=>)) import Control.Monad.Base import Control.Monad.Catch (Handler (..), MonadCatch, MonadMask, MonadThrow) import Control.Monad.IO.Class import Control.Monad.IO.Unlift-import Control.Monad.Reader+import Control.Monad.Reader (ReaderT (ReaderT), ask, lift, runReaderT) import Control.Monad.Trans.Accum (AccumT) import Control.Monad.Trans.Cont (ContT) import Control.Monad.Trans.Control import Control.Monad.Trans.Except (ExceptT) import Control.Monad.Trans.Identity (IdentityT) import Control.Monad.Trans.Maybe (MaybeT)-import qualified Control.Monad.Trans.RWS.CPS as RWS.CPS-import qualified Control.Monad.Trans.RWS.Lazy as RWS.Lazy-import qualified Control.Monad.Trans.RWS.Strict as RWS.Strict+import Control.Monad.Trans.RWS.CPS qualified as RWS.CPS+import Control.Monad.Trans.RWS.Lazy qualified as RWS.Lazy+import Control.Monad.Trans.RWS.Strict qualified as RWS.Strict import Control.Monad.Trans.Select (SelectT)-import qualified Control.Monad.Trans.State.Lazy as State.Lazy-import qualified Control.Monad.Trans.State.Strict as State.Strict-import qualified Control.Monad.Trans.Writer.CPS as Writer.CPS-import qualified Control.Monad.Trans.Writer.Lazy as Writer.Lazy-import qualified Control.Monad.Trans.Writer.Strict as Writer.Strict+import Control.Monad.Trans.State.Lazy qualified as State.Lazy+import Control.Monad.Trans.State.Strict qualified as State.Strict+import Control.Monad.Trans.Writer.CPS qualified as Writer.CPS+import Control.Monad.Trans.Writer.Lazy qualified as Writer.Lazy+import Control.Monad.Trans.Writer.Strict qualified as Writer.Strict import Control.Retry import Data.Aeson (FromJSON (..), ToJSON (..))-import qualified Data.Aeson as A+import Data.Aeson qualified as A import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as BL-import qualified Data.CaseInsensitive as CI+import Data.ByteString qualified as B+import Data.ByteString.Lazy qualified as BL+import Data.CaseInsensitive qualified as CI import Data.Data (Data)+import Data.Default.Class (def) import Data.Function (on) import Data.IORef import Data.Kind (Constraint, Type) import Data.List (foldl', nubBy) import Data.List.NonEmpty (NonEmpty (..))-import qualified Data.List.NonEmpty as NE+import Data.List.NonEmpty qualified as NE import Data.Maybe (fromMaybe) import Data.Proxy import Data.Semigroup (Endo (..))+import Data.Set qualified as S import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Text.Encoding as T+import Data.Text qualified as T+import Data.Text.Encoding qualified as T import Data.Typeable (Typeable, cast) import GHC.Generics import GHC.TypeLits-import qualified Language.Haskell.TH as TH-import qualified Language.Haskell.TH.Quote as TH-import qualified Language.Haskell.TH.Syntax as TH-import qualified Network.Connection as NC-import qualified Network.HTTP.Client as L-import qualified Network.HTTP.Client.Internal as LI-import qualified Network.HTTP.Client.MultipartFormData as LM-import qualified Network.HTTP.Client.TLS as L-import qualified Network.HTTP.Types as Y+import Language.Haskell.TH qualified as TH+import Language.Haskell.TH.Quote qualified as TH+import Language.Haskell.TH.Syntax qualified as TH+import Network.Connection qualified as NC+import Network.HTTP.Client qualified as L+import Network.HTTP.Client.Internal qualified as LI+import Network.HTTP.Client.MultipartFormData qualified as LM+import Network.HTTP.Client.TLS qualified as L+import Network.HTTP.Types qualified as Y import System.IO.Unsafe (unsafePerformIO) import Text.URI (URI)-import qualified Text.URI as URI-import qualified Text.URI.QQ as QQ-import qualified Web.Authenticate.OAuth as OAuth+import Text.URI qualified as URI+import Text.URI.QQ qualified as QQ+import Web.Authenticate.OAuth qualified as OAuth import Web.FormUrlEncoded (FromForm (..), ToForm (..))-import qualified Web.FormUrlEncoded as Form+import Web.FormUrlEncoded qualified as Form import Web.HttpApiData (ToHttpApiData (..)) ----------------------------------------------------------------------------@@ -496,7 +489,7 @@ -- -- @since 3.7.0 reqHandler ::- MonadHttp m =>+ (MonadHttp m) => -- | How to get final result from a 'L.Response' (L.Response L.BodyReader -> IO b) -> -- | 'L.Request' to perform@@ -594,7 +587,7 @@ -- | Perform an action using the global implicit 'L.Manager' that the rest -- of the library uses. This allows to reuse connections that the -- 'L.Manager' controls.-withReqManager :: MonadIO m => (L.Manager -> m a) -> m a+withReqManager :: (MonadIO m) => (L.Manager -> m a) -> m a withReqManager m = liftIO (readIORef globalManager) >>= m -- | The global 'L.Manager' that 'req' uses. Here we just go with the@@ -614,7 +607,7 @@ let settings = L.mkManagerSettingsContext (Just context)- (NC.TLSSettingsSimple False False False)+ def Nothing manager <- L.newManager settings newIORef manager@@ -637,7 +630,7 @@ -- | A type class for monads that support performing HTTP requests. -- Typically, you only need to define the 'handleHttpException' method -- unless you want to tweak 'HttpConfig'.-class MonadIO m => MonadHttp m where+class (MonadIO m) => MonadHttp m where -- | This method describes how to deal with 'HttpException' that was -- caught by the library. One option is to re-throw it if you are OK with -- exceptions, but if you prefer working with something like@@ -721,7 +714,7 @@ -- | Max length of preview fragment of response body. -- -- @since 3.6.0- httpConfigBodyPreviewLength :: forall a. Num a => a+ httpConfigBodyPreviewLength :: forall a. (Num a) => a } deriving (Typeable) @@ -811,27 +804,27 @@ getHttpConfig = lift getHttpConfig -- | @since 3.10.0-instance MonadHttp m => MonadHttp (ContT r m) where+instance (MonadHttp m) => MonadHttp (ContT r m) where handleHttpException = lift . handleHttpException getHttpConfig = lift getHttpConfig -- | @since 3.10.0-instance MonadHttp m => MonadHttp (ExceptT e m) where+instance (MonadHttp m) => MonadHttp (ExceptT e m) where handleHttpException = lift . handleHttpException getHttpConfig = lift getHttpConfig -- | @since 3.10.0-instance MonadHttp m => MonadHttp (IdentityT m) where+instance (MonadHttp m) => MonadHttp (IdentityT m) where handleHttpException = lift . handleHttpException getHttpConfig = lift getHttpConfig -- | @since 3.10.0-instance MonadHttp m => MonadHttp (MaybeT m) where+instance (MonadHttp m) => MonadHttp (MaybeT m) where handleHttpException = lift . handleHttpException getHttpConfig = lift getHttpConfig -- | @since 3.10.0-instance MonadHttp m => MonadHttp (ReaderT r m) where+instance (MonadHttp m) => MonadHttp (ReaderT r m) where handleHttpException = lift . handleHttpException getHttpConfig = lift getHttpConfig @@ -851,17 +844,17 @@ getHttpConfig = lift getHttpConfig -- | @since 3.10.0-instance MonadHttp m => MonadHttp (SelectT r m) where+instance (MonadHttp m) => MonadHttp (SelectT r m) where handleHttpException = lift . handleHttpException getHttpConfig = lift getHttpConfig -- | @since 3.10.0-instance MonadHttp m => MonadHttp (State.Lazy.StateT s m) where+instance (MonadHttp m) => MonadHttp (State.Lazy.StateT s m) where handleHttpException = lift . handleHttpException getHttpConfig = lift getHttpConfig -- | @since 3.10.0-instance MonadHttp m => MonadHttp (State.Strict.StateT s m) where+instance (MonadHttp m) => MonadHttp (State.Strict.StateT s m) where handleHttpException = lift . handleHttpException getHttpConfig = lift getHttpConfig @@ -885,7 +878,7 @@ -- -- @since 0.4.0 runReq ::- MonadIO m =>+ (MonadIO m) => -- | 'HttpConfig' to use HttpConfig -> -- | Computation to run@@ -990,7 +983,7 @@ -- | Return name of the method as a 'ByteString'. httpMethodName :: Proxy a -> ByteString -instance HttpMethod method => RequestComponent (Tagged "method" method) where+instance (HttpMethod method) => RequestComponent (Tagged "method" method) where getRequestMod _ = Endo $ \x -> x {L.method = httpMethodName (Proxy :: Proxy method)} @@ -1038,19 +1031,14 @@ -- With template-haskell >=2.15 and text >=1.2.4 Lift can be derived, however -- the derived lift forgets the type of the scheme.-instance Typeable scheme => TH.Lift (Url scheme) where+instance (Typeable scheme) => TH.Lift (Url scheme) where lift url = TH.dataToExpQ (fmap liftText . cast) url `TH.sigE` case url of Url Http _ -> [t|Url 'Http|] Url Https _ -> [t|Url 'Https|] where liftText t = TH.AppE (TH.VarE 'T.pack) <$> TH.lift (T.unpack t)--#if MIN_VERSION_template_haskell(2,17,0) liftTyped = TH.Code . TH.unsafeTExpCoerce . TH.lift-#elif MIN_VERSION_template_haskell(2,16,0)- liftTyped = TH.unsafeTExpCoerce . TH.lift-#endif -- | Given host name, produce a 'Url' which has “http” as its scheme and -- empty path. This also sets port to @80@.@@ -1066,7 +1054,7 @@ -- path segment can be of any type that is an instance of 'ToHttpApiData'. infixl 5 /~ -(/~) :: ToHttpApiData a => Url scheme -> a -> Url scheme+(/~) :: (ToHttpApiData a) => Url scheme -> a -> Url scheme Url secure path /~ segment = Url secure (NE.cons (toUrlPiece segment) path) -- | A type-constrained version of @('/~')@ to remove ambiguity in the cases@@ -1254,7 +1242,7 @@ -- charset=utf-8\"@ value. newtype ReqBodyJson a = ReqBodyJson a -instance ToJSON a => HttpBody (ReqBodyJson a) where+instance (ToJSON a) => HttpBody (ReqBodyJson a) where getRequestBody (ReqBodyJson a) = L.RequestBodyLBS (A.encode a) getRequestContentType _ = pure "application/json; charset=utf-8" @@ -1353,7 +1341,7 @@ -- | Create 'ReqBodyMultipart' request body from a collection of 'LM.Part's. -- -- @since 0.2.0-reqBodyMultipart :: MonadIO m => [LM.Part] -> m ReqBodyMultipart+reqBodyMultipart :: (MonadIO m) => [LM.Part] -> m ReqBodyMultipart reqBodyMultipart parts = liftIO $ do boundary <- LM.webkitBoundary body <- LM.renderParts boundary parts@@ -1393,7 +1381,7 @@ TypeError ('Text "This HTTP method does not allow attaching a request body.") -instance HttpBody body => RequestComponent (Tagged "body" body) where+instance (HttpBody body) => RequestComponent (Tagged "body" body) where getRequestMod (Tagged body) = Endo $ \x -> x { L.requestBody = getRequestBody body,@@ -1463,7 +1451,7 @@ -- | Finalize given 'L.Request' by applying a finalizer from the given -- 'Option' (if it has any).-finalizeRequest :: MonadIO m => Option scheme -> L.Request -> m L.Request+finalizeRequest :: (MonadIO m) => Option scheme -> L.Request -> m L.Request finalizeRequest (Option _ mfinalizer) = liftIO . fromMaybe pure mfinalizer ----------------------------------------------------------------------------@@ -1496,7 +1484,7 @@ -- This operator is defined in terms of 'queryParam': -- -- > queryFlag name = queryParam name (Nothing :: Maybe ())-queryFlag :: QueryParam param => Text -> param+queryFlag :: (QueryParam param) => Text -> param queryFlag name = queryParam name (Nothing :: Maybe ()) -- | Construct query parameters from a 'ToForm' instance. This function@@ -1526,7 +1514,7 @@ -- 'Nothing', it won't be included at all (i.e. you create a flag this -- way). It's recommended to use @('=:')@ and 'queryFlag' instead of this -- method, because they are easier to read.- queryParam :: ToHttpApiData a => Text -> Maybe a -> param+ queryParam :: (ToHttpApiData a) => Text -> Maybe a -> param -- | Get the query parameter names and values set by 'queryParam'. --@@ -1560,6 +1548,14 @@ attachHeader name value x = x {L.requestHeaders = (CI.mk name, value) : L.requestHeaders x} +-- | Same as 'header', but with redacted values on print.+--+-- @since 3.13.0+headerRedacted :: ByteString -> ByteString -> Option scheme+headerRedacted name value = withRequest $ \x ->+ let y = attachHeader name value x+ in y {L.redactHeaders = CI.mk name `S.insert` L.redactHeaders y}+ ---------------------------------------------------------------------------- -- Request—Optional parameters—Cookies @@ -1767,7 +1763,7 @@ newtype JsonResponse a = JsonResponse (L.Response a) deriving (Show) -instance FromJSON a => HttpResponse (JsonResponse a) where+instance (FromJSON a) => HttpResponse (JsonResponse a) where type HttpResponseBody (JsonResponse a) = a toVanillaResponse (JsonResponse r) = r getHttpResponse r = do@@ -1883,14 +1879,14 @@ -- | Get the response body. responseBody ::- HttpResponse response =>+ (HttpResponse response) => response -> HttpResponseBody response responseBody = L.responseBody . toVanillaResponse -- | Get the response status code. responseStatusCode ::- HttpResponse response =>+ (HttpResponse response) => response -> Int responseStatusCode =@@ -1898,7 +1894,7 @@ -- | Get the response status message. responseStatusMessage ::- HttpResponse response =>+ (HttpResponse response) => response -> ByteString responseStatusMessage =@@ -1906,7 +1902,7 @@ -- | Lookup a particular header from a response. responseHeader ::- HttpResponse response =>+ (HttpResponse response) => -- | Response interpretation response -> -- | Header to lookup@@ -1918,7 +1914,7 @@ -- | Get the response 'L.CookieJar'. responseCookieJar ::- HttpResponse response =>+ (HttpResponse response) => response -> L.CookieJar responseCookieJar = L.responseCookieJar . toVanillaResponse
− Setup.hs
@@ -1,6 +0,0 @@-module Main (main) where--import Distribution.Simple--main :: IO ()-main = defaultMain
httpbin-tests/Network/HTTP/ReqSpec.hs view
@@ -8,24 +8,25 @@ module Network.HTTP.ReqSpec (spec) where import Control.Exception-import Control.Monad.Reader+import Control.Monad (forM_) import Control.Monad.Trans.Control import Data.Aeson (ToJSON (..), Value (..), object, (.=))-import qualified Data.Aeson as A-import qualified Data.Aeson.KeyMap as Aeson.KeyMap-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as BL+import Data.Aeson qualified as A+import Data.Aeson.KeyMap qualified as Aeson.KeyMap+import Data.ByteString qualified as B+import Data.ByteString.Lazy qualified as BL import Data.Functor.Identity (runIdentity) import Data.Maybe (fromJust) import Data.Proxy import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import qualified Data.Text.IO as TIO-import qualified Network.HTTP.Client as L-import qualified Network.HTTP.Client.MultipartFormData as LM-import Network.HTTP.Req-import qualified Network.HTTP.Types as Y+import Data.Text qualified as T+import Data.Text.Encoding qualified as T+import Data.Text.IO qualified as TIO+import Network.HTTP.Client qualified as L+import Network.HTTP.Client.MultipartFormData qualified as LM+import Network.HTTP.Req hiding (req)+import Network.HTTP.Req qualified as Req+import Network.HTTP.Types qualified as Y import Test.Hspec import Test.QuickCheck @@ -84,8 +85,7 @@ .= object [ "Accept-Encoding" .= ("gzip" :: Text), "Foo" .= ("bar" :: Text),- "Baz" .= ("quux" :: Text),- "Host" .= ("httpbin.org" :: Text)+ "Baz" .= ("quux" :: Text) ] ] responseStatusCode r `shouldBe` 200@@ -97,11 +97,10 @@ (stripFunnyHeaders . stripOrigin) (responseBody r) `shouldBe` object [ "args" .= emptyObject,- "url" .= ("https://httpbin.org/get" :: Text),+ "url" .= ("http://localhost:1234/get" :: Text), "headers" .= object- [ "Accept-Encoding" .= ("gzip" :: Text),- "Host" .= ("httpbin.org" :: Text)+ [ "Accept-Encoding" .= ("gzip" :: Text) ] ] responseHeader r "Content-Type" `shouldBe` return "application/json"@@ -118,12 +117,11 @@ [ "args" .= emptyObject, "json" .= text, "data" .= reflected,- "url" .= ("https://httpbin.org/post" :: Text),+ "url" .= ("http://localhost:1234/post" :: Text), "headers" .= object [ "Content-Type" .= ("application/json; charset=utf-8" :: Text), "Accept-Encoding" .= ("gzip" :: Text),- "Host" .= ("httpbin.org" :: Text), "Content-Length" .= show (T.length reflected) ], "files" .= emptyObject,@@ -147,12 +145,11 @@ [ "args" .= emptyObject, "json" .= Null, "data" .= ("" :: Text),- "url" .= ("https://httpbin.org/post" :: Text),+ "url" .= ("http://localhost:1234/post" :: Text), "headers" .= object [ "Content-Type" .= T.decodeUtf8 contentType, "Accept-Encoding" .= ("gzip" :: Text),- "Host" .= ("httpbin.org" :: Text), "Content-Length" .= ("242" :: Text) ], "files" .= emptyObject,@@ -177,11 +174,10 @@ [ "args" .= emptyObject, "json" .= Null, "data" .= contents,- "url" .= ("https://httpbin.org/patch" :: Text),+ "url" .= ("http://localhost:1234/patch" :: Text), "headers" .= object [ "Accept-Encoding" .= ("gzip" :: Text),- "Host" .= ("httpbin.org" :: Text), "Content-Length" .= show (T.length contents) ], "files" .= emptyObject,@@ -203,12 +199,11 @@ [ "args" .= emptyObject, "json" .= Null, "data" .= ("" :: Text),- "url" .= ("https://httpbin.org/put" :: Text),+ "url" .= ("http://localhost:1234/put" :: Text), "headers" .= object [ "Content-Type" .= ("application/x-www-form-urlencoded" :: Text), "Accept-Encoding" .= ("gzip" :: Text),- "Host" .= ("httpbin.org" :: Text), "Content-Length" .= ("18" :: Text) ], "files" .= emptyObject,@@ -306,7 +301,7 @@ (httpbin /: "basic-auth" /~ user /~ password) NoReqBody ignoreResponse- (basicAuth (T.encodeUtf8 user) (T.encodeUtf8 password))+ (basicAuthUnsafe (T.encodeUtf8 user) (T.encodeUtf8 password)) responseStatusCode r `shouldBe` 200 responseStatusMessage r `shouldBe` "OK" @@ -406,9 +401,29 @@ doit _ _ = error "Oops!" -- | 'Url' representing <https://httpbin.org>.-httpbin :: Url 'Https-httpbin = https "httpbin.org"+httpbin :: Url 'Http+httpbin = http "localhost" +req ::+ ( MonadHttp m,+ HttpMethod method,+ HttpBody body,+ HttpResponse response,+ HttpBodyAllowed (AllowsBody method) (ProvidesBody body)+ ) =>+ method ->+ Url scheme ->+ body ->+ Proxy response ->+ Option scheme ->+ m response+req method url body responseProxy options =+ Req.req method url body responseProxy (options <> defaultOptions)++-- | Options to apply by default.+defaultOptions :: Option scheme+defaultOptions = port 1234+ -- | Remove “origin” field from JSON value. Origin may change, we don't want -- to depend on that. stripOrigin :: Value -> Value@@ -417,20 +432,37 @@ -- | Remove funny headers that might break the tests. stripFunnyHeaders :: Value -> Value-stripFunnyHeaders (Object m) =- let f (Object p) = Object $ Aeson.KeyMap.filterWithKey (\k _ -> k `elem` hs) p- f value = value- hs =- [ "Content-Type",- "Accept-Encoding",- "Host",- "Content-Length",- "Foo",- "Baz"- ]- in Object (runIdentity (Aeson.KeyMap.alterF (pure . fmap f) "headers" m))-stripFunnyHeaders value = value+stripFunnyHeaders = \case+ Object m ->+ Object+ ( runIdentity+ ( Aeson.KeyMap.alterF+ (pure . fmap stripFunnyHeaders')+ "headers"+ m+ )+ )+ value -> value +-- | Similar to 'stripFunnyHeaders', but acts directly on the argument+-- without trying to access its "headers" field.+stripFunnyHeaders' :: Value -> Value+stripFunnyHeaders' = \case+ Object p ->+ Object $+ Aeson.KeyMap.filterWithKey+ (\k _ -> k `elem` whitelistedHeaders)+ p+ value -> value+ where+ whitelistedHeaders =+ [ "Content-Type",+ "Accept-Encoding",+ "Content-Length",+ "Foo",+ "Baz"+ ]+ -- | This is a complete test case that makes use of <https://httpbin.org> to -- get various response status codes. checkStatusCode :: Int -> SpecWith ()@@ -471,7 +503,7 @@ emptyObject = Object Aeson.KeyMap.empty -- | Get a rendered JSON value as 'Text'.-reflectJSON :: ToJSON a => a -> Text+reflectJSON :: (ToJSON a) => a -> Text reflectJSON = T.decodeUtf8 . BL.toStrict . A.encode -- | Clarify to the type checker that the inner computation is in the 'IO'
pure-tests/Network/HTTP/ReqSpec.hs view
@@ -13,41 +13,41 @@ module Network.HTTP.ReqSpec (spec) where -import qualified Blaze.ByteString.Builder as BB+import Blaze.ByteString.Builder qualified as BB import Control.Exception (throwIO) import Control.Monad import Control.Retry import Data.Aeson (ToJSON (..))-import qualified Data.Aeson as A+import Data.Aeson qualified as A import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as B8-import qualified Data.ByteString.Lazy as BL-import qualified Data.CaseInsensitive as CI+import Data.ByteString qualified as B+import Data.ByteString.Char8 qualified as B8+import Data.ByteString.Lazy qualified as BL+import Data.CaseInsensitive qualified as CI import Data.Either (isRight)-import qualified Data.List.NonEmpty as NE+import Data.List.NonEmpty qualified as NE import Data.Maybe (fromJust, fromMaybe, isJust, isNothing) import Data.Proxy import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Text.Encoding as T+import Data.Text qualified as T+import Data.Text.Encoding qualified as T import Data.Time import Data.Typeable (Typeable, eqT) import GHC.Exts (IsList (..)) import GHC.Generics-import qualified Language.Haskell.TH as TH-import qualified Language.Haskell.TH.Quote as TH-import qualified Network.HTTP.Client as L+import Language.Haskell.TH qualified as TH+import Language.Haskell.TH.Quote qualified as TH+import Network.HTTP.Client qualified as L import Network.HTTP.Req-import qualified Network.HTTP.Types as Y-import qualified Network.HTTP.Types.Header as Y+import Network.HTTP.Types qualified as Y+import Network.HTTP.Types.Header qualified as Y import Test.Hspec import Test.Hspec.Core.Spec (SpecM) import Test.QuickCheck import Text.URI (URI)-import qualified Text.URI as URI-import qualified Text.URI.QQ as QQ-import qualified Web.FormUrlEncoded as F+import Text.URI qualified as URI+import Text.URI.QQ qualified as QQ+import Web.FormUrlEncoded qualified as F spec :: Spec spec = do@@ -413,7 +413,7 @@ -- 'Https, Option _) type checks, so we can catch if the type of scheme -- is unspecified. let testTypeOfQuoterResult ::- forall a s. Typeable a => (a, Option s) -> Bool+ forall a s. (Typeable a) => (a, Option s) -> Bool testTypeOfQuoterResult _ = isJust $ eqT @a @(Url 'Https) in property $ testTypeOfQuoterResult [urlQ|https://example.org/|] it "doesn't work for invalid urls" $
req.cabal view
@@ -1,11 +1,11 @@ cabal-version: 2.4 name: req-version: 3.12.0+version: 3.13.4 license: BSD-3-Clause license-file: LICENSE.md maintainer: Mark Karpov <markkarpov92@gmail.com> author: Mark Karpov <markkarpov92@gmail.com>-tested-with: ghc ==8.10.7 ghc ==9.0.2 ghc ==9.2.1+tested-with: ghc ==9.6.3 ghc ==9.8.2 ghc ==9.10.1 homepage: https://github.com/mrkkrp/req bug-reports: https://github.com/mrkkrp/req/issues synopsis: HTTP client library@@ -31,71 +31,70 @@ library exposed-modules: Network.HTTP.Req- default-language: Haskell2010+ default-language: GHC2021 build-depends: aeson >=0.9 && <3, authenticate-oauth >=1.5 && <1.8,- base >=4.13 && <5.0,+ base >=4.15 && <5, blaze-builder >=0.3 && <0.5,- bytestring >=0.10.8 && <0.12,+ bytestring >=0.10.8 && <0.13, case-insensitive >=0.2 && <1.3,- connection >=0.2.2 && <0.4,+ containers >=0.5 && <0.7,+ crypton-connection >=0.3 && <0.5,+ data-default-class, exceptions >=0.6 && <0.11,- http-api-data >=0.2 && <0.5,- http-client >=0.7 && <0.8,+ http-api-data >=0.2 && <0.7,+ http-client >=0.7.13.1 && <0.8, http-client-tls >=0.3.2 && <0.4,- http-types >=0.8 && <10.0,+ http-types >=0.8 && <10, modern-uri >=0.3 && <0.4, monad-control >=1.0 && <1.1, mtl >=2.0 && <3.0, retry >=0.8 && <0.10,- template-haskell >=2.14 && <2.19,- text >=0.2 && <2.1,- time >=1.2 && <1.13,- transformers >=0.5.3.0 && <0.6,+ template-haskell >=2.19 && <2.23,+ text >=0.2 && <2.2,+ transformers >=0.5.3.0 && <0.7, transformers-base, unliftio-core >=0.1.1 && <0.3 if flag(dev)- ghc-options: -O0 -Wall -Werror+ ghc-options:+ -Wall -Werror -Wpartial-fields -Wunused-packages+ -Wno-unused-imports else ghc-options: -O2 -Wall - if flag(dev)- ghc-options:- -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns- -Wnoncanonical-monad-instances- test-suite pure-tests type: exitcode-stdio-1.0 main-is: Spec.hs- build-tool-depends: hspec-discover:hspec-discover >=2.0 && <3.0+ build-tool-depends: hspec-discover:hspec-discover >=2 && <3 hs-source-dirs: pure-tests other-modules: Network.HTTP.ReqSpec- default-language: Haskell2010+ default-language: GHC2021 build-depends:- QuickCheck >=2.7 && <3.0,+ QuickCheck >=2.7 && <3, aeson >=0.9 && <3,- base >=4.13 && <5.0,+ base >=4.15 && <5.0, blaze-builder >=0.3 && <0.5,- bytestring >=0.10.8 && <0.12,+ bytestring >=0.10.8 && <0.13, case-insensitive >=0.2 && <1.3,- hspec >=2.0 && <3.0,- hspec-core >=2.0 && <3.0,- http-api-data >=0.2 && <0.5,+ hspec >=2.0 && <3,+ hspec-core >=2.0 && <3,+ http-api-data >=0.2 && <0.7, http-client >=0.7 && <0.8,- http-types >=0.8 && <10.0,+ http-types >=0.8 && <10, modern-uri >=0.3 && <0.4,- mtl >=2.0 && <3.0, req, retry >=0.8 && <0.10,- template-haskell >=2.14 && <2.19,- text >=0.2 && <2.1,+ template-haskell >=2.19 && <2.23,+ text >=0.2 && <2.2, time >=1.2 && <1.13 if flag(dev)- ghc-options: -O0 -Wall -Werror+ ghc-options:+ -Wall -Werror -Wredundant-constraints -Wpartial-fields+ -Wunused-packages else ghc-options: -O2 -Wall@@ -103,25 +102,26 @@ test-suite httpbin-tests type: exitcode-stdio-1.0 main-is: Spec.hs- build-tool-depends: hspec-discover:hspec-discover >=2.0 && <3.0+ build-tool-depends: hspec-discover:hspec-discover >=2 && <3 hs-source-dirs: httpbin-tests other-modules: Network.HTTP.ReqSpec- default-language: Haskell2010+ default-language: GHC2021 build-depends:- QuickCheck >=2.7 && <3.0,+ QuickCheck >=2.7 && <3, aeson >=2 && <3,- base >=4.13 && <5.0,- bytestring >=0.10.8 && <0.12,+ base >=4.15 && <5,+ bytestring >=0.10.8 && <0.13, hspec >=2.0 && <3.0, http-client >=0.7 && <0.8,- http-types >=0.8 && <10.0,+ http-types >=0.8 && <10, monad-control >=1.0 && <1.1,- mtl >=2.0 && <3.0, req,- text >=0.2 && <2.1+ text >=0.2 && <2.2 if flag(dev)- ghc-options: -O0 -Wall -Werror+ ghc-options:+ -Wall -Werror -Wredundant-constraints -Wpartial-fields+ -Wunused-packages else- ghc-options: -O2 -Wall+ buildable: False