servant/servant-client-core/src/Servant/Client/Core/Request.hs

177 lines
6.0 KiB
Haskell
Raw Normal View History

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Servant.Client.Core.Request (
Request,
RequestF (..),
RequestBody (..),
defaultRequest,
-- ** Modifiers
addHeader,
appendToPath,
appendToQueryString,
setRequestBody,
setRequestBodyLBS,
) where
2018-06-30 21:17:08 +02:00
import Prelude ()
import Prelude.Compat
2018-11-09 18:43:55 +01:00
import Control.DeepSeq
(NFData (..))
2019-02-05 10:51:42 +01:00
import Data.Bifoldable
(Bifoldable (..))
import Data.Bifunctor
(Bifunctor (..))
import Data.Bitraversable
(Bitraversable (..), bifoldMapDefault, bimapDefault)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as Builder
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Sequence as Seq
2018-06-30 21:17:08 +02:00
import Data.Text
(Text)
import Data.Text.Encoding
(encodeUtf8)
import Data.Typeable
(Typeable)
2018-06-30 21:17:08 +02:00
import GHC.Generics
(Generic)
import Network.HTTP.Media
(MediaType)
2018-06-30 21:17:08 +02:00
import Network.HTTP.Types
2018-11-09 18:43:55 +01:00
(Header, HeaderName, HttpVersion (..), Method, QueryItem,
http11, methodGet)
2018-11-13 15:51:18 +01:00
import Servant.API
(ToHttpApiData, toEncodedUrlPiece, toHeader, SourceIO)
2018-11-09 18:43:55 +01:00
import Servant.Client.Core.Internal (mediaTypeRnf)
data RequestF body path = Request
{ requestPath :: path
, requestQueryString :: Seq.Seq QueryItem
2019-02-05 10:51:42 +01:00
, requestBody :: Maybe (body, MediaType)
, requestAccept :: Seq.Seq MediaType
, requestHeaders :: Seq.Seq Header
, requestHttpVersion :: HttpVersion
2017-09-07 22:38:31 +02:00
, requestMethod :: Method
} deriving (Generic, Typeable, Eq, Functor, Foldable, Traversable)
2019-02-05 10:51:42 +01:00
instance (Show a, Show b) =>
Show (Servant.Client.Core.Request.RequestF a b) where
showsPrec p req
= showParen
(p >= 11)
( showString "Request {requestPath = "
. showsPrec 0 (requestPath req)
. showString ", requestQueryString = "
. showsPrec 0 (requestQueryString req)
. showString ", requestBody = "
. showsPrec 0 (requestBody req)
. showString ", requestAccept = "
. showsPrec 0 (requestAccept req)
. showString ", requestHeaders = "
. showsPrec 0 (redactSensitiveHeader <$> requestHeaders req))
. showString ", requestHttpVersion = "
. showsPrec 0 (requestHttpVersion req)
. showString ", requestMethod = "
. showsPrec 0 (requestMethod req)
. showString "}"
where
redactSensitiveHeader :: Header -> Header
redactSensitiveHeader ("Authorization", _) = ("Authorization", "<REDACTED>")
redactSensitiveHeader h = h
2019-02-05 10:51:42 +01:00
instance Bifunctor RequestF where bimap = bimapDefault
instance Bifoldable RequestF where bifoldMap = bifoldMapDefault
instance Bitraversable RequestF where
bitraverse f g r = mk
<$> traverse (bitraverse f pure) (requestBody r)
<*> g (requestPath r)
where
mk b p = r { requestBody = b, requestPath = p }
2017-09-14 19:17:19 +02:00
instance (NFData path, NFData body) => NFData (RequestF body path) where
2019-02-05 10:51:42 +01:00
rnf r =
rnf (requestPath r)
`seq` rnf (requestQueryString r)
`seq` rnfB (requestBody r)
`seq` rnf (fmap mediaTypeRnf (requestAccept r))
`seq` rnf (requestHeaders r)
`seq` requestHttpVersion r
`seq` rnf (requestMethod r)
where
rnfB Nothing = ()
rnfB (Just (b, mt)) = rnf b `seq` mediaTypeRnf mt
2019-02-05 10:51:42 +01:00
type Request = RequestF RequestBody Builder.Builder
-- | The request body. R replica of the @http-client@ @RequestBody@.
data RequestBody
= RequestBodyLBS LBS.ByteString
| RequestBodyBS BS.ByteString
| RequestBodySource (SourceIO LBS.ByteString)
deriving (Generic, Typeable)
instance Show RequestBody where
showsPrec d (RequestBodyLBS lbs) = showParen (d > 10)
$ showString "RequestBodyLBS "
. showsPrec 11 lbs
showsPrec d (RequestBodyBS bs) = showParen (d > 10)
$ showString "RequestBodyBS "
. showsPrec 11 bs
showsPrec d (RequestBodySource _) = showParen (d > 10)
$ showString "RequestBodySource <IO>"
2017-10-25 02:12:21 +02:00
2017-09-08 01:07:18 +02:00
-- A GET request to the top-level path
defaultRequest :: Request
defaultRequest = Request
{ requestPath = ""
, requestQueryString = Seq.empty
, requestBody = Nothing
, requestAccept = Seq.empty
, requestHeaders = Seq.empty
, requestHttpVersion = http11
2017-09-07 22:38:31 +02:00
, requestMethod = methodGet
}
appendToPath :: Text -> Request -> Request
appendToPath p req
= req { requestPath = requestPath req <> "/" <> toEncodedUrlPiece p }
2017-09-07 22:38:31 +02:00
appendToQueryString :: Text -- ^ param name
-> Maybe Text -- ^ param value
-> Request
-> Request
appendToQueryString pname pvalue req
= req { requestQueryString = requestQueryString req
Seq.|> (encodeUtf8 pname, encodeUtf8 <$> pvalue)}
addHeader :: ToHttpApiData a => HeaderName -> a -> Request -> Request
addHeader name val req
= req { requestHeaders = requestHeaders req Seq.|> (name, toHeader val)}
-- | Set body and media type of the request being constructed.
--
-- The body is set to the given bytestring using the 'RequestBodyLBS'
-- constructor.
--
-- @since 0.12
--
setRequestBodyLBS :: LBS.ByteString -> MediaType -> Request -> Request
setRequestBodyLBS b t req
= req { requestBody = Just (RequestBodyLBS b, t) }
-- | Set body and media type of the request being constructed.
--
-- @since 0.12
--
setRequestBody :: RequestBody -> MediaType -> Request -> Request
setRequestBody b t req = req { requestBody = Just (b, t) }