packages feed

sockets-and-pipes-0.1: src/SocketsAndPipes/Serve/Loop.hs

module SocketsAndPipes.Serve.Loop ( run ) where

import SocketsAndPipes.Serve.ForkBracket
    ( forkBracket, Cleanup (..), ThreadId )

import SocketsAndPipes.Serve.Sockets
    ( PeerSocket, PassiveSocket, accept,
      closePeerSocketPolitely, closePeerSocketAbruptly )

import Control.Monad ( forever )

run ::
    (PeerSocket -> IO a) -- ^ What to do when a new client connects.
    -> PassiveSocket -- ^ A socket that is listening for connections.
    -> IO b
run server s = forever (acceptAndFork s server)
{- ^
    Perpetually awaits new connections,
    forking a new thread to handle each one.
-}

acceptAndFork ::
    PassiveSocket -- ^ A socket that is listening for connections.
    -> (PeerSocket -> IO a) -- ^ What to do when a new client connects.
    -> IO ThreadId
acceptAndFork s = forkBracket (accept s) socketForkBracketCleanup
{- ^
    Waits until a new client shows up to connect to our server.
    When a peer connects, the socket for talking to them will
    be passed to the given function.
-}

socketForkBracketCleanup :: Cleanup PeerSocket
socketForkBracketCleanup = Cleanup{onForkFail, onThreadEnd}
  where
    onThreadEnd = -- At the end of the thread:
        closePeerSocketPolitely -- Politely conclude the connection.

    onForkFail = -- If an exception occurs before the thread even starts:
        closePeerSocketAbruptly -- Just close the socket abruptly.
            -- Since this happens on the main thread, we don't
            -- want to take the time to wait for a graceful close.