content: module Test.Sandwich.Contexts.Kubernetes.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
  