mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-09-01 18:08:36 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd586dee99 |
@@ -59,6 +59,7 @@ dependencies:
|
|||||||
- network-udp >= 0.0 && < 0.1
|
- network-udp >= 0.0 && < 0.1
|
||||||
- optparse-applicative >= 0.15 && < 0.17
|
- optparse-applicative >= 0.15 && < 0.17
|
||||||
- process == 1.6.*
|
- process == 1.6.*
|
||||||
|
- psqueues
|
||||||
- random >= 1.1 && < 1.3
|
- random >= 1.1 && < 1.3
|
||||||
- simple-logger == 0.1.*
|
- simple-logger == 0.1.*
|
||||||
- socks == 0.6.*
|
- socks == 0.6.*
|
||||||
|
|||||||
@@ -219,6 +219,7 @@ library
|
|||||||
, network-udp ==0.0.*
|
, network-udp ==0.0.*
|
||||||
, optparse-applicative >=0.15 && <0.17
|
, optparse-applicative >=0.15 && <0.17
|
||||||
, process ==1.6.*
|
, process ==1.6.*
|
||||||
|
, psqueues
|
||||||
, random >=1.1 && <1.3
|
, random >=1.1 && <1.3
|
||||||
, simple-logger ==0.1.*
|
, simple-logger ==0.1.*
|
||||||
, socks ==0.6.*
|
, socks ==0.6.*
|
||||||
@@ -293,6 +294,7 @@ executable ntf-server
|
|||||||
, network-udp ==0.0.*
|
, network-udp ==0.0.*
|
||||||
, optparse-applicative >=0.15 && <0.17
|
, optparse-applicative >=0.15 && <0.17
|
||||||
, process ==1.6.*
|
, process ==1.6.*
|
||||||
|
, psqueues
|
||||||
, random >=1.1 && <1.3
|
, random >=1.1 && <1.3
|
||||||
, simple-logger ==0.1.*
|
, simple-logger ==0.1.*
|
||||||
, simplexmq
|
, simplexmq
|
||||||
@@ -368,6 +370,7 @@ executable smp-agent
|
|||||||
, network-udp ==0.0.*
|
, network-udp ==0.0.*
|
||||||
, optparse-applicative >=0.15 && <0.17
|
, optparse-applicative >=0.15 && <0.17
|
||||||
, process ==1.6.*
|
, process ==1.6.*
|
||||||
|
, psqueues
|
||||||
, random >=1.1 && <1.3
|
, random >=1.1 && <1.3
|
||||||
, simple-logger ==0.1.*
|
, simple-logger ==0.1.*
|
||||||
, simplexmq
|
, simplexmq
|
||||||
@@ -443,6 +446,7 @@ executable smp-server
|
|||||||
, network-udp ==0.0.*
|
, network-udp ==0.0.*
|
||||||
, optparse-applicative >=0.15 && <0.17
|
, optparse-applicative >=0.15 && <0.17
|
||||||
, process ==1.6.*
|
, process ==1.6.*
|
||||||
|
, psqueues
|
||||||
, random >=1.1 && <1.3
|
, random >=1.1 && <1.3
|
||||||
, simple-logger ==0.1.*
|
, simple-logger ==0.1.*
|
||||||
, simplexmq
|
, simplexmq
|
||||||
@@ -518,6 +522,7 @@ executable xftp
|
|||||||
, network-udp ==0.0.*
|
, network-udp ==0.0.*
|
||||||
, optparse-applicative >=0.15 && <0.17
|
, optparse-applicative >=0.15 && <0.17
|
||||||
, process ==1.6.*
|
, process ==1.6.*
|
||||||
|
, psqueues
|
||||||
, random >=1.1 && <1.3
|
, random >=1.1 && <1.3
|
||||||
, simple-logger ==0.1.*
|
, simple-logger ==0.1.*
|
||||||
, simplexmq
|
, simplexmq
|
||||||
@@ -593,6 +598,7 @@ executable xftp-server
|
|||||||
, network-udp ==0.0.*
|
, network-udp ==0.0.*
|
||||||
, optparse-applicative >=0.15 && <0.17
|
, optparse-applicative >=0.15 && <0.17
|
||||||
, process ==1.6.*
|
, process ==1.6.*
|
||||||
|
, psqueues
|
||||||
, random >=1.1 && <1.3
|
, random >=1.1 && <1.3
|
||||||
, simple-logger ==0.1.*
|
, simple-logger ==0.1.*
|
||||||
, simplexmq
|
, simplexmq
|
||||||
@@ -707,6 +713,7 @@ test-suite simplexmq-test
|
|||||||
, network-udp ==0.0.*
|
, network-udp ==0.0.*
|
||||||
, optparse-applicative >=0.15 && <0.17
|
, optparse-applicative >=0.15 && <0.17
|
||||||
, process ==1.6.*
|
, process ==1.6.*
|
||||||
|
, psqueues
|
||||||
, random >=1.1 && <1.3
|
, random >=1.1 && <1.3
|
||||||
, silently ==1.2.*
|
, silently ==1.2.*
|
||||||
, simple-logger ==0.1.*
|
, simple-logger ==0.1.*
|
||||||
|
|||||||
@@ -136,19 +136,21 @@ import qualified Data.List.NonEmpty as L
|
|||||||
import Data.Map.Strict (Map)
|
import Data.Map.Strict (Map)
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing, mapMaybe)
|
import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing, mapMaybe)
|
||||||
|
import qualified Data.OrdPSQ as OP
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
import Data.Time.Clock.System (systemToUTCTime)
|
import Data.Time.Clock.System (getSystemTime, systemToUTCTime)
|
||||||
import Data.Traversable (mapAccumL)
|
import Data.Traversable (mapAccumL)
|
||||||
import Data.Word (Word16)
|
import Data.Word (Word16)
|
||||||
|
import Debug.Trace
|
||||||
import Simplex.FileTransfer.Agent (closeXFTPAgent, deleteSndFileInternal, deleteSndFileRemote, deleteSndFilesInternal, deleteSndFilesRemote, startXFTPWorkers, toFSFilePath, xftpDeleteRcvFile', xftpDeleteRcvFiles', xftpReceiveFile', xftpSendDescription', xftpSendFile')
|
import Simplex.FileTransfer.Agent (closeXFTPAgent, deleteSndFileInternal, deleteSndFileRemote, deleteSndFilesInternal, deleteSndFilesRemote, startXFTPWorkers, toFSFilePath, xftpDeleteRcvFile', xftpDeleteRcvFiles', xftpReceiveFile', xftpSendDescription', xftpSendFile')
|
||||||
import Simplex.FileTransfer.Description (ValidFileDescription)
|
import Simplex.FileTransfer.Description (ValidFileDescription)
|
||||||
import Simplex.FileTransfer.Protocol (FileParty (..))
|
import Simplex.FileTransfer.Protocol (FileParty (..))
|
||||||
import Simplex.FileTransfer.Util (removePath)
|
import Simplex.FileTransfer.Util (removePath)
|
||||||
import Simplex.Messaging.Agent.Client
|
import Simplex.Messaging.Agent.Client
|
||||||
import Simplex.Messaging.Agent.Env.SQLite
|
import Simplex.Messaging.Agent.Env.SQLite
|
||||||
import Simplex.Messaging.Agent.Lock (withLock', withLock)
|
import Simplex.Messaging.Agent.Lock (withLock, withLock')
|
||||||
import Simplex.Messaging.Agent.NtfSubSupervisor
|
import Simplex.Messaging.Agent.NtfSubSupervisor
|
||||||
import Simplex.Messaging.Agent.Protocol
|
import Simplex.Messaging.Agent.Protocol
|
||||||
import Simplex.Messaging.Agent.RetryInterval
|
import Simplex.Messaging.Agent.RetryInterval
|
||||||
@@ -159,9 +161,10 @@ import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations
|
|||||||
import Simplex.Messaging.Client (ProtocolClient (..), ServerTransmission)
|
import Simplex.Messaging.Client (ProtocolClient (..), ServerTransmission)
|
||||||
import qualified Simplex.Messaging.Crypto as C
|
import qualified Simplex.Messaging.Crypto as C
|
||||||
import Simplex.Messaging.Crypto.File (CryptoFile, CryptoFileArgs)
|
import Simplex.Messaging.Crypto.File (CryptoFile, CryptoFileArgs)
|
||||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption, PQSupport (..), pattern PQEncOn, pattern PQEncOff, pattern PQSupportOn, pattern PQSupportOff)
|
import Simplex.Messaging.Crypto.Ratchet (PQEncryption, PQSupport (..), pattern PQEncOff, pattern PQEncOn, pattern PQSupportOff, pattern PQSupportOn)
|
||||||
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
||||||
import Simplex.Messaging.Encoding
|
import Simplex.Messaging.Encoding
|
||||||
|
import qualified Simplex.Messaging.Encoding.Base64 as B64
|
||||||
import Simplex.Messaging.Encoding.String
|
import Simplex.Messaging.Encoding.String
|
||||||
import Simplex.Messaging.Notifications.Protocol (DeviceToken, NtfRegCode (NtfRegCode), NtfTknStatus (..), NtfTokenId)
|
import Simplex.Messaging.Notifications.Protocol (DeviceToken, NtfRegCode (NtfRegCode), NtfTknStatus (..), NtfTokenId)
|
||||||
import Simplex.Messaging.Notifications.Server.Push.APNS (PNMessageData (..))
|
import Simplex.Messaging.Notifications.Server.Push.APNS (PNMessageData (..))
|
||||||
@@ -197,7 +200,7 @@ getSMPAgentClient_ clientId cfg initServers store backgroundMode =
|
|||||||
liftIO $ newSMPAgentEnv cfg store >>= runReaderT runAgent
|
liftIO $ newSMPAgentEnv cfg store >>= runReaderT runAgent
|
||||||
where
|
where
|
||||||
runAgent = do
|
runAgent = do
|
||||||
c@AgentClient {acThread} <- atomically . newAgentClient clientId initServers =<< ask
|
c@AgentClient {acThread} <- atomically . newAgentClient clientId initServers =<< ask
|
||||||
t <- runAgentThreads c `forkFinally` const (liftIO $ disconnectAgentClient c)
|
t <- runAgentThreads c `forkFinally` const (liftIO $ disconnectAgentClient c)
|
||||||
atomically . writeTVar acThread . Just =<< mkWeakThreadId t
|
atomically . writeTVar acThread . Just =<< mkWeakThreadId t
|
||||||
pure c
|
pure c
|
||||||
@@ -238,7 +241,7 @@ createUser c = withAgentEnv c .: createUser' c
|
|||||||
{-# INLINE createUser #-}
|
{-# INLINE createUser #-}
|
||||||
|
|
||||||
-- | Delete user record optionally deleting all user's connections on SMP servers
|
-- | Delete user record optionally deleting all user's connections on SMP servers
|
||||||
deleteUser :: AgentClient -> UserId -> Bool -> AE ()
|
deleteUser :: AgentClient -> UserId -> Bool -> AE ()
|
||||||
deleteUser c = withAgentEnv c .: deleteUser' c
|
deleteUser c = withAgentEnv c .: deleteUser' c
|
||||||
{-# INLINE deleteUser #-}
|
{-# INLINE deleteUser #-}
|
||||||
|
|
||||||
@@ -769,7 +772,7 @@ compatibleContactUri (CRContactUri ConnReqUriData {crAgentVRange, crSmpQueues =
|
|||||||
AgentConfig {smpClientVRange, smpAgentVRange} <- asks config
|
AgentConfig {smpClientVRange, smpAgentVRange} <- asks config
|
||||||
pure $
|
pure $
|
||||||
(,)
|
(,)
|
||||||
<$> (qUri `compatibleVersion` smpClientVRange)
|
<$> (qUri `compatibleVersion` smpClientVRange)
|
||||||
<*> (crAgentVRange `compatibleVersion` smpAgentVRange pqSup)
|
<*> (crAgentVRange `compatibleVersion` smpAgentVRange pqSup)
|
||||||
|
|
||||||
versionPQSupport_ :: VersionSMPA -> Maybe CR.VersionE2E -> PQSupport
|
versionPQSupport_ :: VersionSMPA -> Maybe CR.VersionE2E -> PQSupport
|
||||||
@@ -1190,7 +1193,7 @@ enqueueMessage c cData sq msgFlags aMessage =
|
|||||||
{-# INLINE enqueueMessage #-}
|
{-# INLINE enqueueMessage #-}
|
||||||
|
|
||||||
-- this function is used only for sending messages in batch, it returns the list of successes to enqueue additional deliveries
|
-- this function is used only for sending messages in batch, it returns the list of successes to enqueue additional deliveries
|
||||||
enqueueMessageB :: forall t. (Traversable t) => AgentClient -> t (Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage)) -> AM' (t (Either AgentErrorType ((AgentMsgId, PQEncryption), Maybe (ConnData, [SndQueue], AgentMsgId))))
|
enqueueMessageB :: forall t. Traversable t => AgentClient -> t (Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage)) -> AM' (t (Either AgentErrorType ((AgentMsgId, PQEncryption), Maybe (ConnData, [SndQueue], AgentMsgId))))
|
||||||
enqueueMessageB c reqs = do
|
enqueueMessageB c reqs = do
|
||||||
cfg <- asks config
|
cfg <- asks config
|
||||||
reqMids <- withStoreBatch c $ \db -> fmap (bindRight $ storeSentMsg db cfg) reqs
|
reqMids <- withStoreBatch c $ \db -> fmap (bindRight $ storeSentMsg db cfg) reqs
|
||||||
@@ -1223,7 +1226,7 @@ enqueueSavedMessage :: AgentClient -> ConnData -> AgentMsgId -> SndQueue -> AM'
|
|||||||
enqueueSavedMessage c cData msgId sq = enqueueSavedMessageB c $ Identity (cData, [sq], msgId)
|
enqueueSavedMessage c cData msgId sq = enqueueSavedMessageB c $ Identity (cData, [sq], msgId)
|
||||||
{-# INLINE enqueueSavedMessage #-}
|
{-# INLINE enqueueSavedMessage #-}
|
||||||
|
|
||||||
enqueueSavedMessageB :: (Foldable t) => AgentClient -> t (ConnData, [SndQueue], AgentMsgId) -> AM' ()
|
enqueueSavedMessageB :: Foldable t => AgentClient -> t (ConnData, [SndQueue], AgentMsgId) -> AM' ()
|
||||||
enqueueSavedMessageB c reqs = do
|
enqueueSavedMessageB c reqs = do
|
||||||
-- saving to the database is in the start to avoid race conditions when delivery is read from queue before it is saved
|
-- saving to the database is in the start to avoid race conditions when delivery is read from queue before it is saved
|
||||||
void $ withStoreBatch' c $ \db -> concatMap (storeDeliveries db) reqs
|
void $ withStoreBatch' c $ \db -> concatMap (storeDeliveries db) reqs
|
||||||
@@ -2021,7 +2024,18 @@ cleanupManager c@AgentClient {subQ} = do
|
|||||||
notify :: forall e. AEntityI e => EntityId -> ACommand 'Agent e -> AM ()
|
notify :: forall e. AEntityI e => EntityId -> ACommand 'Agent e -> AM ()
|
||||||
notify entId cmd = atomically $ writeTBQueue subQ ("", entId, APC (sAEntity @e) cmd)
|
notify entId cmd = atomically $ writeTBQueue subQ ("", entId, APC (sAEntity @e) cmd)
|
||||||
|
|
||||||
data ACKd = ACKd | ACKPending
|
data ACKd = ACKd | ACKPending String SMP.RecipientId SMP.EntityId
|
||||||
|
|
||||||
|
trackAcks :: AgentClient -> AM ACKd -> AM ()
|
||||||
|
trackAcks AgentClient {agentEnv = Env {acks}} a =
|
||||||
|
a >>= \case
|
||||||
|
ACKd -> pure ()
|
||||||
|
ACKPending label rId msgId -> liftIO $ do
|
||||||
|
now <- liftIO getSystemTime
|
||||||
|
atomically $ modifyTVar' acks $ OP.insert (rId, msgId) now label
|
||||||
|
traceEventIO $ mconcat ["ACK-PENDING ", logEntity rId, "/", logEntity msgId, " ", label]
|
||||||
|
where
|
||||||
|
logEntity bs = B.unpack . B64.encode $ B.take 3 bs
|
||||||
|
|
||||||
-- | make sure to ACK or throw in each message processing branch
|
-- | make sure to ACK or throw in each message processing branch
|
||||||
-- it cannot be finally, unfortunately, as sometimes it needs to be ACK+DEL
|
-- it cannot be finally, unfortunately, as sometimes it needs to be ACK+DEL
|
||||||
@@ -2037,7 +2051,7 @@ processSMPTransmission c@AgentClient {smpClients, subQ} (tSess@(_, srv, _), _v,
|
|||||||
cData@ConnData {userId, connId, connAgentVersion, ratchetSyncState = rss} =
|
cData@ConnData {userId, connId, connAgentVersion, ratchetSyncState = rss} =
|
||||||
withConnLock c connId "processSMP" $ case cmd of
|
withConnLock c connId "processSMP" $ case cmd of
|
||||||
SMP.MSG msg@SMP.RcvMessage {msgId = srvMsgId} ->
|
SMP.MSG msg@SMP.RcvMessage {msgId = srvMsgId} ->
|
||||||
void . handleNotifyAck $ do
|
trackAcks c . handleNotifyAck $ do
|
||||||
msg' <- decryptSMPMessage rq msg
|
msg' <- decryptSMPMessage rq msg
|
||||||
ack' <- handleNotifyAck $ case msg' of
|
ack' <- handleNotifyAck $ case msg' of
|
||||||
SMP.ClientRcvMsgBody {msgTs = srvTs, msgFlags, msgBody} -> processClientMsg srvTs msgFlags msgBody
|
SMP.ClientRcvMsgBody {msgTs = srvTs, msgFlags, msgBody} -> processClientMsg srvTs msgFlags msgBody
|
||||||
@@ -2094,7 +2108,7 @@ processSMPTransmission c@AgentClient {smpClients, subQ} (tSess@(_, srv, _), _v,
|
|||||||
A_MSG body -> do
|
A_MSG body -> do
|
||||||
logServer "<--" c srv rId $ "MSG <MSG>:" <> logSecret srvMsgId
|
logServer "<--" c srv rId $ "MSG <MSG>:" <> logSecret srvMsgId
|
||||||
notify $ MSG msgMeta msgFlags body
|
notify $ MSG msgMeta msgFlags body
|
||||||
pure ACKPending
|
pure $ ACKPending "R/A_MSG" rId srvMsgId
|
||||||
A_RCVD rcpts -> qDuplex conn'' "RCVD" $ messagesRcvd rcpts msgMeta
|
A_RCVD rcpts -> qDuplex conn'' "RCVD" $ messagesRcvd rcpts msgMeta
|
||||||
QCONT addr -> qDuplexAckDel conn'' "QCONT" $ continueSending srvMsgId addr
|
QCONT addr -> qDuplexAckDel conn'' "QCONT" $ continueSending srvMsgId addr
|
||||||
QADD qs -> qDuplexAckDel conn'' "QADD" $ qAddMsg srvMsgId qs
|
QADD qs -> qDuplexAckDel conn'' "QADD" $ qAddMsg srvMsgId qs
|
||||||
@@ -2126,7 +2140,7 @@ processSMPTransmission c@AgentClient {smpClients, subQ} (tSess@(_, srv, _), _v,
|
|||||||
AgentMessage _ (A_MSG body) -> do
|
AgentMessage _ (A_MSG body) -> do
|
||||||
logServer "<--" c srv rId $ "MSG <MSG>:" <> logSecret srvMsgId
|
logServer "<--" c srv rId $ "MSG <MSG>:" <> logSecret srvMsgId
|
||||||
notify $ MSG msgMeta msgFlags body
|
notify $ MSG msgMeta msgFlags body
|
||||||
pure ACKPending
|
pure $ ACKPending "L-DUPLICATE/A_MSG" rId srvMsgId
|
||||||
_ -> ack
|
_ -> ack
|
||||||
_ -> checkDuplicateHash e encryptedMsgHash >> ack
|
_ -> checkDuplicateHash e encryptedMsgHash >> ack
|
||||||
Left (AGENT (A_CRYPTO e)) -> do
|
Left (AGENT (A_CRYPTO e)) -> do
|
||||||
@@ -2317,7 +2331,7 @@ processSMPTransmission c@AgentClient {smpClients, subQ} (tSess@(_, srv, _), _v,
|
|||||||
logServer "<--" c srv rId $ "MSG <RCPT>:" <> logSecret srvMsgId
|
logServer "<--" c srv rId $ "MSG <RCPT>:" <> logSecret srvMsgId
|
||||||
rs <- forM rcpts $ \rcpt -> clientReceipt rcpt `catchAgentError` \e -> notify (ERR e) $> Nothing
|
rs <- forM rcpts $ \rcpt -> clientReceipt rcpt `catchAgentError` \e -> notify (ERR e) $> Nothing
|
||||||
case L.nonEmpty . catMaybes $ L.toList rs of
|
case L.nonEmpty . catMaybes $ L.toList rs of
|
||||||
Just rs' -> notify (RCVD msgMeta rs') $> ACKPending
|
Just rs' -> notify (RCVD msgMeta rs') $> ACKPending "RCVD" rId srvMsgId
|
||||||
Nothing -> ack
|
Nothing -> ack
|
||||||
where
|
where
|
||||||
ack :: AM ACKd
|
ack :: AM ACKd
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ import qualified Data.List.NonEmpty as L
|
|||||||
import Data.Map.Strict (Map)
|
import Data.Map.Strict (Map)
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
import Data.Maybe (isJust, isNothing, listToMaybe)
|
import Data.Maybe (isJust, isNothing, listToMaybe)
|
||||||
|
import qualified Data.OrdPSQ as OP
|
||||||
import Data.Set (Set)
|
import Data.Set (Set)
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
@@ -160,12 +161,14 @@ import Data.Text.Encoding
|
|||||||
import Data.Time (UTCTime, defaultTimeLocale, formatTime, getCurrentTime)
|
import Data.Time (UTCTime, defaultTimeLocale, formatTime, getCurrentTime)
|
||||||
import Data.Time.Clock.System (getSystemTime)
|
import Data.Time.Clock.System (getSystemTime)
|
||||||
import Data.Word (Word16)
|
import Data.Word (Word16)
|
||||||
|
import Debug.Trace
|
||||||
|
import GHC.Conc (unsafeIOToSTM)
|
||||||
import Network.Socket (HostName)
|
import Network.Socket (HostName)
|
||||||
import Simplex.FileTransfer.Client (XFTPChunkSpec (..), XFTPClient, XFTPClientConfig (..), XFTPClientError)
|
import Simplex.FileTransfer.Client (XFTPChunkSpec (..), XFTPClient, XFTPClientConfig (..), XFTPClientError)
|
||||||
import qualified Simplex.FileTransfer.Client as X
|
import qualified Simplex.FileTransfer.Client as X
|
||||||
import Simplex.FileTransfer.Description (ChunkReplicaId (..), FileDigest (..), kb)
|
import Simplex.FileTransfer.Description (ChunkReplicaId (..), FileDigest (..), kb)
|
||||||
import Simplex.FileTransfer.Protocol (FileInfo (..), FileResponse)
|
import Simplex.FileTransfer.Protocol (FileInfo (..), FileResponse)
|
||||||
import Simplex.FileTransfer.Transport (XFTPRcvChunkSpec (..), XFTPErrorType (DIGEST), XFTPVersion)
|
import Simplex.FileTransfer.Transport (XFTPErrorType (DIGEST), XFTPRcvChunkSpec (..), XFTPVersion)
|
||||||
import Simplex.FileTransfer.Types (DeletedSndChunkReplica (..), NewSndChunkReplica (..), RcvFileChunkReplica (..), SndFileChunk (..), SndFileChunkReplica (..))
|
import Simplex.FileTransfer.Types (DeletedSndChunkReplica (..), NewSndChunkReplica (..), RcvFileChunkReplica (..), SndFileChunk (..), SndFileChunkReplica (..))
|
||||||
import Simplex.FileTransfer.Util (uniqueCombine)
|
import Simplex.FileTransfer.Util (uniqueCombine)
|
||||||
import Simplex.Messaging.Agent.Env.SQLite
|
import Simplex.Messaging.Agent.Env.SQLite
|
||||||
@@ -195,6 +198,7 @@ import Simplex.Messaging.Protocol
|
|||||||
ErrorType,
|
ErrorType,
|
||||||
MsgFlags (..),
|
MsgFlags (..),
|
||||||
MsgId,
|
MsgId,
|
||||||
|
NtfPublicAuthKey,
|
||||||
NtfServer,
|
NtfServer,
|
||||||
NtfServerWithAuth,
|
NtfServerWithAuth,
|
||||||
ProtoServer,
|
ProtoServer,
|
||||||
@@ -206,22 +210,21 @@ import Simplex.Messaging.Protocol
|
|||||||
QueueIdsKeys (..),
|
QueueIdsKeys (..),
|
||||||
RcvMessage (..),
|
RcvMessage (..),
|
||||||
RcvNtfPublicDhKey,
|
RcvNtfPublicDhKey,
|
||||||
NtfPublicAuthKey,
|
|
||||||
SMPMsgMeta (..),
|
SMPMsgMeta (..),
|
||||||
SProtocolType (..),
|
SProtocolType (..),
|
||||||
SndPublicAuthKey,
|
SndPublicAuthKey,
|
||||||
SubscriptionMode (..),
|
SubscriptionMode (..),
|
||||||
UserProtocol,
|
UserProtocol,
|
||||||
|
VersionRangeSMPC,
|
||||||
|
VersionSMPC,
|
||||||
XFTPServer,
|
XFTPServer,
|
||||||
XFTPServerWithAuth,
|
XFTPServerWithAuth,
|
||||||
VersionSMPC,
|
|
||||||
VersionRangeSMPC,
|
|
||||||
sameSrvAddr',
|
sameSrvAddr',
|
||||||
)
|
)
|
||||||
import qualified Simplex.Messaging.Protocol as SMP
|
import qualified Simplex.Messaging.Protocol as SMP
|
||||||
import Simplex.Messaging.Transport (SMPVersion)
|
|
||||||
import Simplex.Messaging.TMap (TMap)
|
import Simplex.Messaging.TMap (TMap)
|
||||||
import qualified Simplex.Messaging.TMap as TM
|
import qualified Simplex.Messaging.TMap as TM
|
||||||
|
import Simplex.Messaging.Transport (SMPVersion)
|
||||||
import Simplex.Messaging.Transport.Client (TransportHost)
|
import Simplex.Messaging.Transport.Client (TransportHost)
|
||||||
import Simplex.Messaging.Util
|
import Simplex.Messaging.Util
|
||||||
import Simplex.Messaging.Version
|
import Simplex.Messaging.Version
|
||||||
@@ -531,7 +534,8 @@ getSMPServerClient c@AgentClient {active, smpClients, msgQ} tSess@(userId, srv,
|
|||||||
g <- asks random
|
g <- asks random
|
||||||
env <- ask
|
env <- ask
|
||||||
liftError' (protocolClientError SMP $ B.unpack $ strEncode srv) $
|
liftError' (protocolClientError SMP $ B.unpack $ strEncode srv) $
|
||||||
getProtocolClient g tSess cfg (Just msgQ) $ clientDisconnected env v
|
getProtocolClient g tSess cfg (Just msgQ) $
|
||||||
|
clientDisconnected env v
|
||||||
|
|
||||||
clientDisconnected :: Env -> SMPClientVar -> SMPClient -> IO ()
|
clientDisconnected :: Env -> SMPClientVar -> SMPClient -> IO ()
|
||||||
clientDisconnected env v client = do
|
clientDisconnected env v client = do
|
||||||
@@ -634,7 +638,8 @@ getNtfServerClient c@AgentClient {active, ntfClients} tSess@(userId, srv, _) = d
|
|||||||
cfg <- lift $ getClientConfig c ntfCfg
|
cfg <- lift $ getClientConfig c ntfCfg
|
||||||
g <- asks random
|
g <- asks random
|
||||||
liftError' (protocolClientError NTF $ B.unpack $ strEncode srv) $
|
liftError' (protocolClientError NTF $ B.unpack $ strEncode srv) $
|
||||||
getProtocolClient g tSess cfg Nothing $ clientDisconnected v
|
getProtocolClient g tSess cfg Nothing $
|
||||||
|
clientDisconnected v
|
||||||
|
|
||||||
clientDisconnected :: NtfClientVar -> NtfClient -> IO ()
|
clientDisconnected :: NtfClientVar -> NtfClient -> IO ()
|
||||||
clientDisconnected v client = do
|
clientDisconnected v client = do
|
||||||
@@ -656,7 +661,8 @@ getXFTPServerClient c@AgentClient {active, xftpClients, useNetworkConfig} tSess@
|
|||||||
cfg <- asks $ xftpCfg . config
|
cfg <- asks $ xftpCfg . config
|
||||||
xftpNetworkConfig <- readTVarIO useNetworkConfig
|
xftpNetworkConfig <- readTVarIO useNetworkConfig
|
||||||
liftError' (protocolClientError XFTP $ B.unpack $ strEncode srv) $
|
liftError' (protocolClientError XFTP $ B.unpack $ strEncode srv) $
|
||||||
X.getXFTPClient tSess cfg {xftpNetworkConfig} $ clientDisconnected v
|
X.getXFTPClient tSess cfg {xftpNetworkConfig} $
|
||||||
|
clientDisconnected v
|
||||||
|
|
||||||
clientDisconnected :: XFTPClientVar -> XFTPClient -> IO ()
|
clientDisconnected :: XFTPClientVar -> XFTPClient -> IO ()
|
||||||
clientDisconnected v client = do
|
clientDisconnected v client = do
|
||||||
@@ -1258,9 +1264,22 @@ disableQueuesNtfs :: AgentClient -> [RcvQueue] -> AM' [(RcvQueue, Either AgentEr
|
|||||||
disableQueuesNtfs = sendTSessionBatches "NDEL" 90 id $ sendBatch disableSMPQueuesNtfs
|
disableQueuesNtfs = sendTSessionBatches "NDEL" 90 id $ sendBatch disableSMPQueuesNtfs
|
||||||
|
|
||||||
sendAck :: AgentClient -> RcvQueue -> MsgId -> AM ()
|
sendAck :: AgentClient -> RcvQueue -> MsgId -> AM ()
|
||||||
sendAck c rq@RcvQueue {rcvId, rcvPrivateKey} msgId = do
|
sendAck c@AgentClient {agentEnv = Env {acks, acksLate}} rq@RcvQueue {rcvId, rcvPrivateKey} msgId = do
|
||||||
withSMPClient c rq ("ACK:" <> logSecret msgId) $ \smp ->
|
withSMPClient c rq ("ACK:" <> logSecret msgId) $ \smp -> do
|
||||||
ackSMPMessage smp rcvPrivateKey rcvId msgId
|
ackSMPMessage smp rcvPrivateKey rcvId msgId
|
||||||
|
let k = (rcvId, msgId)
|
||||||
|
atomically $
|
||||||
|
OP.deleteView k <$> readTVar acks >>= \case
|
||||||
|
Just (_time, label, op') -> do
|
||||||
|
writeTVar acks op'
|
||||||
|
unsafeIOToSTM . traceEventIO $ mconcat ["ACK-MET ", B.unpack $ logSecret rcvId, "/", B.unpack $ logSecret msgId, " ", label]
|
||||||
|
Nothing ->
|
||||||
|
OP.deleteView k <$> readTVar acksLate >>= \case
|
||||||
|
Just (_time, label, op') -> do
|
||||||
|
writeTVar acksLate op'
|
||||||
|
unsafeIOToSTM . traceEventIO $ mconcat ["ACK-LATE ", B.unpack $ logSecret rcvId, "/", B.unpack $ logSecret msgId, " ", label]
|
||||||
|
Nothing ->
|
||||||
|
unsafeIOToSTM . traceEventIO $ mconcat ["ACK-DUPL ", B.unpack $ logSecret rcvId, "/", B.unpack $ logSecret msgId]
|
||||||
atomically $ releaseGetLock c rq
|
atomically $ releaseGetLock c rq
|
||||||
|
|
||||||
hasGetLock :: AgentClient -> RcvQueue -> STM Bool
|
hasGetLock :: AgentClient -> RcvQueue -> STM Bool
|
||||||
|
|||||||
@@ -35,17 +35,23 @@ module Simplex.Messaging.Agent.Env.SQLite
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Control.Concurrent (threadDelay)
|
||||||
|
import Control.Monad
|
||||||
import Control.Monad.Except
|
import Control.Monad.Except
|
||||||
import Control.Monad.IO.Unlift
|
import Control.Monad.IO.Unlift
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Crypto.Random
|
import Crypto.Random
|
||||||
import Data.ByteArray (ScrubbedBytes)
|
import Data.ByteArray (ScrubbedBytes)
|
||||||
|
import qualified Data.ByteString.Char8 as B
|
||||||
import Data.Int (Int64)
|
import Data.Int (Int64)
|
||||||
import Data.List.NonEmpty (NonEmpty)
|
import Data.List.NonEmpty (NonEmpty)
|
||||||
import Data.Map (Map)
|
import Data.Map (Map)
|
||||||
|
import Data.OrdPSQ (OrdPSQ)
|
||||||
|
import qualified Data.OrdPSQ as OP
|
||||||
import Data.Time.Clock (NominalDiffTime, nominalDay)
|
import Data.Time.Clock (NominalDiffTime, nominalDay)
|
||||||
import Data.Time.Clock.System (SystemTime (..))
|
import Data.Time.Clock.System (SystemTime (..), getSystemTime)
|
||||||
import Data.Word (Word16)
|
import Data.Word (Word16)
|
||||||
|
import Debug.Trace
|
||||||
import Network.Socket
|
import Network.Socket
|
||||||
import Numeric.Natural
|
import Numeric.Natural
|
||||||
import Simplex.FileTransfer.Client (XFTPClientConfig (..), defaultXFTPClientConfig)
|
import Simplex.FileTransfer.Client (XFTPClientConfig (..), defaultXFTPClientConfig)
|
||||||
@@ -57,10 +63,11 @@ import Simplex.Messaging.Client
|
|||||||
import Simplex.Messaging.Client.Agent ()
|
import Simplex.Messaging.Client.Agent ()
|
||||||
import qualified Simplex.Messaging.Crypto as C
|
import qualified Simplex.Messaging.Crypto as C
|
||||||
import Simplex.Messaging.Crypto.Ratchet (PQSupport, VersionRangeE2E, supportedE2EEncryptVRange)
|
import Simplex.Messaging.Crypto.Ratchet (PQSupport, VersionRangeE2E, supportedE2EEncryptVRange)
|
||||||
|
import qualified Simplex.Messaging.Encoding.Base64 as B64
|
||||||
import Simplex.Messaging.Notifications.Client (defaultNTFClientConfig)
|
import Simplex.Messaging.Notifications.Client (defaultNTFClientConfig)
|
||||||
import Simplex.Messaging.Notifications.Transport (NTFVersion)
|
import Simplex.Messaging.Notifications.Transport (NTFVersion)
|
||||||
import Simplex.Messaging.Notifications.Types
|
import Simplex.Messaging.Notifications.Types
|
||||||
import Simplex.Messaging.Protocol (NtfServer, VersionRangeSMPC, XFTPServer, XFTPServerWithAuth, supportedSMPClientVRange)
|
import Simplex.Messaging.Protocol (EntityId, NtfServer, RecipientId, VersionRangeSMPC, XFTPServer, XFTPServerWithAuth, supportedSMPClientVRange)
|
||||||
import Simplex.Messaging.TMap (TMap)
|
import Simplex.Messaging.TMap (TMap)
|
||||||
import qualified Simplex.Messaging.TMap as TM
|
import qualified Simplex.Messaging.TMap as TM
|
||||||
import Simplex.Messaging.Transport (SMPVersion, TLS, Transport (..))
|
import Simplex.Messaging.Transport (SMPVersion, TLS, Transport (..))
|
||||||
@@ -68,6 +75,7 @@ import Simplex.Messaging.Transport.Client (defaultSMPPort)
|
|||||||
import Simplex.Messaging.Util (allFinally, catchAllErrors, catchAllErrors', tryAllErrors, tryAllErrors')
|
import Simplex.Messaging.Util (allFinally, catchAllErrors, catchAllErrors', tryAllErrors, tryAllErrors')
|
||||||
import System.Random (StdGen, newStdGen)
|
import System.Random (StdGen, newStdGen)
|
||||||
import UnliftIO (Async, SomeException)
|
import UnliftIO (Async, SomeException)
|
||||||
|
import UnliftIO.Async (async, cancel)
|
||||||
import UnliftIO.STM
|
import UnliftIO.STM
|
||||||
|
|
||||||
type AM' a = ReaderT Env IO a
|
type AM' a = ReaderT Env IO a
|
||||||
@@ -201,7 +209,9 @@ data Env = Env
|
|||||||
randomServer :: TVar StdGen,
|
randomServer :: TVar StdGen,
|
||||||
ntfSupervisor :: NtfSupervisor,
|
ntfSupervisor :: NtfSupervisor,
|
||||||
xftpAgent :: XFTPAgent,
|
xftpAgent :: XFTPAgent,
|
||||||
multicastSubscribers :: TMVar Int
|
multicastSubscribers :: TMVar Int,
|
||||||
|
acks :: TVar (OrdPSQ (RecipientId, EntityId) SystemTime String),
|
||||||
|
acksLate :: TVar (OrdPSQ (RecipientId, EntityId) SystemTime String)
|
||||||
}
|
}
|
||||||
|
|
||||||
newSMPAgentEnv :: AgentConfig -> SQLiteStore -> IO Env
|
newSMPAgentEnv :: AgentConfig -> SQLiteStore -> IO Env
|
||||||
@@ -211,7 +221,24 @@ newSMPAgentEnv config store = do
|
|||||||
ntfSupervisor <- atomically . newNtfSubSupervisor $ tbqSize config
|
ntfSupervisor <- atomically . newNtfSubSupervisor $ tbqSize config
|
||||||
xftpAgent <- atomically newXFTPAgent
|
xftpAgent <- atomically newXFTPAgent
|
||||||
multicastSubscribers <- newTMVarIO 0
|
multicastSubscribers <- newTMVarIO 0
|
||||||
pure Env {config, store, random, randomServer, ntfSupervisor, xftpAgent, multicastSubscribers}
|
acks <- newTVarIO OP.empty
|
||||||
|
acksLate <- newTVarIO OP.empty
|
||||||
|
ackMonPid <- async $ ackMonitor acks acksLate
|
||||||
|
void $! mkWeakTVar acks (cancel ackMonPid >> putStrLn "monitor begone!")
|
||||||
|
pure Env {config, store, random, randomServer, ntfSupervisor, xftpAgent, multicastSubscribers, acks, acksLate}
|
||||||
|
|
||||||
|
ackMonitor :: TVar (OrdPSQ (RecipientId, EntityId) SystemTime String) -> TVar (OrdPSQ (RecipientId, EntityId) SystemTime String) -> IO a
|
||||||
|
ackMonitor acks acksLate = forever $ do
|
||||||
|
MkSystemTime now _ns <- liftIO getSystemTime
|
||||||
|
late <- atomically $ do
|
||||||
|
(late, later) <- OP.atMostView (MkSystemTime (now - 30) 0) <$> readTVar acks
|
||||||
|
late <$ writeTVar acks later
|
||||||
|
forM_ late $ \(k@(rId, msgId), time, label) -> do
|
||||||
|
traceEventIO $ concat ["ACK-MISS ", logEntity rId, "/", logEntity msgId, " ", label]
|
||||||
|
atomically $ modifyTVar' acksLate $ OP.insert k time label
|
||||||
|
threadDelay 1000000
|
||||||
|
where
|
||||||
|
logEntity bs = B.unpack . B64.encode $ B.take 3 bs
|
||||||
|
|
||||||
createAgentStore :: FilePath -> ScrubbedBytes -> Bool -> MigrationConfirmation -> IO (Either MigrationError SQLiteStore)
|
createAgentStore :: FilePath -> ScrubbedBytes -> Bool -> MigrationConfirmation -> IO (Either MigrationError SQLiteStore)
|
||||||
createAgentStore dbFilePath dbKey keepKey = createSQLiteStore dbFilePath dbKey keepKey Migrations.app
|
createAgentStore dbFilePath dbKey keepKey = createSQLiteStore dbFilePath dbKey keepKey Migrations.app
|
||||||
|
|||||||
Reference in New Issue
Block a user