websockets-simple-0.0.3: src/Network/WebSockets/Simple/PingPong.hs
{-# LANGUAGE
NamedFieldPuns
, FlexibleContexts
#-}
module Network.WebSockets.Simple.PingPong where
import Network.WebSockets.Simple (WebSocketsApp (..))
import Data.Aeson (ToJSON (..), FromJSON (..))
import Data.Aeson.Types (Value (Array))
import Control.Monad.Trans.Control (MonadBaseControl (..))
import Control.Concurrent.Async.Every (every, reset)
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TVar (newTVarIO, readTVar, writeTVar)
newtype PingPong a = PingPong {getPingPong :: Maybe a}
-- | Assumes @a@ isn't an 'Data.Aeson.Types.Array' of anything
instance ToJSON a => ToJSON (PingPong a) where
toJSON (PingPong Nothing) = toJSON ([] :: [()])
toJSON (PingPong (Just x)) = toJSON x
-- | Assumes @a@ isn't an 'Data.Aeson.Types.Array' of anything
instance FromJSON a => FromJSON (PingPong a) where
parseJSON (Array _) = pure (PingPong Nothing)
parseJSON x = (PingPong . Just) <$> parseJSON x
pingPong :: ( MonadBaseControl IO m
)
=> Int -- ^ Delay in microseconds
-> WebSocketsApp send receive m
-> m (WebSocketsApp (PingPong send) (PingPong receive) m)
pingPong delay WebSocketsApp{onOpen,onReceive} = do
counterVar <- liftBaseWith $ \_ -> newTVarIO Nothing
let halfDelay = delay `div` 2
pure WebSocketsApp
{ onOpen = \send -> do
liftBaseWith $ \runInBase -> do
counter <- every delay (Just halfDelay) $ runInBase $
send (PingPong Nothing)
atomically $ writeTVar counterVar (Just counter)
onOpen (send . PingPong . Just)
, onReceive = \send (PingPong mPingPong) -> do
case mPingPong of
Nothing -> liftBaseWith $ \_ -> do
mCounter <- atomically $ readTVar counterVar
case mCounter of
Nothing -> error "somehow received message before socket was opened"
Just counter -> reset (Just halfDelay) counter
Just r -> onReceive (send . PingPong . Just) r
}