@@ -1,3 +1,4 @@
1+{-# LANGUAGE DataKinds #-}
1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
@@ -17,11 +18,15 @@ import Data.Proxy (Proxy)
17import Data.Typeable (Typeable)
18import Data.Typeable (Typeable)
18import GHC.Generics (Generic)
19import GHC.Generics (Generic)
19import Text.Read (readMaybe)
20import Text.Read (readMaybe)
21+import Network.HTTP.Types.Method (StdMethod (..))
21−-- | A stream endpoint for a given method emits a stream of encoded values at a given Content-Type, delimited by a framing strategy.
23+-- | A Stream endpoint for a given method emits a stream of encoded values at a given Content-Type, delimited by a framing strategy. Steam endpoints always return response code 200 on success. Type synonyms are provided for standard methods.
22data Stream (method :: k1) (framing :: *) (contentType :: *) a
24data Stream (method :: k1) (framing :: *) (contentType :: *) a
23 deriving (Typeable, Generic)
25 deriving (Typeable, Generic)
27+type StreamGet = Stream 'GET
28+type StreamPost = Stream 'POST
25-- | 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).
30-- | 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).
26newtype StreamGenerator a = StreamGenerator {getStreamGenerator :: (a -> IO ()) -> (a -> IO ()) -> IO ()}
31newtype StreamGenerator a = StreamGenerator {getStreamGenerator :: (a -> IO ()) -> (a -> IO ()) -> IO ()}
@@ -32,11 +37,18 @@ class ToStreamGenerator f a where
32instance ToStreamGenerator StreamGenerator a
37instance ToStreamGenerator StreamGenerator a
33 where toStreamGenerator x = x
38 where toStreamGenerator x = x
40+-- | BuildFromStream is intended to be implemented for types such as Conduit, Pipe, etc. By implementing this class, all such streaming abstractions can be used directly on the client side for talking to streaming endpoints. The streams we build from are represented as lazy lists of elements interspersed with possible errors.
41+class BuildFromStream a b where
42+ buildFromStream :: [Either String a] -> b
44+instance BuildFromStream a [Either String a]
45+ where buildFromStream x = x
35-- | The FramingRender class provides the logic for emitting a framing strategy. The strategy emits a header, followed by boundary-delimited data, and finally a termination character. For many strategies, some of these will just be empty bytestrings.
47-- | The FramingRender class provides the logic for emitting a framing strategy. The strategy emits a header, followed by boundary-delimited data, and finally a termination character. For many strategies, some of these will just be empty bytestrings.
36class FramingRender strategy a where
48class FramingRender strategy a where
37− header :: Proxy strategy -> Proxy a -> ByteString
49+ header :: Proxy strategy -> Proxy a -> ByteString
38− boundary :: Proxy strategy -> Proxy a -> BoundaryStrategy
50+ boundary :: Proxy strategy -> Proxy a -> BoundaryStrategy
39− terminate :: Proxy strategy -> Proxy a -> ByteString
51+ trailer :: Proxy strategy -> Proxy a -> ByteString
41-- | The bracketing strategy generates things to precede and follow the content, as with netstrings.
53-- | The bracketing strategy generates things to precede and follow the content, as with netstrings.
42-- The intersperse strategy inserts seperators between things, as with newline framing.
54-- The intersperse strategy inserts seperators between things, as with newline framing.
@@ -45,7 +57,7 @@ data BoundaryStrategy = BoundaryStrategyBracket (ByteString -> (ByteString,ByteS
45 | BoundaryStrategyIntersperse ByteString
57 | BoundaryStrategyIntersperse ByteString
46 | BoundaryStrategyGeneral (ByteString -> ByteString)
58 | BoundaryStrategyGeneral (ByteString -> ByteString)
48−-- | The FramingUnrender class provides the logic for parsing a framing strategy. Given a ByteString, it strips the header, and returns a tuple of the remainder along with a step function that can progressively "uncons" elements from this remainder. The error state is presented per-frame so that protocols that can resume after errors are able to do so.
60+-- | The FramingUnrender class provides the logic for parsing a framing strategy. Given a ByteString, it strips the header, and returns a tuple of the remainder along with a step function that can progressively "uncons" elements from this remainder. The error state is presented per-frame so that protocols that can resume after errors are able to do so. Termination of the unrendering is indicated by return of an empty reminder.
50class FramingUnrender strategy a where
62class FramingUnrender strategy a where
51 unrenderFrames :: Proxy strategy -> Proxy a -> ByteString -> (ByteString, ByteString -> (Either String ByteString, ByteString))
63 unrenderFrames :: Proxy strategy -> Proxy a -> ByteString -> (ByteString, ByteString -> (Either String ByteString, ByteString))
@@ -57,7 +69,7 @@ data NewlineFraming
57instance FramingRender NewlineFraming a where
69instance FramingRender NewlineFraming a where
58 header _ _ = empty
70 header _ _ = empty
59 boundary _ _ = BoundaryStrategyIntersperse "\n"
71 boundary _ _ = BoundaryStrategyIntersperse "\n"
60− terminate _ _ = empty
72+ trailer _ _ = empty
62instance FramingUnrender NewlineFraming a where
74instance FramingUnrender NewlineFraming a where
63 unrenderFrames _ _ = (, (Right *** LB.drop 1) . LB.break (== '\n'))
75 unrenderFrames _ _ = (, (Right *** LB.drop 1) . LB.break (== '\n'))
@@ -68,7 +80,7 @@ data NetstringFraming
68instance FramingRender NetstringFraming a where
80instance FramingRender NetstringFraming a where
69 header _ _ = empty
81 header _ _ = empty
70 boundary _ _ = BoundaryStrategyBracket $ \b -> (LB.pack . show . LB.length $ b, "")
82 boundary _ _ = BoundaryStrategyBracket $ \b -> (LB.pack . show . LB.length $ b, "")
71− terminate _ _ = empty
83+ trailer _ _ = empty
73instance FramingUnrender NetstringFraming a where
85instance FramingUnrender NetstringFraming a where
74 unrenderFrames _ _ = (, \b -> let (i,r) = LB.break (==':') b
86 unrenderFrames _ _ = (, \b -> let (i,r) = LB.break (==':') b