2018-02-27 15:36:49 +01:00
|
|
|
{-# LANGUAGE CPP #-}
|
|
|
|
{-# LANGUAGE DeriveDataTypeable #-}
|
|
|
|
{-# LANGUAGE DeriveFoldable #-}
|
|
|
|
{-# LANGUAGE DeriveFunctor #-}
|
|
|
|
{-# LANGUAGE DeriveGeneric #-}
|
|
|
|
{-# LANGUAGE DeriveTraversable #-}
|
|
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
{-# LANGUAGE RankNTypes #-}
|
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
2017-09-06 23:13:05 +02:00
|
|
|
|
2017-09-07 19:05:12 +02:00
|
|
|
module Servant.Client.Core.Internal.Request where
|
2017-09-06 23:13:05 +02:00
|
|
|
|
2018-06-30 21:17:08 +02:00
|
|
|
import Prelude ()
|
2017-09-06 23:13:05 +02:00
|
|
|
import Prelude.Compat
|
|
|
|
|
2018-06-30 21:17:08 +02:00
|
|
|
import Control.Monad.Catch
|
|
|
|
(Exception)
|
2017-10-25 02:12:21 +02:00
|
|
|
import qualified Data.ByteString as BS
|
2018-02-27 15:36:49 +01:00
|
|
|
import qualified Data.ByteString.Builder as Builder
|
2017-09-06 23:13:05 +02:00
|
|
|
import qualified Data.ByteString.Lazy as LBS
|
2018-06-30 21:17:08 +02:00
|
|
|
import Data.Int
|
|
|
|
(Int64)
|
|
|
|
import Data.Semigroup
|
|
|
|
((<>))
|
2017-09-06 23:13:05 +02:00
|
|
|
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)
|
|
|
|
import GHC.Generics
|
|
|
|
(Generic)
|
|
|
|
import Network.HTTP.Media
|
|
|
|
(MediaType)
|
|
|
|
import Network.HTTP.Types
|
|
|
|
(Header, HeaderName, HttpVersion, Method, QueryItem, Status,
|
|
|
|
http11, methodGet)
|
|
|
|
import Web.HttpApiData
|
|
|
|
(ToHttpApiData, toEncodedUrlPiece, toHeader)
|
2017-09-06 23:13:05 +02:00
|
|
|
|
2017-09-08 01:07:18 +02:00
|
|
|
-- | A type representing possible errors in a request
|
2017-09-13 17:05:48 +02:00
|
|
|
--
|
2017-09-15 15:02:50 +02:00
|
|
|
-- Note that this type substantially changed in 0.12.
|
2017-09-08 01:07:18 +02:00
|
|
|
data ServantError =
|
|
|
|
-- | The server returned an error response
|
|
|
|
FailureResponse Response
|
|
|
|
-- | The body could not be decoded at the expected type
|
2017-09-06 23:13:05 +02:00
|
|
|
| DecodeFailure Text Response
|
2017-09-08 01:07:18 +02:00
|
|
|
-- | The content-type of the response is not supported
|
2017-09-06 23:13:05 +02:00
|
|
|
| UnsupportedContentType MediaType Response
|
2017-09-08 01:07:18 +02:00
|
|
|
-- | The content-type header is invalid
|
2017-09-06 23:13:05 +02:00
|
|
|
| InvalidContentTypeHeader Response
|
2017-09-08 01:07:18 +02:00
|
|
|
-- | There was a connection error, and no response was received
|
2017-09-06 23:13:05 +02:00
|
|
|
| ConnectionError Text
|
|
|
|
deriving (Eq, Show, Generic, Typeable)
|
|
|
|
|
2017-09-15 20:57:03 +02:00
|
|
|
instance Exception ServantError
|
|
|
|
|
2017-09-14 19:17:19 +02:00
|
|
|
data RequestF a = Request
|
|
|
|
{ requestPath :: a
|
2017-09-06 23:13:05 +02:00
|
|
|
, requestQueryString :: Seq.Seq QueryItem
|
|
|
|
, requestBody :: Maybe (RequestBody, MediaType)
|
|
|
|
, requestAccept :: Seq.Seq MediaType
|
|
|
|
, requestHeaders :: Seq.Seq Header
|
|
|
|
, requestHttpVersion :: HttpVersion
|
2017-09-07 22:38:31 +02:00
|
|
|
, requestMethod :: Method
|
2018-02-27 15:36:49 +01:00
|
|
|
} deriving (Generic, Typeable)
|
2017-09-14 19:17:19 +02:00
|
|
|
|
|
|
|
type Request = RequestF Builder.Builder
|
2017-09-06 23:13:05 +02:00
|
|
|
|
2018-02-27 15:36:49 +01:00
|
|
|
-- | The request body. A replica of the @http-client@ @RequestBody@.
|
|
|
|
data RequestBody
|
|
|
|
= RequestBodyLBS LBS.ByteString
|
|
|
|
| RequestBodyBS BS.ByteString
|
|
|
|
| RequestBodyBuilder Int64 Builder.Builder
|
|
|
|
| RequestBodyStream Int64 ((IO BS.ByteString -> IO ()) -> IO ())
|
|
|
|
| RequestBodyStreamChunked ((IO BS.ByteString -> IO ()) -> IO ())
|
|
|
|
| RequestBodyIO (IO RequestBody)
|
|
|
|
deriving (Generic, Typeable)
|
2017-09-06 23:13:05 +02:00
|
|
|
|
2018-01-30 17:40:02 +01:00
|
|
|
data GenResponse a = Response
|
2017-09-06 23:13:05 +02:00
|
|
|
{ responseStatusCode :: Status
|
|
|
|
, responseHeaders :: Seq.Seq Header
|
|
|
|
, responseHttpVersion :: HttpVersion
|
2018-01-30 17:40:02 +01:00
|
|
|
, responseBody :: a
|
|
|
|
} deriving (Eq, Show, Generic, Typeable, Functor, Foldable, Traversable)
|
2017-09-06 23:13:05 +02:00
|
|
|
|
2018-01-30 17:40:02 +01:00
|
|
|
type Response = GenResponse LBS.ByteString
|
|
|
|
newtype StreamingResponse = StreamingResponse { runStreamingResponse :: forall a. (GenResponse (IO BS.ByteString) -> IO a) -> IO a }
|
2017-10-25 02:12:21 +02:00
|
|
|
|
2017-09-08 01:07:18 +02:00
|
|
|
-- A GET request to the top-level path
|
2017-09-06 23:13:05 +02:00
|
|
|
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
|
2017-09-06 23:13:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)}
|
2017-09-06 23:13:05 +02:00
|
|
|
|
|
|
|
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) }
|