@@ -1,13 +1,18 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2module Network.Wai.EventSource (
2module Network.Wai.EventSource (
3 ServerEvent(..),
3 ServerEvent(..),
4− eventSourceApp
4+ eventSourceApp,
7import Blaze.ByteString.Builder (Builder)
10import Blaze.ByteString.Builder (Builder)
8import Control.Concurrent.Chan (Chan, dupChan, readChan)
11import Control.Concurrent.Chan (Chan, dupChan, readChan)
9import Control.Monad.IO.Class (liftIO)
12import Control.Monad.IO.Class (liftIO)
13+import Data.Conduit (($=))
10import qualified Data.Conduit as C
14import qualified Data.Conduit as C
15+import qualified Data.Conduit.List as CL
11import Network.HTTP.Types (statusOK)
16import Network.HTTP.Types (statusOK)
12import Network.Wai (Application, Response(..))
17import Network.Wai (Application, Response(..))
@@ -16,15 +21,31 @@ import Network.Wai.EventSource.EventStream
16-- | Make a new WAI EventSource application reading events from
21-- | Make a new WAI EventSource application reading events from
17-- the given channel.
22-- the given channel.
18eventSourceApp :: Chan ServerEvent -> Application
23eventSourceApp :: Chan ServerEvent -> Application
19−eventSourceApp chan _ = do
24+eventSourceApp = eventSourceAppChan
25+{-# DEPRECATED eventSourceApp "Use evnetSourceAppChan instead." #-}
27+-- | Make a new WAI EventSource application reading events from
29+eventSourceAppChan :: Chan ServerEvent -> Application
30+eventSourceAppChan chan _ = do
20 chan' <- liftIO $ dupChan chan
31 chan' <- liftIO $ dupChan chan
21− return $ res chan'
32+ return $ response chanToSource chan'
23−res :: Chan ServerEvent -> Response
34+-- | Make a new WAI EventSource application reading events from
24−res chan = ResponseSource statusOK [("Content-Type", "text/event-stream")] $ resE chan
35+-- the given source.
36+eventSourceAppSource :: C.Source IO ServerEvent -> Application
37+eventSourceAppSource src _ = return $ response sourceToSource src
39+-- | Make a new WAI EventSource application reading events from
40+-- the given IO action.
41+eventSourceAppIO :: IO ServerEvent -> Application
42+eventSourceAppIO act _ = return $ response ioToSource act
44+response :: (a -> C.Source IO (C.Flush Builder)) -> a -> Response
45+response f a = ResponseSource statusOK [("Content-Type", "text/event-stream")] $ f a
26−resE :: Chan ServerEvent -> C.Source IO (C.Flush Builder)
47+chanToSource :: Chan ServerEvent -> C.Source IO (C.Flush Builder)
27−resE chan =
48+chanToSource chan =
28 C.sourceState Nothing pull
49 C.sourceState Nothing pull
30 pull Nothing = do
51 pull Nothing = do
@@ -33,3 +54,22 @@ resE chan =
33 Nothing -> C.StateClosed
54 Nothing -> C.StateClosed
34 Just y -> C.StateOpen (Just C.Flush) (C.Chunk y)
55 Just y -> C.StateOpen (Just C.Flush) (C.Chunk y)
35 pull (Just x) = return $ C.StateOpen Nothing x
56 pull (Just x) = return $ C.StateOpen Nothing x
58+ioToSource :: IO ServerEvent -> C.Source IO (C.Flush Builder)
60+ C.sourceState Nothing pull
64+ return $ case eventToBuilder x of
65+ Nothing -> C.StateClosed
66+ Just y -> C.StateOpen (Just C.Flush) (C.Chunk y)
67+ pull (Just x) = return $ C.StateOpen Nothing x
69+sourceToSource :: C.Source IO ServerEvent -> C.Source IO (C.Flush Builder)
70+sourceToSource src = src $= CL.concatMap eventToFlushBuilder
72+ eventToFlushBuilder event =
73+ case eventToBuilder event of
75+ Just x -> [C.Chunk x, C.Flush]