module Network.Compat where
import Network.Socket
import Network.BSD (getProtocolNumber)
import System.IO (Handle, IOMode (..))
import qualified Control.Exception as Exception
connectTo :: String
-> PortNumber
-> IO Handle
connectTo :: String -> PortNumber -> IO Handle
connectTo String
host PortNumber
port = do
ProtocolNumber
proto <- String -> IO ProtocolNumber
getProtocolNumber String
"tcp"
let hints :: AddrInfo
hints = AddrInfo
defaultHints { addrFlags :: [AddrInfoFlag]
addrFlags = [AddrInfoFlag
AI_ADDRCONFIG]
, addrProtocol :: ProtocolNumber
addrProtocol = ProtocolNumber
proto
, addrSocketType :: SocketType
addrSocketType = SocketType
Stream }
[AddrInfo]
addrs <- Maybe AddrInfo -> Maybe String -> Maybe String -> IO [AddrInfo]
getAddrInfo (forall a. a -> Maybe a
Just AddrInfo
hints) (forall a. a -> Maybe a
Just String
host) (forall a. a -> Maybe a
Just String
serv)
forall a. String -> [IO a] -> IO a
firstSuccessful String
"connectTo" forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map AddrInfo -> IO Handle
tryToConnect [AddrInfo]
addrs
where
serv :: String
serv = forall a. Show a => a -> String
show PortNumber
port
tryToConnect :: AddrInfo -> IO Handle
tryToConnect AddrInfo
addr =
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
Exception.bracketOnError
(Family -> SocketType -> ProtocolNumber -> IO Socket
socket (AddrInfo -> Family
addrFamily AddrInfo
addr) (AddrInfo -> SocketType
addrSocketType AddrInfo
addr) (AddrInfo -> ProtocolNumber
addrProtocol AddrInfo
addr))
Socket -> IO ()
close
(\Socket
sock -> do
Socket -> SockAddr -> IO ()
connect Socket
sock (AddrInfo -> SockAddr
addrAddress AddrInfo
addr)
Socket -> IOMode -> IO Handle
socketToHandle Socket
sock IOMode
ReadWriteMode
)
firstSuccessful :: String -> [IO a] -> IO a
firstSuccessful :: forall a. String -> [IO a] -> IO a
firstSuccessful String
caller = forall {b}. Maybe IOException -> [IO b] -> IO b
go forall a. Maybe a
Nothing
where
go :: Maybe IOException -> [IO b] -> IO b
go Maybe IOException
_ (IO b
p:[IO b]
ps) =
do Either IOException b
r <- forall a. IO a -> IO (Either IOException a)
tryIO IO b
p
case Either IOException b
r of
Right b
x -> forall (m :: * -> *) a. Monad m => a -> m a
return b
x
Left IOException
e -> Maybe IOException -> [IO b] -> IO b
go (forall a. a -> Maybe a
Just IOException
e) [IO b]
ps
go Maybe IOException
Nothing [] = forall a. IOException -> IO a
ioError forall a b. (a -> b) -> a -> b
$ String -> IOException
userError forall a b. (a -> b) -> a -> b
$ String
caller forall a. [a] -> [a] -> [a]
++ String
": firstSuccessful: empty list"
go (Just IOException
e) [] = forall e a. Exception e => e -> IO a
Exception.throwIO IOException
e
tryIO :: IO a -> IO (Either Exception.IOException a)
tryIO :: forall a. IO a -> IO (Either IOException a)
tryIO IO a
m = forall e a. Exception e => IO a -> (e -> IO a) -> IO a
Exception.catch (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. b -> Either a b
Right IO a
m) (forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left)