{-# LANGUAGE CPP #-}
#include "HsNetDef.h"
module Network.Socket.Wait (
withReadable,
withWritable,
withReadableOv,
withWritableOv,
acceptWith,
recvBufWith,
recvBufFromWith,
sendBufWith,
sendBufToWith,
recvBufMsgWith,
sendBufMsgWith,
connectWith,
isClosedSocketError,
isTransientAcceptError,
isPortUnreachableError,
) where
import Control.Concurrent.STM
import qualified Control.Exception as E
import Foreign.C.Error (
Errno (..),
eCONNABORTED,
#if !defined(mingw32_HOST_OS)
eCONNREFUSED,
#endif
eHOSTDOWN,
eHOSTUNREACH,
eNETDOWN,
eNETUNREACH,
eNONET,
eNOPROTOOPT,
ePROTO,
)
import GHC.IO.Exception (IOErrorType (..), ioe_errno)
import System.IO.Error (ioeGetErrorType)
import Network.Socket.Buffer
#if defined(mingw32_HOST_OS)
import Network.Socket.Cbits
import Network.Socket.Win32.Cancel
#else
import Network.Socket.STM
#endif
import Network.Socket.Flag
import Network.Socket.Imports
import Network.Socket.Syscall
import Network.Socket.Types
#if !defined(mingw32_HOST_OS)
import Network.Socket.Posix.Cmsg
#else
import Network.Socket.Win32.Cmsg
#endif
withReadable :: Socket -> STM a -> IO b -> IO (Either a b)
withReadable :: forall a b. Socket -> STM a -> IO b -> IO (Either a b)
withReadable Socket
s STM a
giveUp IO b
act = Ready
-> Bool -> Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
forall a b.
Ready
-> Bool -> Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReady Ready
Read Bool
False Socket
s STM a
giveUp ((Ptr () -> IO b) -> IO (Either a b))
-> (Ptr () -> IO b) -> IO (Either a b)
forall a b. (a -> b) -> a -> b
$ IO b -> Ptr () -> IO b
forall a b. a -> b -> a
const IO b
act
withWritable :: Socket -> STM a -> IO b -> IO (Either a b)
withWritable :: forall a b. Socket -> STM a -> IO b -> IO (Either a b)
withWritable Socket
s STM a
giveUp IO b
act = Ready
-> Bool -> Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
forall a b.
Ready
-> Bool -> Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReady Ready
Write Bool
False Socket
s STM a
giveUp ((Ptr () -> IO b) -> IO (Either a b))
-> (Ptr () -> IO b) -> IO (Either a b)
forall a b. (a -> b) -> a -> b
$ IO b -> Ptr () -> IO b
forall a b. a -> b -> a
const IO b
act
withReadableOv :: Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReadableOv :: forall a b. Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReadableOv = Ready
-> Bool -> Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
forall a b.
Ready
-> Bool -> Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReady Ready
Read Bool
True
withWritableOv :: Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withWritableOv :: forall a b. Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withWritableOv = Ready
-> Bool -> Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
forall a b.
Ready
-> Bool -> Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReady Ready
Write Bool
True
data Ready = Read | Write
withReady :: Ready -> Bool -> Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReady :: forall a b.
Ready
-> Bool -> Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReady Ready
_ready Bool
_own Socket
s STM a
giveUp Ptr () -> IO b
act = do
gaveUp <- STM (Maybe a) -> IO (Maybe a)
forall a. STM a -> IO a
atomically (STM (Maybe a) -> IO (Maybe a)) -> STM (Maybe a) -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ (a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> STM a -> STM (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> STM a
giveUp) STM (Maybe a) -> STM (Maybe a) -> STM (Maybe a)
forall a. STM a -> STM a -> STM a
`orElse` Maybe a -> STM (Maybe a)
forall a. a -> STM a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing
case gaveUp of
Just a
a -> Either a b -> IO (Either a b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either a b -> IO (Either a b)) -> Either a b -> IO (Either a b)
forall a b. (a -> b) -> a -> b
$ a -> Either a b
forall a b. a -> Either a b
Left a
a
Maybe a
Nothing -> IO (Either a b)
wait
where
#if !defined(mingw32_HOST_OS)
wait :: IO (Either a b)
wait = do
now <- Socket -> (CInt -> IO CInt) -> IO CInt
forall r. Socket -> (CInt -> IO r) -> IO r
withFdSocket Socket
s ((CInt -> IO CInt) -> IO CInt) -> (CInt -> IO CInt) -> IO CInt
forall a b. (a -> b) -> a -> b
$ \CInt
fd ->
CInt -> CInt -> IO CInt
c_ready_now CInt
fd (CInt -> IO CInt) -> CInt -> IO CInt
forall a b. (a -> b) -> a -> b
$ case Ready
_ready of
Ready
Read -> CInt
0
Ready
Write -> CInt
1
if now /= 0 then Right <$> act nullPtr else waitIO
waitIO :: IO (Either a b)
waitIO = do
(ready, cancelWait) <- case Ready
_ready of
Ready
Read -> Socket -> IO (STM (), IO ())
waitAndCancelReadSocketSTM Socket
s
Ready
Write -> Socket -> IO (STM (), IO ())
waitAndCancelWriteSocketSTM Socket
s
ev <-
atomically ((Left <$> giveUp) `orElse` (Right <$> ready))
`E.finally` (cancelWait `E.catch` gone)
either (return . Left) (const $ Right <$> act nullPtr) ev
gone :: E.IOException -> IO ()
gone :: IOException -> IO ()
gone IOException
_ = () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
#else
wait
| _own = withOverlapped waitOn
| otherwise = waitOn nullPtr
waitOn ov = do
(completed, cancelAct) <- windowsViaSTM s ov (act ov)
ev <-
atomically ((Right <$> completed) `orElse` (Left <$> giveUp))
`E.onException` cancelAct
case ev of
Right b -> return $ Right b
Left a -> do
cancelAct
return $ Left a
#endif
#if !defined(mingw32_HOST_OS)
foreign import ccall unsafe "hsnet_ready_now"
c_ready_now :: CInt -> CInt -> IO CInt
#endif
acceptWith :: SocketAddress sa => Socket -> STM a -> IO (Either a (Socket, sa))
acceptWith :: forall sa a.
SocketAddress sa =>
Socket -> STM a -> IO (Either a (Socket, sa))
acceptWith Socket
s STM a
giveUp = Socket -> STM a -> IO (Socket, sa) -> IO (Either a (Socket, sa))
forall a b. Socket -> STM a -> IO b -> IO (Either a b)
withReadable Socket
s STM a
giveUp (IO (Socket, sa) -> IO (Either a (Socket, sa)))
-> IO (Socket, sa) -> IO (Either a (Socket, sa))
forall a b. (a -> b) -> a -> b
$ Socket -> IO (Socket, sa)
forall sa. SocketAddress sa => Socket -> IO (Socket, sa)
accept Socket
s
recvBufWith :: Socket -> Ptr Word8 -> Int -> STM a -> IO (Either a Int)
recvBufWith :: forall a. Socket -> Ptr Word8 -> Int -> STM a -> IO (Either a Int)
recvBufWith Socket
s Ptr Word8
ptr Int
nbytes STM a
giveUp =
Socket -> STM a -> (Ptr () -> IO Int) -> IO (Either a Int)
forall a b. Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReadableOv Socket
s STM a
giveUp ((Ptr () -> IO Int) -> IO (Either a Int))
-> (Ptr () -> IO Int) -> IO (Either a Int)
forall a b. (a -> b) -> a -> b
$ \Ptr ()
ov -> Ptr () -> Socket -> Ptr Word8 -> Int -> IO Int
recvBufOv Ptr ()
ov Socket
s Ptr Word8
ptr Int
nbytes
recvBufFromWith
:: SocketAddress sa
=> Socket -> Ptr a -> Int -> STM b -> IO (Either b (Int, sa))
recvBufFromWith :: forall sa a b.
SocketAddress sa =>
Socket -> Ptr a -> Int -> STM b -> IO (Either b (Int, sa))
recvBufFromWith Socket
s Ptr a
ptr Int
nbytes STM b
giveUp =
Socket
-> STM b -> (Ptr () -> IO (Int, sa)) -> IO (Either b (Int, sa))
forall a b. Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReadableOv Socket
s STM b
giveUp ((Ptr () -> IO (Int, sa)) -> IO (Either b (Int, sa)))
-> (Ptr () -> IO (Int, sa)) -> IO (Either b (Int, sa))
forall a b. (a -> b) -> a -> b
$ \Ptr ()
ov -> Ptr () -> Socket -> Ptr a -> Int -> IO (Int, sa)
forall sa a.
SocketAddress sa =>
Ptr () -> Socket -> Ptr a -> Int -> IO (Int, sa)
recvBufFromOv Ptr ()
ov Socket
s Ptr a
ptr Int
nbytes
sendBufWith :: Socket -> Ptr Word8 -> Int -> STM a -> IO (Either a Int)
sendBufWith :: forall a. Socket -> Ptr Word8 -> Int -> STM a -> IO (Either a Int)
sendBufWith Socket
s Ptr Word8
ptr Int
nbytes STM a
giveUp =
Socket -> STM a -> (Ptr () -> IO Int) -> IO (Either a Int)
forall a b. Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withWritableOv Socket
s STM a
giveUp ((Ptr () -> IO Int) -> IO (Either a Int))
-> (Ptr () -> IO Int) -> IO (Either a Int)
forall a b. (a -> b) -> a -> b
$ \Ptr ()
ov -> Ptr () -> Socket -> Ptr Word8 -> Int -> IO Int
sendBufOv Ptr ()
ov Socket
s Ptr Word8
ptr Int
nbytes
sendBufToWith
:: SocketAddress sa
=> Socket -> Ptr a -> Int -> sa -> STM b -> IO (Either b Int)
sendBufToWith :: forall sa a b.
SocketAddress sa =>
Socket -> Ptr a -> Int -> sa -> STM b -> IO (Either b Int)
sendBufToWith Socket
s Ptr a
ptr Int
nbytes sa
sa STM b
giveUp =
Socket -> STM b -> (Ptr () -> IO Int) -> IO (Either b Int)
forall a b. Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withWritableOv Socket
s STM b
giveUp ((Ptr () -> IO Int) -> IO (Either b Int))
-> (Ptr () -> IO Int) -> IO (Either b Int)
forall a b. (a -> b) -> a -> b
$ \Ptr ()
ov -> Ptr () -> Socket -> Ptr a -> Int -> sa -> IO Int
forall sa a.
SocketAddress sa =>
Ptr () -> Socket -> Ptr a -> Int -> sa -> IO Int
sendBufToOv Ptr ()
ov Socket
s Ptr a
ptr Int
nbytes sa
sa
recvBufMsgWith
:: SocketAddress sa
=> Socket
-> [(Ptr Word8, Int)]
-> Int
-> MsgFlag
-> STM a
-> IO (Either a (sa, Int, [Cmsg], MsgFlag))
recvBufMsgWith :: forall sa a.
SocketAddress sa =>
Socket
-> [(Ptr Word8, Int)]
-> Int
-> MsgFlag
-> STM a
-> IO (Either a (sa, Int, [Cmsg], MsgFlag))
recvBufMsgWith Socket
s [(Ptr Word8, Int)]
bufsizs Int
clen MsgFlag
flags STM a
giveUp =
Socket
-> STM a
-> (Ptr () -> IO (sa, Int, [Cmsg], MsgFlag))
-> IO (Either a (sa, Int, [Cmsg], MsgFlag))
forall a b. Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withReadableOv Socket
s STM a
giveUp ((Ptr () -> IO (sa, Int, [Cmsg], MsgFlag))
-> IO (Either a (sa, Int, [Cmsg], MsgFlag)))
-> (Ptr () -> IO (sa, Int, [Cmsg], MsgFlag))
-> IO (Either a (sa, Int, [Cmsg], MsgFlag))
forall a b. (a -> b) -> a -> b
$ \Ptr ()
ov -> Ptr ()
-> Socket
-> [(Ptr Word8, Int)]
-> Int
-> MsgFlag
-> IO (sa, Int, [Cmsg], MsgFlag)
forall sa.
SocketAddress sa =>
Ptr ()
-> Socket
-> [(Ptr Word8, Int)]
-> Int
-> MsgFlag
-> IO (sa, Int, [Cmsg], MsgFlag)
recvBufMsgOv Ptr ()
ov Socket
s [(Ptr Word8, Int)]
bufsizs Int
clen MsgFlag
flags
sendBufMsgWith
:: SocketAddress sa
=> Socket
-> sa
-> [(Ptr Word8, Int)]
-> [Cmsg]
-> MsgFlag
-> STM a
-> IO (Either a Int)
sendBufMsgWith :: forall sa a.
SocketAddress sa =>
Socket
-> sa
-> [(Ptr Word8, Int)]
-> [Cmsg]
-> MsgFlag
-> STM a
-> IO (Either a Int)
sendBufMsgWith Socket
s sa
sa [(Ptr Word8, Int)]
bufsizs [Cmsg]
cmsgs MsgFlag
flags STM a
giveUp =
Socket -> STM a -> (Ptr () -> IO Int) -> IO (Either a Int)
forall a b. Socket -> STM a -> (Ptr () -> IO b) -> IO (Either a b)
withWritableOv Socket
s STM a
giveUp ((Ptr () -> IO Int) -> IO (Either a Int))
-> (Ptr () -> IO Int) -> IO (Either a Int)
forall a b. (a -> b) -> a -> b
$ \Ptr ()
ov -> Ptr ()
-> Socket
-> sa
-> [(Ptr Word8, Int)]
-> [Cmsg]
-> MsgFlag
-> IO Int
forall sa.
SocketAddress sa =>
Ptr ()
-> Socket
-> sa
-> [(Ptr Word8, Int)]
-> [Cmsg]
-> MsgFlag
-> IO Int
sendBufMsgOv Ptr ()
ov Socket
s sa
sa [(Ptr Word8, Int)]
bufsizs [Cmsg]
cmsgs MsgFlag
flags
connectWith :: SocketAddress sa => Socket -> sa -> STM a -> IO (Either a ())
#if !defined(mingw32_HOST_OS)
connectWith :: forall sa a.
SocketAddress sa =>
Socket -> sa -> STM a -> IO (Either a ())
connectWith Socket
s sa
sa STM a
giveUp = do
gaveUp <- STM (Maybe a) -> IO (Maybe a)
forall a. STM a -> IO a
atomically (STM (Maybe a) -> IO (Maybe a)) -> STM (Maybe a) -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ (a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> STM a -> STM (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> STM a
giveUp) STM (Maybe a) -> STM (Maybe a) -> STM (Maybe a)
forall a. STM a -> STM a -> STM a
`orElse` Maybe a -> STM (Maybe a)
forall a. a -> STM a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing
case gaveUp of
Just a
a -> Either a () -> IO (Either a ())
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either a () -> IO (Either a ()))
-> Either a () -> IO (Either a ())
forall a b. (a -> b) -> a -> b
$ a -> Either a ()
forall a b. a -> Either a b
Left a
a
Maybe a
Nothing -> Socket -> sa -> (IO () -> IO (Either a ())) -> IO (Either a ())
forall sa a.
SocketAddress sa =>
Socket -> sa -> (IO () -> IO (Either a ())) -> IO (Either a ())
connectWhenWritable Socket
s sa
sa ((IO () -> IO (Either a ())) -> IO (Either a ()))
-> (IO () -> IO (Either a ())) -> IO (Either a ())
forall a b. (a -> b) -> a -> b
$ Socket -> STM a -> IO () -> IO (Either a ())
forall a b. Socket -> STM a -> IO b -> IO (Either a b)
withWritable Socket
s STM a
giveUp
#else
connectWith s sa giveUp = withWritable s giveUp $ connect s sa
#endif
isClosedSocketError :: E.IOException -> Bool
isClosedSocketError :: IOException -> Bool
isClosedSocketError IOException
e = IOException -> IOErrorType
ioeGetErrorType IOException
e IOErrorType -> IOErrorType -> Bool
forall a. Eq a => a -> a -> Bool
== IOErrorType
InvalidArgument Bool -> Bool -> Bool
|| IOException -> Bool
forall {p}. p -> Bool
isWinsock IOException
e
where
#if defined(mingw32_HOST_OS)
isWinsock x = ioe_errno x `elem` map Just [wsaEINTR, wsaENOTSOCK]
#else
isWinsock :: p -> Bool
isWinsock p
_ = Bool
False
#endif
isTransientAcceptError :: E.IOException -> Bool
isTransientAcceptError :: IOException -> Bool
isTransientAcceptError IOException
e =
IOException -> IOErrorType
ioeGetErrorType IOException
e IOErrorType -> IOErrorType -> Bool
forall a. Eq a => a -> a -> Bool
== IOErrorType
Interrupted Bool -> Bool -> Bool
|| (Errno -> Bool) -> [Errno] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Errno -> Bool
is [Errno]
errnos Bool -> Bool -> Bool
|| IOException -> Bool
forall {p}. p -> Bool
isWinsock IOException
e
where
is :: Errno -> Bool
is (Errno CInt
n) = CInt
n CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
/= -CInt
1 Bool -> Bool -> Bool
&& IOException -> Maybe CInt
ioe_errno IOException
e Maybe CInt -> Maybe CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt -> Maybe CInt
forall a. a -> Maybe a
Just CInt
n
errnos :: [Errno]
errnos =
[ Errno
eCONNABORTED
, Errno
eHOSTDOWN
, Errno
eHOSTUNREACH
, Errno
eNETDOWN
, Errno
eNETUNREACH
, Errno
eNONET
, Errno
eNOPROTOOPT
, Errno
ePROTO
]
#if defined(mingw32_HOST_OS)
isWinsock x = ioe_errno x `elem` map Just codes
codes =
[ wsaECONNABORTED
, wsaECONNRESET
, wsaEHOSTDOWN
, wsaEHOSTUNREACH
, wsaENETDOWN
, wsaENETRESET
, wsaENETUNREACH
, wsaENOPROTOOPT
]
#else
isWinsock :: p -> Bool
isWinsock p
_ = Bool
False
#endif
isPortUnreachableError :: E.IOException -> Bool
isPortUnreachableError :: IOException -> Bool
isPortUnreachableError IOException
e = IOException -> Maybe CInt
ioe_errno IOException
e Maybe CInt -> Maybe CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt -> Maybe CInt
forall a. a -> Maybe a
Just CInt
code
where
#if defined(mingw32_HOST_OS)
code = wsaECONNRESET
#else
Errno CInt
code = Errno
eCONNREFUSED
#endif