@@ -27,14 +27,13 @@ module Servant.Client
27#if !MIN_VERSION_base(4,8,0)
27#if !MIN_VERSION_base(4,8,0)
28import Control.Applicative ((<$>))
28import Control.Applicative ((<$>))
30−import Control.Monad.Trans.Except
31import Data.ByteString.Lazy (ByteString)
30import Data.ByteString.Lazy (ByteString)
32import Data.List
31import Data.List
33import Data.Proxy
32import Data.Proxy
34import Data.String.Conversions
33import Data.String.Conversions
35import Data.Text (unpack)
34import Data.Text (unpack)
36import GHC.TypeLits
35import GHC.TypeLits
37−import Network.HTTP.Client (Response, Manager)
36+import Network.HTTP.Client (Manager, Response)
38import Network.HTTP.Media
37import Network.HTTP.Media
39import qualified Network.HTTP.Types as H
38import qualified Network.HTTP.Types as H
40import qualified Network.HTTP.Types.Header as HTTP
39import qualified Network.HTTP.Types.Header as HTTP
@@ -54,19 +53,18 @@ import Servant.Common.Req
54-- > myApi :: Proxy MyApi
53-- > myApi :: Proxy MyApi
55-- > myApi = Proxy
54-- > myApi = Proxy
57−-- > getAllBooks :: ExceptT String IO [Book]
56+-- > getAllBooks :: Manager -> BaseUrl -> ClientM [Book]
58−-- > postNewBook :: Book -> ExceptT String IO Book
57+-- > postNewBook :: Book -> Manager -> BaseUrl -> ClientM Book
59−-- > (getAllBooks :<|> postNewBook) = client myApi host manager
58+-- > (getAllBooks :<|> postNewBook) = client myApi
60−-- > where host = BaseUrl Http "localhost" 8080
61−client :: HasClient layout => Proxy layout -> BaseUrl -> Manager -> Client layout
59+client :: HasClient layout => Proxy layout -> Client layout
62−client p baseurl = clientWithRoute p defReq baseurl
60+client p = clientWithRoute p defReq
64-- | This class lets us define how each API combinator
62-- | This class lets us define how each API combinator
65-- influences the creation of an HTTP request. It's mostly
63-- influences the creation of an HTTP request. It's mostly
66-- an internal class, you can just use 'client'.
64-- an internal class, you can just use 'client'.
67class HasClient layout where
65class HasClient layout where
68 type Client layout :: *
66 type Client layout :: *
69− clientWithRoute :: Proxy layout -> Req -> BaseUrl -> Manager -> Client layout
67+ clientWithRoute :: Proxy layout -> Req -> Client layout
72-- | A client querying function for @a ':<|>' b@ will actually hand you
70-- | A client querying function for @a ':<|>' b@ will actually hand you
@@ -79,15 +77,14 @@ class HasClient layout where
79-- > myApi :: Proxy MyApi
77-- > myApi :: Proxy MyApi
80-- > myApi = Proxy
78-- > myApi = Proxy
82−-- > getAllBooks :: ExceptT String IO [Book]
80+-- > getAllBooks :: Manager -> BaseUrl -> ClientM [Book]
83−-- > postNewBook :: Book -> ExceptT String IO Book
81+-- > postNewBook :: Book -> Manager -> BaseUrl -> ClientM Book
84−-- > (getAllBooks :<|> postNewBook) = client myApi host manager
82+-- > (getAllBooks :<|> postNewBook) = client myApi
85−-- > where host = BaseUrl Http "localhost" 8080
86instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
83instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
87 type Client (a :<|> b) = Client a :<|> Client b
84 type Client (a :<|> b) = Client a :<|> Client b
88− clientWithRoute Proxy req baseurl manager =
85+ clientWithRoute Proxy req =
89− clientWithRoute (Proxy :: Proxy a) req baseurl manager :<|>
86+ clientWithRoute (Proxy :: Proxy a) req :<|>
90− clientWithRoute (Proxy :: Proxy b) req baseurl manager
87+ clientWithRoute (Proxy :: Proxy b) req
92-- | If you use a 'Capture' in one of your endpoints in your API,
89-- | If you use a 'Capture' in one of your endpoints in your API,
93-- the corresponding querying function will automatically take
90-- the corresponding querying function will automatically take
@@ -105,9 +102,8 @@ instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
105-- > myApi :: Proxy MyApi
102-- > myApi :: Proxy MyApi
106-- > myApi = Proxy
103-- > myApi = Proxy
108−-- > getBook :: Text -> ExceptT String IO Book
105+-- > getBook :: Text -> Manager -> BaseUrl -> ClientM Book
109−-- > getBook = client myApi host manager
106+-- > getBook = client myApi
110−-- > where host = BaseUrl Http "localhost" 8080
111-- > -- then you can just use "getBook" to query that endpoint
107-- > -- then you can just use "getBook" to query that endpoint
112instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
108instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
113 => HasClient (Capture capture a :> sublayout) where
109 => HasClient (Capture capture a :> sublayout) where
@@ -115,11 +111,9 @@ instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
115 type Client (Capture capture a :> sublayout) =
111 type Client (Capture capture a :> sublayout) =
116 a -> Client sublayout
112 a -> Client sublayout
118− clientWithRoute Proxy req baseurl manager val =
114+ clientWithRoute Proxy req val =
119 clientWithRoute (Proxy :: Proxy sublayout)
115 clientWithRoute (Proxy :: Proxy sublayout)
120 (appendToPath p req)
116 (appendToPath p req)
124 where p = unpack (toUrlPiece val)
118 where p = unpack (toUrlPiece val)
@@ -127,16 +121,17 @@ instance OVERLAPPABLE_
127 -- Note [Non-Empty Content Types]
121 -- Note [Non-Empty Content Types]
128 (MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
122 (MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
129 ) => HasClient (Verb method status cts' a) where
123 ) => HasClient (Verb method status cts' a) where
130− type Client (Verb method status cts' a) = ExceptT ServantError IO a
124+ type Client (Verb method status cts' a) = Manager -> BaseUrl -> ClientM a
131− clientWithRoute Proxy req baseurl manager =
125+ clientWithRoute Proxy req manager baseurl =
132− snd <$> performRequestCT (Proxy :: Proxy ct) method req baseurl manager
126+ snd <$> performRequestCT (Proxy :: Proxy ct) method req manager baseurl
133 where method = reflectMethod (Proxy :: Proxy method)
127 where method = reflectMethod (Proxy :: Proxy method)
135instance OVERLAPPING_
129instance OVERLAPPING_
136 (ReflectMethod method) => HasClient (Verb method status cts NoContent) where
130 (ReflectMethod method) => HasClient (Verb method status cts NoContent) where
137− type Client (Verb method status cts NoContent) = ExceptT ServantError IO NoContent
131+ type Client (Verb method status cts NoContent)
132+ = Manager -> BaseUrl -> ClientM NoContent
138− clientWithRoute Proxy req baseurl manager =
133+ clientWithRoute Proxy req manager baseurl =
139− performRequestNoBody method req baseurl manager >> return NoContent
134+ performRequestNoBody method req manager baseurl >> return NoContent
140 where method = reflectMethod (Proxy :: Proxy method)
135 where method = reflectMethod (Proxy :: Proxy method)
142instance OVERLAPPING_
137instance OVERLAPPING_
@@ -144,10 +139,10 @@ instance OVERLAPPING_
144 ( MimeUnrender ct a, BuildHeadersTo ls, ReflectMethod method, cts' ~ (ct ': cts)
139 ( MimeUnrender ct a, BuildHeadersTo ls, ReflectMethod method, cts' ~ (ct ': cts)
145 ) => HasClient (Verb method status cts' (Headers ls a)) where
140 ) => HasClient (Verb method status cts' (Headers ls a)) where
146 type Client (Verb method status cts' (Headers ls a))
141 type Client (Verb method status cts' (Headers ls a))
147− = ExceptT ServantError IO (Headers ls a)
142+ = Manager -> BaseUrl -> ClientM (Headers ls a)
148− clientWithRoute Proxy req baseurl manager = do
143+ clientWithRoute Proxy req manager baseurl = do
149 let method = reflectMethod (Proxy :: Proxy method)
144 let method = reflectMethod (Proxy :: Proxy method)
150− (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) method req baseurl manager
145+ (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) method req manager baseurl
151 return $ Headers { getResponse = resp
146 return $ Headers { getResponse = resp
152 , getHeadersHList = buildHeadersTo hdrs
147 , getHeadersHList = buildHeadersTo hdrs
@@ -156,10 +151,10 @@ instance OVERLAPPING_
156 ( BuildHeadersTo ls, ReflectMethod method
151 ( BuildHeadersTo ls, ReflectMethod method
157 ) => HasClient (Verb method status cts (Headers ls NoContent)) where
152 ) => HasClient (Verb method status cts (Headers ls NoContent)) where
158 type Client (Verb method status cts (Headers ls NoContent))
153 type Client (Verb method status cts (Headers ls NoContent))
159− = ExceptT ServantError IO (Headers ls NoContent)
154+ = Manager -> BaseUrl -> ClientM (Headers ls NoContent)
160− clientWithRoute Proxy req baseurl manager = do
155+ clientWithRoute Proxy req manager baseurl = do
161 let method = reflectMethod (Proxy :: Proxy method)
156 let method = reflectMethod (Proxy :: Proxy method)
162− hdrs <- performRequestNoBody method req baseurl manager
157+ hdrs <- performRequestNoBody method req manager baseurl
163 return $ Headers { getResponse = NoContent
158 return $ Headers { getResponse = NoContent
164 , getHeadersHList = buildHeadersTo hdrs
159 , getHeadersHList = buildHeadersTo hdrs
@@ -186,9 +181,8 @@ instance OVERLAPPING_
186-- > myApi :: Proxy MyApi
181-- > myApi :: Proxy MyApi
187-- > myApi = Proxy
182-- > myApi = Proxy
189−-- > viewReferer :: Maybe Referer -> ExceptT String IO Book
184+-- > viewReferer :: Maybe Referer -> Manager -> BaseUrl -> ClientM Book
190−-- > viewReferer = client myApi host
185+-- > viewReferer = client myApi
191−-- > where host = BaseUrl Http "localhost" 8080
192-- > -- then you can just use "viewRefer" to query that endpoint
186-- > -- then you can just use "viewRefer" to query that endpoint
193-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
187-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
194instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
188instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
@@ -197,14 +191,12 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
197 type Client (Header sym a :> sublayout) =
191 type Client (Header sym a :> sublayout) =
198 Maybe a -> Client sublayout
192 Maybe a -> Client sublayout
200− clientWithRoute Proxy req baseurl manager mval =
194+ clientWithRoute Proxy req mval =
201 clientWithRoute (Proxy :: Proxy sublayout)
195 clientWithRoute (Proxy :: Proxy sublayout)
202 (maybe req
196 (maybe req
203 (\value -> Servant.Common.Req.addHeader hname value req)
197 (\value -> Servant.Common.Req.addHeader hname value req)
209 where hname = symbolVal (Proxy :: Proxy sym)
201 where hname = symbolVal (Proxy :: Proxy sym)
@@ -239,9 +231,8 @@ instance HasClient sublayout
239-- > myApi :: Proxy MyApi
231-- > myApi :: Proxy MyApi
240-- > myApi = Proxy
232-- > myApi = Proxy
242−-- > getBooksBy :: Maybe Text -> ExceptT String IO [Book]
234+-- > getBooksBy :: Maybe Text -> Manager -> BaseUrl -> ClientM [Book]
243−-- > getBooksBy = client myApi host
235+-- > getBooksBy = client myApi
244−-- > where host = BaseUrl Http "localhost" 8080
245-- > -- then you can just use "getBooksBy" to query that endpoint.
236-- > -- then you can just use "getBooksBy" to query that endpoint.
246-- > -- 'getBooksBy Nothing' for all books
237-- > -- 'getBooksBy Nothing' for all books
247-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
238-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
@@ -252,14 +243,12 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
252 Maybe a -> Client sublayout
243 Maybe a -> Client sublayout
254 -- if mparam = Nothing, we don't add it to the query string
245 -- if mparam = Nothing, we don't add it to the query string
255− clientWithRoute Proxy req baseurl manager mparam =
246+ clientWithRoute Proxy req mparam =
256 clientWithRoute (Proxy :: Proxy sublayout)
247 clientWithRoute (Proxy :: Proxy sublayout)
257 (maybe req
248 (maybe req
258 (flip (appendToQueryString pname) req . Just)
249 (flip (appendToQueryString pname) req . Just)
259 mparamText
250 mparamText
264 where pname = cs pname'
253 where pname = cs pname'
265 pname' = symbolVal (Proxy :: Proxy sym)
254 pname' = symbolVal (Proxy :: Proxy sym)
@@ -286,9 +275,8 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
286-- > myApi :: Proxy MyApi
275-- > myApi :: Proxy MyApi
287-- > myApi = Proxy
276-- > myApi = Proxy
289−-- > getBooksBy :: [Text] -> ExceptT String IO [Book]
278+-- > getBooksBy :: [Text] -> Manager -> BaseUrl -> ClientM [Book]
290−-- > getBooksBy = client myApi host
279+-- > getBooksBy = client myApi
291−-- > where host = BaseUrl Http "localhost" 8080
292-- > -- then you can just use "getBooksBy" to query that endpoint.
280-- > -- then you can just use "getBooksBy" to query that endpoint.
293-- > -- 'getBooksBy []' for all books
281-- > -- 'getBooksBy []' for all books
294-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
282-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
@@ -299,13 +287,12 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
299 type Client (QueryParams sym a :> sublayout) =
287 type Client (QueryParams sym a :> sublayout) =
300 [a] -> Client sublayout
288 [a] -> Client sublayout
302− clientWithRoute Proxy req baseurl manager paramlist =
290+ clientWithRoute Proxy req paramlist =
303 clientWithRoute (Proxy :: Proxy sublayout)
291 clientWithRoute (Proxy :: Proxy sublayout)
304 (foldl' (\ req' -> maybe req' (flip (appendToQueryString pname) req' . Just))
292 (foldl' (\ req' -> maybe req' (flip (appendToQueryString pname) req' . Just))
306 paramlist'
294 paramlist'
310 where pname = cs pname'
297 where pname = cs pname'
311 pname' = symbolVal (Proxy :: Proxy sym)
298 pname' = symbolVal (Proxy :: Proxy sym)
@@ -327,9 +314,8 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout)
327-- > myApi :: Proxy MyApi
314-- > myApi :: Proxy MyApi
328-- > myApi = Proxy
315-- > myApi = Proxy
330−-- > getBooks :: Bool -> ExceptT String IO [Book]
317+-- > getBooks :: Bool -> Manager -> BaseUrl -> ClientM [Book]
331−-- > getBooks = client myApi host
318+-- > getBooks = client myApi
332−-- > where host = BaseUrl Http "localhost" 8080
333-- > -- then you can just use "getBooks" to query that endpoint.
319-- > -- then you can just use "getBooks" to query that endpoint.
334-- > -- 'getBooksBy False' for all books
320-- > -- 'getBooksBy False' for all books
335-- > -- 'getBooksBy True' to only get _already published_ books
321-- > -- 'getBooksBy True' to only get _already published_ books
@@ -339,13 +325,12 @@ instance (KnownSymbol sym, HasClient sublayout)
339 type Client (QueryFlag sym :> sublayout) =
325 type Client (QueryFlag sym :> sublayout) =
340 Bool -> Client sublayout
326 Bool -> Client sublayout
342− clientWithRoute Proxy req baseurl manager flag =
328+ clientWithRoute Proxy req flag =
343 clientWithRoute (Proxy :: Proxy sublayout)
329 clientWithRoute (Proxy :: Proxy sublayout)
345 then appendToQueryString paramname Nothing req
331 then appendToQueryString paramname Nothing req
350 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
335 where paramname = cs $ symbolVal (Proxy :: Proxy sym)
@@ -353,11 +338,12 @@ instance (KnownSymbol sym, HasClient sublayout)
353-- | Pick a 'Method' and specify where the server you want to query is. You get
338-- | Pick a 'Method' and specify where the server you want to query is. You get
354-- back the full `Response`.
339-- back the full `Response`.
355instance HasClient Raw where
340instance HasClient Raw where
356− type Client Raw = H.Method -> ExceptT ServantError IO (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
342+ = H.Method -> Manager -> BaseUrl -> ClientM (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
358− clientWithRoute :: Proxy Raw -> Req -> BaseUrl -> Manager -> Client Raw
344+ clientWithRoute :: Proxy Raw -> Req -> Client Raw
359− clientWithRoute Proxy req baseurl manager httpMethod = do
345+ clientWithRoute Proxy req httpMethod = do
360− performRequest httpMethod req baseurl manager
346+ performRequest httpMethod req
362-- | If you use a 'ReqBody' in one of your endpoints in your API,
348-- | If you use a 'ReqBody' in one of your endpoints in your API,
363-- the corresponding querying function will automatically take
349-- the corresponding querying function will automatically take
@@ -374,9 +360,8 @@ instance HasClient Raw where
374-- > myApi :: Proxy MyApi
360-- > myApi :: Proxy MyApi
375-- > myApi = Proxy
361-- > myApi = Proxy
377−-- > addBook :: Book -> ExceptT String IO Book
363+-- > addBook :: Book -> Manager -> BaseUrl -> ClientM Book
378−-- > addBook = client myApi host manager
364+-- > addBook = client myApi
379−-- > where host = BaseUrl Http "localhost" 8080
380-- > -- then you can just use "addBook" to query that endpoint
365-- > -- then you can just use "addBook" to query that endpoint
381instance (MimeRender ct a, HasClient sublayout)
366instance (MimeRender ct a, HasClient sublayout)
382 => HasClient (ReqBody (ct ': cts) a :> sublayout) where
367 => HasClient (ReqBody (ct ': cts) a :> sublayout) where
@@ -384,43 +369,41 @@ instance (MimeRender ct a, HasClient sublayout)
384 type Client (ReqBody (ct ': cts) a :> sublayout) =
369 type Client (ReqBody (ct ': cts) a :> sublayout) =
385 a -> Client sublayout
370 a -> Client sublayout
387− clientWithRoute Proxy req baseurl manager body =
372+ clientWithRoute Proxy req body =
388 clientWithRoute (Proxy :: Proxy sublayout)
373 clientWithRoute (Proxy :: Proxy sublayout)
389 (let ctProxy = Proxy :: Proxy ct
374 (let ctProxy = Proxy :: Proxy ct
390 in setRQBody (mimeRender ctProxy body)
375 in setRQBody (mimeRender ctProxy body)
391 (contentType ctProxy)
376 (contentType ctProxy)
396-- | Make the querying function append @path@ to the request path.
380-- | Make the querying function append @path@ to the request path.
397instance (KnownSymbol path, HasClient sublayout) => HasClient (path :> sublayout) where
381instance (KnownSymbol path, HasClient sublayout) => HasClient (path :> sublayout) where
398 type Client (path :> sublayout) = Client sublayout
382 type Client (path :> sublayout) = Client sublayout
400− clientWithRoute Proxy req baseurl manager =
384+ clientWithRoute Proxy req =
401 clientWithRoute (Proxy :: Proxy sublayout)
385 clientWithRoute (Proxy :: Proxy sublayout)
402 (appendToPath p req)
386 (appendToPath p req)
405 where p = symbolVal (Proxy :: Proxy path)
388 where p = symbolVal (Proxy :: Proxy path)
407instance HasClient api => HasClient (Vault :> api) where
390instance HasClient api => HasClient (Vault :> api) where
408 type Client (Vault :> api) = Client api
391 type Client (Vault :> api) = Client api
410− clientWithRoute Proxy req baseurl manager =
393+ clientWithRoute Proxy req =
411− clientWithRoute (Proxy :: Proxy api) req baseurl manager
394+ clientWithRoute (Proxy :: Proxy api) req
413instance HasClient api => HasClient (RemoteHost :> api) where
396instance HasClient api => HasClient (RemoteHost :> api) where
414 type Client (RemoteHost :> api) = Client api
397 type Client (RemoteHost :> api) = Client api
416− clientWithRoute Proxy req baseurl manager =
399+ clientWithRoute Proxy req =
417− clientWithRoute (Proxy :: Proxy api) req baseurl manager
400+ clientWithRoute (Proxy :: Proxy api) req
419instance HasClient api => HasClient (IsSecure :> api) where
402instance HasClient api => HasClient (IsSecure :> api) where
420 type Client (IsSecure :> api) = Client api
403 type Client (IsSecure :> api) = Client api
422− clientWithRoute Proxy req baseurl manager =
405+ clientWithRoute Proxy req =
423− clientWithRoute (Proxy :: Proxy api) req baseurl manager
406+ clientWithRoute (Proxy :: Proxy api) req
425instance HasClient subapi =>
408instance HasClient subapi =>
426 HasClient (WithNamedContext name context subapi) where
409 HasClient (WithNamedContext name context subapi) where
@@ -433,16 +416,16 @@ instance ( HasClient api
433 type Client (AuthProtect tag :> api)
416 type Client (AuthProtect tag :> api)
434 = AuthenticateReq (AuthProtect tag) -> Client api
417 = AuthenticateReq (AuthProtect tag) -> Client api
436− clientWithRoute Proxy req baseurl manager (AuthenticateReq (val,func)) =
419+ clientWithRoute Proxy req (AuthenticateReq (val,func)) =
437− clientWithRoute (Proxy :: Proxy api) (func val req) baseurl manager
420+ clientWithRoute (Proxy :: Proxy api) (func val req)
439-- * Basic Authentication
422-- * Basic Authentication
441instance HasClient api => HasClient (BasicAuth realm usr :> api) where
424instance HasClient api => HasClient (BasicAuth realm usr :> api) where
442 type Client (BasicAuth realm usr :> api) = BasicAuthData -> Client api
425 type Client (BasicAuth realm usr :> api) = BasicAuthData -> Client api
444− clientWithRoute Proxy req baseurl manager val =
427+ clientWithRoute Proxy req val =
445− clientWithRoute (Proxy :: Proxy api) (basicAuthReq val req) baseurl manager
428+ clientWithRoute (Proxy :: Proxy api) (basicAuthReq val req)
448{- Note [Non-Empty Content Types]
431{- Note [Non-Empty Content Types]