packages feed

hw-prelude-0.0.1.1: src/HaskellWorks/IO/Network/Port.hs

module HaskellWorks.IO.Network.Port
  ( randomPort,
    reserveRandomPort,
    portInUse,
  ) where

import           Control.Exception
import           Control.Monad                (Monad (..), MonadFail (..))
import           Control.Monad.IO.Class
import           Control.Monad.Trans.Resource
import           Data.Bool
import           Data.Either
import           Data.Function
import           Network.Socket

-- | Return a random available port on a specified host address
randomPort :: ()
  => MonadIO m
  => MonadFail m
  => HostAddress
  -> m PortNumber
randomPort hostAddress = do
  sock <- liftIO $ socket AF_INET Stream defaultProtocol
  -- Allow the port to be reused immediately after the socket is closed
  liftIO $ setSocketOption sock ReuseAddr 1
  liftIO $ bind sock $ SockAddrInet defaultPort hostAddress
  SockAddrInet port _ <- liftIO $ getSocketName sock
  liftIO $ close sock
  return port

reserveRandomPort :: ()
  => MonadFail m
  => MonadResource m
  => HostAddress
  -> m (ReleaseKey, PortNumber)
reserveRandomPort hostAddress = do
  sock <- liftIO $ socket AF_INET Stream defaultProtocol
  liftIO $ setSocketOption sock ReuseAddr 1
  liftIO $ bind sock $ SockAddrInet defaultPort hostAddress
  SockAddrInet port _ <- liftIO $ getSocketName sock
  releaseKey <- register $ close sock
  return (releaseKey, port)

-- | Check if a port is in use on a specified host address
portInUse :: ()
  => MonadIO m
  => HostAddress
  -> PortNumber
  -> m Bool
portInUse hostAddress pn = do
  sock <- liftIO $ socket AF_INET Stream defaultProtocol
  liftIO $ setSocketOption sock ReuseAddr 1
  result <- liftIO $ try @SomeException $ bind sock (SockAddrInet pn hostAddress)
  liftIO $ close sock
  return $ either (const False) (const True) result