@@ -13,38 +13,23 @@ import Prelude ()
13import Prelude.Compat
13import Prelude.Compat
15import Control.Exception
15import Control.Exception
16−import Control.Monad
16+import Control.Monad.Catch (MonadThrow)
17−import Control.Monad.Catch (MonadThrow, MonadCatch)
18−import Data.Foldable (toList)
19−import Data.Functor.Alt (Alt (..))
20import Data.Semigroup ((<>))
17import Data.Semigroup ((<>))
22−import Control.Monad.Error.Class (MonadError(..))
23−import Control.Monad.Trans.Except
26−import Control.Monad.Base (MonadBase (..))
27import Control.Monad.IO.Class ()
19import Control.Monad.IO.Class ()
28−import Control.Monad.Reader
29−import Control.Monad.Trans.Control (MonadBaseControl (..))
30import qualified Data.ByteString.Builder as BS
20import qualified Data.ByteString.Builder as BS
31import Data.ByteString.Lazy hiding (pack, filter, map, null, elem, any)
21import Data.ByteString.Lazy hiding (pack, filter, map, null, elem, any)
32import Data.String
22import Data.String
33import Data.String.Conversions (cs)
23import Data.String.Conversions (cs)
35import Data.Text (Text)
24import Data.Text (Text)
36import Data.Text.Encoding
25import Data.Text.Encoding
37import Data.Typeable
26import Data.Typeable
38import Network.HTTP.Media
27import Network.HTTP.Media
39import Network.HTTP.Types
28import Network.HTTP.Types
40import Network.HTTP.Client hiding (Proxy, path)
29import Network.HTTP.Client hiding (Proxy, path)
41−import qualified Network.HTTP.Types.Header as HTTP
42import Network.URI hiding (path)
30import Network.URI hiding (path)
43−import Servant.API.ContentTypes
44import Servant.Common.BaseUrl
31import Servant.Common.BaseUrl
46−import qualified Network.HTTP.Client as Client
48import Web.HttpApiData
33import Web.HttpApiData
50data ServantError
35data ServantError
@@ -196,90 +181,3 @@ parseRequest url = liftM disableStatusCheck (parseUrl url)
197displayHttpRequest :: Method -> String
182displayHttpRequest :: Method -> String
198displayHttpRequest httpmethod = "HTTP " ++ cs httpmethod ++ " request"
183displayHttpRequest httpmethod = "HTTP " ++ cs httpmethod ++ " request"
202− { manager :: Manager
203− , baseUrl :: BaseUrl
207−-- | @ClientM@ is the monad in which client functions run. Contains the
208−-- 'Manager' and 'BaseUrl' used for requests in the reader environment.
210−newtype ClientM a = ClientM { runClientM' :: ReaderT ClientEnv (ExceptT ServantError IO) a }
211− deriving ( Functor, Applicative, Monad, MonadIO, Generic
212− , MonadReader ClientEnv
213− , MonadError ServantError
214− , MonadThrow, MonadCatch
217−instance MonadBase IO ClientM where
218− liftBase = ClientM . liftBase
220−instance MonadBaseControl IO ClientM where
221− type StM ClientM a = Either ServantError a
223− -- liftBaseWith :: (RunInBase ClientM IO -> IO a) -> ClientM a
224− liftBaseWith f = ClientM (liftBaseWith (\g -> f (g . runClientM')))
226− -- restoreM :: StM ClientM a -> ClientM a
227− restoreM st = ClientM (restoreM st)
229−-- | Try clients in order, last error is preserved.
230−instance Alt ClientM where
231− a <!> b = a `catchError` \_ -> b
233−runClientM :: ClientM a -> ClientEnv -> IO (Either ServantError a)
234−runClientM cm env = runExceptT $ (flip runReaderT env) $ runClientM' cm
237−performRequest :: Method -> Req
238− -> ClientM ( Int, ByteString, MediaType
239− , [HTTP.Header], Response ByteString)
240−performRequest reqMethod req = do
242− reqHost <- asks baseUrl
243− partialRequest <- liftIO $ reqToRequest req reqHost
245− let request = partialRequest { Client.method = reqMethod }
247− eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request m
250− throwError . ConnectionError $ SomeException err
252− Right response -> do
253− let status = Client.responseStatus response
254− body = Client.responseBody response
255− hdrs = Client.responseHeaders response
256− status_code = statusCode status
257− ct <- case lookup "Content-Type" $ Client.responseHeaders response of
258− Nothing -> pure $ "application"//"octet-stream"
259− Just t -> case parseAccept t of
260− Nothing -> throwError $ InvalidContentTypeHeader (cs t) body
262− unless (status_code >= 200 && status_code < 300) $
263− throwError $ FailureResponse (UrlReq reqHost req) status ct body
264− return (status_code, body, ct, hdrs, response)
266−performRequestCT :: MimeUnrender ct result => Proxy ct -> Method -> Req
267− -> ClientM ([HTTP.Header], result)
268−performRequestCT ct reqMethod req = do
269− let acceptCTS = contentTypes ct
270− (_status, respBody, respCT, hdrs, _response) <-
271− performRequest reqMethod (req { reqAccept = toList acceptCTS })
272− unless (any (matches respCT) acceptCTS) $ throwError $ UnsupportedContentType respCT respBody
273− case mimeUnrender ct respBody of
274− Left err -> throwError $ DecodeFailure err respCT respBody
275− Right val -> return (hdrs, val)
277−performRequestNoBody :: Method -> Req -> ClientM [HTTP.Header]
278−performRequestNoBody reqMethod req = do
279− (_status, _body, _ct, hdrs, _response) <- performRequest reqMethod req
282−catchConnectionError :: IO a -> IO (Either ServantError a)
283−catchConnectionError action =
284− catch (Right <$> action) $ \e ->
285− pure . Left . ConnectionError $ SomeException (e :: HttpException)