forked from qnikst/HaskellNet
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
75 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
module Network.Compat where | ||
|
||
import Network.Socket | ||
import Network.BSD (getProtocolNumber) | ||
import System.IO (Handle, IOMode (..)) | ||
|
||
import qualified Control.Exception as Exception | ||
|
||
connectTo :: String -- Hostname | ||
-> PortNumber -- Port Identifier | ||
-> IO Handle -- Connected Socket | ||
connectTo host port = do | ||
proto <- getProtocolNumber "tcp" | ||
let hints = defaultHints { addrFlags = [AI_ADDRCONFIG] | ||
, addrProtocol = proto | ||
, addrSocketType = Stream } | ||
addrs <- getAddrInfo (Just hints) (Just host) (Just serv) | ||
firstSuccessful "connectTo" $ map tryToConnect addrs | ||
where | ||
serv = show port | ||
|
||
tryToConnect addr = | ||
Exception.bracketOnError | ||
(socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)) | ||
close -- only done if there's an error | ||
(\sock -> do | ||
connect sock (addrAddress addr) | ||
socketToHandle sock ReadWriteMode | ||
) | ||
|
||
-- Returns the first action from a list which does not throw an exception. | ||
-- If all the actions throw exceptions (and the list of actions is not empty), | ||
-- the last exception is thrown. | ||
-- The operations are run outside of the catchIO cleanup handler because | ||
-- catchIO masks asynchronous exceptions in the cleanup handler. | ||
-- In the case of complete failure, the last exception is actually thrown. | ||
firstSuccessful :: String -> [IO a] -> IO a | ||
firstSuccessful caller = go Nothing | ||
where | ||
-- Attempt the next operation, remember exception on failure | ||
go _ (p:ps) = | ||
do r <- tryIO p | ||
case r of | ||
Right x -> return x | ||
Left e -> go (Just e) ps | ||
|
||
-- All operations failed, throw error if one exists | ||
go Nothing [] = ioError $ userError $ caller ++ ": firstSuccessful: empty list" | ||
go (Just e) [] = Exception.throwIO e | ||
|
||
-- Version of try implemented in terms of the locally defined catchIO | ||
tryIO :: IO a -> IO (Either Exception.IOException a) | ||
tryIO m = Exception.catch (fmap Right m) (return . Left) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters