将stdin流式传输到Wai.EventSource

时间:2016-05-07 19:28:34

标签: haskell haskell-wai haskell-warp

我想使用text/event-stream通过HTTP连接传输stdin。 Network.Wai.EventSource看起来像是一个很好的候选人。

我尝试使用此代码:

import Network.Wai
import Network.Wai.EventSource
import Network.Wai.Middleware.AddHeaders
import Network.Wai.Handler.Warp (run)
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Char8 as C
import Blaze.ByteString.Builder.ByteString

toEvent :: [L.ByteString] -> ServerEvent
toEvent s = ServerEvent {
    eventName = Nothing,
    eventId = Nothing,
    eventData = map fromLazyByteString s
}

createWaiApp :: IO L.ByteString -> Application
createWaiApp input = eventSourceAppIO $ fmap (toEvent . C.lines) input

main :: IO ()
main = run 1337 $ createWaiApp L.getContents

我认为:

  • 将stdin读为Lazy ByteStream
  • 将ByteStream拆分为行
  • 为所有行生成一个ServerEvent(这感觉不对 - 应该是多个事件?)
  • IO ServerEvent
  • 构建WAI应用程序
  • 将应用程序绑定到端口1337

当我运行它时(例如使用ping -c 5 example.com | stack exec test-exe),直到读完整个stdin才会响应。

如何构建一个Wai应用程序,每次从stdin读取时都会刷新HTTP连接?

1 个答案:

答案 0 :(得分:1)

L.getContents是一个单独的IO操作,因此只会创建一个事件。

以下是eventSourcEventAppIO的示例,其中创建了多个事件:

import Blaze.ByteString.Builder.Char8 (fromString)
...same imports as above...

nextEvent :: IO ServerEvent
nextEvent = do
  s <- getLine
  let event = if s == ""
                then CloseEvent
                else ServerEvent
                     { eventName = Nothing
                     , eventId = Nothing
                     , eventData = [ fromString s ]
                     }
  case event of
    CloseEvent ->     putStrLn "<close event>"
    ServerEvent _ _ _ -> putStrLn "<server event>"
  return event

main :: IO ()
main = run 1337 $ eventSourceAppIO nextEvent

要测试,请在一个窗口中启动服务器,然后在另一个窗口中运行命令curl -v http://localhost:1337。对于您在服务器窗口中输入的每一行,您将从curl获得一个数据框。输入一个空行将关闭HTTP连接,但服务器将继续运行,允许您再次连接到它。