{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Yesod.WebSockets
(
WebSocketsT
, webSockets
, webSocketsWith
, webSocketsOptions
, webSocketsOptionsWith
, receiveData
, receiveDataE
, receiveDataMessageE
, sendPing
, sendPingE
, sendClose
, sendCloseE
, sendTextData
, sendTextDataE
, sendBinaryData
, sendBinaryDataE
, sendDataMessageE
, sourceWS
, sinkWSText
, sinkWSBinary
, race
, race_
, concurrently
, concurrently_
, WS.defaultConnectionOptions
, WS.ConnectionOptions (..)
) where
import Control.Monad (forever, when)
import Control.Monad.Reader (ReaderT, runReaderT, MonadReader, ask)
import Conduit
import qualified Network.Wai.Handler.WebSockets as WaiWS
import qualified Network.WebSockets as WS
import qualified Yesod.Core as Y
import UnliftIO (SomeException, tryAny, MonadIO, liftIO, MonadUnliftIO, withRunInIO, race, race_, concurrently, concurrently_)
type WebSocketsT = ReaderT WS.Connection
webSockets
:: (MonadUnliftIO m, Y.MonadHandler m)
=> WebSocketsT m ()
-> m ()
webSockets :: WebSocketsT m () -> m ()
webSockets = ConnectionOptions -> WebSocketsT m () -> m ()
forall (m :: * -> *).
(MonadUnliftIO m, MonadHandler m) =>
ConnectionOptions -> WebSocketsT m () -> m ()
webSocketsOptions ConnectionOptions
WS.defaultConnectionOptions
webSocketsOptions
:: (MonadUnliftIO m, Y.MonadHandler m)
=> WS.ConnectionOptions
-> WebSocketsT m ()
-> m ()
webSocketsOptions :: ConnectionOptions -> WebSocketsT m () -> m ()
webSocketsOptions ConnectionOptions
opts = ConnectionOptions
-> (RequestHead -> m (Maybe AcceptRequest))
-> WebSocketsT m ()
-> m ()
forall (m :: * -> *).
(MonadUnliftIO m, MonadHandler m) =>
ConnectionOptions
-> (RequestHead -> m (Maybe AcceptRequest))
-> WebSocketsT m ()
-> m ()
webSocketsOptionsWith ConnectionOptions
opts ((RequestHead -> m (Maybe AcceptRequest))
-> WebSocketsT m () -> m ())
-> (RequestHead -> m (Maybe AcceptRequest))
-> WebSocketsT m ()
-> m ()
forall a b. (a -> b) -> a -> b
$ m (Maybe AcceptRequest) -> RequestHead -> m (Maybe AcceptRequest)
forall a b. a -> b -> a
const (m (Maybe AcceptRequest) -> RequestHead -> m (Maybe AcceptRequest))
-> m (Maybe AcceptRequest)
-> RequestHead
-> m (Maybe AcceptRequest)
forall a b. (a -> b) -> a -> b
$ Maybe AcceptRequest -> m (Maybe AcceptRequest)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe AcceptRequest -> m (Maybe AcceptRequest))
-> Maybe AcceptRequest -> m (Maybe AcceptRequest)
forall a b. (a -> b) -> a -> b
$ AcceptRequest -> Maybe AcceptRequest
forall a. a -> Maybe a
Just (AcceptRequest -> Maybe AcceptRequest)
-> AcceptRequest -> Maybe AcceptRequest
forall a b. (a -> b) -> a -> b
$ Maybe ByteString -> Headers -> AcceptRequest
WS.AcceptRequest Maybe ByteString
forall a. Maybe a
Nothing []
webSocketsWith :: (MonadUnliftIO m, Y.MonadHandler m)
=> (WS.RequestHead -> m (Maybe WS.AcceptRequest))
-> WebSocketsT m ()
-> m ()
webSocketsWith :: (RequestHead -> m (Maybe AcceptRequest))
-> WebSocketsT m () -> m ()
webSocketsWith = ConnectionOptions
-> (RequestHead -> m (Maybe AcceptRequest))
-> WebSocketsT m ()
-> m ()
forall (m :: * -> *).
(MonadUnliftIO m, MonadHandler m) =>
ConnectionOptions
-> (RequestHead -> m (Maybe AcceptRequest))
-> WebSocketsT m ()
-> m ()
webSocketsOptionsWith ConnectionOptions
WS.defaultConnectionOptions
webSocketsOptionsWith :: (MonadUnliftIO m, Y.MonadHandler m)
=> WS.ConnectionOptions
-> (WS.RequestHead -> m (Maybe WS.AcceptRequest))
-> WebSocketsT m ()
-> m ()
webSocketsOptionsWith :: ConnectionOptions
-> (RequestHead -> m (Maybe AcceptRequest))
-> WebSocketsT m ()
-> m ()
webSocketsOptionsWith ConnectionOptions
wsConnOpts RequestHead -> m (Maybe AcceptRequest)
buildAr WebSocketsT m ()
inner = do
Request
req <- m Request
forall (m :: * -> *). MonadHandler m => m Request
Y.waiRequest
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Request -> Bool
WaiWS.isWebSocketsReq Request
req) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ do
let rhead :: RequestHead
rhead = Request -> RequestHead
WaiWS.getRequestHead Request
req
Maybe AcceptRequest
mar <- RequestHead -> m (Maybe AcceptRequest)
buildAr RequestHead
rhead
case Maybe AcceptRequest
mar of
Maybe AcceptRequest
Nothing -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just AcceptRequest
ar ->
(IO ByteString -> (ByteString -> IO ()) -> m ()) -> m ()
forall (m :: * -> *) a.
(MonadHandler m, MonadUnliftIO m) =>
(IO ByteString -> (ByteString -> IO ()) -> m ()) -> m a
Y.sendRawResponseNoConduit
((IO ByteString -> (ByteString -> IO ()) -> m ()) -> m ())
-> (IO ByteString -> (ByteString -> IO ()) -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \IO ByteString
src ByteString -> IO ()
sink -> ((forall a. m a -> IO a) -> IO ()) -> m ()
forall (m :: * -> *) b.
MonadUnliftIO m =>
((forall a. m a -> IO a) -> IO b) -> m b
withRunInIO (((forall a. m a -> IO a) -> IO ()) -> m ())
-> ((forall a. m a -> IO a) -> IO ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \forall a. m a -> IO a
runInIO -> ConnectionOptions
-> RequestHead
-> (PendingConnection -> IO ())
-> IO ByteString
-> (ByteString -> IO ())
-> IO ()
forall a.
ConnectionOptions
-> RequestHead
-> (PendingConnection -> IO a)
-> IO ByteString
-> (ByteString -> IO ())
-> IO a
WaiWS.runWebSockets
ConnectionOptions
wsConnOpts
RequestHead
rhead
(\PendingConnection
pconn -> do
Connection
conn <- PendingConnection -> AcceptRequest -> IO Connection
WS.acceptRequestWith PendingConnection
pconn AcceptRequest
ar
let app :: IO ()
app = m () -> IO ()
forall a. m a -> IO a
runInIO (m () -> IO ()) -> m () -> IO ()
forall a b. (a -> b) -> a -> b
$ WebSocketsT m () -> Connection -> m ()
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT WebSocketsT m ()
inner Connection
conn
#if MIN_VERSION_websockets(0,12,6)
Connection -> Int -> IO () -> IO () -> IO ()
forall a. Connection -> Int -> IO () -> IO a -> IO a
WS.withPingThread Connection
conn Int
30 (() -> IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ IO ()
app
#else
WS.forkPingThread conn 30
app
#endif
)
IO ByteString
src
ByteString -> IO ()
sink
wrapWSE :: (MonadIO m, MonadReader WS.Connection m)
=> (WS.Connection -> a -> IO ())
-> a
-> m (Either SomeException ())
wrapWSE :: (Connection -> a -> IO ()) -> a -> m (Either SomeException ())
wrapWSE Connection -> a -> IO ()
ws a
x = do
Connection
conn <- m Connection
forall r (m :: * -> *). MonadReader r m => m r
ask
IO (Either SomeException ()) -> m (Either SomeException ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either SomeException ()) -> m (Either SomeException ()))
-> IO (Either SomeException ()) -> m (Either SomeException ())
forall a b. (a -> b) -> a -> b
$ IO () -> IO (Either SomeException ())
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> m (Either SomeException a)
tryAny (IO () -> IO (Either SomeException ()))
-> IO () -> IO (Either SomeException ())
forall a b. (a -> b) -> a -> b
$ Connection -> a -> IO ()
ws Connection
conn a
x
wrapWS :: (MonadIO m, MonadReader WS.Connection m)
=> (WS.Connection -> a -> IO ())
-> a
-> m ()
wrapWS :: (Connection -> a -> IO ()) -> a -> m ()
wrapWS Connection -> a -> IO ()
ws a
x = do
Connection
conn <- m Connection
forall r (m :: * -> *). MonadReader r m => m r
ask
IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ Connection -> a -> IO ()
ws Connection
conn a
x
receiveData
:: (MonadIO m, MonadReader WS.Connection m, WS.WebSocketsData a)
=> m a
receiveData :: m a
receiveData = do
Connection
conn <- m Connection
forall r (m :: * -> *). MonadReader r m => m r
ask
IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO a -> m a) -> IO a -> m a
forall a b. (a -> b) -> a -> b
$ Connection -> IO a
forall a. WebSocketsData a => Connection -> IO a
WS.receiveData Connection
conn
receiveDataE
:: (MonadIO m, MonadReader WS.Connection m, WS.WebSocketsData a)
=> m (Either SomeException a)
receiveDataE :: m (Either SomeException a)
receiveDataE = do
Connection
conn <- m Connection
forall r (m :: * -> *). MonadReader r m => m r
ask
IO (Either SomeException a) -> m (Either SomeException a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either SomeException a) -> m (Either SomeException a))
-> IO (Either SomeException a) -> m (Either SomeException a)
forall a b. (a -> b) -> a -> b
$ IO a -> IO (Either SomeException a)
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> m (Either SomeException a)
tryAny (IO a -> IO (Either SomeException a))
-> IO a -> IO (Either SomeException a)
forall a b. (a -> b) -> a -> b
$ Connection -> IO a
forall a. WebSocketsData a => Connection -> IO a
WS.receiveData Connection
conn
receiveDataMessageE
:: (MonadIO m, MonadReader WS.Connection m)
=> m (Either SomeException WS.DataMessage)
receiveDataMessageE :: m (Either SomeException DataMessage)
receiveDataMessageE = do
Connection
conn <- m Connection
forall r (m :: * -> *). MonadReader r m => m r
ask
IO (Either SomeException DataMessage)
-> m (Either SomeException DataMessage)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either SomeException DataMessage)
-> m (Either SomeException DataMessage))
-> IO (Either SomeException DataMessage)
-> m (Either SomeException DataMessage)
forall a b. (a -> b) -> a -> b
$ IO DataMessage -> IO (Either SomeException DataMessage)
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> m (Either SomeException a)
tryAny (IO DataMessage -> IO (Either SomeException DataMessage))
-> IO DataMessage -> IO (Either SomeException DataMessage)
forall a b. (a -> b) -> a -> b
$ Connection -> IO DataMessage
WS.receiveDataMessage Connection
conn
sendTextData
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> a
-> m ()
sendTextData :: a -> m ()
sendTextData = (Connection -> a -> IO ()) -> a -> m ()
forall (m :: * -> *) a.
(MonadIO m, MonadReader Connection m) =>
(Connection -> a -> IO ()) -> a -> m ()
wrapWS Connection -> a -> IO ()
forall a. WebSocketsData a => Connection -> a -> IO ()
WS.sendTextData
sendTextDataE
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> a
-> m (Either SomeException ())
sendTextDataE :: a -> m (Either SomeException ())
sendTextDataE = (Connection -> a -> IO ()) -> a -> m (Either SomeException ())
forall (m :: * -> *) a.
(MonadIO m, MonadReader Connection m) =>
(Connection -> a -> IO ()) -> a -> m (Either SomeException ())
wrapWSE Connection -> a -> IO ()
forall a. WebSocketsData a => Connection -> a -> IO ()
WS.sendTextData
sendBinaryData
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> a
-> m ()
sendBinaryData :: a -> m ()
sendBinaryData = (Connection -> a -> IO ()) -> a -> m ()
forall (m :: * -> *) a.
(MonadIO m, MonadReader Connection m) =>
(Connection -> a -> IO ()) -> a -> m ()
wrapWS Connection -> a -> IO ()
forall a. WebSocketsData a => Connection -> a -> IO ()
WS.sendBinaryData
sendBinaryDataE
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> a
-> m (Either SomeException ())
sendBinaryDataE :: a -> m (Either SomeException ())
sendBinaryDataE = (Connection -> a -> IO ()) -> a -> m (Either SomeException ())
forall (m :: * -> *) a.
(MonadIO m, MonadReader Connection m) =>
(Connection -> a -> IO ()) -> a -> m (Either SomeException ())
wrapWSE Connection -> a -> IO ()
forall a. WebSocketsData a => Connection -> a -> IO ()
WS.sendBinaryData
sendPing
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> a
-> m ()
sendPing :: a -> m ()
sendPing = (Connection -> a -> IO ()) -> a -> m ()
forall (m :: * -> *) a.
(MonadIO m, MonadReader Connection m) =>
(Connection -> a -> IO ()) -> a -> m ()
wrapWS Connection -> a -> IO ()
forall a. WebSocketsData a => Connection -> a -> IO ()
WS.sendPing
sendPingE
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> a
-> m (Either SomeException ())
sendPingE :: a -> m (Either SomeException ())
sendPingE = (Connection -> a -> IO ()) -> a -> m (Either SomeException ())
forall (m :: * -> *) a.
(MonadIO m, MonadReader Connection m) =>
(Connection -> a -> IO ()) -> a -> m (Either SomeException ())
wrapWSE Connection -> a -> IO ()
forall a. WebSocketsData a => Connection -> a -> IO ()
WS.sendPing
sendDataMessageE
:: (MonadIO m, MonadReader WS.Connection m)
=> WS.DataMessage
-> m (Either SomeException ())
sendDataMessageE :: DataMessage -> m (Either SomeException ())
sendDataMessageE DataMessage
x = do
Connection
conn <- m Connection
forall r (m :: * -> *). MonadReader r m => m r
ask
IO (Either SomeException ()) -> m (Either SomeException ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either SomeException ()) -> m (Either SomeException ()))
-> IO (Either SomeException ()) -> m (Either SomeException ())
forall a b. (a -> b) -> a -> b
$ IO () -> IO (Either SomeException ())
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> m (Either SomeException a)
tryAny (IO () -> IO (Either SomeException ()))
-> IO () -> IO (Either SomeException ())
forall a b. (a -> b) -> a -> b
$ Connection -> DataMessage -> IO ()
WS.sendDataMessage Connection
conn DataMessage
x
sendClose
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> a
-> m ()
sendClose :: a -> m ()
sendClose = (Connection -> a -> IO ()) -> a -> m ()
forall (m :: * -> *) a.
(MonadIO m, MonadReader Connection m) =>
(Connection -> a -> IO ()) -> a -> m ()
wrapWS Connection -> a -> IO ()
forall a. WebSocketsData a => Connection -> a -> IO ()
WS.sendClose
sendCloseE
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> a
-> m (Either SomeException ())
sendCloseE :: a -> m (Either SomeException ())
sendCloseE = (Connection -> a -> IO ()) -> a -> m (Either SomeException ())
forall (m :: * -> *) a.
(MonadIO m, MonadReader Connection m) =>
(Connection -> a -> IO ()) -> a -> m (Either SomeException ())
wrapWSE Connection -> a -> IO ()
forall a. WebSocketsData a => Connection -> a -> IO ()
WS.sendClose
sourceWS
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> ConduitT i a m ()
sourceWS :: ConduitT i a m ()
sourceWS = ConduitT i a m () -> ConduitT i a m ()
forall (f :: * -> *) a b. Applicative f => f a -> f b
forever (ConduitT i a m () -> ConduitT i a m ())
-> ConduitT i a m () -> ConduitT i a m ()
forall a b. (a -> b) -> a -> b
$ m a -> ConduitT i a m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m a
forall (m :: * -> *) a.
(MonadIO m, MonadReader Connection m, WebSocketsData a) =>
m a
receiveData ConduitT i a m a -> (a -> ConduitT i a m ()) -> ConduitT i a m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= a -> ConduitT i a m ()
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield
sinkWSText
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> ConduitT a o m ()
sinkWSText :: ConduitT a o m ()
sinkWSText = (a -> m ()) -> ConduitT a o m ()
forall (m :: * -> *) a o.
Monad m =>
(a -> m ()) -> ConduitT a o m ()
mapM_C a -> m ()
forall (m :: * -> *) a.
(MonadIO m, WebSocketsData a, MonadReader Connection m) =>
a -> m ()
sendTextData
sinkWSBinary
:: (MonadIO m, WS.WebSocketsData a, MonadReader WS.Connection m)
=> ConduitT a o m ()
sinkWSBinary :: ConduitT a o m ()
sinkWSBinary = (a -> m ()) -> ConduitT a o m ()
forall (m :: * -> *) a o.
Monad m =>
(a -> m ()) -> ConduitT a o m ()
mapM_C a -> m ()
forall (m :: * -> *) a.
(MonadIO m, WebSocketsData a, MonadReader Connection m) =>
a -> m ()
sendBinaryData