Support http-client’s CookieJar in servant-client
This commit is contained in:
parent
0147f4b5c7
commit
5880bdfd73
12 changed files with 37 additions and 12 deletions
|
@ -164,7 +164,7 @@ main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
mgr <- newManager defaultManagerSettings
|
mgr <- newManager defaultManagerSettings
|
||||||
bracket (forkIO $ runApp userDB) killThread $ \_ ->
|
bracket (forkIO $ runApp userDB) killThread $ \_ ->
|
||||||
runClientM (getSite u) (ClientEnv mgr (BaseUrl Http "localhost" 8080 ""))
|
runClientM (getSite u) (ClientEnv mgr (BaseUrl Http "localhost" 8080 "") Nothing)
|
||||||
>>= print
|
>>= print
|
||||||
|
|
||||||
where u = BasicAuthData "foo" "bar"
|
where u = BasicAuthData "foo" "bar"
|
||||||
|
|
|
@ -125,7 +125,7 @@ main = do
|
||||||
initDB connStr
|
initDB connStr
|
||||||
mgr <- newManager defaultManagerSettings
|
mgr <- newManager defaultManagerSettings
|
||||||
bracket (forkIO $ runApp pool) killThread $ \_ -> do
|
bracket (forkIO $ runApp pool) killThread $ \_ -> do
|
||||||
ms <- flip runClientM (ClientEnv mgr (BaseUrl Http "localhost" 8080 "")) $ do
|
ms <- flip runClientM (ClientEnv mgr (BaseUrl Http "localhost" 8080 "") Nothing) $ do
|
||||||
postMsg "hello"
|
postMsg "hello"
|
||||||
postMsg "world"
|
postMsg "world"
|
||||||
getMsgs
|
getMsgs
|
||||||
|
|
|
@ -86,7 +86,7 @@ main = do
|
||||||
initDB dbfile
|
initDB dbfile
|
||||||
mgr <- newManager defaultManagerSettings
|
mgr <- newManager defaultManagerSettings
|
||||||
bracket (forkIO $ runApp dbfile) killThread $ \_ -> do
|
bracket (forkIO $ runApp dbfile) killThread $ \_ -> do
|
||||||
ms <- flip runClientM (ClientEnv mgr (BaseUrl Http "localhost" 8080 "")) $ do
|
ms <- flip runClientM (ClientEnv mgr (BaseUrl Http "localhost" 8080 "") Nothing) $ do
|
||||||
postMsg "hello"
|
postMsg "hello"
|
||||||
postMsg "world"
|
postMsg "world"
|
||||||
getMsgs
|
getMsgs
|
||||||
|
|
|
@ -151,7 +151,7 @@ testClient = do
|
||||||
let (foo :<|> _) = client (Proxy :: Proxy TestAPIClient)
|
let (foo :<|> _) = client (Proxy :: Proxy TestAPIClient)
|
||||||
(BasicAuthData "name" "pass")
|
(BasicAuthData "name" "pass")
|
||||||
res <- runClientM (foo 42)
|
res <- runClientM (foo 42)
|
||||||
(ClientEnv mgr (BaseUrl Http "localhost" port ""))
|
(ClientEnv mgr (BaseUrl Http "localhost" port "") Nothing)
|
||||||
hPutStrLn stderr $ case res of
|
hPutStrLn stderr $ case res of
|
||||||
Left err -> "Error: " ++ show err
|
Left err -> "Error: " ++ show err
|
||||||
Right r -> "Success: " ++ show r
|
Right r -> "Success: " ++ show r
|
||||||
|
|
|
@ -11,7 +11,7 @@ We start with a pretty standard set of imports and definition of the model:
|
||||||
{-# LANGUAGE TypeOperators #-}
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
|
||||||
import Control.Concurrent (forkIO, killThread)
|
import Control.Concurrent (forkIO, killThread)
|
||||||
import Control.Concurrent.STM.TVar (TVar, newTVar, readTVar,
|
import Control.Concurrent.STM.TVar (TVar, newTVar, readTVar,
|
||||||
writeTVar)
|
writeTVar)
|
||||||
import Control.Exception (bracket)
|
import Control.Exception (bracket)
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Monad.IO.Class (liftIO)
|
||||||
|
@ -95,7 +95,7 @@ main = do
|
||||||
bracket (forkIO runApp) killThread $ \_ -> do
|
bracket (forkIO runApp) killThread $ \_ -> do
|
||||||
let getBooksClient :<|> addBookClient = client api
|
let getBooksClient :<|> addBookClient = client api
|
||||||
let printBooks = getBooksClient >>= liftIO . print
|
let printBooks = getBooksClient >>= liftIO . print
|
||||||
_ <- flip runClientM (ClientEnv mgr (BaseUrl Http "localhost" port "")) $ do
|
_ <- flip runClientM (ClientEnv mgr (BaseUrl Http "localhost" port "") Nothing) $ do
|
||||||
_ <- printBooks
|
_ <- printBooks
|
||||||
_ <- addBookClient $ Book "Harry Potter and the Order of the Phoenix"
|
_ <- addBookClient $ Book "Harry Potter and the Order of the Phoenix"
|
||||||
_ <- printBooks
|
_ <- printBooks
|
||||||
|
|
|
@ -136,7 +136,7 @@ queries = do
|
||||||
run :: IO ()
|
run :: IO ()
|
||||||
run = do
|
run = do
|
||||||
manager' <- newManager defaultManagerSettings
|
manager' <- newManager defaultManagerSettings
|
||||||
res <- runClientM queries (ClientEnv manager' (BaseUrl Http "localhost" 8081 ""))
|
res <- runClientM queries (ClientEnv manager' (BaseUrl Http "localhost" 8081 "") Nothing)
|
||||||
case res of
|
case res of
|
||||||
Left err -> putStrLn $ "Error: " ++ show err
|
Left err -> putStrLn $ "Error: " ++ show err
|
||||||
Right (pos, message, em) -> do
|
Right (pos, message, em) -> do
|
||||||
|
|
|
@ -170,6 +170,6 @@ main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
mgr <- newManager defaultManagerSettings
|
mgr <- newManager defaultManagerSettings
|
||||||
let clientBaseUrl = BaseUrl Http "www.example.com" 80 ""
|
let clientBaseUrl = BaseUrl Http "www.example.com" 80 ""
|
||||||
ePos <- runClientM (position apiClient 10 20) $ ClientEnv mgr clientBaseUrl
|
ePos <- runClientM (position apiClient 10 20) $ ClientEnv mgr clientBaseUrl Nothing
|
||||||
print ePos
|
print ePos
|
||||||
```
|
```
|
||||||
|
|
|
@ -51,7 +51,9 @@ library
|
||||||
, mtl >= 2.1 && < 2.3
|
, mtl >= 2.1 && < 2.3
|
||||||
, semigroupoids >= 4.3 && < 5.3
|
, semigroupoids >= 4.3 && < 5.3
|
||||||
, servant-client-core == 0.12.*
|
, servant-client-core == 0.12.*
|
||||||
|
, stm >= 2.1 && < 2.5
|
||||||
, text >= 1.2 && < 1.3
|
, text >= 1.2 && < 1.3
|
||||||
|
, time >= 1.7 && < 1.9
|
||||||
, transformers >= 0.3 && < 0.6
|
, transformers >= 0.3 && < 0.6
|
||||||
, transformers-base >= 0.4.4 && < 0.5
|
, transformers-base >= 0.4.4 && < 0.5
|
||||||
, transformers-compat >= 0.4 && < 0.6
|
, transformers-compat >= 0.4 && < 0.6
|
||||||
|
|
|
@ -16,17 +16,19 @@ module Servant.Client.Internal.HttpClient where
|
||||||
import Prelude ()
|
import Prelude ()
|
||||||
import Prelude.Compat
|
import Prelude.Compat
|
||||||
|
|
||||||
|
import Control.Concurrent.STM.TVar
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.Base (MonadBase (..))
|
import Control.Monad.Base (MonadBase (..))
|
||||||
import Control.Monad.Catch (MonadCatch, MonadThrow)
|
import Control.Monad.Catch (MonadCatch, MonadThrow)
|
||||||
import Control.Monad.Error.Class (MonadError (..))
|
import Control.Monad.Error.Class (MonadError (..))
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
|
import Control.Monad.STM (atomically)
|
||||||
import Control.Monad.Trans.Control (MonadBaseControl (..))
|
import Control.Monad.Trans.Control (MonadBaseControl (..))
|
||||||
import Control.Monad.Trans.Except
|
import Control.Monad.Trans.Except
|
||||||
import Data.ByteString.Builder (toLazyByteString)
|
import Data.ByteString.Builder (toLazyByteString)
|
||||||
import qualified Data.ByteString.Lazy as BSL
|
import qualified Data.ByteString.Lazy as BSL
|
||||||
import Data.Foldable (toList)
|
import Data.Foldable (toList, traverse_)
|
||||||
import Data.Functor.Alt (Alt (..))
|
import Data.Functor.Alt (Alt (..))
|
||||||
import Data.Maybe (maybeToList)
|
import Data.Maybe (maybeToList)
|
||||||
import Data.Monoid ((<>))
|
import Data.Monoid ((<>))
|
||||||
|
@ -34,6 +36,7 @@ import Data.Proxy (Proxy (..))
|
||||||
import Data.Sequence (fromList)
|
import Data.Sequence (fromList)
|
||||||
import Data.String (fromString)
|
import Data.String (fromString)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
import Data.Time.Clock (getCurrentTime)
|
||||||
import GHC.Generics
|
import GHC.Generics
|
||||||
import Network.HTTP.Media (renderHeader)
|
import Network.HTTP.Media (renderHeader)
|
||||||
import Network.HTTP.Types (hContentType, renderQuery,
|
import Network.HTTP.Types (hContentType, renderQuery,
|
||||||
|
@ -47,6 +50,7 @@ data ClientEnv
|
||||||
= ClientEnv
|
= ClientEnv
|
||||||
{ manager :: Client.Manager
|
{ manager :: Client.Manager
|
||||||
, baseUrl :: BaseUrl
|
, baseUrl :: BaseUrl
|
||||||
|
, cookieJar :: Maybe (TVar Client.CookieJar)
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | Generates a set of client functions for an API.
|
-- | Generates a set of client functions for an API.
|
||||||
|
@ -104,12 +108,30 @@ performRequest :: Request -> ClientM Response
|
||||||
performRequest req = do
|
performRequest req = do
|
||||||
m <- asks manager
|
m <- asks manager
|
||||||
burl <- asks baseUrl
|
burl <- asks baseUrl
|
||||||
let request = requestToClientRequest burl req
|
cookieJar' <- asks cookieJar
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
request <- let clientRequest = requestToClientRequest burl req in
|
||||||
|
case cookieJar' of
|
||||||
|
Nothing -> pure clientRequest
|
||||||
|
Just cj -> liftIO . atomically $ do
|
||||||
|
oldCookieJar <- readTVar cj
|
||||||
|
let (newRequest, newCookieJar) =
|
||||||
|
Client.insertCookiesIntoRequest
|
||||||
|
(requestToClientRequest burl req)
|
||||||
|
oldCookieJar
|
||||||
|
now
|
||||||
|
writeTVar cj newCookieJar
|
||||||
|
pure newRequest
|
||||||
|
|
||||||
eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request m
|
eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request m
|
||||||
case eResponse of
|
case eResponse of
|
||||||
Left err -> throwError $ err
|
Left err -> throwError $ err
|
||||||
Right response -> do
|
Right response -> do
|
||||||
|
now' <- liftIO getCurrentTime
|
||||||
|
traverse_
|
||||||
|
(liftIO . atomically . flip modifyTVar'
|
||||||
|
(fst . Client.updateCookieJar response request now'))
|
||||||
|
cookieJar'
|
||||||
let status = Client.responseStatus response
|
let status = Client.responseStatus response
|
||||||
status_code = statusCode status
|
status_code = statusCode status
|
||||||
ourResponse = clientResponseToResponse response
|
ourResponse = clientResponseToResponse response
|
||||||
|
|
|
@ -302,7 +302,7 @@ manager' :: C.Manager
|
||||||
manager' = unsafePerformIO $ C.newManager C.defaultManagerSettings
|
manager' = unsafePerformIO $ C.newManager C.defaultManagerSettings
|
||||||
|
|
||||||
runClient :: ClientM a -> BaseUrl -> IO (Either ServantError a)
|
runClient :: ClientM a -> BaseUrl -> IO (Either ServantError a)
|
||||||
runClient x baseUrl' = runClientM x (ClientEnv manager' baseUrl')
|
runClient x baseUrl' = runClientM x (ClientEnv manager' baseUrl' Nothing)
|
||||||
|
|
||||||
sucessSpec :: Spec
|
sucessSpec :: Spec
|
||||||
sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
|
sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
|
||||||
|
|
|
@ -92,7 +92,7 @@ manager' :: C.Manager
|
||||||
manager' = unsafePerformIO $ C.newManager C.defaultManagerSettings
|
manager' = unsafePerformIO $ C.newManager C.defaultManagerSettings
|
||||||
|
|
||||||
runClient :: ClientM a -> BaseUrl -> IO (Either ServantError a)
|
runClient :: ClientM a -> BaseUrl -> IO (Either ServantError a)
|
||||||
runClient x baseUrl' = runClientM x (ClientEnv manager' baseUrl')
|
runClient x baseUrl' = runClientM x (ClientEnv manager' baseUrl' Nothing)
|
||||||
|
|
||||||
runResultStream :: ResultStream a -> IO (Maybe (Either String a), Maybe (Either String a), Maybe (Either String a), Maybe (Either String a))
|
runResultStream :: ResultStream a -> IO (Maybe (Either String a), Maybe (Either String a), Maybe (Either String a), Maybe (Either String a))
|
||||||
runResultStream (ResultStream k) = k $ \act -> (,,,) <$> act <*> act <*> act <*> act
|
runResultStream (ResultStream k) = k $ \act -> (,,,) <$> act <*> act <*> act <*> act
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
servant
|
servant
|
||||||
servant-server
|
servant-server
|
||||||
servant-client
|
servant-client
|
||||||
|
servant-client-core
|
||||||
servant-docs
|
servant-docs
|
||||||
servant-foreign
|
servant-foreign
|
||||||
|
|
Loading…
Reference in a new issue