sandwich-contexts-0.3.0.0: lib/Test/Sandwich/Contexts/Util/SocketUtil.hs
module Test.Sandwich.Contexts.Util.SocketUtil (
isPortOpen
, simpleSockAddr
) where
-- Taken from
-- https://stackoverflow.com/questions/39139787/i-want-to-check-whether-or-not-a-certain-port-is-open-haskell
-- https://gist.github.com/nh2/0a1442eb71ec0405a1e3ce83a467dfde#file-socketutils-hs
import Foreign.C.Error (Errno(..), eCONNREFUSED)
import GHC.IO.Exception (IOException(..))
import Network.Socket (Family(AF_INET), PortNumber, SocketType(Stream), SockAddr(SockAddrInet), socket, connect, close', tupleToHostAddress)
import Relude
import UnliftIO.Exception
-- | Checks whether @connect()@ to a given TCPv4 `SockAddr` succeeds or
-- returns `eCONNREFUSED`.
--
-- Rethrows connection exceptions in all other cases (e.g. when the host
-- is unroutable).
isPortOpen :: SockAddr -> IO Bool
isPortOpen sockAddr = do
bracket (socket AF_INET Stream 6 {- TCP -}) close' $ \sock -> do
res <- try $ connect sock sockAddr
case res of
Right () -> return True
Left e ->
if (Errno <$> ioe_errno e) == Just eCONNREFUSED
then return False
else throwIO e
-- | Creates a `SockAttr` from host IP and port number.
--
-- Example:
-- > simpleSockAddr (127,0,0,1) 8000
simpleSockAddr :: (Word8, Word8, Word8, Word8) -> PortNumber -> SockAddr
simpleSockAddr addr port = SockAddrInet port (tupleToHostAddress addr)
-- Example usage:
-- > isPortOpen (simpleSockAddr (127,0,0,1) 8000)
-- True