{-# LANGUAGE OverloadedStrings #-}
module Network.GRPC.Client.Connection (
Connection
, withConnection
, Server(..)
, ServerValidation(..)
, SslKeyLog(..)
, ConnParams(..)
, ReconnectPolicy(..)
, ReconnectTo(..)
, exponentialBackoff
, connParams
, getConnectionToServer
, getOutboundCompression
, updateConnectionMeta
) where
import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
import Control.Monad.Catch
import Data.Default
import GHC.Stack
import Network.HPACK qualified as HPACK
import Network.HTTP2.Client qualified as HTTP2.Client
import Network.HTTP2.TLS.Client qualified as HTTP2.TLS.Client
import Network.Run.TCP qualified as Run
import Network.Socket
import Network.TLS (TLSException)
import System.Random
import Network.GRPC.Client.Meta (Meta)
import Network.GRPC.Client.Meta qualified as Meta
import Network.GRPC.Common.Compression qualified as Compr
import Network.GRPC.Common.HTTP2Settings
import Network.GRPC.Spec
import Network.GRPC.Util.GHC
import Network.GRPC.Util.Session qualified as Session
import Network.GRPC.Util.TLS (ServerValidation(..), SslKeyLog(..))
import Network.GRPC.Util.TLS qualified as Util.TLS
data Connection = Connection {
Connection -> ConnParams
connParams :: ConnParams
, Connection -> MVar Meta
connMetaVar :: MVar Meta
, Connection -> TVar ConnectionState
connStateVar :: TVar ConnectionState
}
data ConnParams = ConnParams {
ConnParams -> Negotation
connCompression :: Compr.Negotation
, ConnParams -> Maybe Timeout
connDefaultTimeout :: Maybe Timeout
, ConnParams -> ReconnectPolicy
connReconnectPolicy :: ReconnectPolicy
, ConnParams -> Maybe ContentType
connContentType :: Maybe ContentType
, :: Bool
, ConnParams -> Maybe Compression
connInitCompression :: Maybe Compression
, ConnParams -> HTTP2Settings
connHTTP2Settings :: HTTP2Settings
}
instance Default ConnParams where
def :: ConnParams
def = ConnParams {
connCompression :: Negotation
connCompression = Negotation
forall a. Default a => a
def
, connDefaultTimeout :: Maybe Timeout
connDefaultTimeout = Maybe Timeout
forall a. Maybe a
Nothing
, connReconnectPolicy :: ReconnectPolicy
connReconnectPolicy = ReconnectPolicy
forall a. Default a => a
def
, connContentType :: Maybe ContentType
connContentType = ContentType -> Maybe ContentType
forall a. a -> Maybe a
Just ContentType
ContentTypeDefault
, connVerifyHeaders :: Bool
connVerifyHeaders = Bool
False
, connInitCompression :: Maybe Compression
connInitCompression = Maybe Compression
forall a. Maybe a
Nothing
, connHTTP2Settings :: HTTP2Settings
connHTTP2Settings = HTTP2Settings
forall a. Default a => a
def
}
data ReconnectPolicy =
DontReconnect
| ReconnectAfter ReconnectTo (IO ReconnectPolicy)
data ReconnectTo =
ReconnectToPrevious
| ReconnectToOriginal
| ReconnectToNew Server
instance Default ReconnectPolicy where
def :: ReconnectPolicy
def = ReconnectPolicy
DontReconnect
instance Default ReconnectTo where
def :: ReconnectTo
def = ReconnectTo
ReconnectToPrevious
exponentialBackoff ::
(Int -> IO ())
-> Double
-> (Double, Double)
-> Word
-> ReconnectPolicy
exponentialBackoff :: (Int -> IO ())
-> Double -> (Double, Double) -> Word -> ReconnectPolicy
exponentialBackoff Int -> IO ()
waitFor Double
e = (Double, Double) -> Word -> ReconnectPolicy
go
where
go :: (Double, Double) -> Word -> ReconnectPolicy
go :: (Double, Double) -> Word -> ReconnectPolicy
go (Double, Double)
_ Word
0 = ReconnectPolicy
DontReconnect
go (Double
lo, Double
hi) Word
n = ReconnectTo -> IO ReconnectPolicy -> ReconnectPolicy
ReconnectAfter ReconnectTo
forall a. Default a => a
def (IO ReconnectPolicy -> ReconnectPolicy)
-> IO ReconnectPolicy -> ReconnectPolicy
forall a b. (a -> b) -> a -> b
$ do
delay <- (Double, Double) -> IO Double
forall a (m :: * -> *). (Random a, MonadIO m) => (a, a) -> m a
randomRIO (Double
lo, Double
hi)
waitFor $ round $ delay * 1_000_000
return $ go (lo * e, hi * e) (pred n)
isFatalException :: SomeException -> Bool
isFatalException :: SomeException -> Bool
isFatalException SomeException
err
| Just (TLSException
_tlsException :: TLSException) <- SomeException -> Maybe TLSException
forall e. Exception e => SomeException -> Maybe e
fromException SomeException
err
= Bool
True
| Bool
otherwise
= Bool
False
data Server =
ServerInsecure Address
| ServerSecure ServerValidation SslKeyLog Address
| ServerUnix FilePath
deriving stock (Int -> Server -> ShowS
[Server] -> ShowS
Server -> String
(Int -> Server -> ShowS)
-> (Server -> String) -> ([Server] -> ShowS) -> Show Server
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Server -> ShowS
showsPrec :: Int -> Server -> ShowS
$cshow :: Server -> String
show :: Server -> String
$cshowList :: [Server] -> ShowS
showList :: [Server] -> ShowS
Show)
withConnection ::
ConnParams
-> Server
-> (Connection -> IO a)
-> IO a
withConnection :: forall a. ConnParams -> Server -> (Connection -> IO a) -> IO a
withConnection ConnParams
connParams Server
server Connection -> IO a
k = do
connMetaVar <- Meta -> IO (MVar Meta)
forall a. a -> IO (MVar a)
newMVar (Meta -> IO (MVar Meta)) -> Meta -> IO (MVar Meta)
forall a b. (a -> b) -> a -> b
$ Maybe Compression -> Meta
Meta.init (ConnParams -> Maybe Compression
connInitCompression ConnParams
connParams)
connStateVar <- newTVarIO ConnectionNotReady
connOutOfScope <- newEmptyMVar
let stayConnectedThread :: IO ()
stayConnectedThread =
ConnParams -> Server -> TVar ConnectionState -> MVar () -> IO ()
stayConnected ConnParams
connParams Server
server TVar ConnectionState
connStateVar MVar ()
connOutOfScope
void $ forkLabelled "grapesy:stayConnected" $ stayConnectedThread
k Connection {connParams, connMetaVar, connStateVar}
`finally` putMVar connOutOfScope ()
getConnectionToServer :: forall.
HasCallStack
=> Connection
-> IO (TMVar (Maybe SomeException), Session.ConnectionToServer)
getConnectionToServer :: HasCallStack =>
Connection -> IO (TMVar (Maybe SomeException), ConnectionToServer)
getConnectionToServer Connection{TVar ConnectionState
connStateVar :: Connection -> TVar ConnectionState
connStateVar :: TVar ConnectionState
connStateVar} = STM (TMVar (Maybe SomeException), ConnectionToServer)
-> IO (TMVar (Maybe SomeException), ConnectionToServer)
forall a. STM a -> IO a
atomically (STM (TMVar (Maybe SomeException), ConnectionToServer)
-> IO (TMVar (Maybe SomeException), ConnectionToServer))
-> STM (TMVar (Maybe SomeException), ConnectionToServer)
-> IO (TMVar (Maybe SomeException), ConnectionToServer)
forall a b. (a -> b) -> a -> b
$ do
connState <- TVar ConnectionState -> STM ConnectionState
forall a. TVar a -> STM a
readTVar TVar ConnectionState
connStateVar
case connState of
ConnectionState
ConnectionNotReady -> STM (TMVar (Maybe SomeException), ConnectionToServer)
forall a. STM a
retry
ConnectionReady TMVar (Maybe SomeException)
connClosed ConnectionToServer
conn -> (TMVar (Maybe SomeException), ConnectionToServer)
-> STM (TMVar (Maybe SomeException), ConnectionToServer)
forall a. a -> STM a
forall (m :: * -> *) a. Monad m => a -> m a
return (TMVar (Maybe SomeException)
connClosed, ConnectionToServer
conn)
ConnectionAbandoned SomeException
err -> SomeException
-> STM (TMVar (Maybe SomeException), ConnectionToServer)
forall e a. Exception e => e -> STM a
throwSTM SomeException
err
ConnectionState
ConnectionOutOfScope -> String -> STM (TMVar (Maybe SomeException), ConnectionToServer)
forall a. HasCallStack => String -> a
error String
"impossible"
getOutboundCompression :: Connection -> IO (Maybe Compression)
getOutboundCompression :: Connection -> IO (Maybe Compression)
getOutboundCompression Connection{MVar Meta
connMetaVar :: Connection -> MVar Meta
connMetaVar :: MVar Meta
connMetaVar} =
Meta -> Maybe Compression
Meta.outboundCompression (Meta -> Maybe Compression) -> IO Meta -> IO (Maybe Compression)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> MVar Meta -> IO Meta
forall a. MVar a -> IO a
readMVar MVar Meta
connMetaVar
updateConnectionMeta ::
Connection
-> ResponseHeaders' HandledSynthesized
-> IO ()
updateConnectionMeta :: Connection -> ResponseHeaders' HandledSynthesized -> IO ()
updateConnectionMeta Connection{MVar Meta
connMetaVar :: Connection -> MVar Meta
connMetaVar :: MVar Meta
connMetaVar, ConnParams
connParams :: Connection -> ConnParams
connParams :: ConnParams
connParams} ResponseHeaders' HandledSynthesized
hdrs =
MVar Meta -> (Meta -> IO Meta) -> IO ()
forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ MVar Meta
connMetaVar ((Meta -> IO Meta) -> IO ()) -> (Meta -> IO Meta) -> IO ()
forall a b. (a -> b) -> a -> b
$ Negotation
-> ResponseHeaders' HandledSynthesized -> Meta -> IO Meta
forall (m :: * -> *).
MonadThrow m =>
Negotation -> ResponseHeaders' HandledSynthesized -> Meta -> m Meta
Meta.update (ConnParams -> Negotation
connCompression ConnParams
connParams) ResponseHeaders' HandledSynthesized
hdrs
data ConnectionState =
ConnectionNotReady
| ConnectionReady (TMVar (Maybe SomeException)) Session.ConnectionToServer
| ConnectionAbandoned SomeException
| ConnectionOutOfScope
data Attempt = ConnectionAttempt {
Attempt -> ConnParams
attemptParams :: ConnParams
, Attempt -> TVar ConnectionState
attemptState :: TVar ConnectionState
, Attempt -> MVar ()
attemptOutOfScope :: MVar ()
, Attempt -> TMVar (Maybe SomeException)
attemptClosed :: TMVar (Maybe SomeException)
}
newConnectionAttempt ::
ConnParams
-> TVar ConnectionState
-> MVar ()
-> IO Attempt
newConnectionAttempt :: ConnParams -> TVar ConnectionState -> MVar () -> IO Attempt
newConnectionAttempt ConnParams
attemptParams TVar ConnectionState
attemptState MVar ()
attemptOutOfScope = do
attemptClosed <- IO (TMVar (Maybe SomeException))
forall a. IO (TMVar a)
newEmptyTMVarIO
return ConnectionAttempt{
attemptParams
, attemptState
, attemptOutOfScope
, attemptClosed
}
stayConnected ::
ConnParams
-> Server
-> TVar ConnectionState
-> MVar ()
-> IO ()
stayConnected :: ConnParams -> Server -> TVar ConnectionState -> MVar () -> IO ()
stayConnected ConnParams
connParams Server
initialServer TVar ConnectionState
connStateVar MVar ()
connOutOfScope = do
Server -> ReconnectPolicy -> IO ()
loop Server
initialServer (ConnParams -> ReconnectPolicy
connReconnectPolicy ConnParams
connParams)
where
loop :: Server -> ReconnectPolicy -> IO ()
loop :: Server -> ReconnectPolicy -> IO ()
loop Server
server ReconnectPolicy
remainingReconnectPolicy = do
attempt <- ConnParams -> TVar ConnectionState -> MVar () -> IO Attempt
newConnectionAttempt ConnParams
connParams TVar ConnectionState
connStateVar MVar ()
connOutOfScope
mRes <- try $
case server of
ServerInsecure Address
addr ->
ConnParams -> Attempt -> Address -> IO ()
connectInsecure ConnParams
connParams Attempt
attempt Address
addr
ServerSecure ServerValidation
validation SslKeyLog
sslKeyLog Address
addr ->
ConnParams
-> Attempt -> ServerValidation -> SslKeyLog -> Address -> IO ()
connectSecure ConnParams
connParams Attempt
attempt ServerValidation
validation SslKeyLog
sslKeyLog Address
addr
ServerUnix String
path ->
ConnParams -> Attempt -> String -> IO ()
connectUnix ConnParams
connParams Attempt
attempt String
path
thisReconnectPolicy <- atomically $ do
putTMVar (attemptClosed attempt) $ either Just (\() -> Maybe SomeException
forall a. Maybe a
Nothing) mRes
connState <- readTVar connStateVar
return $ case connState of
ConnectionReady{}->
ConnParams -> ReconnectPolicy
connReconnectPolicy ConnParams
connParams
ConnectionState
_otherwise ->
ReconnectPolicy
remainingReconnectPolicy
case mRes of
Right () -> do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar ConnectionState -> ConnectionState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar ConnectionState
connStateVar (ConnectionState -> STM ()) -> ConnectionState -> STM ()
forall a b. (a -> b) -> a -> b
$ ConnectionState
ConnectionOutOfScope
Left SomeException
err -> do
case (SomeException -> Bool
isFatalException SomeException
err, ReconnectPolicy
thisReconnectPolicy) of
(Bool
True, ReconnectPolicy
_) -> do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar ConnectionState -> ConnectionState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar ConnectionState
connStateVar (ConnectionState -> STM ()) -> ConnectionState -> STM ()
forall a b. (a -> b) -> a -> b
$ SomeException -> ConnectionState
ConnectionAbandoned SomeException
err
(Bool
False, ReconnectPolicy
DontReconnect) -> do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar ConnectionState -> ConnectionState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar ConnectionState
connStateVar (ConnectionState -> STM ()) -> ConnectionState -> STM ()
forall a b. (a -> b) -> a -> b
$ SomeException -> ConnectionState
ConnectionAbandoned SomeException
err
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar ConnectionState -> ConnectionState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar ConnectionState
connStateVar (ConnectionState -> STM ()) -> ConnectionState -> STM ()
forall a b. (a -> b) -> a -> b
$ SomeException -> ConnectionState
ConnectionAbandoned SomeException
err
(Bool
False, ReconnectAfter ReconnectTo
to IO ReconnectPolicy
f) -> do
let
nextServer :: Server
nextServer =
case ReconnectTo
to of
ReconnectTo
ReconnectToPrevious -> Server
server
ReconnectTo
ReconnectToOriginal -> Server
initialServer
ReconnectToNew Server
new -> Server
new
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar ConnectionState -> ConnectionState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar ConnectionState
connStateVar (ConnectionState -> STM ()) -> ConnectionState -> STM ()
forall a b. (a -> b) -> a -> b
$ ConnectionState
ConnectionNotReady
Server -> ReconnectPolicy -> IO ()
loop Server
nextServer (ReconnectPolicy -> IO ()) -> IO ReconnectPolicy -> IO ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO ReconnectPolicy
f
connectUnix :: ConnParams -> Attempt -> FilePath -> IO ()
connectUnix :: ConnParams -> Attempt -> String -> IO ()
connectUnix ConnParams
connParams Attempt
attempt String
path = do
client <- Family -> SocketType -> CInt -> IO Socket
socket Family
AF_UNIX SocketType
Stream CInt
defaultProtocol
connect client $ SockAddrUnix path
connectSocket connParams attempt "localhost" client
connectInsecure :: ConnParams -> Attempt -> Address -> IO ()
connectInsecure :: ConnParams -> Attempt -> Address -> IO ()
connectInsecure ConnParams
connParams Attempt
attempt Address
addr = do
Settings -> String -> String -> (Socket -> IO ()) -> IO ()
forall a. Settings -> String -> String -> (Socket -> IO a) -> IO a
Run.runTCPClientWithSettings
Settings
runSettings
(Address -> String
addressHost Address
addr)
(PortNumber -> String
forall a. Show a => a -> String
show (PortNumber -> String) -> PortNumber -> String
forall a b. (a -> b) -> a -> b
$ Address -> PortNumber
addressPort Address
addr)
((Socket -> IO ()) -> IO ()) -> (Socket -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ ConnParams -> Attempt -> String -> Socket -> IO ()
connectSocket ConnParams
connParams Attempt
attempt (Address -> String
authority Address
addr)
where
ConnParams{HTTP2Settings
connHTTP2Settings :: ConnParams -> HTTP2Settings
connHTTP2Settings :: HTTP2Settings
connHTTP2Settings} = ConnParams
connParams
runSettings :: Run.Settings
runSettings :: Settings
runSettings = Settings
Run.defaultSettings {
Run.settingsOpenClientSocket = openClientSocket connHTTP2Settings
}
connectSocket :: ConnParams -> Attempt -> String -> Socket -> IO ()
connectSocket :: ConnParams -> Attempt -> String -> Socket -> IO ()
connectSocket ConnParams
connParams Attempt
attempt String
connAuthority Socket
sock = do
IO Config -> (Config -> IO ()) -> (Config -> IO ()) -> IO ()
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> (a -> m c) -> (a -> m b) -> m b
bracket (Socket -> Int -> IO Config
HTTP2.Client.allocSimpleConfig Socket
sock Int
writeBufferSize)
Config -> IO ()
HTTP2.Client.freeSimpleConfig ((Config -> IO ()) -> IO ()) -> (Config -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Config
conf ->
ClientConfig -> Config -> Client () -> IO ()
forall a. ClientConfig -> Config -> Client a -> IO a
HTTP2.Client.run ClientConfig
clientConfig Config
conf (Client () -> IO ()) -> Client () -> IO ()
forall a b. (a -> b) -> a -> b
$ \SendRequest
sendRequest Aux
_aux -> do
let conn :: ConnectionToServer
conn = SendRequest -> ConnectionToServer
Session.ConnectionToServer Request -> (Response -> IO a) -> IO a
SendRequest
sendRequest
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$
TVar ConnectionState -> ConnectionState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar (Attempt -> TVar ConnectionState
attemptState Attempt
attempt) (ConnectionState -> STM ()) -> ConnectionState -> STM ()
forall a b. (a -> b) -> a -> b
$
TMVar (Maybe SomeException)
-> ConnectionToServer -> ConnectionState
ConnectionReady (Attempt -> TMVar (Maybe SomeException)
attemptClosed Attempt
attempt) ConnectionToServer
conn
MVar () -> IO ()
forall a. MVar a -> IO a
takeMVar (MVar () -> IO ()) -> MVar () -> IO ()
forall a b. (a -> b) -> a -> b
$ Attempt -> MVar ()
attemptOutOfScope Attempt
attempt
where
ConnParams{HTTP2Settings
connHTTP2Settings :: ConnParams -> HTTP2Settings
connHTTP2Settings :: HTTP2Settings
connHTTP2Settings} = ConnParams
connParams
settings :: HTTP2.Client.Settings
settings :: Settings
settings = Settings
HTTP2.Client.defaultSettings {
HTTP2.Client.maxConcurrentStreams =
Just . fromIntegral $
http2MaxConcurrentStreams connHTTP2Settings
, HTTP2.Client.initialWindowSize =
fromIntegral $
http2StreamWindowSize connHTTP2Settings
}
clientConfig :: HTTP2.Client.ClientConfig
clientConfig :: ClientConfig
clientConfig = ConnParams -> ClientConfig -> ClientConfig
overrideRateLimits ConnParams
connParams (ClientConfig -> ClientConfig) -> ClientConfig -> ClientConfig
forall a b. (a -> b) -> a -> b
$
ClientConfig
HTTP2.Client.defaultClientConfig {
HTTP2.Client.authority = connAuthority
, HTTP2.Client.settings = settings
, HTTP2.Client.connectionWindowSize =
fromIntegral $
http2ConnectionWindowSize connHTTP2Settings
}
connectSecure ::
ConnParams
-> Attempt
-> ServerValidation
-> SslKeyLog
-> Address
-> IO ()
connectSecure :: ConnParams
-> Attempt -> ServerValidation -> SslKeyLog -> Address -> IO ()
connectSecure ConnParams
connParams Attempt
attempt ServerValidation
validation SslKeyLog
sslKeyLog Address
addr = do
keyLogger <- SslKeyLog -> IO (String -> IO ())
Util.TLS.keyLogger SslKeyLog
sslKeyLog
caStore <- Util.TLS.validationCAStore validation
let settings :: HTTP2.TLS.Client.Settings
settings = Settings
HTTP2.TLS.Client.defaultSettings {
HTTP2.TLS.Client.settingsValidateCert =
case validation of
ValidateServer CertificateStoreSpec
_ -> Bool
True
ServerValidation
NoServerValidation -> Bool
False
, HTTP2.TLS.Client.settingsCAStore = caStore
, HTTP2.TLS.Client.settingsKeyLogger = keyLogger
, HTTP2.TLS.Client.settingsAddrInfoFlags = []
, HTTP2.TLS.Client.settingsOpenClientSocket =
openClientSocket connHTTP2Settings
, HTTP2.TLS.Client.settingsConcurrentStreams = fromIntegral $
http2MaxConcurrentStreams connHTTP2Settings
, HTTP2.TLS.Client.settingsStreamWindowSize = fromIntegral $
http2StreamWindowSize connHTTP2Settings
, HTTP2.TLS.Client.settingsConnectionWindowSize = fromIntegral $
http2ConnectionWindowSize connHTTP2Settings
}
clientConfig :: HTTP2.Client.ClientConfig
clientConfig = ConnParams -> ClientConfig -> ClientConfig
overrideRateLimits ConnParams
connParams (ClientConfig -> ClientConfig) -> ClientConfig -> ClientConfig
forall a b. (a -> b) -> a -> b
$
Settings -> String -> ClientConfig
HTTP2.TLS.Client.defaultClientConfig
Settings
settings
(Address -> String
authority Address
addr)
HTTP2.TLS.Client.runWithConfig
clientConfig
settings
(addressHost addr)
(addressPort addr)
$ \SendRequest
sendRequest Aux
_aux -> do
let conn :: ConnectionToServer
conn = SendRequest -> ConnectionToServer
Session.ConnectionToServer Request -> (Response -> IO a) -> IO a
SendRequest
sendRequest
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$
TVar ConnectionState -> ConnectionState -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar (Attempt -> TVar ConnectionState
attemptState Attempt
attempt) (ConnectionState -> STM ()) -> ConnectionState -> STM ()
forall a b. (a -> b) -> a -> b
$
TMVar (Maybe SomeException)
-> ConnectionToServer -> ConnectionState
ConnectionReady (Attempt -> TMVar (Maybe SomeException)
attemptClosed Attempt
attempt) ConnectionToServer
conn
MVar () -> IO ()
forall a. MVar a -> IO a
takeMVar (MVar () -> IO ()) -> MVar () -> IO ()
forall a b. (a -> b) -> a -> b
$ Attempt -> MVar ()
attemptOutOfScope Attempt
attempt
where
ConnParams{HTTP2Settings
connHTTP2Settings :: ConnParams -> HTTP2Settings
connHTTP2Settings :: HTTP2Settings
connHTTP2Settings} = ConnParams
connParams
authority :: Address -> String
authority :: Address -> String
authority Address
addr =
case Address -> Maybe String
addressAuthority Address
addr of
Maybe String
Nothing -> Address -> String
addressHost Address
addr
Just String
auth -> String
auth
overrideRateLimits ::
ConnParams
-> HTTP2.Client.ClientConfig -> HTTP2.Client.ClientConfig
overrideRateLimits :: ConnParams -> ClientConfig -> ClientConfig
overrideRateLimits ConnParams
connParams ClientConfig
clientConfig = ClientConfig
clientConfig {
HTTP2.Client.settings = settings {
HTTP2.Client.pingRateLimit =
case http2OverridePingRateLimit (connHTTP2Settings connParams) of
Maybe Int
Nothing -> Settings -> Int
HTTP2.Client.pingRateLimit Settings
settings
Just Int
limit -> Int
limit
, HTTP2.Client.emptyFrameRateLimit =
case http2OverrideEmptyFrameRateLimit (connHTTP2Settings connParams) of
Maybe Int
Nothing -> Settings -> Int
HTTP2.Client.emptyFrameRateLimit Settings
settings
Just Int
limit -> Int
limit
, HTTP2.Client.settingsRateLimit =
case http2OverrideSettingsRateLimit (connHTTP2Settings connParams) of
Maybe Int
Nothing -> Settings -> Int
HTTP2.Client.settingsRateLimit Settings
settings
Just Int
limit -> Int
limit
, HTTP2.Client.rstRateLimit =
case http2OverrideRstRateLimit (connHTTP2Settings connParams) of
Maybe Int
Nothing -> Settings -> Int
HTTP2.Client.rstRateLimit Settings
settings
Just Int
limit -> Int
limit
}
}
where
settings :: HTTP2.Client.Settings
settings :: Settings
settings = ClientConfig -> Settings
HTTP2.Client.settings ClientConfig
clientConfig
openClientSocket :: HTTP2Settings -> AddrInfo -> IO Socket
openClientSocket :: HTTP2Settings -> AddrInfo -> IO Socket
openClientSocket HTTP2Settings
http2Settings =
[(SocketOption, SockOptValue)] -> AddrInfo -> IO Socket
Run.openClientSocketWithOpts [(SocketOption, SockOptValue)]
socketOptions
where
socketOptions :: [(SocketOption, SockOptValue)]
socketOptions :: [(SocketOption, SockOptValue)]
socketOptions = [[(SocketOption, SockOptValue)]] -> [(SocketOption, SockOptValue)]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [
[ ( SocketOption
NoDelay
, forall a. Storable a => a -> SockOptValue
SockOptValue @Int Int
1
)
| HTTP2Settings -> Bool
http2TcpNoDelay HTTP2Settings
http2Settings
]
, [ ( SocketOption
Linger
, StructLinger -> SockOptValue
forall a. Storable a => a -> SockOptValue
SockOptValue (StructLinger -> SockOptValue) -> StructLinger -> SockOptValue
forall a b. (a -> b) -> a -> b
$ StructLinger { sl_onoff :: CInt
sl_onoff = CInt
1, sl_linger :: CInt
sl_linger = CInt
0 }
)
| HTTP2Settings -> Bool
http2TcpAbortiveClose HTTP2Settings
http2Settings
]
]
writeBufferSize :: HPACK.BufferSize
writeBufferSize :: Int
writeBufferSize = Int
4096