on master help sign in
Merge pull request #959 from jvanbruegge/fix-stream
a66aa8aOleg Grenrus
doc/tutorial/ApiType.lhs+1−1
@@ -137,7 +137,7 @@ type StreamGet = Stream 'GET
137type StreamPost = Stream 'POST
137type StreamPost = Stream 'POST
138```
138```
139
139
140−These describe endpoints that return a stream of values rather than just a single value. They not only take a single content type as a parameter, but also a framing strategy -- this specifies how the individual results are delineated from one another in the stream. The two standard strategies given with Servant are `NewlineFraming` and `NetstringFraming`, but others can be written to match other protocols.
140+These describe endpoints that return a stream of values rather than just a single value. They not only take a single content type as a parameter, but also a framing strategy -- this specifies how the individual results are delineated from one another in the stream. The three standard strategies given with Servant are `NewlineFraming`, `NetstringFraming` and `NoFraming`, but others can be written to match other protocols.
141
141
142
142
143### `Capture`
143### `Capture`
servant-client/test/Servant/StreamSpec.hs+3−2
@@ -40,7 +40,8 @@ import Test.QuickCheck
40import Servant.API ((:<|>) ((:<|>)), (:>), JSON,
40import Servant.API ((:<|>) ((:<|>)), (:>), JSON,
41 NetstringFraming, NewlineFraming,
41 NetstringFraming, NewlineFraming,
42 OctetStream, ResultStream (..),
42 OctetStream, ResultStream (..),
43− StreamGenerator (..), StreamGet)
43+ StreamGenerator (..), StreamGet,
44+ NoFraming)
44import Servant.Client
45import Servant.Client
45import Servant.ClientSpec (Person (..))
46import Servant.ClientSpec (Person (..))
46import qualified Servant.ClientSpec as CS
47import qualified Servant.ClientSpec as CS
@@ -59,7 +60,7 @@ spec = describe "Servant.Stream" $ do
59type StreamApi f =
60type StreamApi f =
60 "streamGetNewline" :> StreamGet NewlineFraming JSON (f Person)
61 "streamGetNewline" :> StreamGet NewlineFraming JSON (f Person)
61 :<|> "streamGetNetstring" :> StreamGet NetstringFraming JSON (f Person)
62 :<|> "streamGetNetstring" :> StreamGet NetstringFraming JSON (f Person)
62− :<|> "streamALot" :> StreamGet NewlineFraming OctetStream (f BS.ByteString)
63+ :<|> "streamALot" :> StreamGet NoFraming OctetStream (f BS.ByteString)
63
64
64
65
65capi :: Proxy (StreamApi ResultStream)
66capi :: Proxy (StreamApi ResultStream)
servant-server/src/Servant/Server/Internal.hs+10−10
@@ -284,10 +284,10 @@ instance OVERLAPPING_
284
284
285instance OVERLAPPABLE_
285instance OVERLAPPABLE_
286 ( MimeRender ctype a, ReflectMethod method,
286 ( MimeRender ctype a, ReflectMethod method,
287− FramingRender framing ctype, ToStreamGenerator f a
287+ FramingRender framing ctype, ToStreamGenerator b a
288− ) => HasServer (Stream method framing ctype (f a)) context where
288+ ) => HasServer (Stream method framing ctype b) context where
289
289
290− type ServerT (Stream method framing ctype (f a)) m = m (f a)
290+ type ServerT (Stream method framing ctype b) m = m b
291 hoistServerWithContext _ _ nt s = nt s
291 hoistServerWithContext _ _ nt s = nt s
292
292
293 route Proxy _ = streamRouter ([],) method (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
293 route Proxy _ = streamRouter ([],) method (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
@@ -295,23 +295,23 @@ instance OVERLAPPABLE_
295
295
296instance OVERLAPPING_
296instance OVERLAPPING_
297 ( MimeRender ctype a, ReflectMethod method,
297 ( MimeRender ctype a, ReflectMethod method,
298− FramingRender framing ctype, ToStreamGenerator f a,
298+ FramingRender framing ctype, ToStreamGenerator b a,
299− GetHeaders (Headers h (f a))
299+ GetHeaders (Headers h b)
300− ) => HasServer (Stream method framing ctype (Headers h (f a))) context where
300+ ) => HasServer (Stream method framing ctype (Headers h b)) context where
301
301
302− type ServerT (Stream method framing ctype (Headers h (f a))) m = m (Headers h (f a))
302+ type ServerT (Stream method framing ctype (Headers h b)) m = m (Headers h b)
303 hoistServerWithContext _ _ nt s = nt s
303 hoistServerWithContext _ _ nt s = nt s
304
304
305 route Proxy _ = streamRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
305 route Proxy _ = streamRouter (\x -> (getHeaders x, getResponse x)) method (Proxy :: Proxy framing) (Proxy :: Proxy ctype)
306 where method = reflectMethod (Proxy :: Proxy method)
306 where method = reflectMethod (Proxy :: Proxy method)
307
307
308
308
309−streamRouter :: (MimeRender ctype a, FramingRender framing ctype, ToStreamGenerator f a) =>
309+streamRouter :: (MimeRender ctype a, FramingRender framing ctype, ToStreamGenerator b a) =>
310− (b -> ([(HeaderName, B.ByteString)], f a))
310+ (c -> ([(HeaderName, B.ByteString)], b))
311 -> Method
311 -> Method
312 -> Proxy framing
312 -> Proxy framing
313 -> Proxy ctype
313 -> Proxy ctype
314− -> Delayed env (Handler b)
314+ -> Delayed env (Handler c)
315 -> Router env
315 -> Router env
316streamRouter splitHeaders method framingproxy ctypeproxy action = leafRouter $ \env request respond ->
316streamRouter splitHeaders method framingproxy ctypeproxy action = leafRouter $ \env request respond ->
317 let accH = fromMaybe ct_wildcard $ lookup hAccept $ requestHeaders request
317 let accH = fromMaybe ct_wildcard $ lookup hAccept $ requestHeaders request
servant/src/Servant/API.hs+2−2
@@ -117,8 +117,8 @@ import Servant.API.Stream
117 (BoundaryStrategy (..), BuildFromStream (..),
117 (BoundaryStrategy (..), BuildFromStream (..),
118 ByteStringParser (..), FramingRender (..),
118 ByteStringParser (..), FramingRender (..),
119 FramingUnrender (..), NetstringFraming, NewlineFraming,
119 FramingUnrender (..), NetstringFraming, NewlineFraming,
120− ResultStream (..), Stream, StreamGenerator (..), StreamGet,
120+ NoFraming, ResultStream (..), Stream, StreamGenerator (..),
121− StreamPost, ToStreamGenerator (..))
121+ StreamGet, StreamPost, ToStreamGenerator (..))
122import Servant.API.Sub
122import Servant.API.Sub
123 ((:>))
123 ((:>))
124import Servant.API.Vault
124import Servant.API.Vault
servant/src/Servant/API/Stream.hs+17−4
@@ -2,6 +2,7 @@
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE DeriveGeneric #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE KindSignatures #-}
6{-# LANGUAGE KindSignatures #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE OverloadedStrings #-}
8{-# LANGUAGE OverloadedStrings #-}
@@ -38,13 +39,13 @@ type StreamGet = Stream 'GET
38type StreamPost = Stream 'POST
39type StreamPost = Stream 'POST
39
40
40-- | Stream endpoints may be implemented as producing a @StreamGenerator@ -- a function that itself takes two emit functions -- the first to be used on the first value the stream emits, and the second to be used on all subsequent values (to allow interspersed framing strategies such as comma separation).
41-- | Stream endpoints may be implemented as producing a @StreamGenerator@ -- a function that itself takes two emit functions -- the first to be used on the first value the stream emits, and the second to be used on all subsequent values (to allow interspersed framing strategies such as comma separation).
41−newtype StreamGenerator a = StreamGenerator {getStreamGenerator :: (a -> IO ()) -> (a -> IO ()) -> IO ()}
42+newtype StreamGenerator a = StreamGenerator {getStreamGenerator :: (a -> IO ()) -> (a -> IO ()) -> IO ()}
42
43
43-- | ToStreamGenerator is intended to be implemented for types such as Conduit, Pipe, etc. By implementing this class, all such streaming abstractions can be used directly as endpoints.
44-- | ToStreamGenerator is intended to be implemented for types such as Conduit, Pipe, etc. By implementing this class, all such streaming abstractions can be used directly as endpoints.
44−class ToStreamGenerator f a where
45+class ToStreamGenerator a b | a -> b where
45− toStreamGenerator :: f a -> StreamGenerator a
46+ toStreamGenerator :: a -> StreamGenerator b
46
47
47−instance ToStreamGenerator StreamGenerator a
48+instance ToStreamGenerator (StreamGenerator a) a
48 where toStreamGenerator x = x
49 where toStreamGenerator x = x
49
50
50-- | Clients reading from streaming endpoints can be implemented as producing a @ResultStream@ that captures the setup, takedown, and incremental logic for a read, being an IO continuation that takes a producer of Just either values or errors that terminates with a Nothing.
51-- | Clients reading from streaming endpoints can be implemented as producing a @ResultStream@ that captures the setup, takedown, and incremental logic for a read, being an IO continuation that takes a producer of Just either values or errors that terminates with a Nothing.
@@ -80,6 +81,18 @@ data ByteStringParser a = ByteStringParser {
80class FramingUnrender strategy a where
81class FramingUnrender strategy a where
81 unrenderFrames :: Proxy strategy -> Proxy a -> ByteStringParser (ByteStringParser (Either String ByteString))
82 unrenderFrames :: Proxy strategy -> Proxy a -> ByteStringParser (ByteStringParser (Either String ByteString))
82
83
84+-- | A framing strategy that does not do any framing at all, it just passes the input data
85+-- This will be used most of the time with binary data, such as files
86+data NoFraming
87+
88+instance FramingRender NoFraming a where
89+ header _ _ = empty
90+ boundary _ _ = BoundaryStrategyGeneral id
91+ trailer _ _ = empty
92+
93+instance FramingUnrender NoFraming a where
94+ unrenderFrames _ _ = ByteStringParser (Just . (go,)) (go,)
95+ where go = ByteStringParser (Just . (, empty) . Right) ((, empty) . Right)
83
96
84-- | A simple framing strategy that has no header or termination, and inserts a newline character between each frame.
97-- | A simple framing strategy that has no header or termination, and inserts a newline character between each frame.
85-- This assumes that it is used with a Content-Type that encodes without newlines (e.g. JSON).
98-- This assumes that it is used with a Content-Type that encodes without newlines (e.g. JSON).