Skip to content

Commit a2b8f29

Browse files
committed
Merge branch 'master' into ab/tls-2
2 parents 4abd389 + f229e13 commit a2b8f29

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1857
-1075
lines changed

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
# 6.0.0
2+
3+
Version 6.0.0.8
4+
5+
Agent:
6+
- enabled fast handshake support.
7+
- batch-send multiple messages in each connection.
8+
- resume subscriptions as soon as agent moves to foreground or as network connection resumes.
9+
- "known" servers to determine whether to use SMP proxy.
10+
- retry on SMP proxy NO_SESSION error.
11+
- fixes to notification subscriptions.
12+
- persistent server statistics.
13+
- better concurrency.
14+
15+
SMP server:
16+
- reduce threads usage.
17+
- additional statistics.
18+
- improve disabling inactive clients.
19+
- additional control port commands for monitoring.
20+
21+
Notification server:
22+
- support onion-only SMP servers.
23+
124
# 5.8.2
225

326
Agent:

package.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: simplexmq
2-
version: 6.0.0.2
2+
version: 6.0.0.8
33
synopsis: SimpleXMQ message broker
44
description: |
55
This package includes <./docs/Simplex-Messaging-Server.html server>,

simplexmq.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cabal-version: 1.12
55
-- see: https://github.com/sol/hpack
66

77
name: simplexmq
8-
version: 6.0.0.2
8+
version: 6.0.0.8
99
synopsis: SimpleXMQ message broker
1010
description: This package includes <./docs/Simplex-Messaging-Server.html server>,
1111
<./docs/Simplex-Messaging-Client.html client> and

