It would be nice to be able to use the Network.WebSockets
module from inside a snaplet, but I can't figure out how to actually do it.
Using the runWebSocketsSnap :: MonadSnap m => ServerApp -> m ()
function from Network.WebSockets.Snap
it is easy to include a simple stateless websocket server in my app:
routes :: [(ByteString, Handler App App ())]
routes = [ ("/ws", runWebSocketsSnap wsApp) ]
wsApp :: PendingConnection -> IO () -- this is the ServerApp type
wsApp pending = do
conn <- acceptRequest pending
forever $ do
msg <- receiveData conn
sendTextData conn ("Echo " `mappend` msg :: Text)
But my goal is to maintain a state for the webscket server (for example, a list of the connected clients, as in http://jaspervdj.be/websockets/example.html). Alternatively, access to the acid-state store of the snaplet would be great.
My first idea was to liftIO
the websocket actions into the Handler App App
monad, and write an app like this:
wsApp :: PendingConnection -> Handler App App ()
wsApp pending = do
conn <- liftIO $ acceptRequest pending
forever $ do
msg <- liftIO $ receiveData conn
update (SetLastMsg msg)
liftIO $ sendTextData conn ("Stored msg in datastore.")
But there is no version of runWebSocketsSnap
that takes an app of the above form, and I can't figure out how to modify the existing one (source on hackage). It seems to me one would need an alternative to forkIO
that takes an action in the Handler App App
monad instead, but my understanding of Haskell and especially concurrency in Snap ends here...