{- |

    The 'PassiveSocket' and 'PeerSocket' newtypes
    distinguish passive sockets from peer sockets.

    This module also includes a few basic operations
    involving these types.

-}
module SocketsAndPipes.Serve.Sockets
    (

      Socket,
      PortNumber,

      PassiveSocket (..), closePassiveSocket,
                          passiveSocketAddress,

      PeerSocket    (..), closePeerSocketAbruptly,
                          closePeerSocketPolitely,

      accept

    ) where

import Network.Socket ( Socket, PortNumber )
import qualified Network.Socket as Socket

newtype PassiveSocket = PassiveSocket { PassiveSocket -> Socket
passiveSocket :: Socket }
    -- ^ A passive socket that is listening for connections.

newtype PeerSocket = PeerSocket { PeerSocket -> Socket
peerSocket :: Socket }
    -- ^ A socket that we're using to talk to a client
    --   that has connected to our server.

closePassiveSocket :: PassiveSocket -> IO ()
closePassiveSocket :: PassiveSocket -> IO ()
closePassiveSocket = Socket -> IO ()
Socket.close (Socket -> IO ())
-> (PassiveSocket -> Socket) -> PassiveSocket -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PassiveSocket -> Socket
passiveSocket

closePeerSocketAbruptly :: PeerSocket -> IO ()
closePeerSocketAbruptly :: PeerSocket -> IO ()
closePeerSocketAbruptly = Socket -> IO ()
Socket.close (Socket -> IO ()) -> (PeerSocket -> Socket) -> PeerSocket -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PeerSocket -> Socket
peerSocket

closePeerSocketPolitely :: PeerSocket -> IO ()
closePeerSocketPolitely :: PeerSocket -> IO ()
closePeerSocketPolitely PeerSocket
s =
    Socket -> Int -> IO ()
Socket.gracefulClose (PeerSocket -> Socket
peerSocket PeerSocket
s) Int
finMilliseconds
{- ^
    Closes a TCP connection by sending a FIN packet, which is more
    respectful to the peer than if we were to simply ghost them.
-}

finMilliseconds :: Int
finMilliseconds :: Int
finMilliseconds = Int
5000
{- ^
    Timeout for the 'fin' action.
    5 seconds = 5000 milliseconds
-}

accept :: PassiveSocket -> IO PeerSocket
accept :: PassiveSocket -> IO PeerSocket
accept PassiveSocket
s =
    Socket -> IO (Socket, SockAddr)
Socket.accept (PassiveSocket -> Socket
passiveSocket PassiveSocket
s) IO (Socket, SockAddr)
-> ((Socket, SockAddr) -> IO PeerSocket) -> IO PeerSocket
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
    \(Socket
s', SockAddr
_) -> PeerSocket -> IO PeerSocket
forall (m :: * -> *) a. Monad m => a -> m a
return (Socket -> PeerSocket
PeerSocket Socket
s')
{- ^
    Waits until a new client shows up to connect to our server.
    Returns the socket that we use to talk to this particular peer.
-}

passiveSocketAddress :: PassiveSocket -> IO Socket.SockAddr
passiveSocketAddress :: PassiveSocket -> IO SockAddr
passiveSocketAddress = Socket -> IO SockAddr
Socket.getSocketName (Socket -> IO SockAddr)
-> (PassiveSocket -> Socket) -> PassiveSocket -> IO SockAddr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PassiveSocket -> Socket
passiveSocket