module Axial.Pong ( PongConfig(..) , defaultPongConfig , withPongServer ) where import Network (withSocketsDo, listenOn, accept, sClose, PortID(..)) import System.IO (hClose, hPutStr, hSetBuffering, BufferMode(..)) import Control.Exception (bracket, finally) import Control.Concurrent (forkIO, forkFinally, ThreadId, killThread) data PongConfig = PongConfig { pongPort :: Int, pongMessage :: IO String } newtype PongServer = PongServer ThreadId defaultPongConfig :: PongConfig defaultPongConfig = PongConfig 10411 $ pure "pong" withPongServer :: PongConfig -> IO a -> IO a withPongServer cfg action = bracket (startServer cfg) stopServer $ const action startServer :: PongConfig -> IO PongServer startServer cfg = withSocketsDo $ do socket <- listenOn portNum threadId <- forkIO $ socketHandler socket pure $ PongServer threadId where socketHandler sock = finally (socketHandlerLoop sock) (sClose sock) socketHandlerLoop sock = do (handle, _, _) <- accept sock _ <- forkFinally (body handle) (const $ hClose handle) socketHandlerLoop sock body handle = do msg <- (pongMessage cfg) hSetBuffering handle NoBuffering hPutStr handle msg portNum = PortNumber $ fromIntegral pongPortNum pongPortNum = pongPort cfg stopServer :: PongServer -> IO () stopServer (PongServer threadId) = killThread threadId