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...