src/Simplex/FileTransfer/Agent.hs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import Data.Either (partitionEithers, rights)
4343
import Data.Int (Int64)
4444
import Data.List (foldl', partition, sortOn)
4545
import qualified Data.List.NonEmpty as L
46-
import Data.Map (Map)
46+
import Data.Map.Strict (Map)
4747
import qualified Data.Map.Strict as M
4848
import Data.Maybe (mapMaybe)
4949
import qualified Data.Set as S
@@ -184,7 +184,7 @@ runXFTPRcvWorker c srv Worker {doWork} = do
184184
cfg <- asks config
185185
forever $ do
186186
lift $ waitForWork doWork
187-
atomically $ assertAgentForeground c
187+
liftIO $ assertAgentForeground c
188188
runXFTPOperation cfg
189189
where
190190
runXFTPOperation :: AgentConfig -> AM ()
@@ -194,6 +194,7 @@ runXFTPRcvWorker c srv Worker {doWork} = do
194194
(fc@RcvFileChunk {userId, rcvFileId, rcvFileEntityId, digest, fileTmpPath, replicas = replica@RcvFileChunkReplica {rcvChunkReplicaId, server, delay} : _}, approvedRelays) -> do
195195
let ri' = maybe ri (\d -> ri {initialInterval = d, increaseAfter = 0}) delay
196196
withRetryIntervalLimit xftpConsecutiveRetries ri' $ \delay' loop -> do
197+
liftIO $ waitWhileSuspended c
197198
liftIO $ waitForUserNetwork c
198199
atomically $ incXFTPServerStat c userId srv downloadAttempts
199200
downloadFileChunk fc replica approvedRelays
@@ -204,7 +205,7 @@ runXFTPRcvWorker c srv Worker {doWork} = do
204205
when (serverHostError e) $ notify c rcvFileEntityId $ RFWARN e
205206
liftIO $ closeXFTPServerClient c userId server digest
206207
withStore' c $ \db -> updateRcvChunkReplicaDelay db rcvChunkReplicaId replicaDelay
207-
atomically $ assertAgentForeground c
208+
liftIO $ assertAgentForeground c
208209
loop
209210
retryDone e = do
210211
atomically . incXFTPServerStat c userId srv $ case e of
@@ -220,7 +221,7 @@ runXFTPRcvWorker c srv Worker {doWork} = do
220221
chunkSpec = XFTPRcvChunkSpec chunkPath chSize (unFileDigest digest)
221222
relChunkPath = fileTmpPath </> takeFileName chunkPath
222223
agentXFTPDownloadChunk c userId digest replica chunkSpec
223-
atomically $ waitUntilForeground c
224+
liftIO $ waitUntilForeground c
224225
(entityId, complete, progress) <- withStore c $ \db -> runExceptT $ do
225226
liftIO $ updateRcvFileChunkReceived db (rcvChunkReplicaId replica) rcvChunkId relChunkPath
226227
RcvFile {size = FileSize currentSize, chunks, redirect} <- ExceptT $ getRcvFile db rcvFileId
@@ -239,7 +240,7 @@ runXFTPRcvWorker c srv Worker {doWork} = do
239240
where
240241
ipAddressProtected' :: AM Bool
241242
ipAddressProtected' = do
242-
cfg <- liftIO $ getNetworkConfig' c
243+
cfg <- liftIO $ getFastNetworkConfig c
243244
pure $ ipAddressProtected cfg srv
244245
receivedSize :: [RcvFileChunk] -> Int64
245246
receivedSize = foldl' (\sz ch -> sz + receivedChunkSize ch) 0
@@ -272,7 +273,7 @@ runXFTPRcvLocalWorker c Worker {doWork} = do
272273
cfg <- asks config
273274
forever $ do
274275
lift $ waitForWork doWork
275-
atomically $ assertAgentForeground c
276+
liftIO $ assertAgentForeground c
276277
runXFTPOperation cfg
277278
where
278279
runXFTPOperation :: AgentConfig -> AM ()
@@ -298,12 +299,12 @@ runXFTPRcvLocalWorker c Worker {doWork} = do
298299
Nothing -> do
299300
notify c rcvFileEntityId $ RFDONE fsSavePath
300301
lift $ forM_ tmpPath (removePath <=< toFSFilePath)
301-
atomically $ waitUntilForeground c
302+
liftIO $ waitUntilForeground c
302303
withStore' c (`updateRcvFileComplete` rcvFileId)
303304
Just RcvFileRedirect {redirectFileInfo, redirectDbId} -> do
304305
let RedirectFileInfo {size = redirectSize, digest = redirectDigest} = redirectFileInfo
305306
lift $ forM_ tmpPath (removePath <=< toFSFilePath)
306-
atomically $ waitUntilForeground c
307+
liftIO $ waitUntilForeground c
307308
withStore' c (`updateRcvFileComplete` rcvFileId)
308309
-- proceed with redirect
309310
yaml <- liftError (FILE . FILE_IO . show) (CF.readFile $ CryptoFile fsSavePath cfArgs) `agentFinally` (lift $ toFSFilePath fsSavePath >>= removePath)
@@ -391,7 +392,7 @@ runXFTPSndPrepareWorker c Worker {doWork} = do
391392
cfg <- asks config
392393
forever $ do
393394
lift $ waitForWork doWork
394-
atomically $ assertAgentForeground c
395+
liftIO $ assertAgentForeground c
395396
runXFTPOperation cfg
396397
where
397398
runXFTPOperation :: AgentConfig -> AM ()
@@ -453,16 +454,17 @@ runXFTPSndPrepareWorker c Worker {doWork} = do
453454
SndFileChunkReplica {server} : _ -> Right server
454455
createChunk :: Int -> SndFileChunk -> AM (ProtocolServer 'PXFTP)
455456
createChunk numRecipients' ch = do
456-
atomically $ assertAgentForeground c
457+
liftIO $ assertAgentForeground c
457458
(replica, ProtoServerWithAuth srv _) <- tryCreate
458459
withStore' c $ \db -> createSndFileReplica db ch replica
459460
pure srv
460461
where
461462
tryCreate = do
462463
usedSrvs <- newTVarIO ([] :: [XFTPServer])
463464
let AgentClient {xftpServers} = c
464-
userSrvCount <- length <$> atomically (TM.lookup userId xftpServers)
465+
userSrvCount <- liftIO $ length <$> TM.lookupIO userId xftpServers
465466
withRetryIntervalCount (riFast ri) $ \n _ loop -> do
467+
liftIO $ waitWhileSuspended c
466468
liftIO $ waitForUserNetwork c
467469
let triedAllSrvs = n > userSrvCount
468470
createWithNextSrv usedSrvs
@@ -472,7 +474,7 @@ runXFTPSndPrepareWorker c Worker {doWork} = do
472474
retryLoop loop triedAllSrvs e = do
473475
flip catchAgentError (\_ -> pure ()) $ do
474476
when (triedAllSrvs && serverHostError e) $ notify c sndFileEntityId $ SFWARN e
475-
atomically $ assertAgentForeground c
477+
liftIO $ assertAgentForeground c
476478
loop
477479
createWithNextSrv usedSrvs = do
478480
deleted <- withStore' c $ \db -> getSndFileDeleted db sndFileId
@@ -492,7 +494,7 @@ runXFTPSndWorker c srv Worker {doWork} = do
492494
cfg <- asks config
493495
forever $ do
494496
lift $ waitForWork doWork
495-
atomically $ assertAgentForeground c
497+
liftIO $ assertAgentForeground c
496498
runXFTPOperation cfg
497499
where
498500
runXFTPOperation :: AgentConfig -> AM ()
@@ -502,6 +504,7 @@ runXFTPSndWorker c srv Worker {doWork} = do
502504
fc@SndFileChunk {userId, sndFileId, sndFileEntityId, filePrefixPath, digest, replicas = replica@SndFileChunkReplica {sndChunkReplicaId, server, delay} : _} -> do
503505
let ri' = maybe ri (\d -> ri {initialInterval = d, increaseAfter = 0}) delay
504506
withRetryIntervalLimit xftpConsecutiveRetries ri' $ \delay' loop -> do
507+
liftIO $ waitWhileSuspended c
505508
liftIO $ waitForUserNetwork c
506509
atomically $ incXFTPServerStat c userId srv uploadAttempts
507510
uploadFileChunk cfg fc replica
@@ -512,7 +515,7 @@ runXFTPSndWorker c srv Worker {doWork} = do
512515
when (serverHostError e) $ notify c sndFileEntityId $ SFWARN e
513516
liftIO $ closeXFTPServerClient c userId server digest
514517
withStore' c $ \db -> updateSndChunkReplicaDelay db sndChunkReplicaId replicaDelay
515-
atomically $ assertAgentForeground c
518+
liftIO $ assertAgentForeground c
516519
loop
517520
retryDone e = do
518521
atomically $ incXFTPServerStat c userId srv uploadErrs
@@ -523,9 +526,9 @@ runXFTPSndWorker c srv Worker {doWork} = do
523526
fsFilePath <- lift $ toFSFilePath filePath
524527
unlessM (doesFileExist fsFilePath) $ throwE $ FILE NO_FILE
525528
let chunkSpec' = chunkSpec {filePath = fsFilePath} :: XFTPChunkSpec
526-
atomically $ assertAgentForeground c
529+
liftIO $ assertAgentForeground c
527530
agentXFTPUploadChunk c userId chunkDigest replica' chunkSpec'
528-
atomically $ waitUntilForeground c
531+
liftIO $ waitUntilForeground c
529532
sf@SndFile {sndFileEntityId, prefixPath, chunks} <- withStore c $ \db -> do
530533
updateSndChunkReplicaStatus db sndChunkReplicaId SFRSUploaded
531534
getSndFile db sndFileId
@@ -663,7 +666,7 @@ runXFTPDelWorker c srv Worker {doWork} = do
663666
cfg <- asks config
664667
forever $ do
665668
lift $ waitForWork doWork
666-
atomically $ assertAgentForeground c
669+
liftIO $ assertAgentForeground c
667670
runXFTPOperation cfg
668671
where
669672
runXFTPOperation :: AgentConfig -> AM ()
@@ -674,6 +677,7 @@ runXFTPDelWorker c srv Worker {doWork} = do
674677
processDeletedReplica replica@DeletedSndChunkReplica {deletedSndChunkReplicaId, userId, server, chunkDigest, delay} = do
675678
let ri' = maybe ri (\d -> ri {initialInterval = d, increaseAfter = 0}) delay
676679
withRetryIntervalLimit xftpConsecutiveRetries ri' $ \delay' loop -> do
680+
liftIO $ waitWhileSuspended c
677681
liftIO $ waitForUserNetwork c
678682
atomically $ incXFTPServerStat c userId srv deleteAttempts
679683
deleteChunkReplica
@@ -684,7 +688,7 @@ runXFTPDelWorker c srv Worker {doWork} = do
684688
when (serverHostError e) $ notify c "" $ SFWARN e
685689
liftIO $ closeXFTPServerClient c userId server chunkDigest
686690
withStore' c $ \db -> updateDeletedSndChunkReplicaDelay db deletedSndChunkReplicaId replicaDelay
687-
atomically $ assertAgentForeground c
691+
liftIO $ assertAgentForeground c
688692
loop
689693
retryDone e = do
690694
atomically $ incXFTPServerStat c userId srv deleteErrs
@@ -699,7 +703,7 @@ delWorkerInternalError c deletedSndChunkReplicaId e = do
699703
withStore' c $ \db -> deleteDeletedSndChunkReplica db deletedSndChunkReplicaId
700704
notify c "" $ SFERR e
701705

702-
assertAgentForeground :: AgentClient -> STM ()
706+
assertAgentForeground :: AgentClient -> IO ()
703707
assertAgentForeground c = do
704708
throwWhenInactive c
705709
waitUntilForeground c

src/Simplex/FileTransfer/Client/Agent.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ defaultXFTPClientAgentConfig =
5353
data XFTPClientAgentError = XFTPClientAgentError XFTPServer XFTPClientError
5454
deriving (Show, Exception)
5555

56-
newXFTPAgent :: XFTPClientAgentConfig -> STM XFTPClientAgent
56+
newXFTPAgent :: XFTPClientAgentConfig -> IO XFTPClientAgent
5757
newXFTPAgent config = do
58-
xftpClients <- TM.empty
58+
xftpClients <- TM.emptyIO
5959
pure XFTPClientAgent {xftpClients, config}
6060

6161
type ME a = ExceptT XFTPClientAgentError IO a

src/Simplex/FileTransfer/Client/Main.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import Data.Int (Int64)
4343
import Data.List (foldl', sortOn)
4444
import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
4545
import qualified Data.List.NonEmpty as L
46-
import Data.Map (Map)
46+
import Data.Map.Strict (Map)
4747
import qualified Data.Map as M
4848
import Data.Maybe (fromMaybe, listToMaybe)
4949
import qualified Data.Text as T
@@ -313,7 +313,7 @@ cliSendFileOpts SendOptions {filePath, outputDir, numRecipients, xftpServers, re
313313
pure (encPath, fdRcv, fdSnd, chunkSpecs, encSize)
314314
uploadFile :: TVar ChaChaDRG -> [XFTPChunkSpec] -> TVar [Int64] -> Int64 -> ExceptT CLIError IO [SentFileChunk]
315315
uploadFile g chunks uploadedChunks encSize = do
316-
a <- atomically $ newXFTPAgent defaultXFTPClientAgentConfig
316+
a <- liftIO $ newXFTPAgent defaultXFTPClientAgentConfig
317317
gen <- newTVarIO =<< liftIO newStdGen
318318
let xftpSrvs = fromMaybe defaultXFTPServers (nonEmpty xftpServers)
319319
srvs <- liftIO $ replicateM (length chunks) $ getXFTPServer gen xftpSrvs
@@ -429,7 +429,7 @@ cliReceiveFile ReceiveOptions {fileDescription, filePath, retryCount, tempPath,
429429
receive (ValidFileDescription FileDescription {size, digest, key, nonce, chunks}) = do
430430
encPath <- getEncPath tempPath "xftp"
431431
createDirectory encPath
432-
a <- atomically $ newXFTPAgent defaultXFTPClientAgentConfig
432+
a <- liftIO $ newXFTPAgent defaultXFTPClientAgentConfig
433433
liftIO $ printNoNewLine "Downloading file..."
434434
downloadedChunks <- newTVarIO []
435435
let srv FileChunk {replicas} = case replicas of
@@ -494,7 +494,7 @@ cliDeleteFile DeleteOptions {fileDescription, retryCount, yes} = do
494494
where
495495
deleteFile :: ValidFileDescription 'FSender -> ExceptT CLIError IO ()
496496
deleteFile (ValidFileDescription FileDescription {chunks}) = do
497-
a <- atomically $ newXFTPAgent defaultXFTPClientAgentConfig
497+
a <- liftIO $ newXFTPAgent defaultXFTPClientAgentConfig
498498
forM_ chunks $ deleteFileChunk a
499499
liftIO $ do
500500
printNoNewLine "File deleted!"

src/Simplex/FileTransfer/Description.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import Data.Int (Int64)
5252
import Data.List (foldl', sortOn)
5353
import Data.List.NonEmpty (NonEmpty (..))
5454
import qualified Data.List.NonEmpty as L
55-
import Data.Map (Map)
55+
import Data.Map.Strict (Map)
5656
import qualified Data.Map as M
5757
import Data.Maybe (fromMaybe)
5858
import Data.String

src/Simplex/FileTransfer/Server.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ xftpServer cfg@XFTPServerConfig {xftpPort, transportConfig, inactiveClientExpira
112112
Right pk' -> pure pk'
113113
Left e -> putStrLn ("servers has no valid key: " <> show e) >> exitFailure
114114
env <- ask
115-
sessions <- atomically TM.empty
115+
sessions <- liftIO TM.emptyIO
116116
let cleanup sessionId = atomically $ TM.delete sessionId sessions
117117
liftIO . runHTTP2Server started xftpPort defaultHTTP2BufferSize serverParams transportConfig inactiveClientExpiration cleanup $ \sessionId sessionALPN r sendResponse -> do
118118
reqBody <- getHTTP2Body r xftpBlockSize
@@ -576,7 +576,7 @@ incFileStat statSel = do
576576
saveServerStats :: M ()
577577
saveServerStats =
578578
asks (serverStatsBackupFile . config)
579-
>>= mapM_ (\f -> asks serverStats >>= atomically . getFileServerStatsData >>= liftIO . saveStats f)
579+
>>= mapM_ (\f -> asks serverStats >>= liftIO . getFileServerStatsData >>= liftIO . saveStats f)
580580
where
581581
saveStats f stats = do
582582
logInfo $ "saving server stats to file " <> T.pack f

src/Simplex/FileTransfer/Server/Env.hs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ module Simplex.FileTransfer.Server.Env where
1111

1212
import Control.Logger.Simple
1313
import Control.Monad
14-
import Control.Monad.IO.Unlift
1514
import Crypto.Random
1615
import Data.Int (Int64)
1716
import Data.List.NonEmpty (NonEmpty)
@@ -105,17 +104,17 @@ supportedXFTPhandshakes = ["xftp/1"]
105104

106105
newXFTPServerEnv :: XFTPServerConfig -> IO XFTPEnv
107106
newXFTPServerEnv config@XFTPServerConfig {storeLogFile, fileSizeQuota, caCertificateFile, certificateFile, privateKeyFile, transportConfig} = do
108-
random <- liftIO C.newRandom
109-
store <- atomically newFileStore
110-
storeLog <- liftIO $ mapM (`readWriteFileStore` store) storeLogFile
107+
random <- C.newRandom
108+
store <- newFileStore
109+
storeLog <- mapM (`readWriteFileStore` store) storeLogFile
111110
used <- countUsedStorage <$> readTVarIO (files store)
112111
atomically $ writeTVar (usedStorage store) used
113112
forM_ fileSizeQuota $ \quota -> do
114113
logInfo $ "Total / available storage: " <> tshow quota <> " / " <> tshow (quota - used)
115114
when (quota < used) $ logInfo "WARNING: storage quota is less than used storage, no files can be uploaded!"
116-
tlsServerParams <- liftIO $ loadTLSServerParams caCertificateFile certificateFile privateKeyFile (alpn transportConfig)
117-
Fingerprint fp <- liftIO $ loadFingerprint caCertificateFile
118-
serverStats <- atomically . newFileServerStats =<< liftIO getCurrentTime
115+
tlsServerParams <- loadTLSServerParams caCertificateFile certificateFile privateKeyFile (alpn transportConfig)
116+
Fingerprint fp <- loadFingerprint caCertificateFile
117+
serverStats <- newFileServerStats =<< getCurrentTime
119118
pure XFTPEnv {config, store, storeLog, random, tlsServerParams, serverIdentity = C.KeyHash fp, serverStats}
120119

121120
countUsedStorage :: M.Map k FileRec -> Int64

src/Simplex/FileTransfer/Server/Stats.hs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,34 +43,34 @@ data FileServerStatsData = FileServerStatsData
4343
}
4444
deriving (Show)
4545

46-
newFileServerStats :: UTCTime -> STM FileServerStats
46+
newFileServerStats :: UTCTime -> IO FileServerStats
4747
newFileServerStats ts = do
48-
fromTime <- newTVar ts
49-
filesCreated <- newTVar 0
50-
fileRecipients <- newTVar 0
51-
filesUploaded <- newTVar 0
52-
filesExpired <- newTVar 0
53-
filesDeleted <- newTVar 0
48+
fromTime <- newTVarIO ts
49+
filesCreated <- newTVarIO 0
50+
fileRecipients <- newTVarIO 0
51+
filesUploaded <- newTVarIO 0
52+
filesExpired <- newTVarIO 0
53+
filesDeleted <- newTVarIO 0
5454
filesDownloaded <- newPeriodStats
55-
fileDownloads <- newTVar 0
56-
fileDownloadAcks <- newTVar 0
57-
filesCount <- newTVar 0
58-
filesSize <- newTVar 0
55+
fileDownloads <- newTVarIO 0
56+
fileDownloadAcks <- newTVarIO 0
57+
filesCount <- newTVarIO 0
58+
filesSize <- newTVarIO 0
5959
pure FileServerStats {fromTime, filesCreated, fileRecipients, filesUploaded, filesExpired, filesDeleted, filesDownloaded, fileDownloads, fileDownloadAcks, filesCount, filesSize}
6060

61-
getFileServerStatsData :: FileServerStats -> STM FileServerStatsData
61+
getFileServerStatsData :: FileServerStats -> IO FileServerStatsData
6262
getFileServerStatsData s = do
63-
_fromTime <- readTVar $ fromTime (s :: FileServerStats)
64-
_filesCreated <- readTVar $ filesCreated s
65-
_fileRecipients <- readTVar $ fileRecipients s
66-
_filesUploaded <- readTVar $ filesUploaded s
67-
_filesExpired <- readTVar $ filesExpired s
68-
_filesDeleted <- readTVar $ filesDeleted s
63+
_fromTime <- readTVarIO $ fromTime (s :: FileServerStats)
64+
_filesCreated <- readTVarIO $ filesCreated s
65+
_fileRecipients <- readTVarIO $ fileRecipients s
66+
_filesUploaded <- readTVarIO $ filesUploaded s
67+
_filesExpired <- readTVarIO $ filesExpired s
68+
_filesDeleted <- readTVarIO $ filesDeleted s
6969
_filesDownloaded <- getPeriodStatsData $ filesDownloaded s
70-
_fileDownloads <- readTVar $ fileDownloads s
71-
_fileDownloadAcks <- readTVar $ fileDownloadAcks s
72-
_filesCount <- readTVar $ filesCount s
73-
_filesSize <- readTVar $ filesSize s
70+
_fileDownloads <- readTVarIO $ fileDownloads s
71+
_fileDownloadAcks <- readTVarIO $ fileDownloadAcks s
72+
_filesCount <- readTVarIO $ filesCount s
73+
_filesSize <- readTVarIO $ filesSize s
7474
pure FileServerStatsData {_fromTime, _filesCreated, _fileRecipients, _filesUploaded, _filesExpired, _filesDeleted, _filesDownloaded, _fileDownloads, _fileDownloadAcks, _filesCount, _filesSize}
7575

7676
setFileServerStats :: FileServerStats -> FileServerStatsData -> STM ()

0 commit comments

Comments
 (0)