socketed-0.1.0.0: Network/Socketed.hs
module Network.Socketed (
SocketedOptions(..), runSocketedServer
) where
import Conduit (
ConduitM, MonadIO,
(.|), mapM_C, runConduit, sinkList
)
import Data.Conduit.TMChan (TMChan, sinkTMChan, dupTMChan, sourceTMChan)
import Control.Concurrent.Async (async)
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TMChan (newBroadcastTMChanIO)
import Data.ByteString (ByteString)
import Data.ByteString.Char8 (pack, unpack)
import Data.ByteString.Lazy (fromStrict)
import Data.Void (Void)
import Network.HTTP.Types (status200)
import Network.Wai (Application, responseLBS)
import Network.Wai.Handler.Warp (
Settings,
defaultSettings, setPort, setHost, runSettings
)
import Network.Wai.Handler.WebSockets (websocketsOr)
import Network.WebSockets (
ServerApp,
acceptRequest, defaultConnectionOptions, sendTextData
)
import Network.Socketed.Internal (
SocketedOptions(..),
stdinLines, showWSHost, takeUntil2Empty
)
import Network.Socketed.Template (evalHtml)
sinkStdinToChan :: MonadIO m => TMChan ByteString -> ConduitM a Void m ()
sinkStdinToChan = (stdinLines .|) . flip sinkTMChan False
serverSettings :: SocketedOptions -> Settings
serverSettings (SocketedOptions h p) = setHost (read hoststr)
$ setPort p defaultSettings where hoststr = "Host \"" ++ h ++ "\""
socketedApp :: [ByteString] -> ByteString -> TMChan ByteString -> Application
socketedApp rls html broadcastChan
= websocketsOr defaultConnectionOptions wsApp backupApp where
wsApp :: ServerApp
wsApp pendingConn = do
conn <- acceptRequest pendingConn
chan <- atomically $ dupTMChan broadcastChan
_ <- mapM (sendTextData conn) rls -- send replayed lines
runConduit $ sourceTMChan chan .| mapM_C (sendTextData conn)
backupApp :: Application
backupApp _ respond = respond
$ responseLBS status200 [] (fromStrict html)
runSocketedServer :: SocketedOptions -> IO ()
runSocketedServer opts@(SocketedOptions h p) = do
putStrLn "Accepting replayed data: "
rls <- runConduit $ stdinLines .| takeUntil2Empty .| sinkList
putStrLn "Replayed data: "
mapM_ print rls
putStrLn $ "\nStart streaming @ " ++ showWSHost h p
chan <- newBroadcastTMChanIO
mirror <- atomically $ dupTMChan chan
_ <- async . runConduit $ sinkStdinToChan chan
_ <- async . runConduit $ sourceTMChan mirror .| mapM_C (putStrLn . unpack)
let app = socketedApp rls (pack $ evalHtml (length rls) opts) chan
runSettings (serverSettings opts) app