|
| 1 | +{-# LANGUAGE ScopedTypeVariables #-} |
1 | 2 | {-# LANGUAGE TypeSynonymInstances #-}
|
2 | 3 | -----------------------------------------------------------------------------
|
3 | 4 | -- |
|
@@ -61,7 +62,7 @@ import Network.Socket ( socketToHandle )
|
61 | 62 | import Data.Char ( toLower )
|
62 | 63 | import Data.Word ( Word8 )
|
63 | 64 | import Control.Concurrent
|
64 |
| -import Control.Exception ( onException ) |
| 65 | +import Control.Exception ( IOException, bracketOnError, try ) |
65 | 66 | import Control.Monad ( liftM, when )
|
66 | 67 | import System.IO ( Handle, hFlush, IOMode(..), hClose )
|
67 | 68 | import System.IO.Error ( isEOFError )
|
@@ -236,15 +237,37 @@ openTCPConnection_ uri port stashInput = do
|
236 | 237 | -- like this as it just does a once-only installation of a shutdown handler to run at program exit,
|
237 | 238 | -- rather than actually shutting down after the action
|
238 | 239 | addrinfos <- withSocketsDo $ getAddrInfo (Just $ defaultHints { addrFamily = AF_UNSPEC, addrSocketType = Stream }) (Just fixedUri) (Just . show $ port)
|
| 240 | + |
| 241 | + let |
| 242 | + connectAddrInfo a = bracketOnError |
| 243 | + (socket (addrFamily a) Stream defaultProtocol) -- acquire |
| 244 | + Network.Socket.close -- release |
| 245 | + ( \s -> do |
| 246 | + setSocketOption s KeepAlive 1 |
| 247 | + connect s (addrAddress a) |
| 248 | + socketConnection_ fixedUri port s stashInput ) |
| 249 | + |
| 250 | + -- try multiple addresses; return Just connected socket or Nothing |
| 251 | + tryAddrInfos [] = return Nothing |
| 252 | + tryAddrInfos (h:t) = |
| 253 | + let next = \(_ :: IOException) -> tryAddrInfos t |
| 254 | + in try (connectAddrInfo h) >>= either next (return . Just) |
| 255 | + |
239 | 256 | case addrinfos of
|
240 | 257 | [] -> fail "openTCPConnection: getAddrInfo returned no address information"
|
241 |
| - (a:_) -> do |
242 |
| - s <- socket (addrFamily a) Stream defaultProtocol |
243 |
| - onException (do |
244 |
| - setSocketOption s KeepAlive 1 |
245 |
| - connect s (addrAddress a) |
246 |
| - socketConnection_ fixedUri port s stashInput |
247 |
| - ) (Network.Socket.close s) |
| 258 | + |
| 259 | + -- single AddrInfo; call connectAddrInfo directly so that specific |
| 260 | + -- exception is thrown in event of failure |
| 261 | + [ai] -> connectAddrInfo ai `catchIO` (\e -> fail $ |
| 262 | + "openTCPConnection: failed to connect to " |
| 263 | + ++ show (addrAddress ai) ++ ": " ++ show e) |
| 264 | + |
| 265 | + -- multiple AddrInfos; try each until we get a connection, or run out |
| 266 | + ais -> |
| 267 | + let |
| 268 | + err = fail $ "openTCPConnection: failed to connect; tried addresses: " |
| 269 | + ++ show (fmap addrAddress ais) |
| 270 | + in tryAddrInfos ais >>= maybe err return |
248 | 271 |
|
249 | 272 | -- | @socketConnection@, like @openConnection@ but using a pre-existing 'Socket'.
|
250 | 273 | socketConnection :: BufferType ty
|
|
0 commit comments