{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Network.Wait (
waitTcp,
waitTcpVerbose,
waitTcpVerboseFormat,
waitTcpWith,
waitSocket,
waitSocketVerbose,
waitSocketVerboseFormat,
waitSocketWith,
recoveringWith,
recoveringWithStatus
) where
import Control.Exception (throwIO)
import Control.Monad.Catch
import Control.Monad.IO.Class
import Control.Retry
import Data.Semigroup
import System.IO.Error
import System.Timeout
import Network.Socket
connectRetryPolicy :: MonadIO m => RetryPolicyM m
connectRetryPolicy :: forall (m :: * -> *). MonadIO m => RetryPolicyM m
connectRetryPolicy = Int -> RetryPolicyM m -> RetryPolicyM m
forall (m :: * -> *).
Monad m =>
Int -> RetryPolicyM m -> RetryPolicyM m
capDelay (Int
3000000) (Int -> RetryPolicyM m
forall (m :: * -> *). MonadIO m => Int -> RetryPolicyM m
fullJitterBackoff Int
100000)
waitTcp
:: (MonadIO m, MonadMask m)
=> RetryPolicyM m -> HostName -> ServiceName -> m Socket
waitTcp :: forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
RetryPolicyM m -> HostName -> HostName -> m Socket
waitTcp = [RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> HostName -> HostName -> m Socket
forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> HostName -> HostName -> m Socket
waitTcpWith []
waitTcpVerbose
:: (MonadIO m, MonadMask m)
=> (String -> m ()) -> RetryPolicyM m -> HostName -> ServiceName
-> m Socket
waitTcpVerbose :: forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
(HostName -> m ())
-> RetryPolicyM m -> HostName -> HostName -> m Socket
waitTcpVerbose HostName -> m ()
out =
forall e (m :: * -> *).
(MonadIO m, MonadMask m, Exception e) =>
(Bool -> e -> RetryStatus -> m ())
-> RetryPolicyM m -> HostName -> HostName -> m Socket
waitTcpVerboseFormat @SomeException ((Bool -> SomeException -> RetryStatus -> m ())
-> RetryPolicyM m -> HostName -> HostName -> m Socket)
-> (Bool -> SomeException -> RetryStatus -> m ())
-> RetryPolicyM m
-> HostName
-> HostName
-> m Socket
forall a b. (a -> b) -> a -> b
$
\Bool
b SomeException
ex RetryStatus
st -> HostName -> m ()
out (HostName -> m ()) -> HostName -> m ()
forall a b. (a -> b) -> a -> b
$ Bool -> SomeException -> RetryStatus -> HostName
forall e. Exception e => Bool -> e -> RetryStatus -> HostName
defaultLogMsg Bool
b SomeException
ex RetryStatus
st
waitTcpVerboseFormat
:: forall e m . (MonadIO m, MonadMask m, Exception e)
=> (Bool -> e -> RetryStatus -> m ())
-> RetryPolicyM m
-> HostName
-> ServiceName
-> m Socket
waitTcpVerboseFormat :: forall e (m :: * -> *).
(MonadIO m, MonadMask m, Exception e) =>
(Bool -> e -> RetryStatus -> m ())
-> RetryPolicyM m -> HostName -> HostName -> m Socket
waitTcpVerboseFormat Bool -> e -> RetryStatus -> m ()
out = [RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> HostName -> HostName -> m Socket
forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> HostName -> HostName -> m Socket
waitTcpWith [RetryStatus -> Handler m Bool
h]
where h :: RetryStatus -> Handler m Bool
h = (e -> m Bool)
-> (Bool -> e -> RetryStatus -> m ())
-> RetryStatus
-> Handler m Bool
forall (m :: * -> *) e.
(Monad m, Exception e) =>
(e -> m Bool)
-> (Bool -> e -> RetryStatus -> m ())
-> RetryStatus
-> Handler m Bool
logRetries (m Bool -> e -> m Bool
forall a b. a -> b -> a
const (m Bool -> e -> m Bool) -> m Bool -> e -> m Bool
forall a b. (a -> b) -> a -> b
$ Bool -> m Bool
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True) Bool -> e -> RetryStatus -> m ()
out
waitTcpWith
:: (MonadIO m, MonadMask m)
=> [RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> HostName -> ServiceName -> m Socket
waitTcpWith :: forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> HostName -> HostName -> m Socket
waitTcpWith [RetryStatus -> Handler m Bool]
hs RetryPolicyM m
policy HostName
host HostName
port = do
let hints :: AddrInfo
hints = AddrInfo
defaultHints { addrSocketType = Stream }
addr <- [AddrInfo] -> AddrInfo
forall a. HasCallStack => [a] -> a
head ([AddrInfo] -> AddrInfo) -> m [AddrInfo] -> m AddrInfo
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [AddrInfo] -> m [AddrInfo]
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Maybe AddrInfo -> Maybe HostName -> Maybe HostName -> IO [AddrInfo]
forall (t :: * -> *).
GetAddrInfo t =>
Maybe AddrInfo
-> Maybe HostName -> Maybe HostName -> IO (t AddrInfo)
getAddrInfo (AddrInfo -> Maybe AddrInfo
forall a. a -> Maybe a
Just AddrInfo
hints) (HostName -> Maybe HostName
forall a. a -> Maybe a
Just HostName
host) (HostName -> Maybe HostName
forall a. a -> Maybe a
Just HostName
port))
waitSocketWith hs policy addr
waitSocket
:: (MonadIO m, MonadMask m)
=> RetryPolicyM m -> AddrInfo -> m Socket
waitSocket :: forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
RetryPolicyM m -> AddrInfo -> m Socket
waitSocket = [RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> AddrInfo -> m Socket
forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> AddrInfo -> m Socket
waitSocketWith []
waitSocketVerbose
:: (MonadIO m, MonadMask m)
=> (String -> m ()) -> RetryPolicyM m -> AddrInfo -> m Socket
waitSocketVerbose :: forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
(HostName -> m ()) -> RetryPolicyM m -> AddrInfo -> m Socket
waitSocketVerbose HostName -> m ()
out =
forall e (m :: * -> *).
(MonadIO m, MonadMask m, Exception e) =>
(Bool -> e -> RetryStatus -> m ())
-> RetryPolicyM m -> AddrInfo -> m Socket
waitSocketVerboseFormat @SomeException ((Bool -> SomeException -> RetryStatus -> m ())
-> RetryPolicyM m -> AddrInfo -> m Socket)
-> (Bool -> SomeException -> RetryStatus -> m ())
-> RetryPolicyM m
-> AddrInfo
-> m Socket
forall a b. (a -> b) -> a -> b
$
\Bool
b SomeException
ex RetryStatus
st -> HostName -> m ()
out (HostName -> m ()) -> HostName -> m ()
forall a b. (a -> b) -> a -> b
$ Bool -> SomeException -> RetryStatus -> HostName
forall e. Exception e => Bool -> e -> RetryStatus -> HostName
defaultLogMsg Bool
b SomeException
ex RetryStatus
st
waitSocketVerboseFormat
:: forall e m . (MonadIO m, MonadMask m, Exception e)
=> (Bool -> e -> RetryStatus -> m ())
-> RetryPolicyM m
-> AddrInfo
-> m Socket
waitSocketVerboseFormat :: forall e (m :: * -> *).
(MonadIO m, MonadMask m, Exception e) =>
(Bool -> e -> RetryStatus -> m ())
-> RetryPolicyM m -> AddrInfo -> m Socket
waitSocketVerboseFormat Bool -> e -> RetryStatus -> m ()
out = [RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> AddrInfo -> m Socket
forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> AddrInfo -> m Socket
waitSocketWith [RetryStatus -> Handler m Bool
h]
where h :: RetryStatus -> Handler m Bool
h = (e -> m Bool)
-> (Bool -> e -> RetryStatus -> m ())
-> RetryStatus
-> Handler m Bool
forall (m :: * -> *) e.
(Monad m, Exception e) =>
(e -> m Bool)
-> (Bool -> e -> RetryStatus -> m ())
-> RetryStatus
-> Handler m Bool
logRetries (m Bool -> e -> m Bool
forall a b. a -> b -> a
const (m Bool -> e -> m Bool) -> m Bool -> e -> m Bool
forall a b. (a -> b) -> a -> b
$ Bool -> m Bool
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True) Bool -> e -> RetryStatus -> m ()
out
waitSocketWith
:: (MonadIO m, MonadMask m)
=> [RetryStatus -> Handler m Bool] -> RetryPolicyM m -> AddrInfo
-> m Socket
waitSocketWith :: forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> AddrInfo -> m Socket
waitSocketWith [RetryStatus -> Handler m Bool]
hs RetryPolicyM m
policy AddrInfo
addr =
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> (RetryStatus -> m Socket) -> m Socket
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> (RetryStatus -> m a) -> m a
recoveringWithStatus [RetryStatus -> Handler m Bool]
hs RetryPolicyM m
policy ((RetryStatus -> m Socket) -> m Socket)
-> (RetryStatus -> m Socket) -> m Socket
forall a b. (a -> b) -> a -> b
$ \RetryStatus
retryStatus ->
IO Socket -> m Socket
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Socket -> m Socket) -> IO Socket -> m Socket
forall a b. (a -> b) -> a -> b
$
IO Socket
-> (Socket -> IO ()) -> (Socket -> IO Socket) -> IO Socket
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> (a -> m c) -> (a -> m b) -> m b
bracket IO Socket
initSocket Socket -> IO ()
close ((Socket -> IO Socket) -> IO Socket)
-> (Socket -> IO Socket) -> IO Socket
forall a b. (a -> b) -> a -> b
$ \Socket
sock -> do
maybeConnectTimeoutUs <- (RetryPolicyM IO -> RetryStatus -> IO (Maybe Int)
forall (m :: * -> *).
RetryPolicyM m -> RetryStatus -> m (Maybe Int)
getRetryPolicyM RetryPolicyM IO
forall (m :: * -> *). MonadIO m => RetryPolicyM m
connectRetryPolicy) RetryStatus
retryStatus
connectTimeoutUs <- case maybeConnectTimeoutUs of
Maybe Int
Nothing -> IOError -> IO Int
forall e a. (HasCallStack, Exception e) => e -> IO a
throwIO (IOError -> IO Int) -> IOError -> IO Int
forall a b. (a -> b) -> a -> b
$ HostName -> IOError
userError HostName
"Timeout in connect attempt"
Just Int
us -> Int -> IO Int
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
us
maybeResult <- timeout connectTimeoutUs (connect sock (addrAddress addr))
case maybeResult of
Maybe ()
Nothing -> IOError -> IO Socket
forall e a. (HasCallStack, Exception e) => e -> IO a
throwIO (IOError -> IO Socket) -> IOError -> IO Socket
forall a b. (a -> b) -> a -> b
$ HostName -> IOError
userError HostName
"Timeout in connect attempt"
Just () -> Socket -> IO Socket
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Socket
sock
where
initSocket :: IO Socket
initSocket =
Family -> SocketType -> ProtocolNumber -> IO Socket
socket (AddrInfo -> Family
addrFamily AddrInfo
addr) (AddrInfo -> SocketType
addrSocketType AddrInfo
addr) (AddrInfo -> ProtocolNumber
addrProtocol AddrInfo
addr)
recoveringWith
:: (MonadIO m, MonadMask m)
=> [RetryStatus -> Handler m Bool] -> RetryPolicyM m -> m a -> m a
recoveringWith :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
[RetryStatus -> Handler m Bool] -> RetryPolicyM m -> m a -> m a
recoveringWith [RetryStatus -> Handler m Bool]
hs RetryPolicyM m
policy =
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> (RetryStatus -> m a) -> m a
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> (RetryStatus -> m a) -> m a
recoveringWithStatus [RetryStatus -> Handler m Bool]
hs RetryPolicyM m
policy ((RetryStatus -> m a) -> m a)
-> (m a -> RetryStatus -> m a) -> m a -> m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m a -> RetryStatus -> m a
forall a b. a -> b -> a
const
recoveringWithStatus
:: (MonadIO m, MonadMask m)
=> [RetryStatus -> Handler m Bool]
-> RetryPolicyM m
-> (RetryStatus -> m a)
-> m a
recoveringWithStatus :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
[RetryStatus -> Handler m Bool]
-> RetryPolicyM m -> (RetryStatus -> m a) -> m a
recoveringWithStatus [RetryStatus -> Handler m Bool]
hs RetryPolicyM m
policy RetryStatus -> m a
action =
RetryPolicyM m
-> [RetryStatus -> Handler m Bool] -> (RetryStatus -> m a) -> m a
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
RetryPolicyM m
-> [RetryStatus -> Handler m Bool] -> (RetryStatus -> m a) -> m a
recovering RetryPolicyM m
policy ([RetryStatus -> Handler m Bool]
forall (m :: * -> *). MonadIO m => [RetryStatus -> Handler m Bool]
skipAsyncExceptions [RetryStatus -> Handler m Bool]
-> [RetryStatus -> Handler m Bool]
-> [RetryStatus -> Handler m Bool]
forall a. Semigroup a => a -> a -> a
<> [RetryStatus -> Handler m Bool]
hs [RetryStatus -> Handler m Bool]
-> [RetryStatus -> Handler m Bool]
-> [RetryStatus -> Handler m Bool]
forall a. Semigroup a => a -> a -> a
<> [RetryStatus -> Handler m Bool
forall {m :: * -> *} {p}. Applicative m => p -> Handler m Bool
defHandler]) ((RetryStatus -> m a) -> m a) -> (RetryStatus -> m a) -> m a
forall a b. (a -> b) -> a -> b
$
RetryStatus -> m a
action
where
defHandler :: p -> Handler m Bool
defHandler p
_ = (SomeException -> m Bool) -> Handler m Bool
forall (m :: * -> *) a e. Exception e => (e -> m a) -> Handler m a
Handler ((SomeException -> m Bool) -> Handler m Bool)
-> (SomeException -> m Bool) -> Handler m Bool
forall a b. (a -> b) -> a -> b
$ \(SomeException
_ :: SomeException) -> Bool -> m Bool
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True