@@ -1,20 +1,9 @@
1−{-# LANGUAGE BangPatterns #-}
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 #-}
@@ -155,11 +144,13 @@ module Network.HTTP.Req
155 -- $query-parameters
144 -- $query-parameters
157 queryFlag,
146 queryFlag,
158 QueryParam (..),
148 QueryParam (..),
160 -- *** Headers
150 -- *** Headers
162 attachHeader,
152 attachHeader,
164 -- *** Cookies
155 -- *** Cookies
165 -- $cookies
156 -- $cookies
@@ -206,58 +197,78 @@ module Network.HTTP.Req
207 -- * Other
198 -- * Other
208 HttpException (..),
199 HttpException (..),
200+ isStatusCodeException,
209 CanHaveBody (..),
201 CanHaveBody (..),
210 Scheme (..),
202 Scheme (..),
214−import qualified Blaze.ByteString.Builder as BB
206+import Blaze.ByteString.Builder qualified as BB
215import Control.Applicative
207import Control.Applicative
216import Control.Arrow (first, second)
208import Control.Arrow (first, second)
217import Control.Exception hiding (Handler (..), TypeError)
209import Control.Exception hiding (Handler (..), TypeError)
210+import Control.Monad (guard, void, (>=>))
218import Control.Monad.Base
211import Control.Monad.Base
219import Control.Monad.Catch (Handler (..), MonadCatch, MonadMask, MonadThrow)
212import Control.Monad.Catch (Handler (..), MonadCatch, MonadMask, MonadThrow)
220import Control.Monad.IO.Class
213import Control.Monad.IO.Class
221import Control.Monad.IO.Unlift
214import Control.Monad.IO.Unlift
222−import Control.Monad.Reader
215+import Control.Monad.Reader (ReaderT (ReaderT), ask, lift, runReaderT)
216+import Control.Monad.Trans.Accum (AccumT)
217+import Control.Monad.Trans.Cont (ContT)
223import Control.Monad.Trans.Control
218import 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
224import Control.Retry
231import Control.Retry
225import Data.Aeson (FromJSON (..), ToJSON (..))
232import Data.Aeson (FromJSON (..), ToJSON (..))
226−import qualified Data.Aeson as A
233+import Data.Aeson qualified as A
227import Data.ByteString (ByteString)
234import Data.ByteString (ByteString)
228−import qualified Data.ByteString as B
235+import Data.ByteString qualified as B
229−import qualified Data.ByteString.Lazy as BL
236+import Data.ByteString.Lazy qualified as BL
230−import qualified Data.CaseInsensitive as CI
237+import Data.CaseInsensitive qualified as CI
231import Data.Data (Data)
238import Data.Data (Data)
239+import Data.Default.Class (def)
232import Data.Function (on)
240import Data.Function (on)
233import Data.IORef
241import Data.IORef
234import Data.Kind (Constraint, Type)
242import Data.Kind (Constraint, Type)
235import Data.List (foldl', nubBy)
243import Data.List (foldl', nubBy)
236import Data.List.NonEmpty (NonEmpty (..))
244import Data.List.NonEmpty (NonEmpty (..))
237−import qualified Data.List.NonEmpty as NE
245+import Data.List.NonEmpty qualified as NE
238import Data.Maybe (fromMaybe)
246import Data.Maybe (fromMaybe)
239import Data.Proxy
247import Data.Proxy
240−import Data.Semigroup hiding (Option, option)
248+import Data.Semigroup (Endo (..))
249+import Data.Set qualified as S
241import Data.Text (Text)
250import Data.Text (Text)
242−import qualified Data.Text as T
251+import Data.Text qualified as T
243−import qualified Data.Text.Encoding as T
252+import Data.Text.Encoding qualified as T
244import Data.Typeable (Typeable, cast)
253import Data.Typeable (Typeable, cast)
245import GHC.Generics
254import GHC.Generics
246import GHC.TypeLits
255import GHC.TypeLits
247−import qualified Language.Haskell.TH as TH
256+import Language.Haskell.TH qualified as TH
248−import qualified Language.Haskell.TH.Quote as TH
257+import Language.Haskell.TH.Quote qualified as TH
249−import qualified Language.Haskell.TH.Syntax as TH
258+import Language.Haskell.TH.Syntax qualified as TH
250−import qualified Network.Connection as NC
259+import Network.Connection qualified as NC
251−import qualified Network.HTTP.Client as L
260+import Network.HTTP.Client qualified as L
252−import qualified Network.HTTP.Client.Internal as LI
261+import Network.HTTP.Client.Internal qualified as LI
253−import qualified Network.HTTP.Client.MultipartFormData as LM
262+import Network.HTTP.Client.MultipartFormData qualified as LM
254−import qualified Network.HTTP.Client.TLS as L
263+import Network.HTTP.Client.TLS qualified as L
255−import qualified Network.HTTP.Types as Y
264+import Network.HTTP.Types qualified as Y
256import System.IO.Unsafe (unsafePerformIO)
265import System.IO.Unsafe (unsafePerformIO)
257import Text.URI (URI)
266import Text.URI (URI)
258−import qualified Text.URI as URI
267+import Text.URI qualified as URI
259−import qualified Text.URI.QQ as QQ
268+import Text.URI.QQ qualified as QQ
260−import qualified Web.Authenticate.OAuth as OAuth
269+import Web.Authenticate.OAuth qualified as OAuth
270+import Web.FormUrlEncoded (FromForm (..), ToForm (..))
271+import Web.FormUrlEncoded qualified as Form
261import Web.HttpApiData (ToHttpApiData (..))
272import Web.HttpApiData (ToHttpApiData (..))
263----------------------------------------------------------------------------
274----------------------------------------------------------------------------
@@ -478,7 +489,7 @@ reqCb method url body responseProxy options adjustRequest =
479-- @since 3.7.0
490-- @since 3.7.0
480reqHandler ::
491reqHandler ::
481− MonadHttp m =>
492+ (MonadHttp m) =>
482 -- | How to get final result from a 'L.Response'
493 -- | How to get final result from a 'L.Response'
483 (L.Response L.BodyReader -> IO b) ->
494 (L.Response L.BodyReader -> IO b) ->
484 -- | 'L.Request' to perform
495 -- | 'L.Request' to perform
@@ -574,10 +585,10 @@ req' method url body options m = do
574 withReqManager (m request)
585 withReqManager (m request)
576-- | 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
577−-- of the library uses. This allows to reuse connections that the
588+-- of the library uses. This allows us to reuse connections that the
578-- 'L.Manager' controls.
589-- 'L.Manager' controls.
579−withReqManager :: MonadIO m => (L.Manager -> m a) -> m a
590+withReqManager :: (MonadIO m) => (L.Manager -> m a) -> m a
580−withReqManager m = liftIO (readIORef globalManager) >>= m
591+withReqManager m = m globalManager
582-- | 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
583-- 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
@@ -590,16 +601,16 @@ withReqManager m = liftIO (readIORef globalManager) >>= m
590-- 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
591-- 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
592-- spoil the plan by inlining the definition, hence the @NOINLINE@ pragma.
603-- spoil the plan by inlining the definition, hence the @NOINLINE@ pragma.
593−globalManager :: IORef L.Manager
604+globalManager :: L.Manager
594globalManager = unsafePerformIO $ do
605globalManager = unsafePerformIO $ do
595 context <- NC.initConnectionContext
606 context <- NC.initConnectionContext
596 let settings =
607 let settings =
597 L.mkManagerSettingsContext
608 L.mkManagerSettingsContext
598 (Just context)
609 (Just context)
599− (NC.TLSSettingsSimple False False False)
610+ def
601 manager <- L.newManager settings
612 manager <- L.newManager settings
602− newIORef manager
613+ return manager
603{-# NOINLINE globalManager #-}
614{-# NOINLINE globalManager #-}
605----------------------------------------------------------------------------
616----------------------------------------------------------------------------
@@ -619,7 +630,7 @@ globalManager = unsafePerformIO $ do
619-- | A type class for monads that support performing HTTP requests.
630-- | A type class for monads that support performing HTTP requests.
620-- Typically, you only need to define the 'handleHttpException' method
631-- Typically, you only need to define the 'handleHttpException' method
621-- unless you want to tweak 'HttpConfig'.
632-- unless you want to tweak 'HttpConfig'.
622−class MonadIO m => MonadHttp m where
633+class (MonadIO m) => MonadHttp m where
623 -- | This method describes how to deal with 'HttpException' that was
634 -- | This method describes how to deal with 'HttpException' that was
624 -- 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
625 -- exceptions, but if you prefer working with something like
636 -- exceptions, but if you prefer working with something like
@@ -680,8 +691,8 @@ data HttpConfig = HttpConfig
680 -- | The retry policy to use for request retrying. By default 'def' is
691 -- | The retry policy to use for request retrying. By default 'def' is
681 -- used (see 'RetryPolicyM').
692 -- used (see 'RetryPolicyM').
683− -- __Note__: signature of this function was changed in the version
694+ -- __Note__: signature of this function was changed to disallow 'IO' in
684− -- /1.0.0/.
695+ -- version /1.0.0/ and then changed back to its current form in /3.1.0/.
686 -- @since 0.3.0
697 -- @since 0.3.0
687 httpConfigRetryPolicy :: RetryPolicyM IO,
698 httpConfigRetryPolicy :: RetryPolicyM IO,
@@ -703,9 +714,8 @@ data HttpConfig = HttpConfig
703 -- | Max length of preview fragment of response body.
714 -- | Max length of preview fragment of response body.
705 -- @since 3.6.0
716 -- @since 3.6.0
706− httpConfigBodyPreviewLength :: forall a. Num a => a
717+ httpConfigBodyPreviewLength :: forall a. (Num a) => a
710-- | The default value of 'HttpConfig'.
720-- | The default value of 'HttpConfig'.
@@ -787,12 +797,87 @@ instance MonadHttp Req where
787 handleHttpException = Req . lift . throwIO
797 handleHttpException = Req . lift . throwIO
788 getHttpConfig = Req ask
798 getHttpConfig = Req ask
801+instance (MonadHttp m, Monoid w) => MonadHttp (AccumT w m) where
802+ handleHttpException = lift . handleHttpException
803+ getHttpConfig = lift getHttpConfig
806+instance (MonadHttp m) => MonadHttp (ContT r m) where
807+ handleHttpException = lift . handleHttpException
808+ getHttpConfig = lift getHttpConfig
811+instance (MonadHttp m) => MonadHttp (ExceptT e m) where
812+ handleHttpException = lift . handleHttpException
813+ getHttpConfig = lift getHttpConfig
816+instance (MonadHttp m) => MonadHttp (IdentityT m) where
817+ handleHttpException = lift . handleHttpException
818+ getHttpConfig = lift getHttpConfig
821+instance (MonadHttp m) => MonadHttp (MaybeT m) where
822+ handleHttpException = lift . handleHttpException
823+ getHttpConfig = lift getHttpConfig
826+instance (MonadHttp m) => MonadHttp (ReaderT r m) where
827+ handleHttpException = lift . handleHttpException
828+ getHttpConfig = lift getHttpConfig
831+instance (MonadHttp m, Monoid w) => MonadHttp (RWS.CPS.RWST r w s m) where
832+ handleHttpException = lift . handleHttpException
833+ getHttpConfig = lift getHttpConfig
836+instance (MonadHttp m, Monoid w) => MonadHttp (RWS.Lazy.RWST r w s m) where
837+ handleHttpException = lift . handleHttpException
838+ getHttpConfig = lift getHttpConfig
841+instance (MonadHttp m, Monoid w) => MonadHttp (RWS.Strict.RWST r w s m) where
842+ handleHttpException = lift . handleHttpException
843+ getHttpConfig = lift getHttpConfig
846+instance (MonadHttp m) => MonadHttp (SelectT r m) where
847+ handleHttpException = lift . handleHttpException
848+ getHttpConfig = lift getHttpConfig
851+instance (MonadHttp m) => MonadHttp (State.Lazy.StateT s m) where
852+ handleHttpException = lift . handleHttpException
853+ getHttpConfig = lift getHttpConfig
856+instance (MonadHttp m) => MonadHttp (State.Strict.StateT s m) where
857+ handleHttpException = lift . handleHttpException
858+ getHttpConfig = lift getHttpConfig
861+instance (MonadHttp m, Monoid w) => MonadHttp (Writer.CPS.WriterT w m) where
862+ handleHttpException = lift . handleHttpException
863+ getHttpConfig = lift getHttpConfig
866+instance (MonadHttp m, Monoid w) => MonadHttp (Writer.Lazy.WriterT w m) where
867+ handleHttpException = lift . handleHttpException
868+ getHttpConfig = lift getHttpConfig
871+instance (MonadHttp m, Monoid w) => MonadHttp (Writer.Strict.WriterT w m) where
872+ handleHttpException = lift . handleHttpException
873+ getHttpConfig = lift getHttpConfig
790-- | Run a computation in the 'Req' monad with the given 'HttpConfig'. In
875-- | Run a computation in the 'Req' monad with the given 'HttpConfig'. In
791-- the case of an exceptional situation an 'HttpException' will be thrown.
876-- the case of an exceptional situation an 'HttpException' will be thrown.
793-- @since 0.4.0
878-- @since 0.4.0
795− MonadIO m =>
880+ (MonadIO m) =>
796 -- | 'HttpConfig' to use
881 -- | 'HttpConfig' to use
797 HttpConfig ->
882 HttpConfig ->
798 -- | Computation to run
883 -- | Computation to run
@@ -897,7 +982,7 @@ class HttpMethod a where
897 -- | Return name of the method as a 'ByteString'.
982 -- | Return name of the method as a 'ByteString'.
898 httpMethodName :: Proxy a -> ByteString
983 httpMethodName :: Proxy a -> ByteString
900−instance HttpMethod method => RequestComponent (Tagged "method" method) where
985+instance (HttpMethod method) => RequestComponent (Tagged "method" method) where
901 getRequestMod _ = Endo $ \x ->
986 getRequestMod _ = Endo $ \x ->
902 x {L.method = httpMethodName (Proxy :: Proxy method)}
987 x {L.method = httpMethodName (Proxy :: Proxy method)}
@@ -939,23 +1024,20 @@ instance HttpMethod method => RequestComponent (Tagged "method" method) where
939-- > -- 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
940data Url (scheme :: Scheme) = Url Scheme (NonEmpty Text)
1025data Url (scheme :: Scheme) = Url Scheme (NonEmpty Text)
941 -- NOTE The second value is the path segments in reversed order.
1026 -- NOTE The second value is the path segments in reversed order.
942− deriving (Eq, Ord, Show, Data, Typeable, Generic)
1027+ deriving (Eq, Ord, Show, Data, Generic)
944type role Url nominal
1029type role Url nominal
946-- 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
947-- the derived lift forgets the type of the scheme.
1032-- the derived lift forgets the type of the scheme.
948−instance Typeable scheme => TH.Lift (Url scheme) where
1033+instance (Typeable scheme) => TH.Lift (Url scheme) where
949 lift url =
1034 lift url =
950 TH.dataToExpQ (fmap liftText . cast) url `TH.sigE` case url of
1035 TH.dataToExpQ (fmap liftText . cast) url `TH.sigE` case url of
951 Url Http _ -> [t|Url 'Http|]
1036 Url Http _ -> [t|Url 'Http|]
952 Url Https _ -> [t|Url 'Https|]
1037 Url Https _ -> [t|Url 'Https|]
954 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)
956−#if MIN_VERSION_template_haskell(2,16,0)
957− liftTyped url = TH.TExp <$> TH.lift url
1040+ liftTyped = TH.Code . TH.unsafeTExpCoerce . TH.lift
960-- | 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
961-- empty path. This also sets port to @80@.
1043-- empty path. This also sets port to @80@.
@@ -971,7 +1053,7 @@ https = Url Https . pure
971-- 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'.
972infixl 5 /~
1054infixl 5 /~
974−(/~) :: ToHttpApiData a => Url scheme -> a -> Url scheme
1056+(/~) :: (ToHttpApiData a) => Url scheme -> a -> Url scheme
975Url secure path /~ segment = Url secure (NE.cons (toUrlPiece segment) path)
1057Url secure path /~ segment = Url secure (NE.cons (toUrlPiece segment) path)
977-- | A type-constrained version of @('/~')@ to remove ambiguity in the cases
1059-- | A type-constrained version of @('/~')@ to remove ambiguity in the cases
@@ -1159,7 +1241,7 @@ instance HttpBody NoReqBody where
1159-- charset=utf-8\"@ value.
1241-- charset=utf-8\"@ value.
1160newtype ReqBodyJson a = ReqBodyJson a
1242newtype ReqBodyJson a = ReqBodyJson a
1162−instance ToJSON a => HttpBody (ReqBodyJson a) where
1244+instance (ToJSON a) => HttpBody (ReqBodyJson a) where
1163 getRequestBody (ReqBodyJson a) = L.RequestBodyLBS (A.encode a)
1245 getRequestBody (ReqBodyJson a) = L.RequestBodyLBS (A.encode a)
1164 getRequestContentType _ = pure "application/json; charset=utf-8"
1246 getRequestContentType _ = pure "application/json; charset=utf-8"
@@ -1212,6 +1294,13 @@ newtype FormUrlEncodedParam = FormUrlEncodedParam [(Text, Maybe Text)]
1212instance QueryParam FormUrlEncodedParam where
1294instance QueryParam FormUrlEncodedParam where
1213 queryParam name mvalue =
1295 queryParam name mvalue =
1214 FormUrlEncodedParam [(name, toQueryParam <$> mvalue)]
1296 FormUrlEncodedParam [(name, toQueryParam <$> mvalue)]
1297+ queryParamToList (FormUrlEncodedParam p) = p
1299+-- | Use 'formToQuery'.
1302+instance FromForm FormUrlEncodedParam where
1303+ fromForm = Right . formToQuery
1216-- | Multipart form data. Please consult the
1305-- | Multipart form data. Please consult the
1217-- "Network.HTTP.Client.MultipartFormData" module for how to construct
1306-- "Network.HTTP.Client.MultipartFormData" module for how to construct
@@ -1251,7 +1340,7 @@ instance HttpBody ReqBodyMultipart where
1251-- | Create 'ReqBodyMultipart' request body from a collection of 'LM.Part's.
1340-- | Create 'ReqBodyMultipart' request body from a collection of 'LM.Part's.
1253-- @since 0.2.0
1342-- @since 0.2.0
1254−reqBodyMultipart :: MonadIO m => [LM.Part] -> m ReqBodyMultipart
1343+reqBodyMultipart :: (MonadIO m) => [LM.Part] -> m ReqBodyMultipart
1255reqBodyMultipart parts = liftIO $ do
1344reqBodyMultipart parts = liftIO $ do
1256 boundary <- LM.webkitBoundary
1345 boundary <- LM.webkitBoundary
1257 body <- LM.renderParts boundary parts
1346 body <- LM.renderParts boundary parts
@@ -1291,7 +1380,7 @@ type family
1291 TypeError
1380 TypeError
1292 ('Text "This HTTP method does not allow attaching a request body.")
1381 ('Text "This HTTP method does not allow attaching a request body.")
1294−instance HttpBody body => RequestComponent (Tagged "body" body) where
1383+instance (HttpBody body) => RequestComponent (Tagged "body" body) where
1295 getRequestMod (Tagged body) = Endo $ \x ->
1384 getRequestMod (Tagged body) = Endo $ \x ->
1297 { L.requestBody = getRequestBody body,
1386 { L.requestBody = getRequestBody body,
@@ -1337,6 +1426,12 @@ instance Monoid (Option scheme) where
1337 mempty = Option mempty Nothing
1426 mempty = Option mempty Nothing
1338 mappend = (<>)
1427 mappend = (<>)
1429+-- | Use 'formToQuery'.
1432+instance FromForm (Option scheme) where
1433+ fromForm = Right . formToQuery
1340-- | A helper to create an 'Option' that modifies only collection of query
1435-- | A helper to create an 'Option' that modifies only collection of query
1341-- parameters. This helper is not a part of the public API.
1436-- parameters. This helper is not a part of the public API.
1342withQueryParams :: (Y.QueryText -> Y.QueryText) -> Option scheme
1437withQueryParams :: (Y.QueryText -> Y.QueryText) -> Option scheme
@@ -1355,7 +1450,7 @@ instance RequestComponent (Option scheme) where
1356-- | Finalize given 'L.Request' by applying a finalizer from the given
1451-- | Finalize given 'L.Request' by applying a finalizer from the given
1357-- 'Option' (if it has any).
1452-- 'Option' (if it has any).
1358−finalizeRequest :: MonadIO m => Option scheme -> L.Request -> m L.Request
1453+finalizeRequest :: (MonadIO m) => Option scheme -> L.Request -> m L.Request
1359finalizeRequest (Option _ mfinalizer) = liftIO . fromMaybe pure mfinalizer
1454finalizeRequest (Option _ mfinalizer) = liftIO . fromMaybe pure mfinalizer
1361----------------------------------------------------------------------------
1456----------------------------------------------------------------------------
@@ -1388,9 +1483,27 @@ name =: value = queryParam name (pure value)
1388-- This operator is defined in terms of 'queryParam':
1483-- This operator is defined in terms of 'queryParam':
1390-- > queryFlag name = queryParam name (Nothing :: Maybe ())
1485-- > queryFlag name = queryParam name (Nothing :: Maybe ())
1391−queryFlag :: QueryParam param => Text -> param
1486+queryFlag :: (QueryParam param) => Text -> param
1392queryFlag name = queryParam name (Nothing :: Maybe ())
1487queryFlag name = queryParam name (Nothing :: Maybe ())
1489+-- | Construct query parameters from a 'ToForm' instance. This function
1490+-- produces the same query params as 'Form.urlEncodeAsFormStable'.
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@).
1498+formToQuery :: (QueryParam param, Monoid param, ToForm f) => f -> param
1499+formToQuery f = mconcat . fmap toParam . Form.toListStable $ toForm f
1501+ toParam (key, val) =
1394-- | A type class for query-parameter-like things. The reason to have an
1507-- | A type class for query-parameter-like things. The reason to have an
1395-- overloaded 'queryParam' is to be able to use it as an 'Option' and as a
1508-- overloaded 'queryParam' is to be able to use it as an 'Option' and as a
1396-- 'FormUrlEncodedParam' when constructing form URL encoded request bodies.
1509-- 'FormUrlEncodedParam' when constructing form URL encoded request bodies.
@@ -1400,11 +1513,17 @@ class QueryParam param where
1400 -- '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
1401 -- way). It's recommended to use @('=:')@ and 'queryFlag' instead of this
1514 -- way). It's recommended to use @('=:')@ and 'queryFlag' instead of this
1402 -- method, because they are easier to read.
1515 -- method, because they are easier to read.
1403− queryParam :: ToHttpApiData a => Text -> Maybe a -> param
1516+ queryParam :: (ToHttpApiData a) => Text -> Maybe a -> param
1518+ -- | Get the query parameter names and values set by 'queryParam'.
1521+ queryParamToList :: param -> [(Text, Maybe Text)]
1405instance QueryParam (Option scheme) where
1523instance QueryParam (Option scheme) where
1406 queryParam name mvalue =
1524 queryParam name mvalue =
1407 withQueryParams ((:) (name, toQueryParam <$> mvalue))
1525 withQueryParams ((:) (name, toQueryParam <$> mvalue))
1526+ queryParamToList (Option f _) = fst $ appEndo f ([], L.defaultRequest)
1409----------------------------------------------------------------------------
1528----------------------------------------------------------------------------
1410-- Request—Optional parameters—Headers
1529-- Request—Optional parameters—Headers
@@ -1428,6 +1547,14 @@ attachHeader :: ByteString -> ByteString -> L.Request -> L.Request
1428attachHeader name value x =
1547attachHeader name value x =
1429 x {L.requestHeaders = (CI.mk name, value) : L.requestHeaders x}
1548 x {L.requestHeaders = (CI.mk name, value) : L.requestHeaders x}
1550+-- | Same as 'header', but with redacted values on print.
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}
1431----------------------------------------------------------------------------
1558----------------------------------------------------------------------------
1432-- Request—Optional parameters—Cookies
1559-- Request—Optional parameters—Cookies
@@ -1635,7 +1762,7 @@ ignoreResponse = Proxy
1635newtype JsonResponse a = JsonResponse (L.Response a)
1762newtype JsonResponse a = JsonResponse (L.Response a)
1636 deriving (Show)
1763 deriving (Show)
1638−instance FromJSON a => HttpResponse (JsonResponse a) where
1765+instance (FromJSON a) => HttpResponse (JsonResponse a) where
1639 type HttpResponseBody (JsonResponse a) = a
1766 type HttpResponseBody (JsonResponse a) = a
1640 toVanillaResponse (JsonResponse r) = r
1767 toVanillaResponse (JsonResponse r) = r
1641 getHttpResponse r = do
1768 getHttpResponse r = do
@@ -1751,14 +1878,14 @@ brReadN br n = go 0 id id
1752-- | Get the response body.
1879-- | Get the response body.
1753responseBody ::
1880responseBody ::
1754− HttpResponse response =>
1881+ (HttpResponse response) =>
1755 response ->
1882 response ->
1756 HttpResponseBody response
1883 HttpResponseBody response
1757responseBody = L.responseBody . toVanillaResponse
1884responseBody = L.responseBody . toVanillaResponse
1759-- | Get the response status code.
1886-- | Get the response status code.
1760responseStatusCode ::
1887responseStatusCode ::
1761− HttpResponse response =>
1888+ (HttpResponse response) =>
1762 response ->
1889 response ->
1764responseStatusCode =
1891responseStatusCode =
@@ -1766,7 +1893,7 @@ responseStatusCode =
1767-- | Get the response status message.
1894-- | Get the response status message.
1768responseStatusMessage ::
1895responseStatusMessage ::
1769− HttpResponse response =>
1896+ (HttpResponse response) =>
1770 response ->
1897 response ->
1771 ByteString
1898 ByteString
1772responseStatusMessage =
1899responseStatusMessage =
@@ -1774,7 +1901,7 @@ responseStatusMessage =
1775-- | Lookup a particular header from a response.
1902-- | Lookup a particular header from a response.
1776responseHeader ::
1903responseHeader ::
1777− HttpResponse response =>
1904+ (HttpResponse response) =>
1778 -- | Response interpretation
1905 -- | Response interpretation
1779 response ->
1906 response ->
1780 -- | Header to lookup
1907 -- | Header to lookup
@@ -1786,7 +1913,7 @@ responseHeader r h =
1787-- | Get the response 'L.CookieJar'.
1914-- | Get the response 'L.CookieJar'.
1788responseCookieJar ::
1915responseCookieJar ::
1789− HttpResponse response =>
1916+ (HttpResponse response) =>
1790 response ->
1917 response ->
1791 L.CookieJar
1918 L.CookieJar
1792responseCookieJar = L.responseCookieJar . toVanillaResponse
1919responseCookieJar = L.responseCookieJar . toVanillaResponse
@@ -1840,6 +1967,16 @@ class HttpResponse response where
1840 acceptHeader :: Proxy response -> Maybe ByteString
1967 acceptHeader :: Proxy response -> Maybe ByteString
1841 acceptHeader Proxy = Nothing
1968 acceptHeader Proxy = Nothing
1970+-- | This instance has been added to make it easier to inspect 'L.Response'
1971+-- using Req's functions like 'responseStatusCode', 'responseStatusMessage',
1975+instance HttpResponse (L.Response ()) where
1976+ type HttpResponseBody (L.Response ()) = ()
1977+ toVanillaResponse = id
1978+ getHttpResponse = return . void
1843----------------------------------------------------------------------------
1980----------------------------------------------------------------------------
@@ -1874,10 +2011,24 @@ data HttpException
1874 | -- | A wrapper with Aeson-produced 'String' describing why decoding
2011 | -- | A wrapper with Aeson-produced 'String' describing why decoding
1875 -- failed
2012 -- failed
1876 JsonHttpException String
2013 JsonHttpException String
1877− deriving (Show, Typeable, Generic)
2014+ deriving (Show, Generic)
1879instance Exception HttpException
2016instance Exception HttpException
2018+-- | Return 'Just' if the given 'HttpException' is wrapping a http-client's
2019+-- 'L.StatusCodeException'. Otherwise, return 'Nothing'.
2022+isStatusCodeException :: HttpException -> Maybe (L.Response ())
2023+isStatusCodeException
2024+ ( VanillaHttpException
2025+ ( L.HttpExceptionRequest
2027+ (L.StatusCodeException r _)
2030+isStatusCodeException _ = Nothing
1881-- | 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
1882-- 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
@@ -1897,4 +2048,4 @@ data Scheme
1898 | -- | HTTPS
2049 | -- | HTTPS
1900− deriving (Eq, Ord, Show, Data, Typeable, Generic, TH.Lift)
2051+ deriving (Eq, Ord, Show, Data, Generic, TH.Lift)