module Network.Socket.Free (openFreePort, getFreePort) where import qualified Network.Socket as N import qualified Control.Exception as E import qualified System.IO.Error as Error -- | Open a TCP socket on a random free port. This is like 'warp''s -- openFreePort. -- -- Since 0.0.0.1 openFreePort :: IO (Int, N.Socket) openFreePort = E.bracketOnError (N.socket N.AF_INET N.Stream N.defaultProtocol) N.close $ \sock -> do N.bind sock $ N.SockAddrInet 0 $ N.tupleToHostAddress (127,0,0,1) N.getSocketName sock >>= \case N.SockAddrInet port _ -> pure (fromIntegral port, sock) addr -> E.throwIO $ Error.mkIOError Error.userErrorType ( "openFreePort was unable to create socket with a SockAddrInet. " <> "Got " <> show addr ) Nothing Nothing -- | Open a TCP socket, get its port and close the socket. -- Useful when you have an external service that needs a fresh port. -- -- There is a small race condition present: -- It's possible to get a free port only for it to -- be bound by some other process or thread before used -- -- Since 0.2.1 getFreePort :: IO Int getFreePort = do (port, socket) <- openFreePort N.close socket pure port