on master help sign in
Add runClientM for streaming-client
0dd8ee7Oleg Grenrus
servant-client-core/servant-client-core.cabal+1
@@ -50,6 +50,7 @@ library
50 base >= 4.9 && < 4.13
50 base >= 4.9 && < 4.13
51 , bytestring >= 0.10.8.1 && < 0.11
51 , bytestring >= 0.10.8.1 && < 0.11
52 , containers >= 0.5.7.1 && < 0.7
52 , containers >= 0.5.7.1 && < 0.7
53+ , deepseq >= 1.4.2.0 && < 1.5
53 , text >= 1.2.3.0 && < 1.3
54 , text >= 1.2.3.0 && < 1.3
54 , transformers >= 0.5.2.0 && < 0.6
55 , transformers >= 0.5.2.0 && < 0.6
55 , template-haskell >= 2.11.1.0 && < 2.15
56 , template-haskell >= 2.11.1.0 && < 2.15
servant-client-core/src/Servant/Client/Core/Internal/Request.hs+29−3
@@ -15,6 +15,8 @@ module Servant.Client.Core.Internal.Request where
15import Prelude ()
15import Prelude ()
16import Prelude.Compat
16import Prelude.Compat
17
17
18+import Control.DeepSeq
19+ (NFData (..))
18import Control.Monad.Catch
20import Control.Monad.Catch
19 (Exception)
21 (Exception)
20import qualified Data.ByteString as BS
22import qualified Data.ByteString as BS
@@ -34,10 +36,10 @@ import Data.Typeable
34import GHC.Generics
36import GHC.Generics
35 (Generic)
37 (Generic)
36import Network.HTTP.Media
38import Network.HTTP.Media
37− (MediaType)
39+ (MediaType, mainType, parameters, subType)
38import Network.HTTP.Types
40import Network.HTTP.Types
39− (Header, HeaderName, HttpVersion, Method, QueryItem, Status,
41+ (Header, HeaderName, HttpVersion (..), Method, QueryItem,
40− http11, methodGet)
42+ Status (..), http11, methodGet)
41import Web.HttpApiData
43import Web.HttpApiData
42 (ToHttpApiData, toEncodedUrlPiece, toHeader)
44 (ToHttpApiData, toEncodedUrlPiece, toHeader)
43
45
@@ -59,6 +61,20 @@ data ServantError =
59
61
60instance Exception ServantError
62instance Exception ServantError
61
63
64+instance NFData ServantError where
65+ rnf (FailureResponse res) = rnf res
66+ rnf (DecodeFailure err res) = rnf err `seq` rnf res
67+ rnf (UnsupportedContentType mt' res) =
68+ mediaTypeRnf mt' `seq`
69+ rnf res
70+ where
71+ mediaTypeRnf mt =
72+ rnf (mainType mt) `seq`
73+ rnf (subType mt) `seq`
74+ rnf (parameters mt)
75+ rnf (InvalidContentTypeHeader res) = rnf res
76+ rnf (ConnectionError err) = rnf err
77+
62data RequestF a = Request
78data RequestF a = Request
63 { requestPath :: a
79 { requestPath :: a
64 , requestQueryString :: Seq.Seq QueryItem
80 , requestQueryString :: Seq.Seq QueryItem
@@ -88,6 +104,16 @@ data GenResponse a = Response
88 , responseBody :: a
104 , responseBody :: a
89 } deriving (Eq, Show, Generic, Typeable, Functor, Foldable, Traversable)
105 } deriving (Eq, Show, Generic, Typeable, Functor, Foldable, Traversable)
90
106
107+instance NFData a => NFData (GenResponse a) where
108+ rnf (Response sc hs hv body) =
109+ rnfStatus sc `seq`
110+ rnf hs `seq`
111+ rnfHttpVersion hv `seq`
112+ rnf body
113+ where
114+ rnfStatus (Status code msg) = rnf code `seq` rnf msg
115+ rnfHttpVersion (HttpVersion _ _) = () -- HttpVersion fields are strict
116+
91type Response = GenResponse LBS.ByteString
117type Response = GenResponse LBS.ByteString
92type StreamingResponse = GenResponse (IO BS.ByteString)
118type StreamingResponse = GenResponse (IO BS.ByteString)
93
119
servant-client/servant-client.cabal+1
@@ -43,6 +43,7 @@ library
43 base >= 4.9 && < 4.13
43 base >= 4.9 && < 4.13
44 , bytestring >= 0.10.8.1 && < 0.11
44 , bytestring >= 0.10.8.1 && < 0.11
45 , containers >= 0.5.7.1 && < 0.7
45 , containers >= 0.5.7.1 && < 0.7
46+ , deepseq >= 1.4.2.0 && < 1.5
46 , mtl >= 2.2.2 && < 2.3
47 , mtl >= 2.2.2 && < 2.3
47 , stm >= 2.4.5.1 && < 2.6
48 , stm >= 2.4.5.1 && < 2.6
48 , text >= 1.2.3.0 && < 1.3
49 , text >= 1.2.3.0 && < 1.3
servant-client/src/Servant/Client/Internal/HttpClient/Streaming.hs+17−1
@@ -20,8 +20,11 @@ import Prelude ()
20import Prelude.Compat
20import Prelude.Compat
21
21
22import Control.Concurrent.STM.TVar
22import Control.Concurrent.STM.TVar
23+import Control.DeepSeq
24+ (NFData, force)
23import Control.Exception
25import Control.Exception
26+ (evaluate, throwIO)
24−import Control.Monad
27+import Control.Monad ()
25import Control.Monad.Base
28import Control.Monad.Base
26 (MonadBase (..))
29 (MonadBase (..))
27import Control.Monad.Codensity
30import Control.Monad.Codensity
@@ -120,6 +123,19 @@ withClientM cm env k =
120 let Codensity f = runExceptT $ flip runReaderT env $ unClientM cm
123 let Codensity f = runExceptT $ flip runReaderT env $ unClientM cm
121 in f k
124 in f k
122
125
126+-- | A 'runClientM' variant for streaming client.
127+--
128+-- It allows using this module's 'ClientM' in a direct style.
129+-- The 'NFData' constraint however prevents using this function with genuine
130+-- streaming response types ('SourceT', 'Conduit', pipes 'Proxy' or 'Machine').
131+-- For those you have to use 'withClientM'.
132+--
133+-- /Note:/ we 'force' the result, so the likehood of accidentally leaking a
134+-- connection is smaller. Use with care.
135+--
136+runClientM :: NFData a => ClientM a -> ClientEnv -> IO (Either ServantError a)
137+runClientM cm env = withClientM cm env (evaluate . force)
138+
123performRequest :: Request -> ClientM Response
139performRequest :: Request -> ClientM Response
124performRequest req = do
140performRequest req = do
125 -- TODO: should use Client.withResponse here too
141 -- TODO: should use Client.withResponse here too
servant-client/src/Servant/Client/Streaming.hs+1
@@ -7,6 +7,7 @@ module Servant.Client.Streaming
7 ( client
7 ( client
8 , ClientM
8 , ClientM
9 , withClientM
9 , withClientM
10+ , runClientM
10 , ClientEnv(..)
11 , ClientEnv(..)
11 , mkClientEnv
12 , mkClientEnv
12 , hoistClient
13 , hoistClient