on master help sign in
Rewrite servant-client.

This commit begins the reorganization of the servant-client project so as to allow multiple backends, and also begins some much-needed refactoring of the servant-client code.

commit95fac329a62603c16dd2163af0dd0bc8d2bf30b4 authorJulian K. Arni <jkarni@gmail.com> dateWed 6 Sep 2017, 14:13 (9 years ago) parentb592b51
15 files changed+279−235
servant-client-core/CHANGELOG.md+5 servant-client-core/LICENSE+30 servant-client-core/Setup.hs+2 servant-client-core/include/overlapping-compat.h+8 servant-client-core/servant-client-core.cabal+40 servant-client/src/Servant/Client/Experimental/Auth.hs → servant-client-core/src/Servant/Client/Core/Internal/Auth.hs+3−3 servant-client/src/Servant/Common/BaseUrl.hs → servant-client-core/src/Servant/Client/Core/Internal/BaseUrl.hs servant-client/src/Servant/Common/BasicAuth.hs → servant-client-core/src/Servant/Client/Core/Internal/BasicAuth.hs+2−2 servant-client-core/src/Servant/Client/Core/Internal/Class.hs+13 servant-client/src/Servant/Client/Generic.hs → servant-client-core/src/Servant/Client/Core/Internal/Generic.hs servant-client-core/src/Servant/Client/Core/Internal/Request.hs+146 servant-client/servant-client.cabal+1 servant-client/src/Servant/Client/Class.hs−15 servant-client/src/Servant/Client/HttpClient.hs+29−32 servant-client/src/Servant/Common/Req.hs−183
servant-client-core/CHANGELOG.mdnew file+5
@@ -0,0 +1,5 @@
1+# Revision history for servant-client-core
2+
3+## 0.11 -- YYYY-mm-dd
4+
5+* First version. Released on an unsuspecting world.
servant-client-core/LICENSEnew file+30
@@ -0,0 +1,30 @@
1+Copyright (c) 2017, Servant Contributors
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions are met:
7+
8+ * Redistributions of source code must retain the above copyright
9+ notice, this list of conditions and the following disclaimer.
10+
11+ * Redistributions in binary form must reproduce the above
12+ copyright notice, this list of conditions and the following
13+ disclaimer in the documentation and/or other materials provided
14+ with the distribution.
15+
16+ * Neither the name of Servant Contributors nor the names of other
17+ contributors may be used to endorse or promote products derived
18+ from this software without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
servant-client-core/Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
servant-client-core/include/overlapping-compat.hnew file+8
@@ -0,0 +1,8 @@
1+#if __GLASGOW_HASKELL__ >= 710
2+#define OVERLAPPABLE_ {-# OVERLAPPABLE #-}
3+#define OVERLAPPING_ {-# OVERLAPPING #-}
4+#else
5+{-# LANGUAGE OverlappingInstances #-}
6+#define OVERLAPPABLE_
7+#define OVERLAPPING_
8+#endif
servant-client-core/servant-client-core.cabalnew file+40
@@ -0,0 +1,40 @@
1+name: servant-client-core
2+version: 0.11
3+synopsis: Core functionality and class for client function generation for servant APIs
4+description:
5+ This library provides a class
6+license: BSD3
7+license-file: LICENSE
8+author: Servant Contributors
9+maintainer: haskell-servant-maintainers@googlegroups.com
10+homepage: http://haskell-servant.readthedocs.org/
11+bug-reports: http://github.com/haskell-servant/servant/issues
12+-- copyright:
13+category: Web
14+build-type: Simple
15+extra-source-files:
16+ include/*.h
17+ CHANGELOG.md
18+ README.md
19+source-repository head
20+ type: git
21+ location: http://github.com/haskell-servant/servant.git
22+cabal-version: >=1.10
23+
24+library
25+ exposed-modules:
26+ build-depends:
27+ base >= 4.7 && < 4.11
28+ , base-compat >= 0.9.1 && < 0.10
29+ hs-source-dirs: src
30+ default-language: Haskell2010
31+
32+test-suite spec
33+ type: exitcode-stdio-1.0
34+ ghc-options: -Wall
35+ default-language: Haskell2010
36+ hs-source-dirs: test
37+ main-is: Spec.hs
38+ other-modules:
39+ build-depends:
40+ base == 4.*
servant-client/src/Servant/Client/Experimental/Auth.hs → servant-client-core/src/Servant/Client/Core/Internal/Auth.hsrenamed+3−3
@@ -10,7 +10,7 @@ module Servant.Client.Experimental.Auth (
1010 , mkAuthenticateReq
1111 ) where
1212
13−import Servant.Common.Req (Req)
13+import Servant.Common.Req (Request)
1414
1515-- | For a resource protected by authentication (e.g. AuthProtect), we need
1616-- to provide the client with some data used to add authentication data
@@ -25,12 +25,12 @@ type family AuthClientData a :: *
2525--
2626-- NOTE: THIS API IS EXPERIMENTAL AND SUBJECT TO CHANGE
2727newtype AuthenticateReq a =
28− AuthenticateReq { unAuthReq :: (AuthClientData a, AuthClientData a -> Req -> Req) }
28+ AuthenticateReq { unAuthReq :: (AuthClientData a, AuthClientData a -> Request -> Request) }
2929
3030-- | Handy helper to avoid wrapping datatypes in tuples everywhere.
3131--
3232-- NOTE: THIS API IS EXPERIMENTAL AND SUBJECT TO CHANGE
3333mkAuthenticateReq :: AuthClientData a
34− -> (AuthClientData a -> Req -> Req)
34+ -> (AuthClientData a -> Request -> Request)
3535 -> AuthenticateReq a
3636mkAuthenticateReq val func = AuthenticateReq (val, func)
servant-client/src/Servant/Common/BaseUrl.hs → servant-client-core/src/Servant/Client/Core/Internal/BaseUrl.hsrenamed

The content is the same.

servant-client/src/Servant/Common/BasicAuth.hs → servant-client-core/src/Servant/Client/Core/Internal/BasicAuth.hsrenamed+2−2
@@ -11,11 +11,11 @@ module Servant.Common.BasicAuth (
1111import Data.ByteString.Base64 (encode)
1212import Data.Monoid ((<>))
1313import Data.Text.Encoding (decodeUtf8)
14−import Servant.Common.Req (addHeader, Req)
14+import Servant.Common.Req (addHeader, Request)
1515import Servant.API.BasicAuth (BasicAuthData(BasicAuthData))
1616
1717-- | Authenticate a request using Basic Authentication
18−basicAuthReq :: BasicAuthData -> Req -> Req
18+basicAuthReq :: BasicAuthData -> Request -> Request
1919basicAuthReq (BasicAuthData user pass) req =
2020 let authText = decodeUtf8 ("Basic " <> encode (user <> ":" <> pass))
2121 in addHeader "Authorization" authText req
servant-client-core/src/Servant/Client/Core/Internal/Class.hsnew file+13
@@ -0,0 +1,13 @@
1+{-# LANGUAGE MultiParamTypeClasses #-}
2+{-| Types for possible backends to run client-side `Request` queries -}
3+module Servant.Client.Class where
4+
5+import Data.Proxy
6+import Network.HTTP.Types
7+import Servant.Common.Req (Request, Response)
8+
9+class (Monad m) => RunClient m ct where
10+ runRequest :: Proxy ct
11+ -> Method
12+ -> Request
13+ -> m Response
servant-client/src/Servant/Client/Generic.hs → servant-client-core/src/Servant/Client/Core/Internal/Generic.hsrenamed

The content is the same.

servant-client-core/src/Servant/Client/Core/Internal/Request.hsnew file+146
@@ -0,0 +1,146 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE DeriveGeneric #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE OverloadedStrings #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TypeFamilies #-}
9+
10+module Servant.Common.Request where
11+
12+import Prelude ()
13+import Prelude.Compat
14+
15+import qualified Data.ByteString.Builder as Builder
16+import qualified Data.ByteString.Lazy as LBS
17+import Data.Semigroup ((<>))
18+import qualified Data.Sequence as Seq
19+import Data.String.Conversions (cs)
20+import Data.Text (Text)
21+import Data.Typeable (Typeable)
22+import GHC.Generics (Generic)
23+import Network.HTTP.Media (MediaType)
24+import Network.HTTP.Types (Header, HeaderName, HttpVersion,
25+ Method, QueryItem, Status, http11)
26+import Web.HttpApiData (ToHttpApiData, toEncodedUrlPiece,
27+ toHeader)
28+
29+data ServantError
30+ = FailureResponse Response
31+ | DecodeFailure Text Response
32+ | UnsupportedContentType MediaType Response
33+ | InvalidContentTypeHeader Response
34+ | ConnectionError Text
35+ deriving (Eq, Show, Generic, Typeable)
36+
37+data Request = Request
38+ { requestPath :: Builder.Builder
39+ , requestQueryString :: Seq.Seq QueryItem
40+ , requestBody :: Maybe (RequestBody, MediaType)
41+ , requestAccept :: Seq.Seq MediaType
42+ , requestHeaders :: Seq.Seq Header
43+ , requestHttpVersion :: HttpVersion
44+ } deriving (Generic, Typeable)
45+
46+newtype RequestBody = RequestBodyLBS LBS.ByteString
47+ deriving (Eq, Ord, Read, Show, Typeable)
48+
49+data Response = Response
50+ { responseStatusCode :: Status
51+ , responseBody :: LBS.ByteString
52+ , responseHeaders :: Seq.Seq Header
53+ , responseHttpVersion :: HttpVersion
54+ } deriving (Eq, Show, Generic, Typeable)
55+
56+defaultRequest :: Request
57+defaultRequest = Request
58+ { requestPath = ""
59+ , requestQueryString = Seq.empty
60+ , requestBody = Nothing
61+ , requestAccept = Seq.empty
62+ , requestHeaders = Seq.empty
63+ , requestHttpVersion = http11
64+ }
65+
66+appendToPath :: Text -> Request -> Request
67+appendToPath p req
68+ = req { requestPath = requestPath req <> "/" <> toEncodedUrlPiece p }
69+
70+{-appendToQueryString :: Text -- ^ param name-}
71+ {--> Maybe Text -- ^ param value-}
72+ {--> Request-}
73+ {--> Request-}
74+{-appendToQueryString pname pvalue req-}
75+ {-= req { requestQueryString = requestQueryString req Seq.|> (pname, pvalue)}-}
76+
77+addHeader :: ToHttpApiData a => HeaderName -> a -> Request -> Request
78+addHeader name val req
79+ = req { requestHeaders = requestHeaders req Seq.|> (name, toHeader val)}
80+
81+-- | Set body and media type of the request being constructed.
82+--
83+-- The body is set to the given bytestring using the 'RequestBodyLBS'
84+-- constructor.
85+--
86+-- @since 0.12
87+--
88+setRequestBodyLBS :: LBS.ByteString -> MediaType -> Request -> Request
89+setRequestBodyLBS b t req
90+ = req { requestBody = Just (RequestBodyLBS b, t) }
91+
92+-- | Set body and media type of the request being constructed.
93+--
94+-- @since 0.12
95+--
96+setRequestBody :: RequestBody -> MediaType -> Request -> Request
97+setRequestBody b t req = req { requestBody = Just (b, t) }
98+
99+{-reqToRequest :: (Functor m, MonadThrow m) => Req -> BaseUrl -> m Request-}
100+{-reqToRequest req (BaseUrl reqScheme reqHost reqPort path) =-}
101+ {-setheaders . setAccept . setrqb . setQS <$> parseRequest url-}
102+
103+ {-where url = show $ nullURI { uriScheme = case reqScheme of-}
104+ {-Http -> "http:"-}
105+ {-Https -> "https:"-}
106+ {-, uriAuthority = Just $-}
107+ {-URIAuth { uriUserInfo = ""-}
108+ {-, uriRegName = reqHost-}
109+ {-, uriPort = ":" ++ show reqPort-}
110+ {-}-}
111+ {-, uriPath = fullPath-}
112+ {-}-}
113+ {-fullPath = path ++ cs (Builder.toLazyByteString (reqPath req))-}
114+
115+ {-setrqb r = case reqBody req of-}
116+ {-Nothing -> r-}
117+ {-Just (b,t) -> r { requestBody = b-}
118+ {-, requestHeaders = requestHeaders r-}
119+ {-++ [(hContentType, cs . show $ t)] }-}
120+ {-setQS = setQueryString $ queryTextToQuery (qs req)-}
121+ {-setheaders r = r { requestHeaders = requestHeaders r-}
122+ {-<> fmap toProperHeader (headers req) }-}
123+ {-setAccept r = r { requestHeaders = filter ((/= "Accept") . fst) (requestHeaders r)-}
124+ {-<> [("Accept", renderHeader $ reqAccept req)-}
125+ {-| not . null . reqAccept $ req] }-}
126+ {-toProperHeader (name, val) =-}
127+ {-(fromString name, encodeUtf8 val)-}
128+
129+#if !MIN_VERSION_http_client(0,4,30)
130+-- 'parseRequest' is introduced in http-client-0.4.30
131+-- it differs from 'parseUrl', by not throwing exceptions on non-2xx http statuses
132+--
133+-- See for implementations:
134+-- http://hackage.haskell.org/package/http-client-0.4.30/docs/src/Network-HTTP-Client-Request.html#parseRequest
135+-- http://hackage.haskell.org/package/http-client-0.5.0/docs/src/Network-HTTP-Client-Request.html#parseRequest
136+parseRequest :: MonadThrow m => String -> m Request
137+parseRequest url = liftM disableStatusCheck (parseUrl url)
138+ where
139+ disableStatusCheck req = req { checkStatus = \ _status _headers _cookies -> Nothing }
140+#endif
141+
142+
143+-- * performing requests
144+
145+displayHttpRequest :: Method -> String
146+displayHttpRequest httpmethod = "HTTP " ++ cs httpmethod ++ " request"
servant-client/servant-client.cabal+1
@@ -44,6 +44,7 @@ library
4444 , attoparsec >= 0.12 && < 0.14
4545 , base64-bytestring >= 1.0.0.1 && < 1.1
4646 , bytestring >= 0.10 && < 0.11
47+ , containers >= 0.5 && < 0.6
4748 , exceptions >= 0.8 && < 0.9
4849 , generics-sop >= 0.1.0.0 && < 0.4
4950 , http-api-data >= 0.3.6 && < 0.4
servant-client/src/Servant/Client/Class.hsdeleted−15
@@ -1,15 +0,0 @@
1−{-# LANGUAGE MultiParamTypeClasses #-}
2−{-| Types for possible backends to run client-side `Req` queries -}
3−module Servant.Client.Class
4− (RunClient(..))
5−where
6−
7−import Data.Proxy
8−import Network.HTTP.Types
9−import Servant.Common.Req
10−
11−class (Monad m) => RunClient m ct result where
12− runRequest :: Proxy ct
13− -> Method
14− -> Req
15− -> m result
servant-client/src/Servant/Client/HttpClient.hs+29−32
@@ -4,44 +4,42 @@
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE GeneralizedNewtypeDeriving #-}
66{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE TypeFamilies #-}
87{-# LANGUAGE OverloadedStrings #-}
98{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE TypeFamilies #-}
1010
1111{-| http-client based client requests executor -}
1212module Servant.Client.HttpClient where
1313
1414
15−import Prelude ()
16−import Prelude.Compat
17−
18−import Control.Exception
19−import Control.Monad
20−import Control.Monad.Catch (MonadThrow, MonadCatch)
21−import Data.Foldable (toList)
22−import Data.Functor.Alt (Alt (..))
23−
24−import Control.Monad.Error.Class (MonadError(..))
25−import Control.Monad.Trans.Except
26−
27−import GHC.Generics
28−import Control.Monad.Base (MonadBase (..))
29−import Control.Monad.IO.Class ()
30−import Control.Monad.Reader
31−import Control.Monad.Trans.Control (MonadBaseControl (..))
32−import Data.ByteString.Lazy hiding (pack, filter, map, null, elem, any)
33−import Data.String.Conversions (cs)
34−import Data.Proxy
35−import Network.HTTP.Media
36−import Network.HTTP.Types
37−import Network.HTTP.Client hiding (Proxy, path)
15+import Prelude ()
16+import Prelude.Compat
17+
18+import Control.Exception
19+import Control.Monad
20+import Control.Monad.Base (MonadBase (..))
21+import Control.Monad.Catch (MonadCatch, MonadThrow)
22+import Control.Monad.Error.Class (MonadError (..))
23+import Control.Monad.IO.Class ()
24+import Control.Monad.Reader
25+import Control.Monad.Trans.Control (MonadBaseControl (..))
26+import Control.Monad.Trans.Except
27+import Data.ByteString.Lazy hiding (any, elem, filter, map,
28+ null, pack)
29+import Data.Foldable (toList)
30+import Data.Functor.Alt (Alt (..))
31+import Data.Proxy
32+import Data.String.Conversions (cs)
33+import GHC.Generics
34+import Network.HTTP.Media
35+import Network.HTTP.Types
36+import Servant.API.ContentTypes
37+import Servant.Client.Class
38+import Servant.Common.BaseUrl
39+import Servant.Common.Req
40+
41+import qualified Network.HTTP.Client as Client
3842import qualified Network.HTTP.Types.Header as HTTP
39−import Servant.API.ContentTypes
40−import Servant.Client.Class
41−import Servant.Common.BaseUrl
42−import Servant.Common.Req
43−
44−import qualified Network.HTTP.Client as Client
4543
4644instance RunClient ClientM NoContent ( Int, ByteString, MediaType
4745 , [HTTP.Header], Response ByteString) where
@@ -89,8 +87,7 @@ runClientM cm env = runExceptT $ (flip runReaderT env) $ runClientM' cm
8987
9088
9189performRequest :: Method -> Req
92− -> ClientM ( Int, ByteString, MediaType
93− , [HTTP.Header], Response ByteString)
90+ -> ClientM Response
9491performRequest reqMethod req = do
9592 m <- asks manager
9693 reqHost <- asks baseUrl
servant-client/src/Servant/Common/Req.hsdeleted−183
@@ -1,183 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveDataTypeable #-}
3−{-# LANGUAGE DeriveGeneric #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE TypeFamilies #-}
7−{-# LANGUAGE OverloadedStrings #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−
10−module Servant.Common.Req where
11−
12−import Prelude ()
13−import Prelude.Compat
14−
15−import Control.Exception
16−import Control.Monad.Catch (MonadThrow)
17−import Data.Semigroup ((<>))
18−
19−import Control.Monad.IO.Class ()
20−import qualified Data.ByteString.Builder as BS
21−import Data.ByteString.Lazy hiding (pack, filter, map, null, elem, any)
22−import Data.String
23−import Data.String.Conversions (cs)
24−import Data.Text (Text)
25−import Data.Text.Encoding
26−import Data.Typeable
27−import Network.HTTP.Media
28−import Network.HTTP.Types
29−import Network.HTTP.Client hiding (Proxy, path)
30−import Network.URI hiding (path)
31−import Servant.Common.BaseUrl
32−
33−import Web.HttpApiData
34−
35−data ServantError
36− = FailureResponse
37− { failingRequest :: UrlReq
38− , responseStatus :: Status
39− , responseContentType :: MediaType
40− , responseBody :: ByteString
41− }
42− | DecodeFailure
43− { decodeError :: String
44− , responseContentType :: MediaType
45− , responseBody :: ByteString
46− }
47− | UnsupportedContentType
48− { responseContentType :: MediaType
49− , responseBody :: ByteString
50− }
51− | InvalidContentTypeHeader
52− { responseContentTypeHeader :: ByteString
53− , responseBody :: ByteString
54− }
55− | ConnectionError
56− { connectionError :: SomeException
57− }
58− deriving (Show, Typeable)
59−
60−instance Eq ServantError where
61− FailureResponse _ a b c == FailureResponse _ x y z =
62− (a, b, c) == (x, y, z)
63− DecodeFailure a b c == DecodeFailure x y z =
64− (a, b, c) == (x, y, z)
65− UnsupportedContentType a b == UnsupportedContentType x y =
66− (a, b) == (x, y)
67− InvalidContentTypeHeader a b == InvalidContentTypeHeader x y =
68− (a, b) == (x, y)
69− ConnectionError a == ConnectionError x =
70− show a == show x
71− _ == _ = False
72−
73−instance Exception ServantError
74−
75−data UrlReq = UrlReq BaseUrl Req
76−
77−instance Show UrlReq where
78− show (UrlReq url req) = showBaseUrl url ++ path ++ "?" ++ show (qs req)
79− where
80− path = cs (BS.toLazyByteString (reqPath req))
81−
82−data Req = Req
83− { reqPath :: BS.Builder
84− , qs :: QueryText
85− , reqBody :: Maybe (RequestBody, MediaType)
86− , reqAccept :: [MediaType]
87− , headers :: [(String, Text)]
88− }
89−
90−defReq :: Req
91−defReq = Req "" [] Nothing [] []
92−
93−appendToPath :: String -> Req -> Req
94−appendToPath p req =
95− req { reqPath = reqPath req <> "/" <> toEncodedUrlPiece p }
96−
97−appendToQueryString :: Text -- ^ param name
98− -> Maybe Text -- ^ param value
99− -> Req
100− -> Req
101−appendToQueryString pname pvalue req =
102− req { qs = qs req ++ [(pname, pvalue)]
103− }
104−
105−addHeader :: ToHttpApiData a => String -> a -> Req -> Req
106−addHeader name val req = req { headers = headers req
107− ++ [(name, decodeUtf8 (toHeader val))]
108− }
109−
110−-- | Set body and media type of the request being constructed.
111−--
112−-- The body is set to the given bytestring using the 'RequestBodyLBS'
113−-- constructor.
114−--
115−{-# DEPRECATED setRQBody "Use setReqBodyLBS instead" #-}
116−setRQBody :: ByteString -> MediaType -> Req -> Req
117−setRQBody = setReqBodyLBS
118−
119−-- | Set body and media type of the request being constructed.
120−--
121−-- The body is set to the given bytestring using the 'RequestBodyLBS'
122−-- constructor.
123−--
124−-- @since 0.9.2.0
125−--
126−setReqBodyLBS :: ByteString -> MediaType -> Req -> Req
127−setReqBodyLBS b t req = req { reqBody = Just (RequestBodyLBS b, t) }
128−
129−-- | Set body and media type of the request being constructed.
130−--
131−-- @since 0.9.2.0
132−--
133−setReqBody :: RequestBody -> MediaType -> Req -> Req
134−setReqBody b t req = req { reqBody = Just (b, t) }
135−
136−reqToRequest :: (Functor m, MonadThrow m) => Req -> BaseUrl -> m Request
137−reqToRequest req (BaseUrl reqScheme reqHost reqPort path) =
138− setheaders . setAccept . setrqb . setQS <$> parseRequest url
139−
140− where url = show $ nullURI { uriScheme = case reqScheme of
141− Http -> "http:"
142− Https -> "https:"
143− , uriAuthority = Just $
144− URIAuth { uriUserInfo = ""
145− , uriRegName = reqHost
146− , uriPort = ":" ++ show reqPort
147− }
148− , uriPath = fullPath
149− }
150− fullPath = path ++ cs (BS.toLazyByteString (reqPath req))
151−
152− setrqb r = case reqBody req of
153− Nothing -> r
154− Just (b,t) -> r { requestBody = b
155− , requestHeaders = requestHeaders r
156− ++ [(hContentType, cs . show $ t)] }
157− setQS = setQueryString $ queryTextToQuery (qs req)
158− setheaders r = r { requestHeaders = requestHeaders r
159− <> fmap toProperHeader (headers req) }
160− setAccept r = r { requestHeaders = filter ((/= "Accept") . fst) (requestHeaders r)
161− <> [("Accept", renderHeader $ reqAccept req)
162− | not . null . reqAccept $ req] }
163− toProperHeader (name, val) =
164− (fromString name, encodeUtf8 val)
165−
166−#if !MIN_VERSION_http_client(0,4,30)
167−-- 'parseRequest' is introduced in http-client-0.4.30
168−-- it differs from 'parseUrl', by not throwing exceptions on non-2xx http statuses
169−--
170−-- See for implementations:
171−-- http://hackage.haskell.org/package/http-client-0.4.30/docs/src/Network-HTTP-Client-Request.html#parseRequest
172−-- http://hackage.haskell.org/package/http-client-0.5.0/docs/src/Network-HTTP-Client-Request.html#parseRequest
173−parseRequest :: MonadThrow m => String -> m Request
174−parseRequest url = liftM disableStatusCheck (parseUrl url)
175− where
176− disableStatusCheck req = req { checkStatus = \ _status _headers _cookies -> Nothing }
177−#endif
178−
179−
180−-- * performing requests
181−
182−displayHttpRequest :: Method -> String
183−displayHttpRequest httpmethod = "HTTP " ++ cs httpmethod ++ " request"