mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-08-28 00:44:38 +00:00
agent: add known servers (#1225)
* agent: add known servers * test delays * ServerCfg * json encoding * enabledServerCfg * checkUserServers
This commit is contained in:
@@ -77,6 +77,7 @@ module Simplex.Messaging.Agent
|
||||
getConnectionServers,
|
||||
getConnectionRatchetAdHash,
|
||||
setProtocolServers,
|
||||
checkUserServers,
|
||||
testProtocolServer,
|
||||
setNtfServers,
|
||||
setNetworkConfig,
|
||||
@@ -172,7 +173,7 @@ import Simplex.Messaging.Notifications.Protocol (DeviceToken, NtfRegCode (NtfReg
|
||||
import Simplex.Messaging.Notifications.Server.Push.APNS (PNMessageData (..))
|
||||
import Simplex.Messaging.Notifications.Types
|
||||
import Simplex.Messaging.Parsers (parse)
|
||||
import Simplex.Messaging.Protocol (BrokerMsg, Cmd (..), EntityId, ErrorType (AUTH), MsgBody, MsgFlags (..), NtfServer, ProtoServerWithAuth, ProtocolTypeI (..), SMPMsgMeta, SParty (..), SProtocolType (..), SndPublicAuthKey, SubscriptionMode (..), UserProtocol, VersionSMPC, XFTPServerWithAuth, sndAuthKeySMPClientVersion)
|
||||
import Simplex.Messaging.Protocol (BrokerMsg, Cmd (..), EntityId, ErrorType (AUTH), MsgBody, MsgFlags (..), NtfServer, ProtoServerWithAuth, ProtocolType (..), ProtocolTypeI (..), SMPMsgMeta, SParty (..), SProtocolType (..), SndPublicAuthKey, SubscriptionMode (..), UserProtocol, VersionSMPC, sndAuthKeySMPClientVersion)
|
||||
import qualified Simplex.Messaging.Protocol as SMP
|
||||
import Simplex.Messaging.ServiceScheme (ServiceScheme (..))
|
||||
import qualified Simplex.Messaging.TMap as TM
|
||||
@@ -197,15 +198,18 @@ getSMPAgentClient = getSMPAgentClient_ 1
|
||||
{-# INLINE getSMPAgentClient #-}
|
||||
|
||||
getSMPAgentClient_ :: Int -> AgentConfig -> InitialAgentServers -> SQLiteStore -> Bool -> IO AgentClient
|
||||
getSMPAgentClient_ clientId cfg initServers store backgroundMode =
|
||||
liftIO $ newSMPAgentEnv cfg store >>= runReaderT runAgent
|
||||
getSMPAgentClient_ clientId cfg initServers@InitialAgentServers {smp, xftp} store backgroundMode =
|
||||
newSMPAgentEnv cfg store >>= runReaderT runAgent
|
||||
where
|
||||
runAgent = do
|
||||
liftIO $ checkServers "SMP" smp >> checkServers "XFTP" xftp
|
||||
currentTs <- liftIO getCurrentTime
|
||||
c@AgentClient {acThread} <- atomically . newAgentClient clientId initServers currentTs =<< ask
|
||||
t <- runAgentThreads c `forkFinally` const (liftIO $ disconnectAgentClient c)
|
||||
atomically . writeTVar acThread . Just =<< mkWeakThreadId t
|
||||
pure c
|
||||
checkServers protocol srvs =
|
||||
forM_ (M.assocs srvs) $ \(userId, srvs') -> checkUserServers ("getSMPAgentClient " <> protocol <> " " <> tshow userId) srvs'
|
||||
runAgentThreads c
|
||||
| backgroundMode = run c "subscriber" $ subscriber c
|
||||
| otherwise = do
|
||||
@@ -271,7 +275,7 @@ resumeAgentClient :: AgentClient -> IO ()
|
||||
resumeAgentClient c = atomically $ writeTVar (active c) True
|
||||
{-# INLINE resumeAgentClient #-}
|
||||
|
||||
createUser :: AgentClient -> NonEmpty SMPServerWithAuth -> NonEmpty XFTPServerWithAuth -> AE UserId
|
||||
createUser :: AgentClient -> NonEmpty (ServerCfg 'PSMP) -> NonEmpty (ServerCfg 'PXFTP) -> AE UserId
|
||||
createUser c = withAgentEnv c .: createUser' c
|
||||
{-# INLINE createUser #-}
|
||||
|
||||
@@ -600,11 +604,13 @@ logConnection c connected =
|
||||
let event = if connected then "connected to" else "disconnected from"
|
||||
in logInfo $ T.unwords ["client", tshow (clientId c), event, "Agent"]
|
||||
|
||||
createUser' :: AgentClient -> NonEmpty SMPServerWithAuth -> NonEmpty XFTPServerWithAuth -> AM UserId
|
||||
createUser' :: AgentClient -> NonEmpty (ServerCfg 'PSMP) -> NonEmpty (ServerCfg 'PXFTP) -> AM UserId
|
||||
createUser' c smp xftp = do
|
||||
liftIO $ checkUserServers "createUser SMP" smp
|
||||
liftIO $ checkUserServers "createUser XFTP" xftp
|
||||
userId <- withStore' c createUserRecord
|
||||
atomically $ TM.insert userId smp $ smpServers c
|
||||
atomically $ TM.insert userId xftp $ xftpServers c
|
||||
atomically $ TM.insert userId (mkUserServers smp) $ smpServers c
|
||||
atomically $ TM.insert userId (mkUserServers xftp) $ xftpServers c
|
||||
pure userId
|
||||
|
||||
deleteUser' :: AgentClient -> UserId -> Bool -> AM ()
|
||||
@@ -1815,10 +1821,17 @@ connectionStats = \case
|
||||
ratchetSyncSupported = connAgentVersion >= ratchetSyncSMPAgentVersion
|
||||
}
|
||||
|
||||
-- | Change servers to be used for creating new queues, in Reader monad
|
||||
setProtocolServers :: (ProtocolTypeI p, UserProtocol p) => AgentClient -> UserId -> NonEmpty (ProtoServerWithAuth p) -> IO ()
|
||||
setProtocolServers c userId srvs = atomically $ TM.insert userId srvs (userServers c)
|
||||
{-# INLINE setProtocolServers #-}
|
||||
-- | Change servers to be used for creating new queues.
|
||||
-- This function will set all servers as enabled in case all passed servers are disabled.
|
||||
setProtocolServers :: forall p. (ProtocolTypeI p, UserProtocol p) => AgentClient -> UserId -> NonEmpty (ServerCfg p) -> IO ()
|
||||
setProtocolServers c userId srvs = do
|
||||
checkUserServers "setProtocolServers" srvs
|
||||
atomically $ TM.insert userId (mkUserServers srvs) (userServers c)
|
||||
|
||||
checkUserServers :: Text -> NonEmpty (ServerCfg p) -> IO ()
|
||||
checkUserServers name srvs =
|
||||
unless (any (\ServerCfg {enabled} -> enabled) srvs) $
|
||||
logWarn (name <> ": all passed servers are disabled, using all servers.")
|
||||
|
||||
registerNtfToken' :: AgentClient -> DeviceToken -> NotificationsMode -> AM NtfTknStatus
|
||||
registerNtfToken' c suppliedDeviceToken suppliedNtfMode =
|
||||
|
||||
@@ -236,6 +236,7 @@ import Simplex.Messaging.Protocol
|
||||
ProtoServerWithAuth (..),
|
||||
Protocol (..),
|
||||
ProtocolServer (..),
|
||||
ProtocolType (..),
|
||||
ProtocolTypeI (..),
|
||||
QueueId,
|
||||
QueueIdsKeys (..),
|
||||
@@ -289,7 +290,7 @@ data AgentClient = AgentClient
|
||||
active :: TVar Bool,
|
||||
subQ :: TBQueue ATransmission,
|
||||
msgQ :: TBQueue (ServerTransmissionBatch SMPVersion ErrorType BrokerMsg),
|
||||
smpServers :: TMap UserId (NonEmpty SMPServerWithAuth),
|
||||
smpServers :: TMap UserId (UserServers 'PSMP),
|
||||
smpClients :: TMap SMPTransportSession SMPClientVar,
|
||||
-- smpProxiedRelays:
|
||||
-- SMPTransportSession defines connection from proxy to relay,
|
||||
@@ -297,7 +298,7 @@ data AgentClient = AgentClient
|
||||
smpProxiedRelays :: TMap SMPTransportSession SMPServerWithAuth,
|
||||
ntfServers :: TVar [NtfServer],
|
||||
ntfClients :: TMap NtfTransportSession NtfClientVar,
|
||||
xftpServers :: TMap UserId (NonEmpty XFTPServerWithAuth),
|
||||
xftpServers :: TMap UserId (UserServers 'PXFTP),
|
||||
xftpClients :: TMap XFTPTransportSession XFTPClientVar,
|
||||
useNetworkConfig :: TVar (NetworkConfig, NetworkConfig), -- (slow, fast) networks
|
||||
userNetworkInfo :: TVar UserNetworkInfo,
|
||||
@@ -456,12 +457,12 @@ newAgentClient clientId InitialAgentServers {smp, ntf, xftp, netCfg} currentTs a
|
||||
active <- newTVar True
|
||||
subQ <- newTBQueue qSize
|
||||
msgQ <- newTBQueue qSize
|
||||
smpServers <- newTVar smp
|
||||
smpServers <- newTVar $ M.map mkUserServers smp
|
||||
smpClients <- TM.empty
|
||||
smpProxiedRelays <- TM.empty
|
||||
ntfServers <- newTVar ntf
|
||||
ntfClients <- TM.empty
|
||||
xftpServers <- newTVar xftp
|
||||
xftpServers <- newTVar $ M.map mkUserServers xftp
|
||||
xftpClients <- TM.empty
|
||||
useNetworkConfig <- newTVar (slowNetworkConfig netCfg, netCfg)
|
||||
userNetworkInfo <- newTVar $ UserNetworkInfo UNOther True
|
||||
@@ -1069,7 +1070,7 @@ sendOrProxySMPCommand c userId destSrv cmdStr senderId sendCmdViaProxy sendCmdDi
|
||||
SPFAllow -> True
|
||||
SPFAllowProtected -> ipAddressProtected cfg destSrv
|
||||
SPFProhibit -> False
|
||||
unknownServer = maybe True (all ((destSrv /=) . protoServer)) <$> TM.lookup userId (userServers c)
|
||||
unknownServer = maybe True (notElem destSrv . knownSrvs) <$> TM.lookup userId (smpServers c)
|
||||
sendViaProxy destSess@(_, _, qId) = do
|
||||
r <- tryAgentError . withProxySession c destSess senderId ("PFWD " <> cmdStr) $ \(SMPConnectedClient smp _, proxySess) -> do
|
||||
r' <- liftClient SMP (clientServer smp) $ sendCmdViaProxy smp proxySess
|
||||
@@ -1904,7 +1905,7 @@ storeError = \case
|
||||
SEDatabaseBusy e -> CRITICAL True $ B.unpack e
|
||||
e -> INTERNAL $ show e
|
||||
|
||||
userServers :: forall p. (ProtocolTypeI p, UserProtocol p) => AgentClient -> TMap UserId (NonEmpty (ProtoServerWithAuth p))
|
||||
userServers :: forall p. (ProtocolTypeI p, UserProtocol p) => AgentClient -> TMap UserId (UserServers p)
|
||||
userServers c = case protocolTypeI @p of
|
||||
SPSMP -> smpServers c
|
||||
SPXFTP -> xftpServers c
|
||||
@@ -1926,7 +1927,7 @@ getNextServer c userId usedSrvs = withUserServers c userId $ \srvs ->
|
||||
withUserServers :: forall p a. (ProtocolTypeI p, UserProtocol p) => AgentClient -> UserId -> (NonEmpty (ProtoServerWithAuth p) -> AM a) -> AM a
|
||||
withUserServers c userId action =
|
||||
atomically (TM.lookup userId $ userServers c) >>= \case
|
||||
Just srvs -> action srvs
|
||||
Just srvs -> action $ enabledSrvs srvs
|
||||
_ -> throwE $ INTERNAL "unknown userId - no user servers"
|
||||
|
||||
withNextSrv :: forall p a. (ProtocolTypeI p, UserProtocol p) => AgentClient -> UserId -> TVar [ProtocolServer p] -> [ProtocolServer p] -> (ProtoServerWithAuth p -> AM a) -> AM a
|
||||
@@ -1935,7 +1936,7 @@ withNextSrv c userId usedSrvs initUsed action = do
|
||||
srvAuth@(ProtoServerWithAuth srv _) <- getNextServer c userId used
|
||||
atomically $ do
|
||||
srvs_ <- TM.lookup userId $ userServers c
|
||||
let unused = maybe [] ((\\ used) . map protoServer . L.toList) srvs_
|
||||
let unused = maybe [] ((\\ used) . map protoServer . L.toList . enabledSrvs) srvs_
|
||||
used' = if null unused then initUsed else srv : used
|
||||
writeTVar usedSrvs $! used'
|
||||
action srvAuth
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
{-# LANGUAGE NumericUnderscores #-}
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}
|
||||
|
||||
@@ -15,7 +16,12 @@ module Simplex.Messaging.Agent.Env.SQLite
|
||||
AM,
|
||||
AgentConfig (..),
|
||||
InitialAgentServers (..),
|
||||
ServerCfg (..),
|
||||
UserServers (..),
|
||||
NetworkConfig (..),
|
||||
presetServerCfg,
|
||||
enabledServerCfg,
|
||||
mkUserServers,
|
||||
defaultAgentConfig,
|
||||
defaultReconnectInterval,
|
||||
tryAgentError,
|
||||
@@ -39,10 +45,14 @@ import Control.Monad.Except
|
||||
import Control.Monad.IO.Unlift
|
||||
import Control.Monad.Reader
|
||||
import Crypto.Random
|
||||
import Data.Aeson (FromJSON (..), ToJSON (..))
|
||||
import qualified Data.Aeson.TH as JQ
|
||||
import Data.ByteArray (ScrubbedBytes)
|
||||
import Data.Int (Int64)
|
||||
import Data.List.NonEmpty (NonEmpty)
|
||||
import qualified Data.List.NonEmpty as L
|
||||
import Data.Map (Map)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Time.Clock (NominalDiffTime, nominalDay)
|
||||
import Data.Time.Clock.System (SystemTime (..))
|
||||
import Data.Word (Word16)
|
||||
@@ -59,7 +69,8 @@ import Simplex.Messaging.Crypto.Ratchet (VersionRangeE2E, supportedE2EEncryptVRa
|
||||
import Simplex.Messaging.Notifications.Client (defaultNTFClientConfig)
|
||||
import Simplex.Messaging.Notifications.Transport (NTFVersion)
|
||||
import Simplex.Messaging.Notifications.Types
|
||||
import Simplex.Messaging.Protocol (NtfServer, VersionRangeSMPC, XFTPServer, XFTPServerWithAuth, supportedSMPClientVRange)
|
||||
import Simplex.Messaging.Parsers (defaultJSON)
|
||||
import Simplex.Messaging.Protocol (NtfServer, ProtoServerWithAuth, ProtocolServer, ProtocolType (..), ProtocolTypeI, VersionRangeSMPC, XFTPServer, supportedSMPClientVRange)
|
||||
import Simplex.Messaging.TMap (TMap)
|
||||
import qualified Simplex.Messaging.TMap as TM
|
||||
import Simplex.Messaging.Transport (SMPVersion, TLS, Transport (..))
|
||||
@@ -74,12 +85,38 @@ type AM' a = ReaderT Env IO a
|
||||
type AM a = ExceptT AgentErrorType (ReaderT Env IO) a
|
||||
|
||||
data InitialAgentServers = InitialAgentServers
|
||||
{ smp :: Map UserId (NonEmpty SMPServerWithAuth),
|
||||
{ smp :: Map UserId (NonEmpty (ServerCfg 'PSMP)),
|
||||
ntf :: [NtfServer],
|
||||
xftp :: Map UserId (NonEmpty XFTPServerWithAuth),
|
||||
xftp :: Map UserId (NonEmpty (ServerCfg 'PXFTP)),
|
||||
netCfg :: NetworkConfig
|
||||
}
|
||||
|
||||
data ServerCfg p = ServerCfg
|
||||
{ server :: ProtoServerWithAuth p,
|
||||
preset :: Bool,
|
||||
tested :: Maybe Bool,
|
||||
enabled :: Bool
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
enabledServerCfg :: ProtoServerWithAuth p -> ServerCfg p
|
||||
enabledServerCfg server = ServerCfg {server, preset = False, tested = Nothing, enabled = True}
|
||||
|
||||
presetServerCfg :: Bool -> ProtoServerWithAuth p -> ServerCfg p
|
||||
presetServerCfg enabled server = ServerCfg {server, preset = True, tested = Nothing, enabled}
|
||||
|
||||
data UserServers p = UserServers
|
||||
{ enabledSrvs :: NonEmpty (ProtoServerWithAuth p),
|
||||
knownSrvs :: NonEmpty (ProtocolServer p)
|
||||
}
|
||||
|
||||
-- This function sets all servers as enabled in case all passed servers are disabled.
|
||||
mkUserServers :: NonEmpty (ServerCfg p) -> UserServers p
|
||||
mkUserServers srvs = UserServers {enabledSrvs, knownSrvs}
|
||||
where
|
||||
enabledSrvs = L.map (\ServerCfg {server} -> server) $ fromMaybe srvs $ L.nonEmpty $ L.filter (\ServerCfg {enabled} -> enabled) srvs
|
||||
knownSrvs = L.map (\ServerCfg {server = ProtoServerWithAuth srv _} -> srv) srvs
|
||||
|
||||
data AgentConfig = AgentConfig
|
||||
{ tcpPort :: Maybe ServiceName,
|
||||
rcvAuthAlg :: C.AuthAlg,
|
||||
@@ -294,3 +331,12 @@ updateRestartCount :: SystemTime -> RestartCount -> RestartCount
|
||||
updateRestartCount t (RestartCount minute count) = do
|
||||
let min' = systemSeconds t `div` 60
|
||||
in RestartCount min' $ if minute == min' then count + 1 else 1
|
||||
|
||||
$(pure [])
|
||||
|
||||
instance ProtocolTypeI p => ToJSON (ServerCfg p) where
|
||||
toEncoding = $(JQ.mkToEncoding defaultJSON ''ServerCfg)
|
||||
toJSON = $(JQ.mkToJSON defaultJSON ''ServerCfg)
|
||||
|
||||
instance ProtocolTypeI p => FromJSON (ServerCfg p) where
|
||||
parseJSON = $(JQ.mkParseJSON defaultJSON ''ServerCfg)
|
||||
|
||||
Reference in New Issue
Block a user