packages feed

socketson-0.1.0.0: src/Network/Socketson.hs

{-# LANGUAGE OverloadedStrings #-}
module Network.Socketson where

-- data:
import Data.Aeson (ToJSON, FromJSON)
import Data.Serialize (Serialize)
import qualified Data.Text as T
-- wai, websockets, etc:
import Network.Wai.Handler.WebSockets (websocketsOr)
import Network.Wai.Handler.Warp (run)
import Network.Wai (responseLBS, Application)
import Network.WebSockets (defaultConnectionOptions)
import Network.HTTP.Types (status400)
-- intern:
import Network.Socketson.Protocol
import Network.Socketson.Report
import Network.Socketson.ServerState
-- concurent:
import Control.Concurrent.MVar
-- monads/transformers:
import Control.Monad.Trans.Either


{-| The warp-wai 'Application', based upon the socketson protocol. It is a 'ServerApp' which got lifted into a wai 'Application' with the `wai-websockets` handler. 'app' is basically 'socketsonapp' with a fall back, if a http request comes which isn't a websocket one.
-}
app :: (ToJSON sobj, FromJSON rcv, Serialize sessd)
                      => MVar ServerState
                      -> (Maybe sessd -> rcv -> EitherT String IO (Maybe sessd, Reaction sobj))
                      -> Application
app mstate pf = websocketsOr defaultConnectionOptions (socketsonapp mstate pf) backupApp
  where
    backupApp :: Application
    backupApp _ respond = respond $ responseLBS status400 [] "Not a WebSocket request"


{-| 'socketson' starts a warp-wai websockets server, based on the socketson protocol and given protocol function.

    socketson pathToSessionStore maxAllowedClients port protocolFunction
-}
socketson :: (ToJSON sobj, FromJSON rcv, Serialize sessd)
                      => FilePath
                      -- ^ path to session store
                      -> Int
                      -- ^ number of maximal connected clients
                      -> Int
                      -- ^ port where socketson listens
                      -> (Maybe sessd -> rcv -> EitherT String IO (Maybe sessd, Reaction sobj))
                      -- ^ protocol function
                      -> IO ()
socketson fp maxcl port pf =
  do st     <- newState fp maxcl
     mstate <- newMVar st
     eitherT (\e -> putStrLn $ "report failed: " ++ show e)
             (\_ -> return ())
             (report "<global>" (Info High $ "Socketson listens at ws://localhost:" `T.append` T.pack (show port)))
     run port (app mstate pf)