on a4f757f help sign in
.github/dependabot.ymldeleted−6
since splitsnapshots
unifiedsplit
@@ -1,6 +0,0 @@
1−version: 2
2−updates:
3− - package-ecosystem: "github-actions"
4− directory: "/"
5− schedule:
6− interval: "weekly"
.github/workflows/ci.yaml+15−16
@@ -11,25 +11,27 @@ jobs:
11 ormolu:
11 ormolu:
12 runs-on: ubuntu-latest
12 runs-on: ubuntu-latest
13 steps:
13 steps:
14− - uses: actions/checkout@v4
14+ - uses: actions/checkout@v2.3.4
15− - uses: haskell-actions/run-ormolu@v17
15+ - uses: mrkkrp/ormolu-action@v2
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.12"]
21+ cabal: ["3.4"]
22− ghc: ["9.6.3", "9.8.2", "9.10.1"]
22+ ghc: ["8.8.4", "8.10.4", "9.0.1"]
23+ env:
24+ CONFIG: "--enable-tests --enable-benchmarks --flags=dev"
23 steps:
25 steps:
24− - uses: actions/checkout@v4
26+ - uses: actions/checkout@v2.3.4
25− - uses: haskell-actions/setup@v2
27+ - uses: haskell/actions/setup@v1.2
26 id: setup-haskell-cabal
28 id: setup-haskell-cabal
27 with:
29 with:
28 ghc-version: ${{ matrix.ghc }}
30 ghc-version: ${{ matrix.ghc }}
29 cabal-version: ${{ matrix.cabal }}
31 cabal-version: ${{ matrix.cabal }}
30− - run: cabal update
32+ - run: cabal v2-update
31− - run: cabal freeze
33+ - run: cabal v2-freeze $CONFIG
32− - uses: actions/cache@v4.2.3
34+ - uses: actions/cache@v2.1.3
33 with:
35 with:
34 path: |
36 path: |
35 ${{ steps.setup-haskell-cabal.outputs.cabal-store }}
37 ${{ steps.setup-haskell-cabal.outputs.cabal-store }}
@@ -38,10 +40,7 @@ jobs:
38 restore-keys: |
40 restore-keys: |
39 ${{ runner.os }}-${{ matrix.ghc }}-
41 ${{ runner.os }}-${{ matrix.ghc }}-
40 - run: cabal format && git diff --exit-code --color=always
42 - run: cabal format && git diff --exit-code --color=always
41− - run: cabal build
43+ - run: cabal v2-build $CONFIG
42− - run: |
43− docker pull kennethreitz/httpbin
44− docker run -p 1234:80 kennethreitz/httpbin &
45− cabal test
44+ - run: cabal v2-test $CONFIG
46− - run: cabal haddock
45+ - run: cabal v2-haddock $CONFIG
47− - run: cabal sdist
46+ - run: cabal v2-sdist
.gitignore−1
@@ -25,6 +25,5 @@ cabal.project.local
25cabal.sandbox.config
25cabal.sandbox.config
26dist-*/
26dist-*/
27dist/
27dist/
28−hie.yaml
29stack.yaml
28stack.yaml
30stack.yaml.lock
29stack.yaml.lock
CHANGELOG.md−51
@@ -1,54 +1,3 @@
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−
37−## Req 3.11.0
38−
39−* Add the `queryParamToList` method to the `QueryParam` type class.
40−* Add the `formToQuery` function. [Issue 126](https://github.com/mrkkrp/req/issues/126).
41−* Add `FromForm` instances (in the `Web.FormUrlEncoded` module) to the
42− `Option` and `FormUrlEncodedParam` types.
43−
44−## Req 3.10.0
45−
46−* Add `MonadHttp` instances for `transformers` types.
47−
48−## Req 3.9.2
49−
50−* The test suite works with `aeson-2.x.x.x`.
51−
52## Req 3.9.1
1## Req 3.9.1
53
2
54* Builds with GHC 9.0.
3* Builds with GHC 9.0.
Network/HTTP/Req.hs+69−218
@@ -1,9 +1,20 @@
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE CPP #-}
1{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE DeriveDataTypeable #-}
5+{-# LANGUAGE DeriveGeneric #-}
6+{-# LANGUAGE DeriveLift #-}
7+{-# LANGUAGE FlexibleInstances #-}
8+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE LambdaCase #-}
9{-# LANGUAGE LambdaCase #-}
10+{-# LANGUAGE MultiParamTypeClasses #-}
3{-# LANGUAGE OverloadedStrings #-}
11{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE QuasiQuotes #-}
12{-# LANGUAGE QuasiQuotes #-}
13+{-# LANGUAGE RankNTypes #-}
5{-# LANGUAGE RecordWildCards #-}
14{-# LANGUAGE RecordWildCards #-}
6{-# LANGUAGE RoleAnnotations #-}
15{-# LANGUAGE RoleAnnotations #-}
16+{-# LANGUAGE ScopedTypeVariables #-}
17+{-# LANGUAGE StandaloneDeriving #-}
7{-# LANGUAGE TemplateHaskellQuotes #-}
18{-# LANGUAGE TemplateHaskellQuotes #-}
8{-# LANGUAGE TypeFamilies #-}
19{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE UndecidableInstances #-}
20{-# LANGUAGE UndecidableInstances #-}
@@ -144,13 +155,11 @@ module Network.HTTP.Req
144 -- $query-parameters
155 -- $query-parameters
145 (=:),
156 (=:),
146 queryFlag,
157 queryFlag,
147− formToQuery,
148 QueryParam (..),
158 QueryParam (..),
149
159
150 -- *** Headers
160 -- *** Headers
151 header,
161 header,
152 attachHeader,
162 attachHeader,
153− headerRedacted,
154
163
155 -- *** Cookies
164 -- *** Cookies
156 -- $cookies
165 -- $cookies
@@ -197,78 +206,58 @@ module Network.HTTP.Req
197
206
198 -- * Other
207 -- * Other
199 HttpException (..),
208 HttpException (..),
200− isStatusCodeException,
201 CanHaveBody (..),
209 CanHaveBody (..),
202 Scheme (..),
210 Scheme (..),
203 )
211 )
204where
212where
205
213
206−import Blaze.ByteString.Builder qualified as BB
214+import qualified Blaze.ByteString.Builder as BB
207import Control.Applicative
215import Control.Applicative
208import Control.Arrow (first, second)
216import Control.Arrow (first, second)
209import Control.Exception hiding (Handler (..), TypeError)
217import Control.Exception hiding (Handler (..), TypeError)
210−import Control.Monad (guard, void, (>=>))
211import Control.Monad.Base
218import Control.Monad.Base
212import Control.Monad.Catch (Handler (..), MonadCatch, MonadMask, MonadThrow)
219import Control.Monad.Catch (Handler (..), MonadCatch, MonadMask, MonadThrow)
213import Control.Monad.IO.Class
220import Control.Monad.IO.Class
214import Control.Monad.IO.Unlift
221import Control.Monad.IO.Unlift
215−import Control.Monad.Reader (ReaderT (ReaderT), ask, lift, runReaderT)
222+import Control.Monad.Reader
216−import Control.Monad.Trans.Accum (AccumT)
217−import Control.Monad.Trans.Cont (ContT)
218import Control.Monad.Trans.Control
223import Control.Monad.Trans.Control
219−import Control.Monad.Trans.Except (ExceptT)
220−import Control.Monad.Trans.Identity (IdentityT)
221−import Control.Monad.Trans.Maybe (MaybeT)
222−import Control.Monad.Trans.RWS.CPS qualified as RWS.CPS
223−import Control.Monad.Trans.RWS.Lazy qualified as RWS.Lazy
224−import Control.Monad.Trans.RWS.Strict qualified as RWS.Strict
225−import Control.Monad.Trans.Select (SelectT)
226−import Control.Monad.Trans.State.Lazy qualified as State.Lazy
227−import Control.Monad.Trans.State.Strict qualified as State.Strict
228−import Control.Monad.Trans.Writer.CPS qualified as Writer.CPS
229−import Control.Monad.Trans.Writer.Lazy qualified as Writer.Lazy
230−import Control.Monad.Trans.Writer.Strict qualified as Writer.Strict
231import Control.Retry
224import Control.Retry
232import Data.Aeson (FromJSON (..), ToJSON (..))
225import Data.Aeson (FromJSON (..), ToJSON (..))
233−import Data.Aeson qualified as A
226+import qualified Data.Aeson as A
234import Data.ByteString (ByteString)
227import Data.ByteString (ByteString)
235−import Data.ByteString qualified as B
228+import qualified Data.ByteString as B
236−import Data.ByteString.Lazy qualified as BL
229+import qualified Data.ByteString.Lazy as BL
237−import Data.CaseInsensitive qualified as CI
230+import qualified Data.CaseInsensitive as CI
238import Data.Data (Data)
231import Data.Data (Data)
239−import Data.Default.Class (def)
240import Data.Function (on)
232import Data.Function (on)
241import Data.IORef
233import Data.IORef
242import Data.Kind (Constraint, Type)
234import Data.Kind (Constraint, Type)
243import Data.List (foldl', nubBy)
235import Data.List (foldl', nubBy)
244import Data.List.NonEmpty (NonEmpty (..))
236import Data.List.NonEmpty (NonEmpty (..))
245−import Data.List.NonEmpty qualified as NE
237+import qualified Data.List.NonEmpty as NE
246import Data.Maybe (fromMaybe)
238import Data.Maybe (fromMaybe)
247import Data.Proxy
239import Data.Proxy
248−import Data.Semigroup (Endo (..))
240+import Data.Semigroup hiding (Option, option)
249−import Data.Set qualified as S
250import Data.Text (Text)
241import Data.Text (Text)
251−import Data.Text qualified as T
242+import qualified Data.Text as T
252−import Data.Text.Encoding qualified as T
243+import qualified Data.Text.Encoding as T
253import Data.Typeable (Typeable, cast)
244import Data.Typeable (Typeable, cast)
254import GHC.Generics
245import GHC.Generics
255import GHC.TypeLits
246import GHC.TypeLits
256−import Language.Haskell.TH qualified as TH
247+import qualified Language.Haskell.TH as TH
257−import Language.Haskell.TH.Quote qualified as TH
248+import qualified Language.Haskell.TH.Quote as TH
258−import Language.Haskell.TH.Syntax qualified as TH
249+import qualified Language.Haskell.TH.Syntax as TH
259−import Network.Connection qualified as NC
250+import qualified Network.Connection as NC
260−import Network.HTTP.Client qualified as L
251+import qualified Network.HTTP.Client as L
261−import Network.HTTP.Client.Internal qualified as LI
252+import qualified Network.HTTP.Client.Internal as LI
262−import Network.HTTP.Client.MultipartFormData qualified as LM
253+import qualified Network.HTTP.Client.MultipartFormData as LM
263−import Network.HTTP.Client.TLS qualified as L
254+import qualified Network.HTTP.Client.TLS as L
264−import Network.HTTP.Types qualified as Y
255+import qualified Network.HTTP.Types as Y
265import System.IO.Unsafe (unsafePerformIO)
256import System.IO.Unsafe (unsafePerformIO)
266import Text.URI (URI)
257import Text.URI (URI)
267−import Text.URI qualified as URI
258+import qualified Text.URI as URI
268−import Text.URI.QQ qualified as QQ
259+import qualified Text.URI.QQ as QQ
269−import Web.Authenticate.OAuth qualified as OAuth
260+import qualified Web.Authenticate.OAuth as OAuth
270−import Web.FormUrlEncoded (FromForm (..), ToForm (..))
271−import Web.FormUrlEncoded qualified as Form
272import Web.HttpApiData (ToHttpApiData (..))
261import Web.HttpApiData (ToHttpApiData (..))
273
262
274----------------------------------------------------------------------------
263----------------------------------------------------------------------------
@@ -489,7 +478,7 @@ reqCb method url body responseProxy options adjustRequest =
489--
478--
490-- @since 3.7.0
479-- @since 3.7.0
491reqHandler ::
480reqHandler ::
492− (MonadHttp m) =>
481+ MonadHttp m =>
493 -- | How to get final result from a 'L.Response'
482 -- | How to get final result from a 'L.Response'
494 (L.Response L.BodyReader -> IO b) ->
483 (L.Response L.BodyReader -> IO b) ->
495 -- | 'L.Request' to perform
484 -- | 'L.Request' to perform
@@ -585,10 +574,10 @@ req' method url body options m = do
585 withReqManager (m request)
574 withReqManager (m request)
586
575
587-- | Perform an action using the global implicit 'L.Manager' that the rest
576-- | Perform an action using the global implicit 'L.Manager' that the rest
588−-- of the library uses. This allows us to reuse connections that the
577+-- of the library uses. This allows to reuse connections that the
589-- 'L.Manager' controls.
578-- 'L.Manager' controls.
590−withReqManager :: (MonadIO m) => (L.Manager -> m a) -> m a
579+withReqManager :: MonadIO m => (L.Manager -> m a) -> m a
591−withReqManager m = m globalManager
580+withReqManager m = liftIO (readIORef globalManager) >>= m
592
581
593-- | The global 'L.Manager' that 'req' uses. Here we just go with the
582-- | The global 'L.Manager' that 'req' uses. Here we just go with the
594-- default settings, so users don't need to deal with this manager stuff at
583-- default settings, so users don't need to deal with this manager stuff at
@@ -601,16 +590,16 @@ withReqManager m = m globalManager
601-- manager. From that moment on the 'IORef' will be just reused—exactly the
590-- manager. From that moment on the 'IORef' will be just reused—exactly the
602-- behavior we want here in order to maximize connection sharing. GHC could
591-- behavior we want here in order to maximize connection sharing. GHC could
603-- spoil the plan by inlining the definition, hence the @NOINLINE@ pragma.
592-- spoil the plan by inlining the definition, hence the @NOINLINE@ pragma.
604−globalManager :: L.Manager
593+globalManager :: IORef L.Manager
605globalManager = unsafePerformIO $ do
594globalManager = unsafePerformIO $ do
606 context <- NC.initConnectionContext
595 context <- NC.initConnectionContext
607 let settings =
596 let settings =
608 L.mkManagerSettingsContext
597 L.mkManagerSettingsContext
609 (Just context)
598 (Just context)
610− def
599+ (NC.TLSSettingsSimple False False False)
611 Nothing
600 Nothing
612 manager <- L.newManager settings
601 manager <- L.newManager settings
613− return manager
602+ newIORef manager
614{-# NOINLINE globalManager #-}
603{-# NOINLINE globalManager #-}
615
604
616----------------------------------------------------------------------------
605----------------------------------------------------------------------------
@@ -630,7 +619,7 @@ globalManager = unsafePerformIO $ do
630-- | A type class for monads that support performing HTTP requests.
619-- | A type class for monads that support performing HTTP requests.
631-- Typically, you only need to define the 'handleHttpException' method
620-- Typically, you only need to define the 'handleHttpException' method
632-- unless you want to tweak 'HttpConfig'.
621-- unless you want to tweak 'HttpConfig'.
633−class (MonadIO m) => MonadHttp m where
622+class MonadIO m => MonadHttp m where
634 -- | This method describes how to deal with 'HttpException' that was
623 -- | This method describes how to deal with 'HttpException' that was
635 -- caught by the library. One option is to re-throw it if you are OK with
624 -- caught by the library. One option is to re-throw it if you are OK with
636 -- exceptions, but if you prefer working with something like
625 -- exceptions, but if you prefer working with something like
@@ -691,8 +680,8 @@ data HttpConfig = HttpConfig
691 -- | The retry policy to use for request retrying. By default 'def' is
680 -- | The retry policy to use for request retrying. By default 'def' is
692 -- used (see 'RetryPolicyM').
681 -- used (see 'RetryPolicyM').
693 --
682 --
694− -- __Note__: signature of this function was changed to disallow 'IO' in
683+ -- __Note__: signature of this function was changed in the version
695− -- version /1.0.0/ and then changed back to its current form in /3.1.0/.
684+ -- /1.0.0/.
696 --
685 --
697 -- @since 0.3.0
686 -- @since 0.3.0
698 httpConfigRetryPolicy :: RetryPolicyM IO,
687 httpConfigRetryPolicy :: RetryPolicyM IO,
@@ -714,8 +703,9 @@ data HttpConfig = HttpConfig
714 -- | Max length of preview fragment of response body.
703 -- | Max length of preview fragment of response body.
715 --
704 --
716 -- @since 3.6.0
705 -- @since 3.6.0
717− httpConfigBodyPreviewLength :: forall a. (Num a) => a
706+ httpConfigBodyPreviewLength :: forall a. Num a => a
718 }
707 }
708+ deriving (Typeable)
719
709
720-- | The default value of 'HttpConfig'.
710-- | The default value of 'HttpConfig'.
721--
711--
@@ -797,87 +787,12 @@ instance MonadHttp Req where
797 handleHttpException = Req . lift . throwIO
787 handleHttpException = Req . lift . throwIO
798 getHttpConfig = Req ask
788 getHttpConfig = Req ask
799
789
800−-- | @since 3.10.0
801−instance (MonadHttp m, Monoid w) => MonadHttp (AccumT w m) where
802− handleHttpException = lift . handleHttpException
803− getHttpConfig = lift getHttpConfig
804−
805−-- | @since 3.10.0
806−instance (MonadHttp m) => MonadHttp (ContT r m) where
807− handleHttpException = lift . handleHttpException
808− getHttpConfig = lift getHttpConfig
809−
810−-- | @since 3.10.0
811−instance (MonadHttp m) => MonadHttp (ExceptT e m) where
812− handleHttpException = lift . handleHttpException
813− getHttpConfig = lift getHttpConfig
814−
815−-- | @since 3.10.0
816−instance (MonadHttp m) => MonadHttp (IdentityT m) where
817− handleHttpException = lift . handleHttpException
818− getHttpConfig = lift getHttpConfig
819−
820−-- | @since 3.10.0
821−instance (MonadHttp m) => MonadHttp (MaybeT m) where
822− handleHttpException = lift . handleHttpException
823− getHttpConfig = lift getHttpConfig
824−
825−-- | @since 3.10.0
826−instance (MonadHttp m) => MonadHttp (ReaderT r m) where
827− handleHttpException = lift . handleHttpException
828− getHttpConfig = lift getHttpConfig
829−
830−-- | @since 3.10.0
831−instance (MonadHttp m, Monoid w) => MonadHttp (RWS.CPS.RWST r w s m) where
832− handleHttpException = lift . handleHttpException
833− getHttpConfig = lift getHttpConfig
834−
835−-- | @since 3.10.0
836−instance (MonadHttp m, Monoid w) => MonadHttp (RWS.Lazy.RWST r w s m) where
837− handleHttpException = lift . handleHttpException
838− getHttpConfig = lift getHttpConfig
839−
840−-- | @since 3.10.0
841−instance (MonadHttp m, Monoid w) => MonadHttp (RWS.Strict.RWST r w s m) where
842− handleHttpException = lift . handleHttpException
843− getHttpConfig = lift getHttpConfig
844−
845−-- | @since 3.10.0
846−instance (MonadHttp m) => MonadHttp (SelectT r m) where
847− handleHttpException = lift . handleHttpException
848− getHttpConfig = lift getHttpConfig
849−
850−-- | @since 3.10.0
851−instance (MonadHttp m) => MonadHttp (State.Lazy.StateT s m) where
852− handleHttpException = lift . handleHttpException
853− getHttpConfig = lift getHttpConfig
854−
855−-- | @since 3.10.0
856−instance (MonadHttp m) => MonadHttp (State.Strict.StateT s m) where
857− handleHttpException = lift . handleHttpException
858− getHttpConfig = lift getHttpConfig
859−
860−-- | @since 3.10.0
861−instance (MonadHttp m, Monoid w) => MonadHttp (Writer.CPS.WriterT w m) where
862− handleHttpException = lift . handleHttpException
863− getHttpConfig = lift getHttpConfig
864−
865−-- | @since 3.10.0
866−instance (MonadHttp m, Monoid w) => MonadHttp (Writer.Lazy.WriterT w m) where
867− handleHttpException = lift . handleHttpException
868− getHttpConfig = lift getHttpConfig
869−
870−-- | @since 3.10.0
871−instance (MonadHttp m, Monoid w) => MonadHttp (Writer.Strict.WriterT w m) where
872− handleHttpException = lift . handleHttpException
873− getHttpConfig = lift getHttpConfig
874−
875-- | Run a computation in the 'Req' monad with the given 'HttpConfig'. In
790-- | Run a computation in the 'Req' monad with the given 'HttpConfig'. In
876-- the case of an exceptional situation an 'HttpException' will be thrown.
791-- the case of an exceptional situation an 'HttpException' will be thrown.
877--
792--
878-- @since 0.4.0
793-- @since 0.4.0
879runReq ::
794runReq ::
880− (MonadIO m) =>
795+ MonadIO m =>
881 -- | 'HttpConfig' to use
796 -- | 'HttpConfig' to use
882 HttpConfig ->
797 HttpConfig ->
883 -- | Computation to run
798 -- | Computation to run
@@ -982,7 +897,7 @@ class HttpMethod a where
982 -- | Return name of the method as a 'ByteString'.
897 -- | Return name of the method as a 'ByteString'.
983 httpMethodName :: Proxy a -> ByteString
898 httpMethodName :: Proxy a -> ByteString
984
899
985−instance (HttpMethod method) => RequestComponent (Tagged "method" method) where
900+instance HttpMethod method => RequestComponent (Tagged "method" method) where
986 getRequestMod _ = Endo $ \x ->
901 getRequestMod _ = Endo $ \x ->
987 x {L.method = httpMethodName (Proxy :: Proxy method)}
902 x {L.method = httpMethodName (Proxy :: Proxy method)}
988
903
@@ -1024,20 +939,25 @@ instance (HttpMethod method) => RequestComponent (Tagged "method" method) where
1024-- > -- https://%D1%8E%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4.%D1%80%D1%84
939-- > -- https://%D1%8E%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4.%D1%80%D1%84
1025data Url (scheme :: Scheme) = Url Scheme (NonEmpty Text)
940data Url (scheme :: Scheme) = Url Scheme (NonEmpty Text)
1026 -- NOTE The second value is the path segments in reversed order.
941 -- NOTE The second value is the path segments in reversed order.
1027− deriving (Eq, Ord, Show, Data, Generic)
942+ deriving (Eq, Ord, Show, Data, Typeable, Generic)
1028
943
1029type role Url nominal
944type role Url nominal
1030
945
1031-- With template-haskell >=2.15 and text >=1.2.4 Lift can be derived, however
946-- With template-haskell >=2.15 and text >=1.2.4 Lift can be derived, however
1032-- the derived lift forgets the type of the scheme.
947-- the derived lift forgets the type of the scheme.
1033−instance (Typeable scheme) => TH.Lift (Url scheme) where
948+instance Typeable scheme => TH.Lift (Url scheme) where
1034 lift url =
949 lift url =
1035 TH.dataToExpQ (fmap liftText . cast) url `TH.sigE` case url of
950 TH.dataToExpQ (fmap liftText . cast) url `TH.sigE` case url of
1036 Url Http _ -> [t|Url 'Http|]
951 Url Http _ -> [t|Url 'Http|]
1037 Url Https _ -> [t|Url 'Https|]
952 Url Https _ -> [t|Url 'Https|]
1038 where
953 where
1039 liftText t = TH.AppE (TH.VarE 'T.pack) <$> TH.lift (T.unpack t)
954 liftText t = TH.AppE (TH.VarE 'T.pack) <$> TH.lift (T.unpack t)
955+
956+#if MIN_VERSION_template_haskell(2,17,0)
1040 liftTyped = TH.Code . TH.unsafeTExpCoerce . TH.lift
957 liftTyped = TH.Code . TH.unsafeTExpCoerce . TH.lift
958+#elif MIN_VERSION_template_haskell(2,16,0)
959+ liftTyped = TH.unsafeTExpCoerce . TH.lift
960+#endif
1041
961
1042-- | Given host name, produce a 'Url' which has “http” as its scheme and
962-- | Given host name, produce a 'Url' which has “http” as its scheme and
1043-- empty path. This also sets port to @80@.
963-- empty path. This also sets port to @80@.
@@ -1053,7 +973,7 @@ https = Url Https . pure
1053-- path segment can be of any type that is an instance of 'ToHttpApiData'.
973-- path segment can be of any type that is an instance of 'ToHttpApiData'.
1054infixl 5 /~
974infixl 5 /~
1055
975
1056−(/~) :: (ToHttpApiData a) => Url scheme -> a -> Url scheme
976+(/~) :: ToHttpApiData a => Url scheme -> a -> Url scheme
1057Url secure path /~ segment = Url secure (NE.cons (toUrlPiece segment) path)
977Url secure path /~ segment = Url secure (NE.cons (toUrlPiece segment) path)
1058
978
1059-- | A type-constrained version of @('/~')@ to remove ambiguity in the cases
979-- | A type-constrained version of @('/~')@ to remove ambiguity in the cases
@@ -1241,7 +1161,7 @@ instance HttpBody NoReqBody where
1241-- charset=utf-8\"@ value.
1161-- charset=utf-8\"@ value.
1242newtype ReqBodyJson a = ReqBodyJson a
1162newtype ReqBodyJson a = ReqBodyJson a
1243
1163
1244−instance (ToJSON a) => HttpBody (ReqBodyJson a) where
1164+instance ToJSON a => HttpBody (ReqBodyJson a) where
1245 getRequestBody (ReqBodyJson a) = L.RequestBodyLBS (A.encode a)
1165 getRequestBody (ReqBodyJson a) = L.RequestBodyLBS (A.encode a)
1246 getRequestContentType _ = pure "application/json; charset=utf-8"
1166 getRequestContentType _ = pure "application/json; charset=utf-8"
1247
1167
@@ -1294,13 +1214,6 @@ newtype FormUrlEncodedParam = FormUrlEncodedParam [(Text, Maybe Text)]
1294instance QueryParam FormUrlEncodedParam where
1214instance QueryParam FormUrlEncodedParam where
1295 queryParam name mvalue =
1215 queryParam name mvalue =
1296 FormUrlEncodedParam [(name, toQueryParam <$> mvalue)]
1216 FormUrlEncodedParam [(name, toQueryParam <$> mvalue)]
1297− queryParamToList (FormUrlEncodedParam p) = p
1298−
1299−-- | Use 'formToQuery'.
1300−--
1301−-- @since 3.11.0
1302−instance FromForm FormUrlEncodedParam where
1303− fromForm = Right . formToQuery
1304
1217
1305-- | Multipart form data. Please consult the
1218-- | Multipart form data. Please consult the
1306-- "Network.HTTP.Client.MultipartFormData" module for how to construct
1219-- "Network.HTTP.Client.MultipartFormData" module for how to construct
@@ -1340,7 +1253,7 @@ instance HttpBody ReqBodyMultipart where
1340-- | Create 'ReqBodyMultipart' request body from a collection of 'LM.Part's.
1253-- | Create 'ReqBodyMultipart' request body from a collection of 'LM.Part's.
1341--
1254--
1342-- @since 0.2.0
1255-- @since 0.2.0
1343−reqBodyMultipart :: (MonadIO m) => [LM.Part] -> m ReqBodyMultipart
1256+reqBodyMultipart :: MonadIO m => [LM.Part] -> m ReqBodyMultipart
1344reqBodyMultipart parts = liftIO $ do
1257reqBodyMultipart parts = liftIO $ do
1345 boundary <- LM.webkitBoundary
1258 boundary <- LM.webkitBoundary
1346 body <- LM.renderParts boundary parts
1259 body <- LM.renderParts boundary parts
@@ -1380,7 +1293,7 @@ type family
1380 TypeError
1293 TypeError
1381 ('Text "This HTTP method does not allow attaching a request body.")
1294 ('Text "This HTTP method does not allow attaching a request body.")
1382
1295
1383−instance (HttpBody body) => RequestComponent (Tagged "body" body) where
1296+instance HttpBody body => RequestComponent (Tagged "body" body) where
1384 getRequestMod (Tagged body) = Endo $ \x ->
1297 getRequestMod (Tagged body) = Endo $ \x ->
1385 x
1298 x
1386 { L.requestBody = getRequestBody body,
1299 { L.requestBody = getRequestBody body,
@@ -1426,12 +1339,6 @@ instance Monoid (Option scheme) where
1426 mempty = Option mempty Nothing
1339 mempty = Option mempty Nothing
1427 mappend = (<>)
1340 mappend = (<>)
1428
1341
1429−-- | Use 'formToQuery'.
1430−--
1431−-- @since 3.11.0
1432−instance FromForm (Option scheme) where
1433− fromForm = Right . formToQuery
1434−
1435-- | A helper to create an 'Option' that modifies only collection of query
1342-- | A helper to create an 'Option' that modifies only collection of query
1436-- parameters. This helper is not a part of the public API.
1343-- parameters. This helper is not a part of the public API.
1437withQueryParams :: (Y.QueryText -> Y.QueryText) -> Option scheme
1344withQueryParams :: (Y.QueryText -> Y.QueryText) -> Option scheme
@@ -1450,7 +1357,7 @@ instance RequestComponent (Option scheme) where
1450
1357
1451-- | Finalize given 'L.Request' by applying a finalizer from the given
1358-- | Finalize given 'L.Request' by applying a finalizer from the given
1452-- 'Option' (if it has any).
1359-- 'Option' (if it has any).
1453−finalizeRequest :: (MonadIO m) => Option scheme -> L.Request -> m L.Request
1360+finalizeRequest :: MonadIO m => Option scheme -> L.Request -> m L.Request
1454finalizeRequest (Option _ mfinalizer) = liftIO . fromMaybe pure mfinalizer
1361finalizeRequest (Option _ mfinalizer) = liftIO . fromMaybe pure mfinalizer
1455
1362
1456----------------------------------------------------------------------------
1363----------------------------------------------------------------------------
@@ -1483,27 +1390,9 @@ name =: value = queryParam name (pure value)
1483-- This operator is defined in terms of 'queryParam':
1390-- This operator is defined in terms of 'queryParam':
1484--
1391--
1485-- > queryFlag name = queryParam name (Nothing :: Maybe ())
1392-- > queryFlag name = queryParam name (Nothing :: Maybe ())
1486−queryFlag :: (QueryParam param) => Text -> param
1393+queryFlag :: QueryParam param => Text -> param
1487queryFlag name = queryParam name (Nothing :: Maybe ())
1394queryFlag name = queryParam name (Nothing :: Maybe ())
1488
1395
1489−-- | Construct query parameters from a 'ToForm' instance. This function
1490−-- produces the same query params as 'Form.urlEncodeAsFormStable'.
1491−--
1492−-- Note that 'Form.Form' doesn't have the concept of parameters with the
1493−-- empty value (i.e. what you can get by @key =: ""@). If the value is
1494−-- empty, it will be encoded as a valueless parameter (i.e. what you can get
1495−-- by @queryFlag key@).
1496−--
1497−-- @since 3.11.0
1498−formToQuery :: (QueryParam param, Monoid param, ToForm f) => f -> param
1499−formToQuery f = mconcat . fmap toParam . Form.toListStable $ toForm f
1500− where
1501− toParam (key, val) =
1502− queryParam key $
1503− if val == ""
1504− then Nothing
1505− else Just val
1506−
1507-- | A type class for query-parameter-like things. The reason to have an
1396-- | A type class for query-parameter-like things. The reason to have an
1508-- overloaded 'queryParam' is to be able to use it as an 'Option' and as a
1397-- overloaded 'queryParam' is to be able to use it as an 'Option' and as a
1509-- 'FormUrlEncodedParam' when constructing form URL encoded request bodies.
1398-- 'FormUrlEncodedParam' when constructing form URL encoded request bodies.
@@ -1513,17 +1402,11 @@ class QueryParam param where
1513 -- 'Nothing', it won't be included at all (i.e. you create a flag this
1402 -- 'Nothing', it won't be included at all (i.e. you create a flag this
1514 -- way). It's recommended to use @('=:')@ and 'queryFlag' instead of this
1403 -- way). It's recommended to use @('=:')@ and 'queryFlag' instead of this
1515 -- method, because they are easier to read.
1404 -- method, because they are easier to read.
1516− queryParam :: (ToHttpApiData a) => Text -> Maybe a -> param
1405+ queryParam :: ToHttpApiData a => Text -> Maybe a -> param
1517−
1518− -- | Get the query parameter names and values set by 'queryParam'.
1519− --
1520− -- @since 3.11.0
1521− queryParamToList :: param -> [(Text, Maybe Text)]
1522
1406
1523instance QueryParam (Option scheme) where
1407instance QueryParam (Option scheme) where
1524 queryParam name mvalue =
1408 queryParam name mvalue =
1525 withQueryParams ((:) (name, toQueryParam <$> mvalue))
1409 withQueryParams ((:) (name, toQueryParam <$> mvalue))
1526− queryParamToList (Option f _) = fst $ appEndo f ([], L.defaultRequest)
1527
1410
1528----------------------------------------------------------------------------
1411----------------------------------------------------------------------------
1529-- Request—Optional parameters—Headers
1412-- Request—Optional parameters—Headers
@@ -1547,14 +1430,6 @@ attachHeader :: ByteString -> ByteString -> L.Request -> L.Request
1547attachHeader name value x =
1430attachHeader name value x =
1548 x {L.requestHeaders = (CI.mk name, value) : L.requestHeaders x}
1431 x {L.requestHeaders = (CI.mk name, value) : L.requestHeaders x}
1549
1432
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−
1558----------------------------------------------------------------------------
1433----------------------------------------------------------------------------
1559-- Request—Optional parameters—Cookies
1434-- Request—Optional parameters—Cookies
1560
1435
@@ -1762,7 +1637,7 @@ ignoreResponse = Proxy
1762newtype JsonResponse a = JsonResponse (L.Response a)
1637newtype JsonResponse a = JsonResponse (L.Response a)
1763 deriving (Show)
1638 deriving (Show)
1764
1639
1765−instance (FromJSON a) => HttpResponse (JsonResponse a) where
1640+instance FromJSON a => HttpResponse (JsonResponse a) where
1766 type HttpResponseBody (JsonResponse a) = a
1641 type HttpResponseBody (JsonResponse a) = a
1767 toVanillaResponse (JsonResponse r) = r
1642 toVanillaResponse (JsonResponse r) = r
1768 getHttpResponse r = do
1643 getHttpResponse r = do
@@ -1878,14 +1753,14 @@ brReadN br n = go 0 id id
1878
1753
1879-- | Get the response body.
1754-- | Get the response body.
1880responseBody ::
1755responseBody ::
1881− (HttpResponse response) =>
1756+ HttpResponse response =>
1882 response ->
1757 response ->
1883 HttpResponseBody response
1758 HttpResponseBody response
1884responseBody = L.responseBody . toVanillaResponse
1759responseBody = L.responseBody . toVanillaResponse
1885
1760
1886-- | Get the response status code.
1761-- | Get the response status code.
1887responseStatusCode ::
1762responseStatusCode ::
1888− (HttpResponse response) =>
1763+ HttpResponse response =>
1889 response ->
1764 response ->
1890 Int
1765 Int
1891responseStatusCode =
1766responseStatusCode =
@@ -1893,7 +1768,7 @@ responseStatusCode =
1893
1768
1894-- | Get the response status message.
1769-- | Get the response status message.
1895responseStatusMessage ::
1770responseStatusMessage ::
1896− (HttpResponse response) =>
1771+ HttpResponse response =>
1897 response ->
1772 response ->
1898 ByteString
1773 ByteString
1899responseStatusMessage =
1774responseStatusMessage =
@@ -1901,7 +1776,7 @@ responseStatusMessage =
1901
1776
1902-- | Lookup a particular header from a response.
1777-- | Lookup a particular header from a response.
1903responseHeader ::
1778responseHeader ::
1904− (HttpResponse response) =>
1779+ HttpResponse response =>
1905 -- | Response interpretation
1780 -- | Response interpretation
1906 response ->
1781 response ->
1907 -- | Header to lookup
1782 -- | Header to lookup
@@ -1913,7 +1788,7 @@ responseHeader r h =
1913
1788
1914-- | Get the response 'L.CookieJar'.
1789-- | Get the response 'L.CookieJar'.
1915responseCookieJar ::
1790responseCookieJar ::
1916− (HttpResponse response) =>
1791+ HttpResponse response =>
1917 response ->
1792 response ->
1918 L.CookieJar
1793 L.CookieJar
1919responseCookieJar = L.responseCookieJar . toVanillaResponse
1794responseCookieJar = L.responseCookieJar . toVanillaResponse
@@ -1967,16 +1842,6 @@ class HttpResponse response where
1967 acceptHeader :: Proxy response -> Maybe ByteString
1842 acceptHeader :: Proxy response -> Maybe ByteString
1968 acceptHeader Proxy = Nothing
1843 acceptHeader Proxy = Nothing
1969
1844
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−
1980----------------------------------------------------------------------------
1845----------------------------------------------------------------------------
1981-- Other
1846-- Other
1982
1847
@@ -2011,24 +1876,10 @@ data HttpException
2011 | -- | A wrapper with Aeson-produced 'String' describing why decoding
1876 | -- | A wrapper with Aeson-produced 'String' describing why decoding
2012 -- failed
1877 -- failed
2013 JsonHttpException String
1878 JsonHttpException String
2014− deriving (Show, Generic)
1879+ deriving (Show, Typeable, Generic)
2015
1880
2016instance Exception HttpException
1881instance Exception HttpException
2017
1882
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−
2032-- | A simple type isomorphic to 'Bool' that we only have for better error
1883-- | A simple type isomorphic to 'Bool' that we only have for better error
2033-- messages. We use it as a kind and its data constructors as type-level
1884-- messages. We use it as a kind and its data constructors as type-level
2034-- tags.
1885-- tags.
@@ -2048,4 +1899,4 @@ data Scheme
2048 Http
1899 Http
2049 | -- | HTTPS
1900 | -- | HTTPS
2050 Https
1901 Https
2051− deriving (Eq, Ord, Show, Data, Generic, TH.Lift)
1902+ deriving (Eq, Ord, Show, Data, Typeable, Generic, TH.Lift)
README.md+3−1
@@ -4,8 +4,10 @@
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/actions/workflows/ci.yaml/badge.svg)](https://github.com/mrkkrp/req/actions/workflows/ci.yaml)
7+![CI](https://github.com/mrkkrp/req/workflows/CI/badge.svg?branch=master)
8
8
9+* [Motivation and Req vs other libraries](#motivation-and-req-vs-other-libraries)
10+* [Unsolved problems](#unsolved-problems)
9* [Related packages](#related-packages)
11* [Related packages](#related-packages)
10* [Blog posts](#blog-posts)
12* [Blog posts](#blog-posts)
11* [Contribution](#contribution)
13* [Contribution](#contribution)
Setup.hsnew file+6
@@ -0,0 +1,6 @@
1+module Main (main) where
2+
3+import Distribution.Simple
4+
5+main :: IO ()
6+main = defaultMain
cabal.projectdeleted−4
@@ -1,4 +0,0 @@
1−packages: .
2−tests: True
3−benchmarks: True
4−constraints: req +dev
httpbin-tests/Network/HTTP/ReqSpec.hs+60−103
@@ -1,6 +1,5 @@
1{-# LANGUAGE DataKinds #-}
1{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE LambdaCase #-}
4{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE RankNTypes #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
@@ -8,25 +7,22 @@
8module Network.HTTP.ReqSpec (spec) where
7module Network.HTTP.ReqSpec (spec) where
9
8
10import Control.Exception
9import Control.Exception
11−import Control.Monad (forM_)
10+import Control.Monad.Reader
12import Control.Monad.Trans.Control
11import Control.Monad.Trans.Control
13import Data.Aeson (ToJSON (..), Value (..), object, (.=))
12import Data.Aeson (ToJSON (..), Value (..), object, (.=))
14−import Data.Aeson qualified as A
13+import qualified Data.Aeson as A
15−import Data.Aeson.KeyMap qualified as Aeson.KeyMap
16−import Data.ByteString qualified as B
14+import qualified Data.ByteString as B
17−import Data.ByteString.Lazy qualified as BL
15+import qualified Data.ByteString.Lazy as BL
18−import Data.Functor.Identity (runIdentity)
16+import qualified Data.HashMap.Strict as HM
19−import Data.Maybe (fromJust)
20import Data.Proxy
17import Data.Proxy
21import Data.Text (Text)
18import Data.Text (Text)
22−import Data.Text qualified as T
19+import qualified Data.Text as T
23−import Data.Text.Encoding qualified as T
20+import qualified Data.Text.Encoding as T
24−import Data.Text.IO qualified as TIO
21+import qualified Data.Text.IO as TIO
25−import Network.HTTP.Client qualified as L
22+import qualified Network.HTTP.Client as L
26−import Network.HTTP.Client.MultipartFormData qualified as LM
23+import qualified Network.HTTP.Client.MultipartFormData as LM
27−import Network.HTTP.Req hiding (req)
24+import Network.HTTP.Req
28−import Network.HTTP.Req qualified as Req
29−import Network.HTTP.Types qualified as Y
25+import qualified Network.HTTP.Types as Y
30import Test.Hspec
26import Test.Hspec
31import Test.QuickCheck
27import Test.QuickCheck
32
28
@@ -50,11 +46,6 @@ spec = do
50 blindlyThrowing (req GET httpbin NoReqBody ignoreResponse mempty)
46 blindlyThrowing (req GET httpbin NoReqBody ignoreResponse mempty)
51 `shouldThrow` anyException
47 `shouldThrow` anyException
52
48
53− describe "isStatusCodeException" $
54− it "extracts non-2xx response" $
55− req GET (httpbin /: "foo") NoReqBody ignoreResponse mempty
56− `shouldThrow` selector404ByStatusCodeException
57−
58 describe "receiving user-agent header back" $
49 describe "receiving user-agent header back" $
59 it "works" $ do
50 it "works" $ do
60 r <-
51 r <-
@@ -85,7 +76,8 @@ spec = do
85 .= object
76 .= object
86 [ "Accept-Encoding" .= ("gzip" :: Text),
77 [ "Accept-Encoding" .= ("gzip" :: Text),
87 "Foo" .= ("bar" :: Text),
78 "Foo" .= ("bar" :: Text),
88− "Baz" .= ("quux" :: Text)
79+ "Baz" .= ("quux" :: Text),
80+ "Host" .= ("httpbin.org" :: Text)
89 ]
81 ]
90 ]
82 ]
91 responseStatusCode r `shouldBe` 200
83 responseStatusCode r `shouldBe` 200
@@ -97,10 +89,11 @@ spec = do
97 (stripFunnyHeaders . stripOrigin) (responseBody r)
89 (stripFunnyHeaders . stripOrigin) (responseBody r)
98 `shouldBe` object
90 `shouldBe` object
99 [ "args" .= emptyObject,
91 [ "args" .= emptyObject,
100− "url" .= ("http://localhost:1234/get" :: Text),
92+ "url" .= ("https://httpbin.org/get" :: Text),
101 "headers"
93 "headers"
102 .= object
94 .= object
103− [ "Accept-Encoding" .= ("gzip" :: Text)
95+ [ "Accept-Encoding" .= ("gzip" :: Text),
96+ "Host" .= ("httpbin.org" :: Text)
104 ]
97 ]
105 ]
98 ]
106 responseHeader r "Content-Type" `shouldBe` return "application/json"
99 responseHeader r "Content-Type" `shouldBe` return "application/json"
@@ -117,11 +110,12 @@ spec = do
117 [ "args" .= emptyObject,
110 [ "args" .= emptyObject,
118 "json" .= text,
111 "json" .= text,
119 "data" .= reflected,
112 "data" .= reflected,
120− "url" .= ("http://localhost:1234/post" :: Text),
113+ "url" .= ("https://httpbin.org/post" :: Text),
121 "headers"
114 "headers"
122 .= object
115 .= object
123 [ "Content-Type" .= ("application/json; charset=utf-8" :: Text),
116 [ "Content-Type" .= ("application/json; charset=utf-8" :: Text),
124 "Accept-Encoding" .= ("gzip" :: Text),
117 "Accept-Encoding" .= ("gzip" :: Text),
118+ "Host" .= ("httpbin.org" :: Text),
125 "Content-Length" .= show (T.length reflected)
119 "Content-Length" .= show (T.length reflected)
126 ],
120 ],
127 "files" .= emptyObject,
121 "files" .= emptyObject,
@@ -139,17 +133,18 @@ spec = do
139 LM.partBS "bar" "bar data!"
133 LM.partBS "bar" "bar data!"
140 ]
134 ]
141 r <- req POST (httpbin /: "post") body jsonResponse mempty
135 r <- req POST (httpbin /: "post") body jsonResponse mempty
142− let contentType = fromJust (getRequestContentType body)
136+ let Just contentType = getRequestContentType body
143 (stripFunnyHeaders . stripOrigin) (responseBody r)
137 (stripFunnyHeaders . stripOrigin) (responseBody r)
144 `shouldBe` object
138 `shouldBe` object
145 [ "args" .= emptyObject,
139 [ "args" .= emptyObject,
146 "json" .= Null,
140 "json" .= Null,
147 "data" .= ("" :: Text),
141 "data" .= ("" :: Text),
148− "url" .= ("http://localhost:1234/post" :: Text),
142+ "url" .= ("https://httpbin.org/post" :: Text),
149 "headers"
143 "headers"
150 .= object
144 .= object
151 [ "Content-Type" .= T.decodeUtf8 contentType,
145 [ "Content-Type" .= T.decodeUtf8 contentType,
152 "Accept-Encoding" .= ("gzip" :: Text),
146 "Accept-Encoding" .= ("gzip" :: Text),
147+ "Host" .= ("httpbin.org" :: Text),
153 "Content-Length" .= ("242" :: Text)
148 "Content-Length" .= ("242" :: Text)
154 ],
149 ],
155 "files" .= emptyObject,
150 "files" .= emptyObject,
@@ -174,10 +169,11 @@ spec = do
174 [ "args" .= emptyObject,
169 [ "args" .= emptyObject,
175 "json" .= Null,
170 "json" .= Null,
176 "data" .= contents,
171 "data" .= contents,
177− "url" .= ("http://localhost:1234/patch" :: Text),
172+ "url" .= ("https://httpbin.org/patch" :: Text),
178 "headers"
173 "headers"
179 .= object
174 .= object
180 [ "Accept-Encoding" .= ("gzip" :: Text),
175 [ "Accept-Encoding" .= ("gzip" :: Text),
176+ "Host" .= ("httpbin.org" :: Text),
181 "Content-Length" .= show (T.length contents)
177 "Content-Length" .= show (T.length contents)
182 ],
178 ],
183 "files" .= emptyObject,
179 "files" .= emptyObject,
@@ -199,11 +195,12 @@ spec = do
199 [ "args" .= emptyObject,
195 [ "args" .= emptyObject,
200 "json" .= Null,
196 "json" .= Null,
201 "data" .= ("" :: Text),
197 "data" .= ("" :: Text),
202− "url" .= ("http://localhost:1234/put" :: Text),
198+ "url" .= ("https://httpbin.org/put" :: Text),
203 "headers"
199 "headers"
204 .= object
200 .= object
205 [ "Content-Type" .= ("application/x-www-form-urlencoded" :: Text),
201 [ "Content-Type" .= ("application/x-www-form-urlencoded" :: Text),
206 "Accept-Encoding" .= ("gzip" :: Text),
202 "Accept-Encoding" .= ("gzip" :: Text),
203+ "Host" .= ("httpbin.org" :: Text),
207 "Content-Length" .= ("18" :: Text)
204 "Content-Length" .= ("18" :: Text)
208 ],
205 ],
209 "files" .= emptyObject,
206 "files" .= emptyObject,
@@ -261,17 +258,21 @@ spec = do
261 -- TODO /response-headers
258 -- TODO /response-headers
262 -- TODO /redirect
259 -- TODO /redirect
263
260
261+ -- FIXME Redirects test is temporarily disabled due to
262+ --
263+ -- https://github.com/postmanlabs/httpbin/issues/617
264+
264− describe "redirects" $
265+ -- describe "redirects" $
265− it "follows redirects" $ do
266+ -- it "follows redirects" $ do
266− r <-
267+ -- r <-
267− req
268+ -- req
268− GET
269+ -- GET
269− (httpbin /: "redirect-to")
270+ -- (httpbin /: "redirect-to")
270− NoReqBody
271+ -- NoReqBody
271− ignoreResponse
272+ -- ignoreResponse
272− ("url" =: ("https://httpbin.org" :: Text))
273+ -- ("url" =: ("https://httpbin.org" :: Text))
273− responseStatusCode r `shouldBe` 200
274+ -- responseStatusCode r `shouldBe` 200
274− responseStatusMessage r `shouldBe` "OK"
275+ -- responseStatusMessage r `shouldBe` "OK"
275
276
276 -- TODO /relative-redicet
277 -- TODO /relative-redicet
277 -- TODO /absolute-redirect
278 -- TODO /absolute-redirect
@@ -301,7 +302,7 @@ spec = do
301 (httpbin /: "basic-auth" /~ user /~ password)
302 (httpbin /: "basic-auth" /~ user /~ password)
302 NoReqBody
303 NoReqBody
303 ignoreResponse
304 ignoreResponse
304− (basicAuthUnsafe (T.encodeUtf8 user) (T.encodeUtf8 password))
305+ (basicAuth (T.encodeUtf8 user) (T.encodeUtf8 password))
305 responseStatusCode r `shouldBe` 200
306 responseStatusCode r `shouldBe` 200
306 responseStatusMessage r `shouldBe` "OK"
307 responseStatusMessage r `shouldBe` "OK"
307
308
@@ -401,67 +402,30 @@ blindlyThrowing = runReq defaultHttpConfig {httpConfigCheckResponse = doit}
401 doit _ _ = error "Oops!"
402 doit _ _ = error "Oops!"
402
403
403-- | 'Url' representing <https://httpbin.org>.
404-- | 'Url' representing <https://httpbin.org>.
404−httpbin :: Url 'Http
405+httpbin :: Url 'Https
405−httpbin = http "localhost"
406+httpbin = https "httpbin.org"
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
426
407
427-- | Remove “origin” field from JSON value. Origin may change, we don't want
408-- | Remove “origin” field from JSON value. Origin may change, we don't want
428-- to depend on that.
409-- to depend on that.
429stripOrigin :: Value -> Value
410stripOrigin :: Value -> Value
430−stripOrigin (Object m) = Object (Aeson.KeyMap.delete "origin" m)
411+stripOrigin (Object m) = Object (HM.delete "origin" m)
431stripOrigin value = value
412stripOrigin value = value
432
413
433-- | Remove funny headers that might break the tests.
414-- | Remove funny headers that might break the tests.
434stripFunnyHeaders :: Value -> Value
415stripFunnyHeaders :: Value -> Value
435−stripFunnyHeaders = \case
416+stripFunnyHeaders (Object m) =
436− Object m ->
417+ let f (Object p) = Object $ HM.filterWithKey (\k _ -> k `elem` hs) p
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 $
453− Aeson.KeyMap.filterWithKey
454− (\k _ -> k `elem` whitelistedHeaders)
455− p
456− value -> value
418+ f value = value
457− where
419+ hs =
458− whitelistedHeaders =
459− [ "Content-Type",
420+ [ "Content-Type",
460− "Accept-Encoding",
421+ "Accept-Encoding",
422+ "Host",
461− "Content-Length",
423+ "Content-Length",
462− "Foo",
424+ "Foo",
463− "Baz"
425+ "Baz"
464− ]
426+ ]
427+ in Object (HM.adjust f "headers" m)
428+stripFunnyHeaders value = value
465
429
466-- | This is a complete test case that makes use of <https://httpbin.org> to
430-- | This is a complete test case that makes use of <https://httpbin.org> to
467-- get various response status codes.
431-- get various response status codes.
@@ -491,19 +455,12 @@ selector404
491 L.responseStatus response == Y.status404 && not (B.null chunk)
455 L.responseStatus response == Y.status404 && not (B.null chunk)
492selector404 _ = False
456selector404 _ = False
493
457
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−
501-- | The empty JSON 'Object'.
458-- | The empty JSON 'Object'.
502emptyObject :: Value
459emptyObject :: Value
503−emptyObject = Object Aeson.KeyMap.empty
460+emptyObject = Object HM.empty
504
461
505-- | Get a rendered JSON value as 'Text'.
462-- | Get a rendered JSON value as 'Text'.
506−reflectJSON :: (ToJSON a) => a -> Text
463+reflectJSON :: ToJSON a => a -> Text
507reflectJSON = T.decodeUtf8 . BL.toStrict . A.encode
464reflectJSON = T.decodeUtf8 . BL.toStrict . A.encode
508
465
509-- | Clarify to the type checker that the inner computation is in the 'IO'
466-- | Clarify to the type checker that the inner computation is in the 'IO'
pure-tests/Network/HTTP/ReqSpec.hs+21−53
@@ -13,41 +13,39 @@
13
13
14module Network.HTTP.ReqSpec (spec) where
14module Network.HTTP.ReqSpec (spec) where
15
15
16−import Blaze.ByteString.Builder qualified as BB
16+import qualified Blaze.ByteString.Builder 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 Data.Aeson qualified as A
21+import qualified Data.Aeson as A
22import Data.ByteString (ByteString)
22import Data.ByteString (ByteString)
23−import Data.ByteString qualified as B
23+import qualified Data.ByteString as B
24−import Data.ByteString.Char8 qualified as B8
24+import qualified Data.ByteString.Char8 as B8
25−import Data.ByteString.Lazy qualified as BL
25+import qualified Data.ByteString.Lazy as BL
26−import Data.CaseInsensitive qualified as CI
26+import qualified Data.CaseInsensitive as CI
27import Data.Either (isRight)
27import Data.Either (isRight)
28−import Data.List.NonEmpty qualified as NE
28+import qualified Data.List.NonEmpty 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 Data.Text qualified as T
32+import qualified Data.Text as T
33−import Data.Text.Encoding qualified as T
33+import qualified Data.Text.Encoding as T
34import Data.Time
34import Data.Time
35import Data.Typeable (Typeable, eqT)
35import Data.Typeable (Typeable, eqT)
36−import GHC.Exts (IsList (..))
37import GHC.Generics
36import GHC.Generics
38−import Language.Haskell.TH qualified as TH
37+import qualified Language.Haskell.TH as TH
39−import Language.Haskell.TH.Quote qualified as TH
38+import qualified Language.Haskell.TH.Quote as TH
40−import Network.HTTP.Client qualified as L
39+import qualified Network.HTTP.Client as L
41import Network.HTTP.Req
40import Network.HTTP.Req
42−import Network.HTTP.Types qualified as Y
41+import qualified Network.HTTP.Types as Y
43−import Network.HTTP.Types.Header qualified as Y
42+import qualified Network.HTTP.Types.Header as Y
44import Test.Hspec
43import Test.Hspec
45import Test.Hspec.Core.Spec (SpecM)
44import Test.Hspec.Core.Spec (SpecM)
46import Test.QuickCheck
45import Test.QuickCheck
47import Text.URI (URI)
46import Text.URI (URI)
48−import Text.URI qualified as URI
47+import qualified Text.URI as URI
49−import Text.URI.QQ qualified as QQ
48+import qualified Text.URI.QQ as QQ
50−import Web.FormUrlEncoded qualified as F
51
49
52spec :: Spec
50spec :: Spec
53spec = do
51spec = do
@@ -161,13 +159,11 @@ spec = do
161 let uriHttp = uri' {URI.uriScheme = Just [QQ.scheme|http|]}
159 let uriHttp = uri' {URI.uriScheme = Just [QQ.scheme|http|]}
162 uriHttps = uri' {URI.uriScheme = Just [QQ.scheme|https|]}
160 uriHttps = uri' {URI.uriScheme = Just [QQ.scheme|https|]}
163 requestHttp <-
161 requestHttp <-
164− case fromJust (useURI uriHttp) of
162+ let Left (url', options) = fromJust (useURI uriHttp)
165− Left (url', options) -> req_ GET url' NoReqBody options
163+ in req_ GET url' NoReqBody options
166− _ -> error "(useURI uriHttp) should have returned Left"
167 requestHttps <-
164 requestHttps <-
168− case fromJust (useURI uriHttps) of
165+ let Right (url', options) = fromJust (useURI uriHttps)
169− Right (url', options) -> req_ GET url' NoReqBody options
166+ in req_ GET url' NoReqBody options
170− _ -> error "(useURI uriHttps) should have returned Right"
171 L.host requestHttp `shouldBe` uriHost uriHttp
167 L.host requestHttp `shouldBe` uriHost uriHttp
172 L.host requestHttps `shouldBe` uriHost uriHttps
168 L.host requestHttps `shouldBe` uriHost uriHttps
173 L.port requestHttp `shouldBe` uriPort 80 uriHttp
169 L.port requestHttp `shouldBe` uriPort 80 uriHttp
@@ -237,19 +233,6 @@ spec = do
237 L.RequestBodyLBS x -> x `shouldBe` renderQuery params
233 L.RequestBodyLBS x -> x `shouldBe` renderQuery params
238 _ -> expectationFailure "Wrong request body constructor."
234 _ -> expectationFailure "Wrong request body constructor."
239
235
240− describe "query params" $ do
241− describe "FormUrlEncodedParam" $ do
242− describe "formToQuery" $ do
243− it "should produce the same parameters as F.urlEncodeFormStable" $
244− property $ \form -> do
245− request <- req_ POST url (ReqBodyUrlEnc $ formToQuery form) mempty
246− case L.requestBody request of
247− L.RequestBodyLBS x -> x `shouldBe` F.urlEncodeFormStable form
248− _ -> expectationFailure "Wrong request body constructor"
249− specParamToList (Proxy :: Proxy FormUrlEncodedParam)
250− describe "Option" $ do
251− specParamToList (Proxy :: Proxy (Option 'Http))
252−
253 describe "optional parameters" $ do
236 describe "optional parameters" $ do
254 describe "header" $ do
237 describe "header" $ do
255 it "sets specified header value" $
238 it "sets specified header value" $
@@ -413,7 +396,7 @@ spec = do
413 -- 'Https, Option _) type checks, so we can catch if the type of scheme
396 -- 'Https, Option _) type checks, so we can catch if the type of scheme
414 -- is unspecified.
397 -- is unspecified.
415 let testTypeOfQuoterResult ::
398 let testTypeOfQuoterResult ::
416− forall a s. (Typeable a) => (a, Option s) -> Bool
399+ forall a s. Typeable a => (a, Option s) -> Bool
417 testTypeOfQuoterResult _ = isJust $ eqT @a @(Url 'Https)
400 testTypeOfQuoterResult _ = isJust $ eqT @a @(Url 'Https)
418 in property $ testTypeOfQuoterResult [urlQ|https://example.org/|]
401 in property $ testTypeOfQuoterResult [urlQ|https://example.org/|]
419 it "doesn't work for invalid urls" $
402 it "doesn't work for invalid urls" $
@@ -503,9 +486,6 @@ instance Arbitrary Day where
503instance Arbitrary DiffTime where
486instance Arbitrary DiffTime where
504 arbitrary = secondsToDiffTime <$> arbitrary
487 arbitrary = secondsToDiffTime <$> arbitrary
505
488
506−instance Arbitrary F.Form where
507− arbitrary = (F.Form . fromList) <$> arbitrary
508−
509----------------------------------------------------------------------------
489----------------------------------------------------------------------------
510-- Helper types
490-- Helper types
511
491
@@ -618,15 +598,3 @@ basicProxyAuthHeader :: ByteString -> ByteString -> ByteString
618basicProxyAuthHeader username password =
598basicProxyAuthHeader username password =
619 fromJust . lookup Y.hProxyAuthorization . L.requestHeaders $
599 fromJust . lookup Y.hProxyAuthorization . L.requestHeaders $
620 L.applyBasicProxyAuth username password L.defaultRequest
600 L.applyBasicProxyAuth username password L.defaultRequest
621−
622−-- | Spec about 'paramToList' for the type @p@.
623−specParamToList :: (QueryParam p, Monoid p) => Proxy p -> Spec
624−specParamToList typeProxy = do
625− describe "paramToList" $ do
626− it "should reproduce the parameters given by queryParam" $
627− property $ \(QueryParams params) -> do
628− let queryParam0 =
629− (mconcat $ fmap (uncurry queryParam) params)
630− `asProxyTypeOf` typeProxy
631− got = queryParamToList queryParam0
632− got `shouldBe` params
req.cabal+59−59
@@ -1,11 +1,11 @@
1−cabal-version: 2.4
1+cabal-version: 1.18
2name: req
2name: req
3−version: 3.13.4
3+version: 3.9.0
4−license: BSD-3-Clause
4+license: BSD3
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 ==9.6.3 ghc ==9.8.2 ghc ==9.10.1
8+tested-with: ghc ==8.8.4 ghc ==8.10.4 ghc ==9.0.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,97 +31,97 @@ flag dev
31
31
32library
32library
33 exposed-modules: Network.HTTP.Req
33 exposed-modules: Network.HTTP.Req
34− default-language: GHC2021
34+ default-language: Haskell2010
35 build-depends:
35 build-depends:
36− aeson >=0.9 && <3,
36+ aeson >=0.9 && <1.6,
37− authenticate-oauth >=1.5 && <1.8,
37+ authenticate-oauth >=1.5 && <1.7,
38− base >=4.15 && <5,
38+ base >=4.13 && <5.0,
39 blaze-builder >=0.3 && <0.5,
39 blaze-builder >=0.3 && <0.5,
40− bytestring >=0.10.8 && <0.13,
40+ bytestring >=0.10.8 && <0.12,
41 case-insensitive >=0.2 && <1.3,
41 case-insensitive >=0.2 && <1.3,
42− containers >=0.5 && <0.8,
43− crypton-connection >=0.4.2 && <0.5,
42+ connection >=0.2.2 && <0.4,
44− data-default-class >=0.2.0.0,
45 exceptions >=0.6 && <0.11,
43 exceptions >=0.6 && <0.11,
46− http-api-data >=0.2 && <0.7,
44+ http-api-data >=0.2 && <0.5,
47− http-client >=0.7.13.1 && <0.8,
45+ http-client >=0.7 && <0.8,
48 http-client-tls >=0.3.2 && <0.4,
46 http-client-tls >=0.3.2 && <0.4,
49− http-types >=0.8 && <10,
47+ http-types >=0.8 && <10.0,
50 modern-uri >=0.3 && <0.4,
48 modern-uri >=0.3 && <0.4,
51 monad-control >=1.0 && <1.1,
49 monad-control >=1.0 && <1.1,
52 mtl >=2.0 && <3.0,
50 mtl >=2.0 && <3.0,
53− retry >=0.8 && <0.10,
51+ retry >=0.8 && <0.9,
54− template-haskell >=2.19 && <2.23,
52+ template-haskell >=2.14 && <2.18,
55− text >=0.2 && <2.2,
53+ text >=0.2 && <1.3,
54+ time >=1.2 && <1.10,
56− transformers >=0.5.3.0 && <0.7,
55+ transformers >=0.4 && <0.6,
57 transformers-base,
56 transformers-base,
58 unliftio-core >=0.1.1 && <0.3
57 unliftio-core >=0.1.1 && <0.3
59
58
60 if flag(dev)
59 if flag(dev)
61− ghc-options:
60+ ghc-options: -O0 -Wall -Werror
62− -Wall -Werror -Wpartial-fields -Wunused-packages
63− -Wno-unused-imports
64
61
65 else
62 else
66 ghc-options: -O2 -Wall
63 ghc-options: -O2 -Wall
67
64
65+ if flag(dev)
66+ ghc-options:
67+ -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns
68+ -Wnoncanonical-monad-instances
69+
68test-suite pure-tests
70test-suite pure-tests
69− type: exitcode-stdio-1.0
71+ type: exitcode-stdio-1.0
70− main-is: Spec.hs
72+ main-is: Spec.hs
71− build-tool-depends: hspec-discover:hspec-discover >=2 && <3
73+ build-tools: hspec-discover >=2.0 && <3.0
72− hs-source-dirs: pure-tests
74+ hs-source-dirs: pure-tests
73− other-modules: Network.HTTP.ReqSpec
75+ other-modules: Network.HTTP.ReqSpec
74− default-language: GHC2021
76+ default-language: Haskell2010
75 build-depends:
77 build-depends:
76− QuickCheck >=2.7 && <3,
78+ QuickCheck >=2.7 && <3.0,
77− aeson >=0.9 && <3,
79+ aeson >=0.9 && <1.6,
78− base >=4.15 && <5.0,
80+ base >=4.13 && <5.0,
79 blaze-builder >=0.3 && <0.5,
81 blaze-builder >=0.3 && <0.5,
80− bytestring >=0.10.8 && <0.13,
82+ bytestring >=0.10.8 && <0.12,
81 case-insensitive >=0.2 && <1.3,
83 case-insensitive >=0.2 && <1.3,
82− hspec >=2.0 && <3,
84+ hspec >=2.0 && <3.0,
83− hspec-core >=2.0 && <3,
85+ hspec-core >=2.0 && <3.0,
84− http-api-data >=0.2 && <0.7,
85 http-client >=0.7 && <0.8,
86 http-client >=0.7 && <0.8,
86− http-types >=0.8 && <10,
87+ http-types >=0.8 && <10.0,
87 modern-uri >=0.3 && <0.4,
88 modern-uri >=0.3 && <0.4,
89+ mtl >=2.0 && <3.0,
88 req,
90 req,
89− retry >=0.8 && <0.10,
91+ retry >=0.8 && <0.9,
90− template-haskell >=2.19 && <2.23,
92+ template-haskell >=2.14 && <2.18,
91− text >=0.2 && <2.2,
93+ text >=0.2 && <1.3,
92− time >=1.2 && <1.15
94+ time >=1.2 && <1.10
93
95
94 if flag(dev)
96 if flag(dev)
95− ghc-options:
97+ ghc-options: -O0 -Wall -Werror
96− -Wall -Werror -Wredundant-constraints -Wpartial-fields
97− -Wunused-packages
98
98
99 else
99 else
100 ghc-options: -O2 -Wall
100 ghc-options: -O2 -Wall
101
101
102test-suite httpbin-tests
102test-suite httpbin-tests
103− type: exitcode-stdio-1.0
103+ type: exitcode-stdio-1.0
104− main-is: Spec.hs
104+ main-is: Spec.hs
105− build-tool-depends: hspec-discover:hspec-discover >=2 && <3
105+ build-tools: hspec-discover >=2.0 && <3.0
106− hs-source-dirs: httpbin-tests
106+ hs-source-dirs: httpbin-tests
107− other-modules: Network.HTTP.ReqSpec
107+ other-modules: Network.HTTP.ReqSpec
108− default-language: GHC2021
108+ default-language: Haskell2010
109 build-depends:
109 build-depends:
110− QuickCheck >=2.7 && <3,
110+ QuickCheck >=2.7 && <3.0,
111− aeson >=2 && <3,
111+ aeson >=0.9 && <1.6,
112− base >=4.15 && <5,
112+ base >=4.13 && <5.0,
113− bytestring >=0.10.8 && <0.13,
113+ bytestring >=0.10.8 && <0.12,
114 hspec >=2.0 && <3.0,
114 hspec >=2.0 && <3.0,
115 http-client >=0.7 && <0.8,
115 http-client >=0.7 && <0.8,
116− http-types >=0.8 && <10,
116+ http-types >=0.8 && <10.0,
117 monad-control >=1.0 && <1.1,
117 monad-control >=1.0 && <1.1,
118+ mtl >=2.0 && <3.0,
118 req,
119 req,
119− text >=0.2 && <2.2
120+ text >=0.2 && <1.3,
121+ unordered-containers >=0.2.5 && <0.3
120
122
121 if flag(dev)
123 if flag(dev)
122− ghc-options:
124+ ghc-options: -O0 -Wall -Werror
123− -Wall -Werror -Wredundant-constraints -Wpartial-fields
124− -Wunused-packages
125
125
126 else
126 else
127− buildable: False
127+ ghc-options: -O2 -Wall