on master help sign in
.github/dependabot.ymlnew file+6
since splitsnapshots
unifiedsplit
@@ -0,0 +1,6 @@
1+version: 2
2+updates:
3+ - package-ecosystem: "github-actions"
4+ directory: "/"
5+ schedule:
6+ interval: "weekly"
.github/workflows/ci.yaml+14−13
@@ -11,27 +11,25 @@ jobs:
11 ormolu:
11 ormolu:
12 runs-on: ubuntu-latest
12 runs-on: ubuntu-latest
13 steps:
13 steps:
14− - uses: actions/checkout@v2.3.4
14+ - uses: actions/checkout@v4
15− - uses: mrkkrp/ormolu-action@v5
15+ - uses: haskell-actions/run-ormolu@v17
16 build:
16 build:
17 runs-on: ubuntu-latest
17 runs-on: ubuntu-latest
18 needs: ormolu
18 needs: ormolu
19 strategy:
19 strategy:
20 matrix:
20 matrix:
21− cabal: ["3.6"]
21+ cabal: ["3.12"]
22− ghc: ["8.10.7", "9.0.2", "9.2.1"]
22+ ghc: ["9.6.3", "9.8.2", "9.10.1"]
23− env:
24− CONFIG: "--enable-tests --enable-benchmarks --flags=dev"
25 steps:
23 steps:
26− - uses: actions/checkout@v2.3.4
24+ - uses: actions/checkout@v4
27− - uses: haskell/actions/setup@v1.2.9
25+ - uses: haskell-actions/setup@v2
28 id: setup-haskell-cabal
26 id: setup-haskell-cabal
29 with:
27 with:
30 ghc-version: ${{ matrix.ghc }}
28 ghc-version: ${{ matrix.ghc }}
31 cabal-version: ${{ matrix.cabal }}
29 cabal-version: ${{ matrix.cabal }}
32 - run: cabal update
30 - run: cabal update
33− - run: cabal freeze $CONFIG
31+ - run: cabal freeze
34− - uses: actions/cache@v2.1.3
32+ - uses: actions/cache@v4.2.3
35 with:
33 with:
36 path: |
34 path: |
37 ${{ steps.setup-haskell-cabal.outputs.cabal-store }}
35 ${{ steps.setup-haskell-cabal.outputs.cabal-store }}
@@ -40,7 +38,10 @@ jobs:
40 restore-keys: |
38 restore-keys: |
41 ${{ runner.os }}-${{ matrix.ghc }}-
39 ${{ runner.os }}-${{ matrix.ghc }}-
42 - run: cabal format && git diff --exit-code --color=always
40 - run: cabal format && git diff --exit-code --color=always
43− - run: cabal build $CONFIG
41+ - run: cabal build
42+ - run: |
43+ docker pull kennethreitz/httpbin
44+ docker run -p 1234:80 kennethreitz/httpbin &
44− - run: cabal test $CONFIG
45+ cabal test
45− - run: cabal haddock $CONFIG
46+ - run: cabal haddock
46 - run: cabal sdist
47 - run: cabal sdist
.gitignore+1
@@ -25,5 +25,6 @@ cabal.project.local
25cabal.sandbox.config
25cabal.sandbox.config
26dist-*/
26dist-*/
27dist/
27dist/
28+hie.yaml
28stack.yaml
29stack.yaml
29stack.yaml.lock
30stack.yaml.lock
CHANGELOG.md+36
@@ -1,3 +1,39 @@
1+## Req 3.13.4
2+
3+* Fixed empty ciphersuite list when compiling against `tls < 2.0.6`
4+ (see [PR 175](https://github.com/mrkkrp/req/pull/175)). As a side effect, now
5+ compatible with older versions of `crypton-connection` (>= 0.3).
6+
7+## Req 3.13.3
8+
9+* Works with `crypton-connection-0.4` and newer.
10+
11+## Req 3.13.2
12+
13+* Disable the problematic `httpbin-tests` test suite by default. Only enable
14+ it when the `dev` flag is enabled. In that case it is expected that an
15+ httpbin server is run locally at `localhost:1234`.
16+
17+## Req 3.13.1
18+
19+* Switched the non-pure test suite to use https://httpbun.org instead of
20+ https://httpbin.org since the latter proved to be highly unreliable
21+ lately.
22+
23+* Switched from `connection` to `crypton-connection`.
24+
25+* Builds with GHC 9.6.1.
26+
27+## Req 3.13.0
28+
29+* Add `headerRedacted` function to add header fields, which will be with
30+ redacted values on print.
31+
32+## Req 3.12.0
33+
34+* Add `isStatusCodeException` function.
35+* Add `instance HttpResponse (Network.HTTP.Client.Response ())`.
36+
1## Req 3.11.0
37## Req 3.11.0
2
38
3* Add the `queryParamToList` method to the `QueryParam` type class.
39* Add the `queryParamToList` method to the `QueryParam` type class.
Network/HTTP/Req.hs+103−83
@@ -1,20 +1,9 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3{-# LANGUAGE DataKinds #-}
1{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE DeriveDataTypeable #-}
5−{-# LANGUAGE DeriveGeneric #-}
6−{-# LANGUAGE DeriveLift #-}
7−{-# LANGUAGE FlexibleInstances #-}
8−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE LambdaCase #-}
10−{-# LANGUAGE MultiParamTypeClasses #-}
11{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
12{-# LANGUAGE QuasiQuotes #-}
4{-# LANGUAGE QuasiQuotes #-}
13−{-# LANGUAGE RankNTypes #-}
14{-# LANGUAGE RecordWildCards #-}
5{-# LANGUAGE RecordWildCards #-}
15{-# LANGUAGE RoleAnnotations #-}
6{-# LANGUAGE RoleAnnotations #-}
16−{-# LANGUAGE ScopedTypeVariables #-}
17−{-# LANGUAGE StandaloneDeriving #-}
18{-# LANGUAGE TemplateHaskellQuotes #-}
7{-# LANGUAGE TemplateHaskellQuotes #-}
19{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
20{-# LANGUAGE UndecidableInstances #-}
9{-# LANGUAGE UndecidableInstances #-}
@@ -161,6 +150,7 @@ module Network.HTTP.Req
161 -- *** Headers
150 -- *** Headers
162 header,
151 header,
163 attachHeader,
152 attachHeader,
153+ headerRedacted,
164
154
165 -- *** Cookies
155 -- *** Cookies
166 -- $cookies
156 -- $cookies
@@ -207,74 +197,78 @@ module Network.HTTP.Req
207
197
208 -- * Other
198 -- * Other
209 HttpException (..),
199 HttpException (..),
200+ isStatusCodeException,
210 CanHaveBody (..),
201 CanHaveBody (..),
211 Scheme (..),
202 Scheme (..),
212 )
203 )
213where
204where
214
205
215−import qualified Blaze.ByteString.Builder as BB
206+import Blaze.ByteString.Builder qualified as BB
216import Control.Applicative
207import Control.Applicative
217import Control.Arrow (first, second)
208import Control.Arrow (first, second)
218import Control.Exception hiding (Handler (..), TypeError)
209import Control.Exception hiding (Handler (..), TypeError)
210+import Control.Monad (guard, void, (>=>))
219import Control.Monad.Base
211import Control.Monad.Base
220import Control.Monad.Catch (Handler (..), MonadCatch, MonadMask, MonadThrow)
212import Control.Monad.Catch (Handler (..), MonadCatch, MonadMask, MonadThrow)
221import Control.Monad.IO.Class
213import Control.Monad.IO.Class
222import Control.Monad.IO.Unlift
214import Control.Monad.IO.Unlift
223−import Control.Monad.Reader
215+import Control.Monad.Reader (ReaderT (ReaderT), ask, lift, runReaderT)
224import Control.Monad.Trans.Accum (AccumT)
216import Control.Monad.Trans.Accum (AccumT)
225import Control.Monad.Trans.Cont (ContT)
217import Control.Monad.Trans.Cont (ContT)
226import Control.Monad.Trans.Control
218import Control.Monad.Trans.Control
227import Control.Monad.Trans.Except (ExceptT)
219import Control.Monad.Trans.Except (ExceptT)
228import Control.Monad.Trans.Identity (IdentityT)
220import Control.Monad.Trans.Identity (IdentityT)
229import Control.Monad.Trans.Maybe (MaybeT)
221import Control.Monad.Trans.Maybe (MaybeT)
230−import qualified Control.Monad.Trans.RWS.CPS as RWS.CPS
222+import Control.Monad.Trans.RWS.CPS qualified as RWS.CPS
231−import qualified Control.Monad.Trans.RWS.Lazy as RWS.Lazy
223+import Control.Monad.Trans.RWS.Lazy qualified as RWS.Lazy
232−import qualified Control.Monad.Trans.RWS.Strict as RWS.Strict
224+import Control.Monad.Trans.RWS.Strict qualified as RWS.Strict
233import Control.Monad.Trans.Select (SelectT)
225import Control.Monad.Trans.Select (SelectT)
234−import qualified Control.Monad.Trans.State.Lazy as State.Lazy
226+import Control.Monad.Trans.State.Lazy qualified as State.Lazy
235−import qualified Control.Monad.Trans.State.Strict as State.Strict
227+import Control.Monad.Trans.State.Strict qualified as State.Strict
236−import qualified Control.Monad.Trans.Writer.CPS as Writer.CPS
228+import Control.Monad.Trans.Writer.CPS qualified as Writer.CPS
237−import qualified Control.Monad.Trans.Writer.Lazy as Writer.Lazy
229+import Control.Monad.Trans.Writer.Lazy qualified as Writer.Lazy
238−import qualified Control.Monad.Trans.Writer.Strict as Writer.Strict
230+import Control.Monad.Trans.Writer.Strict qualified as Writer.Strict
239import Control.Retry
231import Control.Retry
240import Data.Aeson (FromJSON (..), ToJSON (..))
232import Data.Aeson (FromJSON (..), ToJSON (..))
241−import qualified Data.Aeson as A
233+import Data.Aeson qualified as A
242import Data.ByteString (ByteString)
234import Data.ByteString (ByteString)
243−import qualified Data.ByteString as B
235+import Data.ByteString qualified as B
244−import qualified Data.ByteString.Lazy as BL
236+import Data.ByteString.Lazy qualified as BL
245−import qualified Data.CaseInsensitive as CI
237+import Data.CaseInsensitive qualified as CI
246import Data.Data (Data)
238import Data.Data (Data)
239+import Data.Default.Class (def)
247import Data.Function (on)
240import Data.Function (on)
248import Data.IORef
241import Data.IORef
249import Data.Kind (Constraint, Type)
242import Data.Kind (Constraint, Type)
250import Data.List (foldl', nubBy)
243import Data.List (foldl', nubBy)
251import Data.List.NonEmpty (NonEmpty (..))
244import Data.List.NonEmpty (NonEmpty (..))
252−import qualified Data.List.NonEmpty as NE
245+import Data.List.NonEmpty qualified as NE
253import Data.Maybe (fromMaybe)
246import Data.Maybe (fromMaybe)
254import Data.Proxy
247import Data.Proxy
255import Data.Semigroup (Endo (..))
248import Data.Semigroup (Endo (..))
249+import Data.Set qualified as S
256import Data.Text (Text)
250import Data.Text (Text)
257−import qualified Data.Text as T
251+import Data.Text qualified as T
258−import qualified Data.Text.Encoding as T
252+import Data.Text.Encoding qualified as T
259import Data.Typeable (Typeable, cast)
253import Data.Typeable (Typeable, cast)
260import GHC.Generics
254import GHC.Generics
261import GHC.TypeLits
255import GHC.TypeLits
262−import qualified Language.Haskell.TH as TH
256+import Language.Haskell.TH qualified as TH
263−import qualified Language.Haskell.TH.Quote as TH
257+import Language.Haskell.TH.Quote qualified as TH
264−import qualified Language.Haskell.TH.Syntax as TH
258+import Language.Haskell.TH.Syntax qualified as TH
265−import qualified Network.Connection as NC
259+import Network.Connection qualified as NC
266−import qualified Network.HTTP.Client as L
260+import Network.HTTP.Client qualified as L
267−import qualified Network.HTTP.Client.Internal as LI
261+import Network.HTTP.Client.Internal qualified as LI
268−import qualified Network.HTTP.Client.MultipartFormData as LM
262+import Network.HTTP.Client.MultipartFormData qualified as LM
269−import qualified Network.HTTP.Client.TLS as L
263+import Network.HTTP.Client.TLS qualified as L
270−import qualified Network.HTTP.Types as Y
264+import Network.HTTP.Types qualified as Y
271import System.IO.Unsafe (unsafePerformIO)
265import System.IO.Unsafe (unsafePerformIO)
272import Text.URI (URI)
266import Text.URI (URI)
273−import qualified Text.URI as URI
267+import Text.URI qualified as URI
274−import qualified Text.URI.QQ as QQ
268+import Text.URI.QQ qualified as QQ
275−import qualified Web.Authenticate.OAuth as OAuth
269+import Web.Authenticate.OAuth qualified as OAuth
276import Web.FormUrlEncoded (FromForm (..), ToForm (..))
270import Web.FormUrlEncoded (FromForm (..), ToForm (..))
277−import qualified Web.FormUrlEncoded as Form
271+import Web.FormUrlEncoded qualified as Form
278import Web.HttpApiData (ToHttpApiData (..))
272import Web.HttpApiData (ToHttpApiData (..))
279
273
280----------------------------------------------------------------------------
274----------------------------------------------------------------------------
@@ -495,7 +489,7 @@ reqCb method url body responseProxy options adjustRequest =
495--
489--
496-- @since 3.7.0
490-- @since 3.7.0
497reqHandler ::
491reqHandler ::
498− MonadHttp m =>
492+ (MonadHttp m) =>
499 -- | How to get final result from a 'L.Response'
493 -- | How to get final result from a 'L.Response'
500 (L.Response L.BodyReader -> IO b) ->
494 (L.Response L.BodyReader -> IO b) ->
501 -- | 'L.Request' to perform
495 -- | 'L.Request' to perform
@@ -591,10 +585,10 @@ req' method url body options m = do
591 withReqManager (m request)
585 withReqManager (m request)
592
586
593-- | Perform an action using the global implicit 'L.Manager' that the rest
587-- | Perform an action using the global implicit 'L.Manager' that the rest
594−-- of the library uses. This allows to reuse connections that the
588+-- of the library uses. This allows us to reuse connections that the
595-- 'L.Manager' controls.
589-- 'L.Manager' controls.
596−withReqManager :: MonadIO m => (L.Manager -> m a) -> m a
590+withReqManager :: (MonadIO m) => (L.Manager -> m a) -> m a
597−withReqManager m = liftIO (readIORef globalManager) >>= m
591+withReqManager m = m globalManager
598
592
599-- | The global 'L.Manager' that 'req' uses. Here we just go with the
593-- | The global 'L.Manager' that 'req' uses. Here we just go with the
600-- default settings, so users don't need to deal with this manager stuff at
594-- default settings, so users don't need to deal with this manager stuff at
@@ -607,16 +601,16 @@ withReqManager m = liftIO (readIORef globalManager) >>= m
607-- manager. From that moment on the 'IORef' will be just reused—exactly the
601-- manager. From that moment on the 'IORef' will be just reused—exactly the
608-- behavior we want here in order to maximize connection sharing. GHC could
602-- behavior we want here in order to maximize connection sharing. GHC could
609-- spoil the plan by inlining the definition, hence the @NOINLINE@ pragma.
603-- spoil the plan by inlining the definition, hence the @NOINLINE@ pragma.
610−globalManager :: IORef L.Manager
604+globalManager :: L.Manager
611globalManager = unsafePerformIO $ do
605globalManager = unsafePerformIO $ do
612 context <- NC.initConnectionContext
606 context <- NC.initConnectionContext
613 let settings =
607 let settings =
614 L.mkManagerSettingsContext
608 L.mkManagerSettingsContext
615 (Just context)
609 (Just context)
616− (NC.TLSSettingsSimple False False False)
610+ def
617 Nothing
611 Nothing
618 manager <- L.newManager settings
612 manager <- L.newManager settings
619− newIORef manager
613+ return manager
620{-# NOINLINE globalManager #-}
614{-# NOINLINE globalManager #-}
621
615
622----------------------------------------------------------------------------
616----------------------------------------------------------------------------
@@ -636,7 +630,7 @@ globalManager = unsafePerformIO $ do
636-- | A type class for monads that support performing HTTP requests.
630-- | A type class for monads that support performing HTTP requests.
637-- Typically, you only need to define the 'handleHttpException' method
631-- Typically, you only need to define the 'handleHttpException' method
638-- unless you want to tweak 'HttpConfig'.
632-- unless you want to tweak 'HttpConfig'.
639−class MonadIO m => MonadHttp m where
633+class (MonadIO m) => MonadHttp m where
640 -- | This method describes how to deal with 'HttpException' that was
634 -- | This method describes how to deal with 'HttpException' that was
641 -- caught by the library. One option is to re-throw it if you are OK with
635 -- caught by the library. One option is to re-throw it if you are OK with
642 -- exceptions, but if you prefer working with something like
636 -- exceptions, but if you prefer working with something like
@@ -720,9 +714,8 @@ data HttpConfig = HttpConfig
720 -- | Max length of preview fragment of response body.
714 -- | Max length of preview fragment of response body.
721 --
715 --
722 -- @since 3.6.0
716 -- @since 3.6.0
723− httpConfigBodyPreviewLength :: forall a. Num a => a
717+ httpConfigBodyPreviewLength :: forall a. (Num a) => a
724 }
718 }
725− deriving (Typeable)
726
719
727-- | The default value of 'HttpConfig'.
720-- | The default value of 'HttpConfig'.
728--
721--
@@ -810,27 +803,27 @@ instance (MonadHttp m, Monoid w) => MonadHttp (AccumT w m) where
810 getHttpConfig = lift getHttpConfig
803 getHttpConfig = lift getHttpConfig
811
804
812-- | @since 3.10.0
805-- | @since 3.10.0
813−instance MonadHttp m => MonadHttp (ContT r m) where
806+instance (MonadHttp m) => MonadHttp (ContT r m) where
814 handleHttpException = lift . handleHttpException
807 handleHttpException = lift . handleHttpException
815 getHttpConfig = lift getHttpConfig
808 getHttpConfig = lift getHttpConfig
816
809
817-- | @since 3.10.0
810-- | @since 3.10.0
818−instance MonadHttp m => MonadHttp (ExceptT e m) where
811+instance (MonadHttp m) => MonadHttp (ExceptT e m) where
819 handleHttpException = lift . handleHttpException
812 handleHttpException = lift . handleHttpException
820 getHttpConfig = lift getHttpConfig
813 getHttpConfig = lift getHttpConfig
821
814
822-- | @since 3.10.0
815-- | @since 3.10.0
823−instance MonadHttp m => MonadHttp (IdentityT m) where
816+instance (MonadHttp m) => MonadHttp (IdentityT m) where
824 handleHttpException = lift . handleHttpException
817 handleHttpException = lift . handleHttpException
825 getHttpConfig = lift getHttpConfig
818 getHttpConfig = lift getHttpConfig
826
819
827-- | @since 3.10.0
820-- | @since 3.10.0
828−instance MonadHttp m => MonadHttp (MaybeT m) where
821+instance (MonadHttp m) => MonadHttp (MaybeT m) where
829 handleHttpException = lift . handleHttpException
822 handleHttpException = lift . handleHttpException
830 getHttpConfig = lift getHttpConfig
823 getHttpConfig = lift getHttpConfig
831
824
832-- | @since 3.10.0
825-- | @since 3.10.0
833−instance MonadHttp m => MonadHttp (ReaderT r m) where
826+instance (MonadHttp m) => MonadHttp (ReaderT r m) where
834 handleHttpException = lift . handleHttpException
827 handleHttpException = lift . handleHttpException
835 getHttpConfig = lift getHttpConfig
828 getHttpConfig = lift getHttpConfig
836
829
@@ -850,17 +843,17 @@ instance (MonadHttp m, Monoid w) => MonadHttp (RWS.Strict.RWST r w s m) where
850 getHttpConfig = lift getHttpConfig
843 getHttpConfig = lift getHttpConfig
851
844
852-- | @since 3.10.0
845-- | @since 3.10.0
853−instance MonadHttp m => MonadHttp (SelectT r m) where
846+instance (MonadHttp m) => MonadHttp (SelectT r m) where
854 handleHttpException = lift . handleHttpException
847 handleHttpException = lift . handleHttpException
855 getHttpConfig = lift getHttpConfig
848 getHttpConfig = lift getHttpConfig
856
849
857-- | @since 3.10.0
850-- | @since 3.10.0
858−instance MonadHttp m => MonadHttp (State.Lazy.StateT s m) where
851+instance (MonadHttp m) => MonadHttp (State.Lazy.StateT s m) where
859 handleHttpException = lift . handleHttpException
852 handleHttpException = lift . handleHttpException
860 getHttpConfig = lift getHttpConfig
853 getHttpConfig = lift getHttpConfig
861
854
862-- | @since 3.10.0
855-- | @since 3.10.0
863−instance MonadHttp m => MonadHttp (State.Strict.StateT s m) where
856+instance (MonadHttp m) => MonadHttp (State.Strict.StateT s m) where
864 handleHttpException = lift . handleHttpException
857 handleHttpException = lift . handleHttpException
865 getHttpConfig = lift getHttpConfig
858 getHttpConfig = lift getHttpConfig
866
859
@@ -884,7 +877,7 @@ instance (MonadHttp m, Monoid w) => MonadHttp (Writer.Strict.WriterT w m) where
884--
877--
885-- @since 0.4.0
878-- @since 0.4.0
886runReq ::
879runReq ::
887− MonadIO m =>
880+ (MonadIO m) =>
888 -- | 'HttpConfig' to use
881 -- | 'HttpConfig' to use
889 HttpConfig ->
882 HttpConfig ->
890 -- | Computation to run
883 -- | Computation to run
@@ -989,7 +982,7 @@ class HttpMethod a where
989 -- | Return name of the method as a 'ByteString'.
982 -- | Return name of the method as a 'ByteString'.
990 httpMethodName :: Proxy a -> ByteString
983 httpMethodName :: Proxy a -> ByteString
991
984
992−instance HttpMethod method => RequestComponent (Tagged "method" method) where
985+instance (HttpMethod method) => RequestComponent (Tagged "method" method) where
993 getRequestMod _ = Endo $ \x ->
986 getRequestMod _ = Endo $ \x ->
994 x {L.method = httpMethodName (Proxy :: Proxy method)}
987 x {L.method = httpMethodName (Proxy :: Proxy method)}
995
988
@@ -1031,25 +1024,20 @@ instance HttpMethod method => RequestComponent (Tagged "method" method) where
1031-- > -- https://%D1%8E%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4.%D1%80%D1%84
1024-- > -- https://%D1%8E%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4.%D1%80%D1%84
1032data Url (scheme :: Scheme) = Url Scheme (NonEmpty Text)
1025data Url (scheme :: Scheme) = Url Scheme (NonEmpty Text)
1033 -- NOTE The second value is the path segments in reversed order.
1026 -- NOTE The second value is the path segments in reversed order.
1034− deriving (Eq, Ord, Show, Data, Typeable, Generic)
1027+ deriving (Eq, Ord, Show, Data, Generic)
1035
1028
1036type role Url nominal
1029type role Url nominal
1037
1030
1038-- With template-haskell >=2.15 and text >=1.2.4 Lift can be derived, however
1031-- With template-haskell >=2.15 and text >=1.2.4 Lift can be derived, however
1039-- the derived lift forgets the type of the scheme.
1032-- the derived lift forgets the type of the scheme.
1040−instance Typeable scheme => TH.Lift (Url scheme) where
1033+instance (Typeable scheme) => TH.Lift (Url scheme) where
1041 lift url =
1034 lift url =
1042 TH.dataToExpQ (fmap liftText . cast) url `TH.sigE` case url of
1035 TH.dataToExpQ (fmap liftText . cast) url `TH.sigE` case url of
1043 Url Http _ -> [t|Url 'Http|]
1036 Url Http _ -> [t|Url 'Http|]
1044 Url Https _ -> [t|Url 'Https|]
1037 Url Https _ -> [t|Url 'Https|]
1045 where
1038 where
1046 liftText t = TH.AppE (TH.VarE 'T.pack) <$> TH.lift (T.unpack t)
1039 liftText t = TH.AppE (TH.VarE 'T.pack) <$> TH.lift (T.unpack t)
1047−
1048−#if MIN_VERSION_template_haskell(2,17,0)
1049 liftTyped = TH.Code . TH.unsafeTExpCoerce . TH.lift
1040 liftTyped = TH.Code . TH.unsafeTExpCoerce . TH.lift
1050−#elif MIN_VERSION_template_haskell(2,16,0)
1051− liftTyped = TH.unsafeTExpCoerce . TH.lift
1052−#endif
1053
1041
1054-- | Given host name, produce a 'Url' which has “http” as its scheme and
1042-- | Given host name, produce a 'Url' which has “http” as its scheme and
1055-- empty path. This also sets port to @80@.
1043-- empty path. This also sets port to @80@.
@@ -1065,7 +1053,7 @@ https = Url Https . pure
1065-- path segment can be of any type that is an instance of 'ToHttpApiData'.
1053-- path segment can be of any type that is an instance of 'ToHttpApiData'.
1066infixl 5 /~
1054infixl 5 /~
1067
1055
1068−(/~) :: ToHttpApiData a => Url scheme -> a -> Url scheme
1056+(/~) :: (ToHttpApiData a) => Url scheme -> a -> Url scheme
1069Url secure path /~ segment = Url secure (NE.cons (toUrlPiece segment) path)
1057Url secure path /~ segment = Url secure (NE.cons (toUrlPiece segment) path)
1070
1058
1071-- | A type-constrained version of @('/~')@ to remove ambiguity in the cases
1059-- | A type-constrained version of @('/~')@ to remove ambiguity in the cases
@@ -1253,7 +1241,7 @@ instance HttpBody NoReqBody where
1253-- charset=utf-8\"@ value.
1241-- charset=utf-8\"@ value.
1254newtype ReqBodyJson a = ReqBodyJson a
1242newtype ReqBodyJson a = ReqBodyJson a
1255
1243
1256−instance ToJSON a => HttpBody (ReqBodyJson a) where
1244+instance (ToJSON a) => HttpBody (ReqBodyJson a) where
1257 getRequestBody (ReqBodyJson a) = L.RequestBodyLBS (A.encode a)
1245 getRequestBody (ReqBodyJson a) = L.RequestBodyLBS (A.encode a)
1258 getRequestContentType _ = pure "application/json; charset=utf-8"
1246 getRequestContentType _ = pure "application/json; charset=utf-8"
1259
1247
@@ -1352,7 +1340,7 @@ instance HttpBody ReqBodyMultipart where
1352-- | Create 'ReqBodyMultipart' request body from a collection of 'LM.Part's.
1340-- | Create 'ReqBodyMultipart' request body from a collection of 'LM.Part's.
1353--
1341--
1354-- @since 0.2.0
1342-- @since 0.2.0
1355−reqBodyMultipart :: MonadIO m => [LM.Part] -> m ReqBodyMultipart
1343+reqBodyMultipart :: (MonadIO m) => [LM.Part] -> m ReqBodyMultipart
1356reqBodyMultipart parts = liftIO $ do
1344reqBodyMultipart parts = liftIO $ do
1357 boundary <- LM.webkitBoundary
1345 boundary <- LM.webkitBoundary
1358 body <- LM.renderParts boundary parts
1346 body <- LM.renderParts boundary parts
@@ -1392,7 +1380,7 @@ type family
1392 TypeError
1380 TypeError
1393 ('Text "This HTTP method does not allow attaching a request body.")
1381 ('Text "This HTTP method does not allow attaching a request body.")
1394
1382
1395−instance HttpBody body => RequestComponent (Tagged "body" body) where
1383+instance (HttpBody body) => RequestComponent (Tagged "body" body) where
1396 getRequestMod (Tagged body) = Endo $ \x ->
1384 getRequestMod (Tagged body) = Endo $ \x ->
1397 x
1385 x
1398 { L.requestBody = getRequestBody body,
1386 { L.requestBody = getRequestBody body,
@@ -1462,7 +1450,7 @@ instance RequestComponent (Option scheme) where
1462
1450
1463-- | Finalize given 'L.Request' by applying a finalizer from the given
1451-- | Finalize given 'L.Request' by applying a finalizer from the given
1464-- 'Option' (if it has any).
1452-- 'Option' (if it has any).
1465−finalizeRequest :: MonadIO m => Option scheme -> L.Request -> m L.Request
1453+finalizeRequest :: (MonadIO m) => Option scheme -> L.Request -> m L.Request
1466finalizeRequest (Option _ mfinalizer) = liftIO . fromMaybe pure mfinalizer
1454finalizeRequest (Option _ mfinalizer) = liftIO . fromMaybe pure mfinalizer
1467
1455
1468----------------------------------------------------------------------------
1456----------------------------------------------------------------------------
@@ -1495,7 +1483,7 @@ name =: value = queryParam name (pure value)
1495-- This operator is defined in terms of 'queryParam':
1483-- This operator is defined in terms of 'queryParam':
1496--
1484--
1497-- > queryFlag name = queryParam name (Nothing :: Maybe ())
1485-- > queryFlag name = queryParam name (Nothing :: Maybe ())
1498−queryFlag :: QueryParam param => Text -> param
1486+queryFlag :: (QueryParam param) => Text -> param
1499queryFlag name = queryParam name (Nothing :: Maybe ())
1487queryFlag name = queryParam name (Nothing :: Maybe ())
1500
1488
1501-- | Construct query parameters from a 'ToForm' instance. This function
1489-- | Construct query parameters from a 'ToForm' instance. This function
@@ -1525,7 +1513,7 @@ class QueryParam param where
1525 -- 'Nothing', it won't be included at all (i.e. you create a flag this
1513 -- 'Nothing', it won't be included at all (i.e. you create a flag this
1526 -- way). It's recommended to use @('=:')@ and 'queryFlag' instead of this
1514 -- way). It's recommended to use @('=:')@ and 'queryFlag' instead of this
1527 -- method, because they are easier to read.
1515 -- method, because they are easier to read.
1528− queryParam :: ToHttpApiData a => Text -> Maybe a -> param
1516+ queryParam :: (ToHttpApiData a) => Text -> Maybe a -> param
1529
1517
1530 -- | Get the query parameter names and values set by 'queryParam'.
1518 -- | Get the query parameter names and values set by 'queryParam'.
1531 --
1519 --
@@ -1559,6 +1547,14 @@ attachHeader :: ByteString -> ByteString -> L.Request -> L.Request
1559attachHeader name value x =
1547attachHeader name value x =
1560 x {L.requestHeaders = (CI.mk name, value) : L.requestHeaders x}
1548 x {L.requestHeaders = (CI.mk name, value) : L.requestHeaders x}
1561
1549
1550+-- | Same as 'header', but with redacted values on print.
1551+--
1552+-- @since 3.13.0
1553+headerRedacted :: ByteString -> ByteString -> Option scheme
1554+headerRedacted name value = withRequest $ \x ->
1555+ let y = attachHeader name value x
1556+ in y {L.redactHeaders = CI.mk name `S.insert` L.redactHeaders y}
1557+
1562----------------------------------------------------------------------------
1558----------------------------------------------------------------------------
1563-- Request—Optional parameters—Cookies
1559-- Request—Optional parameters—Cookies
1564
1560
@@ -1766,7 +1762,7 @@ ignoreResponse = Proxy
1766newtype JsonResponse a = JsonResponse (L.Response a)
1762newtype JsonResponse a = JsonResponse (L.Response a)
1767 deriving (Show)
1763 deriving (Show)
1768
1764
1769−instance FromJSON a => HttpResponse (JsonResponse a) where
1765+instance (FromJSON a) => HttpResponse (JsonResponse a) where
1770 type HttpResponseBody (JsonResponse a) = a
1766 type HttpResponseBody (JsonResponse a) = a
1771 toVanillaResponse (JsonResponse r) = r
1767 toVanillaResponse (JsonResponse r) = r
1772 getHttpResponse r = do
1768 getHttpResponse r = do
@@ -1882,14 +1878,14 @@ brReadN br n = go 0 id id
1882
1878
1883-- | Get the response body.
1879-- | Get the response body.
1884responseBody ::
1880responseBody ::
1885− HttpResponse response =>
1881+ (HttpResponse response) =>
1886 response ->
1882 response ->
1887 HttpResponseBody response
1883 HttpResponseBody response
1888responseBody = L.responseBody . toVanillaResponse
1884responseBody = L.responseBody . toVanillaResponse
1889
1885
1890-- | Get the response status code.
1886-- | Get the response status code.
1891responseStatusCode ::
1887responseStatusCode ::
1892− HttpResponse response =>
1888+ (HttpResponse response) =>
1893 response ->
1889 response ->
1894 Int
1890 Int
1895responseStatusCode =
1891responseStatusCode =
@@ -1897,7 +1893,7 @@ responseStatusCode =
1897
1893
1898-- | Get the response status message.
1894-- | Get the response status message.
1899responseStatusMessage ::
1895responseStatusMessage ::
1900− HttpResponse response =>
1896+ (HttpResponse response) =>
1901 response ->
1897 response ->
1902 ByteString
1898 ByteString
1903responseStatusMessage =
1899responseStatusMessage =
@@ -1905,7 +1901,7 @@ responseStatusMessage =
1905
1901
1906-- | Lookup a particular header from a response.
1902-- | Lookup a particular header from a response.
1907responseHeader ::
1903responseHeader ::
1908− HttpResponse response =>
1904+ (HttpResponse response) =>
1909 -- | Response interpretation
1905 -- | Response interpretation
1910 response ->
1906 response ->
1911 -- | Header to lookup
1907 -- | Header to lookup
@@ -1917,7 +1913,7 @@ responseHeader r h =
1917
1913
1918-- | Get the response 'L.CookieJar'.
1914-- | Get the response 'L.CookieJar'.
1919responseCookieJar ::
1915responseCookieJar ::
1920− HttpResponse response =>
1916+ (HttpResponse response) =>
1921 response ->
1917 response ->
1922 L.CookieJar
1918 L.CookieJar
1923responseCookieJar = L.responseCookieJar . toVanillaResponse
1919responseCookieJar = L.responseCookieJar . toVanillaResponse
@@ -1971,6 +1967,16 @@ class HttpResponse response where
1971 acceptHeader :: Proxy response -> Maybe ByteString
1967 acceptHeader :: Proxy response -> Maybe ByteString
1972 acceptHeader Proxy = Nothing
1968 acceptHeader Proxy = Nothing
1973
1969
1970+-- | This instance has been added to make it easier to inspect 'L.Response'
1971+-- using Req's functions like 'responseStatusCode', 'responseStatusMessage',
1972+-- etc.
1973+--
1974+-- @since 3.12.0
1975+instance HttpResponse (L.Response ()) where
1976+ type HttpResponseBody (L.Response ()) = ()
1977+ toVanillaResponse = id
1978+ getHttpResponse = return . void
1979+
1974----------------------------------------------------------------------------
1980----------------------------------------------------------------------------
1975-- Other
1981-- Other
1976
1982
@@ -2005,10 +2011,24 @@ data HttpException
2005 | -- | A wrapper with Aeson-produced 'String' describing why decoding
2011 | -- | A wrapper with Aeson-produced 'String' describing why decoding
2006 -- failed
2012 -- failed
2007 JsonHttpException String
2013 JsonHttpException String
2008− deriving (Show, Typeable, Generic)
2014+ deriving (Show, Generic)
2009
2015
2010instance Exception HttpException
2016instance Exception HttpException
2011
2017
2018+-- | Return 'Just' if the given 'HttpException' is wrapping a http-client's
2019+-- 'L.StatusCodeException'. Otherwise, return 'Nothing'.
2020+--
2021+-- @since 3.12.0
2022+isStatusCodeException :: HttpException -> Maybe (L.Response ())
2023+isStatusCodeException
2024+ ( VanillaHttpException
2025+ ( L.HttpExceptionRequest
2026+ _
2027+ (L.StatusCodeException r _)
2028+ )
2029+ ) = Just r
2030+isStatusCodeException _ = Nothing
2031+
2012-- | A simple type isomorphic to 'Bool' that we only have for better error
2032-- | A simple type isomorphic to 'Bool' that we only have for better error
2013-- messages. We use it as a kind and its data constructors as type-level
2033-- messages. We use it as a kind and its data constructors as type-level
2014-- tags.
2034-- tags.
@@ -2028,4 +2048,4 @@ data Scheme
2028 Http
2048 Http
2029 | -- | HTTPS
2049 | -- | HTTPS
2030 Https
2050 Https
2031− deriving (Eq, Ord, Show, Data, Typeable, Generic, TH.Lift)
2051+ deriving (Eq, Ord, Show, Data, Generic, TH.Lift)
README.md+1−1
@@ -4,7 +4,7 @@
4[![Hackage](https://img.shields.io/hackage/v/req.svg?style=flat)](https://hackage.haskell.org/package/req)
4[![Hackage](https://img.shields.io/hackage/v/req.svg?style=flat)](https://hackage.haskell.org/package/req)
5[![Stackage Nightly](http://stackage.org/package/req/badge/nightly)](http://stackage.org/nightly/package/req)
5[![Stackage Nightly](http://stackage.org/package/req/badge/nightly)](http://stackage.org/nightly/package/req)
6[![Stackage LTS](http://stackage.org/package/req/badge/lts)](http://stackage.org/lts/package/req)
6[![Stackage LTS](http://stackage.org/package/req/badge/lts)](http://stackage.org/lts/package/req)
7−![CI](https://github.com/mrkkrp/req/workflows/CI/badge.svg?branch=master)
7+[![CI](https://github.com/mrkkrp/req/actions/workflows/ci.yaml/badge.svg)](https://github.com/mrkkrp/req/actions/workflows/ci.yaml)
8
8
9* [Related packages](#related-packages)
9* [Related packages](#related-packages)
10* [Blog posts](#blog-posts)
10* [Blog posts](#blog-posts)
Setup.hsdeleted−6
@@ -1,6 +0,0 @@
1−module Main (main) where
2−
3−import Distribution.Simple
4−
5−main :: IO ()
6−main = defaultMain
cabal.projectnew file+4
@@ -0,0 +1,4 @@
1+packages: .
2+tests: True
3+benchmarks: True
4+constraints: req +dev
httpbin-tests/Network/HTTP/ReqSpec.hs+86−42
@@ -8,24 +8,25 @@
8module Network.HTTP.ReqSpec (spec) where
8module Network.HTTP.ReqSpec (spec) where
9
9
10import Control.Exception
10import Control.Exception
11−import Control.Monad.Reader
11+import Control.Monad (forM_)
12import Control.Monad.Trans.Control
12import Control.Monad.Trans.Control
13import Data.Aeson (ToJSON (..), Value (..), object, (.=))
13import Data.Aeson (ToJSON (..), Value (..), object, (.=))
14−import qualified Data.Aeson as A
14+import Data.Aeson qualified as A
15−import qualified Data.Aeson.KeyMap as Aeson.KeyMap
15+import Data.Aeson.KeyMap qualified as Aeson.KeyMap
16−import qualified Data.ByteString as B
16+import Data.ByteString qualified as B
17−import qualified Data.ByteString.Lazy as BL
17+import Data.ByteString.Lazy qualified as BL
18import Data.Functor.Identity (runIdentity)
18import Data.Functor.Identity (runIdentity)
19import Data.Maybe (fromJust)
19import Data.Maybe (fromJust)
20import Data.Proxy
20import Data.Proxy
21import Data.Text (Text)
21import Data.Text (Text)
22−import qualified Data.Text as T
22+import Data.Text qualified as T
23−import qualified Data.Text.Encoding as T
23+import Data.Text.Encoding qualified as T
24−import qualified Data.Text.IO as TIO
24+import Data.Text.IO qualified as TIO
25−import qualified Network.HTTP.Client as L
25+import Network.HTTP.Client qualified as L
26−import qualified Network.HTTP.Client.MultipartFormData as LM
26+import Network.HTTP.Client.MultipartFormData qualified as LM
27−import Network.HTTP.Req
27+import Network.HTTP.Req hiding (req)
28+import Network.HTTP.Req qualified as Req
28−import qualified Network.HTTP.Types as Y
29+import Network.HTTP.Types qualified as Y
29import Test.Hspec
30import Test.Hspec
30import Test.QuickCheck
31import Test.QuickCheck
31
32
@@ -49,6 +50,11 @@ spec = do
49 blindlyThrowing (req GET httpbin NoReqBody ignoreResponse mempty)
50 blindlyThrowing (req GET httpbin NoReqBody ignoreResponse mempty)
50 `shouldThrow` anyException
51 `shouldThrow` anyException
51
52
53+ describe "isStatusCodeException" $
54+ it "extracts non-2xx response" $
55+ req GET (httpbin /: "foo") NoReqBody ignoreResponse mempty
56+ `shouldThrow` selector404ByStatusCodeException
57+
52 describe "receiving user-agent header back" $
58 describe "receiving user-agent header back" $
53 it "works" $ do
59 it "works" $ do
54 r <-
60 r <-
@@ -79,8 +85,7 @@ spec = do
79 .= object
85 .= object
80 [ "Accept-Encoding" .= ("gzip" :: Text),
86 [ "Accept-Encoding" .= ("gzip" :: Text),
81 "Foo" .= ("bar" :: Text),
87 "Foo" .= ("bar" :: Text),
82− "Baz" .= ("quux" :: Text),
88+ "Baz" .= ("quux" :: Text)
83− "Host" .= ("httpbin.org" :: Text)
84 ]
89 ]
85 ]
90 ]
86 responseStatusCode r `shouldBe` 200
91 responseStatusCode r `shouldBe` 200
@@ -92,11 +97,10 @@ spec = do
92 (stripFunnyHeaders . stripOrigin) (responseBody r)
97 (stripFunnyHeaders . stripOrigin) (responseBody r)
93 `shouldBe` object
98 `shouldBe` object
94 [ "args" .= emptyObject,
99 [ "args" .= emptyObject,
95− "url" .= ("https://httpbin.org/get" :: Text),
100+ "url" .= ("http://localhost:1234/get" :: Text),
96 "headers"
101 "headers"
97 .= object
102 .= object
98− [ "Accept-Encoding" .= ("gzip" :: Text),
103+ [ "Accept-Encoding" .= ("gzip" :: Text)
99− "Host" .= ("httpbin.org" :: Text)
100 ]
104 ]
101 ]
105 ]
102 responseHeader r "Content-Type" `shouldBe` return "application/json"
106 responseHeader r "Content-Type" `shouldBe` return "application/json"
@@ -113,12 +117,11 @@ spec = do
113 [ "args" .= emptyObject,
117 [ "args" .= emptyObject,
114 "json" .= text,
118 "json" .= text,
115 "data" .= reflected,
119 "data" .= reflected,
116− "url" .= ("https://httpbin.org/post" :: Text),
120+ "url" .= ("http://localhost:1234/post" :: Text),
117 "headers"
121 "headers"
118 .= object
122 .= object
119 [ "Content-Type" .= ("application/json; charset=utf-8" :: Text),
123 [ "Content-Type" .= ("application/json; charset=utf-8" :: Text),
120 "Accept-Encoding" .= ("gzip" :: Text),
124 "Accept-Encoding" .= ("gzip" :: Text),
121− "Host" .= ("httpbin.org" :: Text),
122 "Content-Length" .= show (T.length reflected)
125 "Content-Length" .= show (T.length reflected)
123 ],
126 ],
124 "files" .= emptyObject,
127 "files" .= emptyObject,
@@ -142,12 +145,11 @@ spec = do
142 [ "args" .= emptyObject,
145 [ "args" .= emptyObject,
143 "json" .= Null,
146 "json" .= Null,
144 "data" .= ("" :: Text),
147 "data" .= ("" :: Text),
145− "url" .= ("https://httpbin.org/post" :: Text),
148+ "url" .= ("http://localhost:1234/post" :: Text),
146 "headers"
149 "headers"
147 .= object
150 .= object
148 [ "Content-Type" .= T.decodeUtf8 contentType,
151 [ "Content-Type" .= T.decodeUtf8 contentType,
149 "Accept-Encoding" .= ("gzip" :: Text),
152 "Accept-Encoding" .= ("gzip" :: Text),
150− "Host" .= ("httpbin.org" :: Text),
151 "Content-Length" .= ("242" :: Text)
153 "Content-Length" .= ("242" :: Text)
152 ],
154 ],
153 "files" .= emptyObject,
155 "files" .= emptyObject,
@@ -172,11 +174,10 @@ spec = do
172 [ "args" .= emptyObject,
174 [ "args" .= emptyObject,
173 "json" .= Null,
175 "json" .= Null,
174 "data" .= contents,
176 "data" .= contents,
175− "url" .= ("https://httpbin.org/patch" :: Text),
177+ "url" .= ("http://localhost:1234/patch" :: Text),
176 "headers"
178 "headers"
177 .= object
179 .= object
178 [ "Accept-Encoding" .= ("gzip" :: Text),
180 [ "Accept-Encoding" .= ("gzip" :: Text),
179− "Host" .= ("httpbin.org" :: Text),
180 "Content-Length" .= show (T.length contents)
181 "Content-Length" .= show (T.length contents)
181 ],
182 ],
182 "files" .= emptyObject,
183 "files" .= emptyObject,
@@ -198,12 +199,11 @@ spec = do
198 [ "args" .= emptyObject,
199 [ "args" .= emptyObject,
199 "json" .= Null,
200 "json" .= Null,
200 "data" .= ("" :: Text),
201 "data" .= ("" :: Text),
201− "url" .= ("https://httpbin.org/put" :: Text),
202+ "url" .= ("http://localhost:1234/put" :: Text),
202 "headers"
203 "headers"
203 .= object
204 .= object
204 [ "Content-Type" .= ("application/x-www-form-urlencoded" :: Text),
205 [ "Content-Type" .= ("application/x-www-form-urlencoded" :: Text),
205 "Accept-Encoding" .= ("gzip" :: Text),
206 "Accept-Encoding" .= ("gzip" :: Text),
206− "Host" .= ("httpbin.org" :: Text),
207 "Content-Length" .= ("18" :: Text)
207 "Content-Length" .= ("18" :: Text)
208 ],
208 ],
209 "files" .= emptyObject,
209 "files" .= emptyObject,
@@ -301,7 +301,7 @@ spec = do
301 (httpbin /: "basic-auth" /~ user /~ password)
301 (httpbin /: "basic-auth" /~ user /~ password)
302 NoReqBody
302 NoReqBody
303 ignoreResponse
303 ignoreResponse
304− (basicAuth (T.encodeUtf8 user) (T.encodeUtf8 password))
304+ (basicAuthUnsafe (T.encodeUtf8 user) (T.encodeUtf8 password))
305 responseStatusCode r `shouldBe` 200
305 responseStatusCode r `shouldBe` 200
306 responseStatusMessage r `shouldBe` "OK"
306 responseStatusMessage r `shouldBe` "OK"
307
307
@@ -401,8 +401,28 @@ blindlyThrowing = runReq defaultHttpConfig {httpConfigCheckResponse = doit}
401 doit _ _ = error "Oops!"
401 doit _ _ = error "Oops!"
402
402
403-- | 'Url' representing <https://httpbin.org>.
403-- | 'Url' representing <https://httpbin.org>.
404−httpbin :: Url 'Https
404+httpbin :: Url 'Http
405−httpbin = https "httpbin.org"
405+httpbin = http "localhost"
406+
407+req ::
408+ ( MonadHttp m,
409+ HttpMethod method,
410+ HttpBody body,
411+ HttpResponse response,
412+ HttpBodyAllowed (AllowsBody method) (ProvidesBody body)
413+ ) =>
414+ method ->
415+ Url scheme ->
416+ body ->
417+ Proxy response ->
418+ Option scheme ->
419+ m response
420+req method url body responseProxy options =
421+ Req.req method url body responseProxy (options <> defaultOptions)
422+
423+-- | Options to apply by default.
424+defaultOptions :: Option scheme
425+defaultOptions = port 1234
406
426
407-- | Remove “origin” field from JSON value. Origin may change, we don't want
427-- | Remove “origin” field from JSON value. Origin may change, we don't want
408-- to depend on that.
428-- to depend on that.
@@ -412,19 +432,36 @@ stripOrigin value = value
412
432
413-- | Remove funny headers that might break the tests.
433-- | Remove funny headers that might break the tests.
414stripFunnyHeaders :: Value -> Value
434stripFunnyHeaders :: Value -> Value
415−stripFunnyHeaders (Object m) =
435+stripFunnyHeaders = \case
436+ Object m ->
437+ Object
438+ ( runIdentity
439+ ( Aeson.KeyMap.alterF
440+ (pure . fmap stripFunnyHeaders')
441+ "headers"
442+ m
443+ )
444+ )
445+ value -> value
446+
447+-- | Similar to 'stripFunnyHeaders', but acts directly on the argument
448+-- without trying to access its "headers" field.
449+stripFunnyHeaders' :: Value -> Value
450+stripFunnyHeaders' = \case
451+ Object p ->
452+ Object $
416− let f (Object p) = Object $ Aeson.KeyMap.filterWithKey (\k _ -> k `elem` hs) p
453+ Aeson.KeyMap.filterWithKey
454+ (\k _ -> k `elem` whitelistedHeaders)
455+ p
417− f value = value
456+ value -> value
418− hs =
457+ where
458+ whitelistedHeaders =
419− [ "Content-Type",
459+ [ "Content-Type",
420− "Accept-Encoding",
460+ "Accept-Encoding",
421− "Host",
422− "Content-Length",
461+ "Content-Length",
423− "Foo",
462+ "Foo",
424− "Baz"
463+ "Baz"
425− ]
464+ ]
426− in Object (runIdentity (Aeson.KeyMap.alterF (pure . fmap f) "headers" m))
427−stripFunnyHeaders value = value
428
465
429-- | This is a complete test case that makes use of <https://httpbin.org> to
466-- | This is a complete test case that makes use of <https://httpbin.org> to
430-- get various response status codes.
467-- get various response status codes.
@@ -454,12 +491,19 @@ selector404
454 L.responseStatus response == Y.status404 && not (B.null chunk)
491 L.responseStatus response == Y.status404 && not (B.null chunk)
455selector404 _ = False
492selector404 _ = False
456
493
494+-- | Same as 'selector404' except that it uses 'isStatusCodeException'.
495+selector404ByStatusCodeException :: HttpException -> Bool
496+selector404ByStatusCodeException e =
497+ case isStatusCodeException e of
498+ Nothing -> False
499+ Just r -> responseStatusCode r == 404
500+
457-- | The empty JSON 'Object'.
501-- | The empty JSON 'Object'.
458emptyObject :: Value
502emptyObject :: Value
459emptyObject = Object Aeson.KeyMap.empty
503emptyObject = Object Aeson.KeyMap.empty
460
504
461-- | Get a rendered JSON value as 'Text'.
505-- | Get a rendered JSON value as 'Text'.
462−reflectJSON :: ToJSON a => a -> Text
506+reflectJSON :: (ToJSON a) => a -> Text
463reflectJSON = T.decodeUtf8 . BL.toStrict . A.encode
507reflectJSON = T.decodeUtf8 . BL.toStrict . A.encode
464
508
465-- | Clarify to the type checker that the inner computation is in the 'IO'
509-- | Clarify to the type checker that the inner computation is in the 'IO'
pure-tests/Network/HTTP/ReqSpec.hs+18−18
@@ -13,41 +13,41 @@
13
13
14module Network.HTTP.ReqSpec (spec) where
14module Network.HTTP.ReqSpec (spec) where
15
15
16−import qualified Blaze.ByteString.Builder as BB
16+import Blaze.ByteString.Builder qualified as BB
17import Control.Exception (throwIO)
17import Control.Exception (throwIO)
18import Control.Monad
18import Control.Monad
19import Control.Retry
19import Control.Retry
20import Data.Aeson (ToJSON (..))
20import Data.Aeson (ToJSON (..))
21−import qualified Data.Aeson as A
21+import Data.Aeson qualified as A
22import Data.ByteString (ByteString)
22import Data.ByteString (ByteString)
23−import qualified Data.ByteString as B
23+import Data.ByteString qualified as B
24−import qualified Data.ByteString.Char8 as B8
24+import Data.ByteString.Char8 qualified as B8
25−import qualified Data.ByteString.Lazy as BL
25+import Data.ByteString.Lazy qualified as BL
26−import qualified Data.CaseInsensitive as CI
26+import Data.CaseInsensitive qualified as CI
27import Data.Either (isRight)
27import Data.Either (isRight)
28−import qualified Data.List.NonEmpty as NE
28+import Data.List.NonEmpty qualified as NE
29import Data.Maybe (fromJust, fromMaybe, isJust, isNothing)
29import Data.Maybe (fromJust, fromMaybe, isJust, isNothing)
30import Data.Proxy
30import Data.Proxy
31import Data.Text (Text)
31import Data.Text (Text)
32−import qualified Data.Text as T
32+import Data.Text qualified as T
33−import qualified Data.Text.Encoding as T
33+import Data.Text.Encoding qualified as T
34import Data.Time
34import Data.Time
35import Data.Typeable (Typeable, eqT)
35import Data.Typeable (Typeable, eqT)
36import GHC.Exts (IsList (..))
36import GHC.Exts (IsList (..))
37import GHC.Generics
37import GHC.Generics
38−import qualified Language.Haskell.TH as TH
38+import Language.Haskell.TH qualified as TH
39−import qualified Language.Haskell.TH.Quote as TH
39+import Language.Haskell.TH.Quote qualified as TH
40−import qualified Network.HTTP.Client as L
40+import Network.HTTP.Client qualified as L
41import Network.HTTP.Req
41import Network.HTTP.Req
42−import qualified Network.HTTP.Types as Y
42+import Network.HTTP.Types qualified as Y
43−import qualified Network.HTTP.Types.Header as Y
43+import Network.HTTP.Types.Header qualified as Y
44import Test.Hspec
44import Test.Hspec
45import Test.Hspec.Core.Spec (SpecM)
45import Test.Hspec.Core.Spec (SpecM)
46import Test.QuickCheck
46import Test.QuickCheck
47import Text.URI (URI)
47import Text.URI (URI)
48−import qualified Text.URI as URI
48+import Text.URI qualified as URI
49−import qualified Text.URI.QQ as QQ
49+import Text.URI.QQ qualified as QQ
50−import qualified Web.FormUrlEncoded as F
50+import Web.FormUrlEncoded qualified as F
51
51
52spec :: Spec
52spec :: Spec
53spec = do
53spec = do
@@ -413,7 +413,7 @@ spec = do
413 -- 'Https, Option _) type checks, so we can catch if the type of scheme
413 -- 'Https, Option _) type checks, so we can catch if the type of scheme
414 -- is unspecified.
414 -- is unspecified.
415 let testTypeOfQuoterResult ::
415 let testTypeOfQuoterResult ::
416− forall a s. Typeable a => (a, Option s) -> Bool
416+ forall a s. (Typeable a) => (a, Option s) -> Bool
417 testTypeOfQuoterResult _ = isJust $ eqT @a @(Url 'Https)
417 testTypeOfQuoterResult _ = isJust $ eqT @a @(Url 'Https)
418 in property $ testTypeOfQuoterResult [urlQ|https://example.org/|]
418 in property $ testTypeOfQuoterResult [urlQ|https://example.org/|]
419 it "doesn't work for invalid urls" $
419 it "doesn't work for invalid urls" $
req.cabal+43−43
@@ -1,11 +1,11 @@
1cabal-version: 2.4
1cabal-version: 2.4
2name: req
2name: req
3−version: 3.10.0
3+version: 3.13.4
4license: BSD-3-Clause
4license: BSD-3-Clause
5license-file: LICENSE.md
5license-file: LICENSE.md
6maintainer: Mark Karpov <markkarpov92@gmail.com>
6maintainer: Mark Karpov <markkarpov92@gmail.com>
7author: Mark Karpov <markkarpov92@gmail.com>
7author: Mark Karpov <markkarpov92@gmail.com>
8−tested-with: ghc ==8.10.7 ghc ==9.0.2 ghc ==9.2.1
8+tested-with: ghc ==9.6.3 ghc ==9.8.2 ghc ==9.10.1
9homepage: https://github.com/mrkkrp/req
9homepage: https://github.com/mrkkrp/req
10bug-reports: https://github.com/mrkkrp/req/issues
10bug-reports: https://github.com/mrkkrp/req/issues
11synopsis: HTTP client library
11synopsis: HTTP client library
@@ -31,71 +31,70 @@ flag dev
31
31
32library
32library
33 exposed-modules: Network.HTTP.Req
33 exposed-modules: Network.HTTP.Req
34− default-language: Haskell2010
34+ default-language: GHC2021
35 build-depends:
35 build-depends:
36 aeson >=0.9 && <3,
36 aeson >=0.9 && <3,
37 authenticate-oauth >=1.5 && <1.8,
37 authenticate-oauth >=1.5 && <1.8,
38− base >=4.13 && <5.0,
38+ base >=4.15 && <5,
39 blaze-builder >=0.3 && <0.5,
39 blaze-builder >=0.3 && <0.5,
40− bytestring >=0.10.8 && <0.12,
40+ bytestring >=0.10.8 && <0.13,
41 case-insensitive >=0.2 && <1.3,
41 case-insensitive >=0.2 && <1.3,
42+ containers >=0.5 && <0.8,
42− connection >=0.2.2 && <0.4,
43+ crypton-connection >=0.4.2 && <0.5,
44+ data-default-class >=0.2.0.0,
43 exceptions >=0.6 && <0.11,
45 exceptions >=0.6 && <0.11,
44− http-api-data >=0.2 && <0.5,
46+ http-api-data >=0.2 && <0.7,
45− http-client >=0.7 && <0.8,
47+ http-client >=0.7.13.1 && <0.8,
46 http-client-tls >=0.3.2 && <0.4,
48 http-client-tls >=0.3.2 && <0.4,
47− http-types >=0.8 && <10.0,
49+ http-types >=0.8 && <10,
48 modern-uri >=0.3 && <0.4,
50 modern-uri >=0.3 && <0.4,
49 monad-control >=1.0 && <1.1,
51 monad-control >=1.0 && <1.1,
50 mtl >=2.0 && <3.0,
52 mtl >=2.0 && <3.0,
51 retry >=0.8 && <0.10,
53 retry >=0.8 && <0.10,
52− template-haskell >=2.14 && <2.19,
54+ template-haskell >=2.19 && <2.23,
53− text >=0.2 && <2.1,
55+ text >=0.2 && <2.2,
54− time >=1.2 && <1.13,
55− transformers >=0.5.3.0 && <0.6,
56+ transformers >=0.5.3.0 && <0.7,
56 transformers-base,
57 transformers-base,
57 unliftio-core >=0.1.1 && <0.3
58 unliftio-core >=0.1.1 && <0.3
58
59
59 if flag(dev)
60 if flag(dev)
60− ghc-options: -O0 -Wall -Werror
61+ ghc-options:
62+ -Wall -Werror -Wpartial-fields -Wunused-packages
63+ -Wno-unused-imports
61
64
62 else
65 else
63 ghc-options: -O2 -Wall
66 ghc-options: -O2 -Wall
64
67
65− if flag(dev)
66− ghc-options:
67− -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns
68− -Wnoncanonical-monad-instances
69−
70test-suite pure-tests
68test-suite pure-tests
71 type: exitcode-stdio-1.0
69 type: exitcode-stdio-1.0
72 main-is: Spec.hs
70 main-is: Spec.hs
73− build-tool-depends: hspec-discover:hspec-discover >=2.0 && <3.0
71+ build-tool-depends: hspec-discover:hspec-discover >=2 && <3
74 hs-source-dirs: pure-tests
72 hs-source-dirs: pure-tests
75 other-modules: Network.HTTP.ReqSpec
73 other-modules: Network.HTTP.ReqSpec
76− default-language: Haskell2010
74+ default-language: GHC2021
77 build-depends:
75 build-depends:
78− QuickCheck >=2.7 && <3.0,
76+ QuickCheck >=2.7 && <3,
79 aeson >=0.9 && <3,
77 aeson >=0.9 && <3,
80− base >=4.13 && <5.0,
78+ base >=4.15 && <5.0,
81 blaze-builder >=0.3 && <0.5,
79 blaze-builder >=0.3 && <0.5,
82− bytestring >=0.10.8 && <0.12,
80+ bytestring >=0.10.8 && <0.13,
83 case-insensitive >=0.2 && <1.3,
81 case-insensitive >=0.2 && <1.3,
84− hspec >=2.0 && <3.0,
82+ hspec >=2.0 && <3,
85− hspec-core >=2.0 && <3.0,
83+ hspec-core >=2.0 && <3,
86− http-api-data >=0.2 && <0.5,
84+ http-api-data >=0.2 && <0.7,
87 http-client >=0.7 && <0.8,
85 http-client >=0.7 && <0.8,
88− http-types >=0.8 && <10.0,
86+ http-types >=0.8 && <10,
89 modern-uri >=0.3 && <0.4,
87 modern-uri >=0.3 && <0.4,
90− mtl >=2.0 && <3.0,
91 req,
88 req,
92 retry >=0.8 && <0.10,
89 retry >=0.8 && <0.10,
93− template-haskell >=2.14 && <2.19,
90+ template-haskell >=2.19 && <2.23,
94− text >=0.2 && <2.1,
91+ text >=0.2 && <2.2,
95− time >=1.2 && <1.13
92+ time >=1.2 && <1.15
96
93
97 if flag(dev)
94 if flag(dev)
98− ghc-options: -O0 -Wall -Werror
95+ ghc-options:
96+ -Wall -Werror -Wredundant-constraints -Wpartial-fields
97+ -Wunused-packages
99
98
100 else
99 else
101 ghc-options: -O2 -Wall
100 ghc-options: -O2 -Wall
@@ -103,25 +102,26 @@ test-suite pure-tests
103test-suite httpbin-tests
102test-suite httpbin-tests
104 type: exitcode-stdio-1.0
103 type: exitcode-stdio-1.0
105 main-is: Spec.hs
104 main-is: Spec.hs
106− build-tool-depends: hspec-discover:hspec-discover >=2.0 && <3.0
105+ build-tool-depends: hspec-discover:hspec-discover >=2 && <3
107 hs-source-dirs: httpbin-tests
106 hs-source-dirs: httpbin-tests
108 other-modules: Network.HTTP.ReqSpec
107 other-modules: Network.HTTP.ReqSpec
109− default-language: Haskell2010
108+ default-language: GHC2021
110 build-depends:
109 build-depends:
111− QuickCheck >=2.7 && <3.0,
110+ QuickCheck >=2.7 && <3,
112 aeson >=2 && <3,
111 aeson >=2 && <3,
113− base >=4.13 && <5.0,
112+ base >=4.15 && <5,
114− bytestring >=0.10.8 && <0.12,
113+ bytestring >=0.10.8 && <0.13,
115 hspec >=2.0 && <3.0,
114 hspec >=2.0 && <3.0,
116 http-client >=0.7 && <0.8,
115 http-client >=0.7 && <0.8,
117− http-types >=0.8 && <10.0,
116+ http-types >=0.8 && <10,
118 monad-control >=1.0 && <1.1,
117 monad-control >=1.0 && <1.1,
119− mtl >=2.0 && <3.0,
120 req,
118 req,
121− text >=0.2 && <2.1
119+ text >=0.2 && <2.2
122
120
123 if flag(dev)
121 if flag(dev)
124− ghc-options: -O0 -Wall -Werror
122+ ghc-options:
123+ -Wall -Werror -Wredundant-constraints -Wpartial-fields
124+ -Wunused-packages
125
125
126 else
126 else
127− ghc-options: -O2 -Wall
127+ buildable: False