Как использовать Network.WebSockets.Snap в снаплете?

Было бы неплохо иметь возможность использовать модуль Network.WebSockets внутри снимка, но я не могу понять, как это сделать на самом деле.

Используя функцию runWebSocketsSnap :: MonadSnap m => ServerApp -> m () из Network.WebSockets.Snap, легко включить в мое приложение простой сервер веб-сокетов без сохранения состояния:

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)

Но моя цель — поддерживать состояние сервера веб-сайта (например, список подключенных клиентов, как в http://jaspervdj.be/websockets/example.html). В качестве альтернативы доступ к кислотному хранилищу Snaplet был бы отличным.

Моей первой идеей было liftIO liftIO действия веб-сокета в Handler App App монаде и написать такое приложение:

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

Но не существует версии runWebSocketsSnap, которая принимает приложение вышеуказанной формы, и я не могу понять, как модифицировать существующую (источник взлома). Мне кажется, что нужна альтернатива forkIO, которая вместо этого выполняет действие в монаде Handler App App, но мое понимание Haskell и особенно параллелизма в Snap на этом заканчивается...


person dermoritz    schedule 22.03.2014    source источник


Ответы (1)


Функция runWebSocketsSnap требует, чтобы ее аргумент был типа PendingConnection -> IO (). Это означает, что вы не можете напрямую получить доступ к структуре данных вашего приложения внутри этой функции. Что вам нужно сделать, так это передать информацию функции в качестве аргумента примерно так.

routes = [ ("/ws", webSocketsDriver) ]

webSocketsDriver :: Handler App App ()
webSocketsDriver = do
    appState <- get
    runWebSocketsSnap (wsApp appState)

wsApp :: App -> PendingConnection -> IO ()
wsApp app pending = do
    ...
person mightybyte    schedule 25.03.2014
comment
Первоначально это не казалось мне состоянием. Но, я полагаю, вы можете кинуть из TVars, MVars или IORefs в приложение для управления состоянием, если вам нужно. - person Boyd Stephen Smith Jr.; 26.03.2014
comment
Я согласен с @boyd-stephen-smith-jr, и это решает мою проблему. В частности, я могу делать такие вещи, как acidState <- getAcidState, передавать их в wsApp, а затем использовать кислотное состояние оттуда: Data.Acid.update acidState (SetLastMsg msg) - person dermoritz; 26.03.2014
comment
Монада Handler по своей природе имеет состояние, потому что она делает приложение (или b и v) доступным для вас, не передавая его явно. Кроме того, если вы хотите упростить его, вы можете определить встроенную функцию wsApp (после строки get выполните let wsApp = ...), и тогда вам не нужно ничего передавать. - person mightybyte; 26.03.2014