mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-04-22 16:55:46 +00:00
refactor: parameterize XFTP store with FSType singleton dispatch
This commit is contained in:
@@ -238,7 +238,7 @@ xftpServer cfg@XFTPServerConfig {xftpPort, transportConfig, inactiveClientExpira
|
||||
|
||||
stopServer :: M s ()
|
||||
stopServer = do
|
||||
st <- asks store
|
||||
st <- asks fileStore
|
||||
liftIO $ closeFileStore st
|
||||
saveServerStats
|
||||
logNote "Server stopped"
|
||||
@@ -366,13 +366,13 @@ xftpServer cfg@XFTPServerConfig {xftpPort, transportConfig, inactiveClientExpira
|
||||
XFTPServerConfig {controlPortUserAuth = user, controlPortAdminAuth = admin} = cfg
|
||||
CPStatsRTS -> E.tryAny getRTSStats >>= either (hPrint h) (hPrint h)
|
||||
CPDelete fileId -> withUserRole $ unliftIO u $ do
|
||||
fs <- asks store
|
||||
fs <- asks fileStore
|
||||
r <- runExceptT $ do
|
||||
(fr, _) <- ExceptT $ liftIO $ getFile fs SFRecipient fileId
|
||||
ExceptT $ deleteServerFile_ fr
|
||||
liftIO . hPutStrLn h $ either (\e -> "error: " <> show e) (\() -> "ok") r
|
||||
CPBlock fileId info -> withUserRole $ unliftIO u $ do
|
||||
fs <- asks store
|
||||
fs <- asks fileStore
|
||||
r <- runExceptT $ do
|
||||
(fr, _) <- ExceptT $ liftIO $ getFile fs SFRecipient fileId
|
||||
ExceptT $ blockServerFile fr info
|
||||
@@ -449,7 +449,7 @@ verifyXFTPTransmission thAuth (tAuth, authorized, (corrId, fId, cmd)) =
|
||||
where
|
||||
verifyCmd :: SFileParty p -> M s VerificationResult
|
||||
verifyCmd party = do
|
||||
st <- asks store
|
||||
st <- asks fileStore
|
||||
liftIO $ verify =<< getFile st party fId
|
||||
where
|
||||
verify = \case
|
||||
@@ -485,7 +485,7 @@ processXFTPRequest HTTP2Body {bodyPart} = \case
|
||||
noFile resp = pure (resp, Nothing)
|
||||
createFile :: FileInfo -> NonEmpty RcvPublicAuthKey -> M s FileResponse
|
||||
createFile file rks = do
|
||||
st <- asks store
|
||||
st <- asks fileStore
|
||||
r <- runExceptT $ do
|
||||
sizes <- asks $ allowedChunkSizes . config
|
||||
unless (size file `elem` sizes) $ throwE SIZE
|
||||
@@ -522,7 +522,7 @@ processXFTPRequest HTTP2Body {bodyPart} = \case
|
||||
r -> pure r
|
||||
addRecipients :: XFTPFileId -> NonEmpty RcvPublicAuthKey -> M s FileResponse
|
||||
addRecipients sId rks = do
|
||||
st <- asks store
|
||||
st <- asks fileStore
|
||||
r <- runExceptT $ do
|
||||
rcps <- mapM (ExceptT . addRecipientRetry st 3 sId) rks
|
||||
lift $ withFileLog $ \sl -> logAddRecipients sl sId rcps
|
||||
@@ -559,7 +559,7 @@ processXFTPRequest HTTP2Body {bodyPart} = \case
|
||||
receiveChunk (XFTPRcvChunkSpec fPath size digest) >>= \case
|
||||
Right () -> do
|
||||
stats <- asks serverStats
|
||||
st <- asks store
|
||||
st <- asks fileStore
|
||||
liftIO (setFilePath st senderId fPath) >>= \case
|
||||
Right () -> do
|
||||
withFileLog $ \sl -> logPutFile sl senderId fPath
|
||||
@@ -608,7 +608,7 @@ processXFTPRequest HTTP2Body {bodyPart} = \case
|
||||
ackFileReception :: RecipientId -> FileRec -> M s FileResponse
|
||||
ackFileReception rId fr = do
|
||||
withFileLog (`logAckFile` rId)
|
||||
st <- asks store
|
||||
st <- asks fileStore
|
||||
liftIO $ deleteRecipient st rId fr
|
||||
incFileStat fileDownloadAcks
|
||||
pure FROk
|
||||
@@ -629,7 +629,7 @@ deleteOrBlockServerFile_ FileRec {filePath, fileInfo} stat storeAction = runExce
|
||||
path <- readTVarIO filePath
|
||||
stats <- asks serverStats
|
||||
ExceptT $ first (\(_ :: SomeException) -> FILE_IO) <$> try (forM_ path $ \p -> whenM (doesFileExist p) (removeFile p >> deletedStats stats))
|
||||
st <- asks store
|
||||
st <- asks fileStore
|
||||
ExceptT $ liftIO $ storeAction st
|
||||
forM_ path $ \_ -> do
|
||||
us <- asks usedStorage
|
||||
@@ -645,7 +645,7 @@ getFileTime = getRoundedSystemTime
|
||||
|
||||
expireServerFiles :: FileStoreClass s => Maybe Int -> ExpirationConfig -> M s ()
|
||||
expireServerFiles itemDelay expCfg = do
|
||||
st <- asks store
|
||||
st <- asks fileStore
|
||||
us <- asks usedStorage
|
||||
usedStart <- readTVarIO us
|
||||
old <- liftIO $ expireBeforeEpoch expCfg
|
||||
@@ -704,7 +704,7 @@ restoreServerStats = asks (serverStatsBackupFile . config) >>= mapM_ restoreStat
|
||||
liftIO (strDecode <$> B.readFile f) >>= \case
|
||||
Right d@FileServerStatsData {_filesCount = statsFilesCount, _filesSize = statsFilesSize} -> do
|
||||
s <- asks serverStats
|
||||
st <- asks store
|
||||
st <- asks fileStore
|
||||
_filesCount <- liftIO $ getFileCount st
|
||||
_filesSize <- readTVarIO =<< asks usedStorage
|
||||
liftIO $ setFileServerStats s d {_filesCount, _filesSize}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE GADTs #-}
|
||||
{-# LANGUAGE KindSignatures #-}
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
@@ -8,16 +9,23 @@
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE StrictData #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
|
||||
module Simplex.FileTransfer.Server.Env
|
||||
( XFTPServerConfig (..),
|
||||
XFTPStoreConfig (..),
|
||||
XFTPEnv (..),
|
||||
XFTPRequest (..),
|
||||
XFTPStoreType,
|
||||
FileStore (..),
|
||||
AFStoreType (..),
|
||||
fileStore,
|
||||
fromFileStore,
|
||||
defaultInactiveClientExpiration,
|
||||
defFileExpirationHours,
|
||||
defaultFileExpiration,
|
||||
newXFTPServerEnv,
|
||||
readFileStoreType,
|
||||
runWithStoreConfig,
|
||||
checkFileStoreMode,
|
||||
importToDatabase,
|
||||
@@ -111,9 +119,31 @@ data XFTPStoreConfig s where
|
||||
XSCDatabase :: PostgresFileStoreCfg -> XFTPStoreConfig PostgresFileStore
|
||||
#endif
|
||||
|
||||
type family XFTPStoreType (fs :: FSType) where
|
||||
XFTPStoreType 'FSMemory = STMFileStore
|
||||
#if defined(dbServerPostgres)
|
||||
XFTPStoreType 'FSPostgres = PostgresFileStore
|
||||
#endif
|
||||
|
||||
data FileStore s where
|
||||
StoreMemory :: STMFileStore -> FileStore STMFileStore
|
||||
#if defined(dbServerPostgres)
|
||||
StoreDatabase :: PostgresFileStore -> FileStore PostgresFileStore
|
||||
#endif
|
||||
|
||||
data AFStoreType = forall fs. FileStoreClass (XFTPStoreType fs) => AFSType (SFSType fs)
|
||||
|
||||
fromFileStore :: FileStore s -> s
|
||||
fromFileStore = \case
|
||||
StoreMemory s -> s
|
||||
#if defined(dbServerPostgres)
|
||||
StoreDatabase s -> s
|
||||
#endif
|
||||
{-# INLINE fromFileStore #-}
|
||||
|
||||
data XFTPEnv s = XFTPEnv
|
||||
{ config :: XFTPServerConfig s,
|
||||
store :: s,
|
||||
fileStore_ :: FileStore s,
|
||||
usedStorage :: TVar Int64,
|
||||
storeLog :: Maybe (StoreLog 'WriteMode),
|
||||
random :: TVar ChaChaDRG,
|
||||
@@ -123,6 +153,10 @@ data XFTPEnv s = XFTPEnv
|
||||
serverStats :: FileServerStats
|
||||
}
|
||||
|
||||
fileStore :: XFTPEnv s -> s
|
||||
fileStore = fromFileStore . fileStore_
|
||||
{-# INLINE fileStore #-}
|
||||
|
||||
defFileExpirationHours :: Int64
|
||||
defFileExpirationHours = 48
|
||||
|
||||
@@ -136,18 +170,18 @@ defaultFileExpiration =
|
||||
newXFTPServerEnv :: FileStoreClass s => XFTPServerConfig s -> IO (XFTPEnv s)
|
||||
newXFTPServerEnv config@XFTPServerConfig {serverStoreCfg, fileSizeQuota, xftpCredentials, httpCredentials} = do
|
||||
random <- C.newRandom
|
||||
(store, storeLog) <- case serverStoreCfg of
|
||||
(fileStore_, storeLog) <- case serverStoreCfg of
|
||||
XSCMemory storeLogPath -> do
|
||||
st <- newFileStore ()
|
||||
sl <- mapM (`readWriteFileStore` st) storeLogPath
|
||||
atomically $ writeTVar (stmStoreLog st) sl
|
||||
pure (st, sl)
|
||||
pure (StoreMemory st, sl)
|
||||
#if defined(dbServerPostgres)
|
||||
XSCDatabase dbCfg -> do
|
||||
st <- newFileStore dbCfg
|
||||
pure (st, Nothing)
|
||||
pure (StoreDatabase st, Nothing)
|
||||
#endif
|
||||
used <- getUsedStorage store
|
||||
used <- getUsedStorage (fromFileStore fileStore_)
|
||||
usedStorage <- newTVarIO used
|
||||
forM_ fileSizeQuota $ \quota -> do
|
||||
logNote $ "Total / available storage: " <> tshow quota <> " / " <> tshow (quota - used)
|
||||
@@ -156,35 +190,45 @@ newXFTPServerEnv config@XFTPServerConfig {serverStoreCfg, fileSizeQuota, xftpCre
|
||||
httpServerCreds <- mapM loadServerCredential httpCredentials
|
||||
Fingerprint fp <- loadFingerprint xftpCredentials
|
||||
serverStats <- newFileServerStats =<< getCurrentTime
|
||||
pure XFTPEnv {config, store, usedStorage, storeLog, random, tlsServerCreds, httpServerCreds, serverIdentity = C.KeyHash fp, serverStats}
|
||||
pure XFTPEnv {config, fileStore_, usedStorage, storeLog, random, tlsServerCreds, httpServerCreds, serverIdentity = C.KeyHash fp, serverStats}
|
||||
|
||||
data XFTPRequest
|
||||
= XFTPReqNew FileInfo (NonEmpty RcvPublicAuthKey) (Maybe BasicAuth)
|
||||
| XFTPReqCmd XFTPFileId FileRec FileCmd
|
||||
| XFTPReqPing
|
||||
|
||||
-- | Select and run the store config based on INI settings.
|
||||
readFileStoreType :: String -> Either String AFStoreType
|
||||
readFileStoreType = \case
|
||||
"memory" -> Right $ AFSType SFSMemory
|
||||
#if defined(dbServerPostgres)
|
||||
"database" -> Right $ AFSType SFSPostgres
|
||||
#else
|
||||
"database" -> Left "Error: server binary is compiled without support for PostgreSQL database.\nPlease re-compile with `cabal build -fserver_postgres`."
|
||||
#endif
|
||||
other -> Left $ "Invalid store_files value: " <> other
|
||||
|
||||
-- | Dispatch store config from AFStoreType singleton and run the callback.
|
||||
-- CPP guards for Postgres are handled here so Main.hs stays CPP-free.
|
||||
runWithStoreConfig ::
|
||||
AFStoreType ->
|
||||
Ini ->
|
||||
String ->
|
||||
Maybe FilePath ->
|
||||
FilePath ->
|
||||
MigrationConfirmation ->
|
||||
(forall s. FileStoreClass s => XFTPStoreConfig s -> IO ()) ->
|
||||
IO ()
|
||||
runWithStoreConfig _ini storeType storeLogFile_ _storeLogFilePath _confirmMigrations run = case storeType of
|
||||
"memory" -> run $ XSCMemory storeLogFile_
|
||||
runWithStoreConfig (AFSType fs) ini storeLogFilePath confirmMigrations run =
|
||||
run $ iniStoreCfg fs
|
||||
where
|
||||
enableStoreLog' = settingIsOn "STORE_LOG" "enable" ini
|
||||
iniStoreCfg :: SFSType fs -> XFTPStoreConfig (XFTPStoreType fs)
|
||||
iniStoreCfg SFSMemory = XSCMemory (enableStoreLog' $> storeLogFilePath)
|
||||
#if defined(dbServerPostgres)
|
||||
"database" -> run $ XSCDatabase dbCfg
|
||||
where
|
||||
enableDbStoreLog' = settingIsOn "STORE_LOG" "db_store_log" _ini
|
||||
dbStoreLogPath = enableDbStoreLog' $> _storeLogFilePath
|
||||
dbCfg = PostgresFileStoreCfg {dbOpts = iniDBOptions _ini defaultXFTPDBOpts, dbStoreLogPath, confirmMigrations = _confirmMigrations}
|
||||
#else
|
||||
"database" -> error "Error: server binary is compiled without support for PostgreSQL database.\nPlease re-compile with `cabal build -fserver_postgres`."
|
||||
iniStoreCfg SFSPostgres = XSCDatabase dbCfg
|
||||
where
|
||||
enableDbStoreLog' = settingIsOn "STORE_LOG" "db_store_log" ini
|
||||
dbStoreLogPath = enableDbStoreLog' $> storeLogFilePath
|
||||
dbCfg = PostgresFileStoreCfg {dbOpts = iniDBOptions ini defaultXFTPDBOpts, dbStoreLogPath, confirmMigrations}
|
||||
#endif
|
||||
_ -> error $ "Invalid store_files value: " <> storeType
|
||||
|
||||
-- | Validate startup config when store_files=database.
|
||||
checkFileStoreMode :: Ini -> String -> FilePath -> IO ()
|
||||
|
||||
@@ -29,7 +29,7 @@ import Options.Applicative
|
||||
import Simplex.FileTransfer.Chunks
|
||||
import Simplex.FileTransfer.Description (FileSize (..))
|
||||
import Simplex.FileTransfer.Server (runXFTPServer)
|
||||
import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), XFTPStoreConfig, defFileExpirationHours, defaultFileExpiration, defaultInactiveClientExpiration, runWithStoreConfig, checkFileStoreMode, importToDatabase, exportFromDatabase)
|
||||
import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), XFTPStoreConfig, AFStoreType (..), defFileExpirationHours, defaultFileExpiration, defaultInactiveClientExpiration, readFileStoreType, runWithStoreConfig, checkFileStoreMode, importToDatabase, exportFromDatabase)
|
||||
import Simplex.FileTransfer.Transport (alpnSupportedXFTPhandshakes, supportedFileServerVRange)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Encoding.String
|
||||
@@ -214,20 +214,22 @@ xftpServerCLI_ generateSite serveStaticFiles cfgPath logPath = do
|
||||
printSourceCode (sourceCode <$> information)
|
||||
let storeType = fromRight "memory" $ T.unpack <$> lookupValue "STORE_LOG" "store_files" ini
|
||||
checkFileStoreMode ini storeType storeLogFilePath
|
||||
runWithStoreConfig ini storeType (enableStoreLog $> storeLogFilePath) storeLogFilePath confirmMigrations $ \storeCfg -> do
|
||||
let cfg = serverConfig storeCfg
|
||||
printXFTPConfig cfg
|
||||
case webStaticPath' of
|
||||
Just path -> do
|
||||
let onionHost =
|
||||
either (const Nothing) (find isOnion) $
|
||||
strDecode @(L.NonEmpty TransportHost) . encodeUtf8 =<< lookupValue "TRANSPORT" "host" ini
|
||||
webHttpPort = eitherToMaybe (lookupValue "WEB" "http" ini) >>= readMaybe . T.unpack
|
||||
generateSite cfg information onionHost path
|
||||
when (isJust webHttpPort || isJust webHttpsParams') $
|
||||
serveStaticFiles EmbeddedWebParams {webStaticPath = path, webHttpPort, webHttpsParams = webHttpsParams'}
|
||||
Nothing -> pure ()
|
||||
runXFTPServer cfg
|
||||
case readFileStoreType storeType of
|
||||
Left err -> error err
|
||||
Right fsType -> runWithStoreConfig fsType ini storeLogFilePath confirmMigrations $ \storeCfg -> do
|
||||
let cfg = serverConfig storeCfg
|
||||
printXFTPConfig cfg
|
||||
case webStaticPath' of
|
||||
Just path -> do
|
||||
let onionHost =
|
||||
either (const Nothing) (find isOnion) $
|
||||
strDecode @(L.NonEmpty TransportHost) . encodeUtf8 =<< lookupValue "TRANSPORT" "host" ini
|
||||
webHttpPort = eitherToMaybe (lookupValue "WEB" "http" ini) >>= readMaybe . T.unpack
|
||||
generateSite cfg information onionHost path
|
||||
when (isJust webHttpPort || isJust webHttpsParams') $
|
||||
serveStaticFiles EmbeddedWebParams {webStaticPath = path, webHttpPort, webHttpsParams = webHttpsParams'}
|
||||
Nothing -> pure ()
|
||||
runXFTPServer cfg
|
||||
where
|
||||
isOnion = \case THOnionHost _ -> True; _ -> False
|
||||
enableStoreLog = settingIsOn "STORE_LOG" "enable" ini
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE GADTs #-}
|
||||
{-# LANGUAGE KindSignatures #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
@@ -7,7 +8,9 @@
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
|
||||
module Simplex.FileTransfer.Server.Store
|
||||
( FileStoreClass (..),
|
||||
( FSType (..),
|
||||
SFSType (..),
|
||||
FileStoreClass (..),
|
||||
FileRec (..),
|
||||
FileRecipient (..),
|
||||
STMFileStore (..),
|
||||
@@ -16,6 +19,8 @@ module Simplex.FileTransfer.Server.Store
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Kind (Type)
|
||||
|
||||
import Control.Concurrent.STM
|
||||
import Control.Monad (forM, void)
|
||||
import qualified Data.Attoparsec.ByteString.Char8 as A
|
||||
@@ -38,6 +43,12 @@ import Simplex.Messaging.TMap (TMap)
|
||||
import qualified Simplex.Messaging.TMap as TM
|
||||
import Simplex.Messaging.Util (ifM)
|
||||
|
||||
data FSType = FSMemory | FSPostgres
|
||||
|
||||
data SFSType :: FSType -> Type where
|
||||
SFSMemory :: SFSType 'FSMemory
|
||||
SFSPostgres :: SFSType 'FSPostgres
|
||||
|
||||
data FileRec = FileRec
|
||||
{ senderId :: SenderId,
|
||||
fileInfo :: FileInfo,
|
||||
|
||||
@@ -34,7 +34,8 @@ import Test.Hspec hiding (fit, it)
|
||||
import Util
|
||||
import XFTPAgent
|
||||
import XFTPCLI (xftpCLIFileTests)
|
||||
import XFTPClient (xftpMemoryServer, xftpMemoryServer2)
|
||||
import Simplex.FileTransfer.Server.Env (AFStoreType (..))
|
||||
import Simplex.FileTransfer.Server.Store (SFSType (..))
|
||||
import XFTPServerTests (xftpServerTests)
|
||||
import WebTests (webTests)
|
||||
import XFTPWebTests (xftpWebTests)
|
||||
@@ -54,7 +55,7 @@ import PostgresSchemaDump (postgresSchemaDumpTest)
|
||||
import SMPClient (testServerDBConnectInfo, testStoreDBOpts)
|
||||
import Simplex.Messaging.Notifications.Server.Store.Migrations (ntfServerMigrations)
|
||||
import Simplex.Messaging.Server.QueueStore.Postgres.Migrations (serverMigrations)
|
||||
import XFTPClient (testXFTPDBConnectInfo, xftpPostgresServer, xftpPostgresServer2)
|
||||
import XFTPClient (testXFTPDBConnectInfo)
|
||||
#endif
|
||||
|
||||
#if defined(dbPostgres) || defined(dbServerPostgres)
|
||||
@@ -152,19 +153,22 @@ main = do
|
||||
before (pure $ ASType SQSMemory SMSJournal) smpProxyTests
|
||||
describe "XFTP" $ do
|
||||
describe "XFTP server" $
|
||||
before (pure xftpMemoryServer) xftpServerTests
|
||||
before (pure $ AFSType SFSMemory) xftpServerTests
|
||||
describe "XFTP file description" fileDescriptionTests
|
||||
describe "XFTP CLI (memory)" $
|
||||
before (pure (xftpMemoryServer, xftpMemoryServer2)) xftpCLIFileTests
|
||||
describe "XFTP agent" xftpAgentTests
|
||||
before (pure (AFSType SFSMemory, AFSType SFSMemory)) xftpCLIFileTests
|
||||
describe "XFTP agent" $
|
||||
before (pure $ AFSType SFSMemory) xftpAgentTests
|
||||
#if defined(dbServerPostgres)
|
||||
around_ (postgressBracket testXFTPDBConnectInfo) $ do
|
||||
describe "XFTP Postgres store operations" xftpStoreTests
|
||||
describe "XFTP migration round-trip" xftpMigrationTests
|
||||
describe "XFTP server (PostgreSQL)" $
|
||||
before (pure xftpPostgresServer) xftpServerTests
|
||||
before (pure $ AFSType SFSPostgres) xftpServerTests
|
||||
describe "XFTP agent (PostgreSQL)" $
|
||||
before (pure $ AFSType SFSPostgres) xftpAgentTests
|
||||
describe "XFTP CLI (PostgreSQL)" $
|
||||
before (pure (xftpPostgresServer, xftpPostgresServer2)) xftpCLIFileTests
|
||||
before (pure (AFSType SFSPostgres, AFSType SFSPostgres)) xftpCLIFileTests
|
||||
#endif
|
||||
#if defined(dbPostgres)
|
||||
describe "XFTP Web Client" $ xftpWebTests (dropAllSchemasExceptSystem testDBConnectInfo)
|
||||
|
||||
@@ -26,7 +26,7 @@ import SMPClient (xit'')
|
||||
import Simplex.FileTransfer.Client (XFTPClientConfig (..))
|
||||
import Simplex.FileTransfer.Description (FileChunk (..), FileDescription (..), FileDescriptionURI (..), ValidFileDescription, fileDescriptionURI, kb, mb, qrSizeLimit, pattern ValidFileDescription)
|
||||
import Simplex.FileTransfer.Protocol (FileParty (..))
|
||||
import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..))
|
||||
import Simplex.FileTransfer.Server.Env (AFStoreType, XFTPServerConfig (..))
|
||||
import Simplex.FileTransfer.Server.Store (STMFileStore)
|
||||
import Simplex.FileTransfer.Transport (XFTPErrorType (AUTH))
|
||||
import Simplex.FileTransfer.Types (RcvFileId, SndFileId)
|
||||
@@ -55,44 +55,44 @@ import Fixtures
|
||||
import Simplex.Messaging.Agent.Store.Postgres.Util (dropAllSchemasExceptSystem)
|
||||
#endif
|
||||
|
||||
xftpAgentTests :: Spec
|
||||
xftpAgentTests :: SpecWith AFStoreType
|
||||
xftpAgentTests =
|
||||
around_ testBracket
|
||||
#if defined(dbPostgres)
|
||||
. after_ (dropAllSchemasExceptSystem testDBConnectInfo)
|
||||
#endif
|
||||
. describe "agent XFTP API" $ do
|
||||
it "should send and receive file" $ withXFTPServer testXFTPAgentSendReceive
|
||||
it "should send and receive file" $ \fsType -> withXFTPServer fsType testXFTPAgentSendReceive
|
||||
-- uncomment CPP option slow_servers and run hpack to run this test
|
||||
xit "should send and receive file with slow server responses" $
|
||||
xit "should send and receive file with slow server responses" $ \_ ->
|
||||
withXFTPServerCfg testXFTPServerConfig {responseDelay = 500000} $
|
||||
\_ -> testXFTPAgentSendReceive
|
||||
it "should send and receive with encrypted local files" testXFTPAgentSendReceiveEncrypted
|
||||
it "should send and receive large file with a redirect" testXFTPAgentSendReceiveRedirect
|
||||
it "should send and receive small file without a redirect" testXFTPAgentSendReceiveNoRedirect
|
||||
describe "sending and receiving with version negotiation" testXFTPAgentSendReceiveMatrix
|
||||
it "should resume receiving file after restart" testXFTPAgentReceiveRestore
|
||||
it "should cleanup rcv tmp path after permanent error" testXFTPAgentReceiveCleanup
|
||||
it "should resume sending file after restart" testXFTPAgentSendRestore
|
||||
xit'' "should cleanup snd prefix path after permanent error" testXFTPAgentSendCleanup
|
||||
it "should delete sent file on server" testXFTPAgentDelete
|
||||
it "should resume deleting file after restart" testXFTPAgentDeleteRestore
|
||||
it "should send and receive with encrypted local files" $ \fsType -> testXFTPAgentSendReceiveEncrypted fsType
|
||||
it "should send and receive large file with a redirect" $ \fsType -> testXFTPAgentSendReceiveRedirect fsType
|
||||
it "should send and receive small file without a redirect" $ \fsType -> testXFTPAgentSendReceiveNoRedirect fsType
|
||||
describe "sending and receiving with version negotiation" $ beforeWith (const (pure ())) testXFTPAgentSendReceiveMatrix
|
||||
it "should resume receiving file after restart" $ \_ -> testXFTPAgentReceiveRestore
|
||||
it "should cleanup rcv tmp path after permanent error" $ \_ -> testXFTPAgentReceiveCleanup
|
||||
it "should resume sending file after restart" $ \_ -> testXFTPAgentSendRestore
|
||||
xit'' "should cleanup snd prefix path after permanent error" $ \_ -> testXFTPAgentSendCleanup
|
||||
it "should delete sent file on server" $ \fsType -> testXFTPAgentDelete fsType
|
||||
it "should resume deleting file after restart" $ \_ -> testXFTPAgentDeleteRestore
|
||||
-- TODO when server is fixed to correctly send AUTH error, this test has to be modified to expect AUTH error
|
||||
it "if file is deleted on server, should limit retries and continue receiving next file" testXFTPAgentDeleteOnServer
|
||||
it "if file is expired on server, should report error and continue receiving next file" testXFTPAgentExpiredOnServer
|
||||
it "should request additional recipient IDs when number of recipients exceeds maximum per request" testXFTPAgentRequestAdditionalRecipientIDs
|
||||
it "if file is deleted on server, should limit retries and continue receiving next file" $ \fsType -> testXFTPAgentDeleteOnServer fsType
|
||||
it "if file is expired on server, should report error and continue receiving next file" $ \fsType -> testXFTPAgentExpiredOnServer fsType
|
||||
it "should request additional recipient IDs when number of recipients exceeds maximum per request" $ \fsType -> testXFTPAgentRequestAdditionalRecipientIDs fsType
|
||||
describe "XFTP server test via agent API" $ do
|
||||
it "should pass without basic auth" $ testXFTPServerTest Nothing (noAuthSrv testXFTPServer2) `shouldReturn` Nothing
|
||||
it "should pass without basic auth" $ \_ -> testXFTPServerTest Nothing (noAuthSrv testXFTPServer2) `shouldReturn` Nothing
|
||||
let srv1 = testXFTPServer2 {keyHash = "1234"}
|
||||
it "should fail with incorrect fingerprint" $ do
|
||||
it "should fail with incorrect fingerprint" $ \_ -> do
|
||||
testXFTPServerTest Nothing (noAuthSrv srv1) `shouldReturn` Just (ProtocolTestFailure TSConnect $ BROKER (B.unpack $ strEncode srv1) $ NETWORK NEUnknownCAError)
|
||||
describe "server with password" $ do
|
||||
let auth = Just "abcd"
|
||||
srv = ProtoServerWithAuth testXFTPServer2
|
||||
authErr = Just (ProtocolTestFailure TSCreateFile $ XFTP (B.unpack $ strEncode testXFTPServer2) AUTH)
|
||||
it "should pass with correct password" $ testXFTPServerTest auth (srv auth) `shouldReturn` Nothing
|
||||
it "should fail without password" $ testXFTPServerTest auth (srv Nothing) `shouldReturn` authErr
|
||||
it "should fail with incorrect password" $ testXFTPServerTest auth (srv $ Just "wrong") `shouldReturn` authErr
|
||||
it "should pass with correct password" $ \_ -> testXFTPServerTest auth (srv auth) `shouldReturn` Nothing
|
||||
it "should fail without password" $ \_ -> testXFTPServerTest auth (srv Nothing) `shouldReturn` authErr
|
||||
it "should fail with incorrect password" $ \_ -> testXFTPServerTest auth (srv $ Just "wrong") `shouldReturn` authErr
|
||||
|
||||
testXFTPServerTest :: HasCallStack => Maybe BasicAuth -> XFTPServerWithAuth -> IO (Maybe ProtocolTestFailure)
|
||||
testXFTPServerTest newFileBasicAuth srv =
|
||||
@@ -143,8 +143,8 @@ testXFTPAgentSendReceive = do
|
||||
rfId <- runRight $ testReceive rcp rfd originalFilePath
|
||||
xftpDeleteRcvFile rcp rfId
|
||||
|
||||
testXFTPAgentSendReceiveEncrypted :: HasCallStack => IO ()
|
||||
testXFTPAgentSendReceiveEncrypted = withXFTPServer $ do
|
||||
testXFTPAgentSendReceiveEncrypted :: HasCallStack => AFStoreType -> IO ()
|
||||
testXFTPAgentSendReceiveEncrypted fsType = withXFTPServer fsType $ do
|
||||
g <- C.newRandom
|
||||
filePath <- createRandomFile
|
||||
s <- LB.readFile filePath
|
||||
@@ -164,8 +164,8 @@ testXFTPAgentSendReceiveEncrypted = withXFTPServer $ do
|
||||
rfId <- runRight $ testReceiveCF rcp rfd cfArgs originalFilePath
|
||||
xftpDeleteRcvFile rcp rfId
|
||||
|
||||
testXFTPAgentSendReceiveRedirect :: HasCallStack => IO ()
|
||||
testXFTPAgentSendReceiveRedirect = withXFTPServer $ do
|
||||
testXFTPAgentSendReceiveRedirect :: HasCallStack => AFStoreType -> IO ()
|
||||
testXFTPAgentSendReceiveRedirect fsType = withXFTPServer fsType $ do
|
||||
--- sender
|
||||
filePathIn <- createRandomFile
|
||||
let fileSize = mb 17
|
||||
@@ -222,8 +222,8 @@ testXFTPAgentSendReceiveRedirect = withXFTPServer $ do
|
||||
inBytes <- B.readFile filePathIn
|
||||
B.readFile out `shouldReturn` inBytes
|
||||
|
||||
testXFTPAgentSendReceiveNoRedirect :: HasCallStack => IO ()
|
||||
testXFTPAgentSendReceiveNoRedirect = withXFTPServer $ do
|
||||
testXFTPAgentSendReceiveNoRedirect :: HasCallStack => AFStoreType -> IO ()
|
||||
testXFTPAgentSendReceiveNoRedirect fsType = withXFTPServer fsType $ do
|
||||
--- sender
|
||||
let fileSize = mb 5
|
||||
filePathIn <- createRandomFile_ fileSize "testfile"
|
||||
@@ -506,9 +506,9 @@ testXFTPAgentSendCleanup = withGlobalLogging logCfgNoLogs $ do
|
||||
doesDirectoryExist prefixPath `shouldReturn` False
|
||||
doesFileExist encPath `shouldReturn` False
|
||||
|
||||
testXFTPAgentDelete :: HasCallStack => IO ()
|
||||
testXFTPAgentDelete = withGlobalLogging logCfgNoLogs $
|
||||
withXFTPServer $ do
|
||||
testXFTPAgentDelete :: HasCallStack => AFStoreType -> IO ()
|
||||
testXFTPAgentDelete fsType = withGlobalLogging logCfgNoLogs $
|
||||
withXFTPServer fsType $ do
|
||||
filePath <- createRandomFile
|
||||
|
||||
-- send file
|
||||
@@ -576,9 +576,9 @@ testXFTPAgentDeleteRestore = withGlobalLogging logCfgNoLogs $ do
|
||||
rfGet rcp2
|
||||
liftIO $ rfId' `shouldBe` rfId
|
||||
|
||||
testXFTPAgentDeleteOnServer :: HasCallStack => IO ()
|
||||
testXFTPAgentDeleteOnServer = withGlobalLogging logCfgNoLogs $
|
||||
withXFTPServer $ do
|
||||
testXFTPAgentDeleteOnServer :: HasCallStack => AFStoreType -> IO ()
|
||||
testXFTPAgentDeleteOnServer fsType = withGlobalLogging logCfgNoLogs $
|
||||
withXFTPServer fsType $ do
|
||||
filePath1 <- createRandomFile' "testfile1"
|
||||
|
||||
-- send file 1
|
||||
@@ -614,10 +614,9 @@ testXFTPAgentDeleteOnServer = withGlobalLogging logCfgNoLogs $
|
||||
-- receive file 2
|
||||
testReceive' rcp rfd2 filePath2
|
||||
|
||||
testXFTPAgentExpiredOnServer :: HasCallStack => IO ()
|
||||
testXFTPAgentExpiredOnServer = withGlobalLogging logCfgNoLogs $ do
|
||||
let fastExpiration = ExpirationConfig {ttl = 2, checkInterval = 1}
|
||||
withXFTPServerCfg testXFTPServerConfig {fileExpiration = Just fastExpiration} . const $ do
|
||||
testXFTPAgentExpiredOnServer :: HasCallStack => AFStoreType -> IO ()
|
||||
testXFTPAgentExpiredOnServer fsType = withGlobalLogging logCfgNoLogs $
|
||||
withXFTPServerConfigOn (updateXFTPCfg (cfgFS fsType) $ \c -> c {fileExpiration = Just fastExpiration}) . const $ do
|
||||
filePath1 <- createRandomFile' "testfile1"
|
||||
|
||||
-- send file 1
|
||||
@@ -652,9 +651,11 @@ testXFTPAgentExpiredOnServer = withGlobalLogging logCfgNoLogs $ do
|
||||
|
||||
-- receive file 2 successfully
|
||||
runRight_ . void $ testReceive' rcp rfd2 filePath2
|
||||
where
|
||||
fastExpiration = ExpirationConfig {ttl = 2, checkInterval = 1}
|
||||
|
||||
testXFTPAgentRequestAdditionalRecipientIDs :: HasCallStack => IO ()
|
||||
testXFTPAgentRequestAdditionalRecipientIDs = withXFTPServer $ do
|
||||
testXFTPAgentRequestAdditionalRecipientIDs :: HasCallStack => AFStoreType -> IO ()
|
||||
testXFTPAgentRequestAdditionalRecipientIDs fsType = withXFTPServer fsType $ do
|
||||
filePath <- createRandomFile
|
||||
|
||||
-- send file
|
||||
|
||||
@@ -11,17 +11,18 @@ import System.FilePath ((</>))
|
||||
import System.IO.Silently (capture_)
|
||||
import Test.Hspec hiding (fit, it)
|
||||
import Util
|
||||
import XFTPClient (XFTPTestServer (..), testXFTPServerStr, testXFTPServerStr2, xftpServerFiles, xftpServerFiles2)
|
||||
import Simplex.FileTransfer.Server.Env (AFStoreType)
|
||||
import XFTPClient (cfgFS, cfgFS2, withXFTPServerConfigOn, testXFTPServerStr, testXFTPServerStr2, xftpServerFiles, xftpServerFiles2)
|
||||
|
||||
xftpCLIFileTests :: SpecWith (XFTPTestServer, XFTPTestServer)
|
||||
xftpCLIFileTests :: SpecWith (AFStoreType, AFStoreType)
|
||||
xftpCLIFileTests = around_ testBracket $ do
|
||||
it "should send and receive file" $ \(XFTPTestServer withSrv, _) ->
|
||||
withSrv id testXFTPCLISendReceive_
|
||||
it "should send and receive file with 2 servers" $ \(XFTPTestServer withSrv1, XFTPTestServer withSrv2) ->
|
||||
withSrv1 id $ withSrv2 id testXFTPCLISendReceive2servers_
|
||||
it "should delete file from 2 servers" $ \(XFTPTestServer withSrv1, XFTPTestServer withSrv2) ->
|
||||
withSrv1 id $ withSrv2 id testXFTPCLIDelete_
|
||||
it "prepareChunkSizes should use 2 chunk sizes" $ \(_, _) -> testPrepareChunkSizes
|
||||
it "should send and receive file" $ \(fsType, _) ->
|
||||
withXFTPServerConfigOn (cfgFS fsType) $ \_ -> testXFTPCLISendReceive_
|
||||
it "should send and receive file with 2 servers" $ \(fsType1, fsType2) ->
|
||||
withXFTPServerConfigOn (cfgFS fsType1) $ \_ -> withXFTPServerConfigOn (cfgFS2 fsType2) $ \_ -> testXFTPCLISendReceive2servers_
|
||||
it "should delete file from 2 servers" $ \(fsType1, fsType2) ->
|
||||
withXFTPServerConfigOn (cfgFS fsType1) $ \_ -> withXFTPServerConfigOn (cfgFS2 fsType2) $ \_ -> testXFTPCLIDelete_
|
||||
it "prepareChunkSizes should use 2 chunk sizes" $ \_ -> testPrepareChunkSizes
|
||||
|
||||
testBracket :: IO () -> IO ()
|
||||
testBracket =
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE GADTs #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
@@ -8,8 +9,6 @@
|
||||
module XFTPClient where
|
||||
|
||||
import Control.Concurrent (ThreadId, threadDelay)
|
||||
import Control.Exception (SomeException, catch)
|
||||
import System.Directory (removeFile)
|
||||
import Control.Monad (void)
|
||||
import Data.String (fromString)
|
||||
import Data.Time.Clock (getCurrentTime)
|
||||
@@ -18,8 +17,8 @@ import SMPClient (serverBracket)
|
||||
import Simplex.FileTransfer.Client
|
||||
import Simplex.FileTransfer.Description
|
||||
import Simplex.FileTransfer.Server (runXFTPServerBlocking)
|
||||
import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), XFTPStoreConfig (..), defaultFileExpiration, defaultInactiveClientExpiration)
|
||||
import Simplex.FileTransfer.Server.Store (FileStoreClass, STMFileStore)
|
||||
import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), XFTPStoreConfig (..), AFStoreType (..), defaultFileExpiration, defaultInactiveClientExpiration)
|
||||
import Simplex.FileTransfer.Server.Store (FileStoreClass, SFSType (..), STMFileStore)
|
||||
import Simplex.FileTransfer.Transport (alpnSupportedXFTPhandshakes, supportedFileServerVRange)
|
||||
import Simplex.Messaging.Protocol (XFTPServer)
|
||||
import Simplex.Messaging.Transport.HTTP2 (httpALPN)
|
||||
@@ -33,27 +32,27 @@ import Simplex.Messaging.Agent.Store.Postgres.Options (DBOpts (..))
|
||||
import Simplex.Messaging.Agent.Store.Shared (MigrationConfirmation (..))
|
||||
#endif
|
||||
|
||||
-- Parameterized server bracket
|
||||
data AXFTPServerConfig = forall s. FileStoreClass s => AXFTPSrvCfg (XFTPServerConfig s)
|
||||
|
||||
newtype XFTPTestServer = XFTPTestServer
|
||||
{ runServer :: forall a. (forall s. XFTPServerConfig s -> XFTPServerConfig s) -> IO a -> IO a
|
||||
}
|
||||
updateXFTPCfg :: AXFTPServerConfig -> (forall s. XFTPServerConfig s -> XFTPServerConfig s) -> AXFTPServerConfig
|
||||
updateXFTPCfg (AXFTPSrvCfg cfg) f = AXFTPSrvCfg (f cfg)
|
||||
|
||||
-- Store-log-dependent agent tests need the bracket + a way to clear server state
|
||||
type XFTPTestServerClear = (XFTPTestServer, IO ())
|
||||
cfgFS :: AFStoreType -> AXFTPServerConfig
|
||||
cfgFS (AFSType fs) = case fs of
|
||||
SFSMemory -> AXFTPSrvCfg testXFTPServerConfig
|
||||
#if defined(dbServerPostgres)
|
||||
SFSPostgres -> AXFTPSrvCfg testXFTPServerConfig {serverStoreCfg = XSCDatabase testXFTPPostgresCfg}
|
||||
#endif
|
||||
|
||||
xftpMemoryServer :: XFTPTestServer
|
||||
xftpMemoryServer = XFTPTestServer $ \cfgF test -> withXFTPServerCfg (cfgF testXFTPServerConfig) $ \_ -> test
|
||||
cfgFS2 :: AFStoreType -> AXFTPServerConfig
|
||||
cfgFS2 (AFSType fs) = case fs of
|
||||
SFSMemory -> AXFTPSrvCfg testXFTPServerConfig2
|
||||
#if defined(dbServerPostgres)
|
||||
SFSPostgres -> AXFTPSrvCfg testXFTPServerConfig2 {serverStoreCfg = XSCDatabase testXFTPPostgresCfg}
|
||||
#endif
|
||||
|
||||
xftpMemoryServerWithLog :: XFTPTestServer
|
||||
xftpMemoryServerWithLog = XFTPTestServer $ \cfgF test ->
|
||||
withXFTPServerCfg (cfgF testXFTPServerConfig {serverStoreCfg = XSCMemory (Just testXFTPLogFile), storeLogFile = Just testXFTPLogFile, serverStatsBackupFile = Just testXFTPStatsBackupFile}) $ \_ -> test
|
||||
|
||||
xftpMemoryServerClear :: XFTPTestServerClear
|
||||
xftpMemoryServerClear = (xftpMemoryServerWithLog, removeFile testXFTPLogFile `catch` \(_ :: SomeException) -> pure ())
|
||||
|
||||
xftpMemoryServer2 :: XFTPTestServer
|
||||
xftpMemoryServer2 = XFTPTestServer $ \cfgF test -> withXFTPServerCfg (cfgF testXFTPServerConfig2) $ \_ -> test
|
||||
withXFTPServerConfigOn :: HasCallStack => AXFTPServerConfig -> (HasCallStack => ThreadId -> IO a) -> IO a
|
||||
withXFTPServerConfigOn (AXFTPSrvCfg cfg) = withXFTPServerCfg cfg
|
||||
|
||||
#if defined(dbServerPostgres)
|
||||
testXFTPDBConnectInfo :: ConnectInfo
|
||||
@@ -76,15 +75,6 @@ testXFTPPostgresCfg =
|
||||
confirmMigrations = MCYesUp
|
||||
}
|
||||
|
||||
xftpPostgresServer :: XFTPTestServer
|
||||
xftpPostgresServer = XFTPTestServer $ \cfgF test -> withXFTPServerCfg (cfgF testXFTPServerConfig {serverStoreCfg = XSCDatabase testXFTPPostgresCfg}) $ \_ -> test
|
||||
|
||||
xftpPostgresServer2 :: XFTPTestServer
|
||||
xftpPostgresServer2 = XFTPTestServer $ \cfgF test -> withXFTPServerCfg (cfgF testXFTPServerConfig2 {serverStoreCfg = XSCDatabase testXFTPPostgresCfg}) $ \_ -> test
|
||||
|
||||
xftpPostgresServerClear :: XFTPTestServerClear
|
||||
xftpPostgresServerClear = (xftpPostgresServer, clearXFTPPostgresStore)
|
||||
|
||||
clearXFTPPostgresStore :: IO ()
|
||||
clearXFTPPostgresStore = do
|
||||
let DBOpts {connstr} = dbOpts testXFTPPostgresCfg
|
||||
@@ -94,23 +84,23 @@ clearXFTPPostgresStore = do
|
||||
PSQL.close conn
|
||||
#endif
|
||||
|
||||
xftpTest :: HasCallStack => (HasCallStack => XFTPClient -> IO ()) -> XFTPTestServer -> Expectation
|
||||
xftpTest test (XFTPTestServer withSrv) = withSrv id (testXFTPClient test) `shouldReturn` ()
|
||||
xftpTest :: HasCallStack => (HasCallStack => XFTPClient -> IO ()) -> AFStoreType -> Expectation
|
||||
xftpTest test fsType = withXFTPServerConfigOn (cfgFS fsType) (\_ -> testXFTPClient test) `shouldReturn` ()
|
||||
|
||||
xftpTestN :: HasCallStack => Int -> (HasCallStack => [XFTPClient] -> IO ()) -> XFTPTestServer -> Expectation
|
||||
xftpTestN nClients test (XFTPTestServer withSrv) = withSrv id (run nClients []) `shouldReturn` ()
|
||||
xftpTestN :: HasCallStack => Int -> (HasCallStack => [XFTPClient] -> IO ()) -> AFStoreType -> Expectation
|
||||
xftpTestN nClients test fsType = withXFTPServerConfigOn (cfgFS fsType) (\_ -> run nClients []) `shouldReturn` ()
|
||||
where
|
||||
run :: Int -> [XFTPClient] -> IO ()
|
||||
run 0 hs = test hs
|
||||
run n hs = testXFTPClient $ \h -> run (n - 1) (h : hs)
|
||||
|
||||
xftpTest2 :: HasCallStack => (HasCallStack => XFTPClient -> XFTPClient -> IO ()) -> XFTPTestServer -> Expectation
|
||||
xftpTest2 :: HasCallStack => (HasCallStack => XFTPClient -> XFTPClient -> IO ()) -> AFStoreType -> Expectation
|
||||
xftpTest2 test = xftpTestN 2 _test
|
||||
where
|
||||
_test [h1, h2] = test h1 h2
|
||||
_test _ = error "expected 2 handles"
|
||||
|
||||
xftpTest4 :: HasCallStack => (HasCallStack => XFTPClient -> XFTPClient -> XFTPClient -> XFTPClient -> IO ()) -> XFTPTestServer -> Expectation
|
||||
xftpTest4 :: HasCallStack => (HasCallStack => XFTPClient -> XFTPClient -> XFTPClient -> XFTPClient -> IO ()) -> AFStoreType -> Expectation
|
||||
xftpTest4 test = xftpTestN 4 _test
|
||||
where
|
||||
_test [h1, h2, h3, h4] = test h1 h2 h3 h4
|
||||
@@ -125,11 +115,11 @@ withXFTPServerCfg cfg =
|
||||
withXFTPServerCfgNoALPN :: (HasCallStack, FileStoreClass s) => XFTPServerConfig s -> (HasCallStack => ThreadId -> IO a) -> IO a
|
||||
withXFTPServerCfgNoALPN cfg = withXFTPServerCfg cfg {transportConfig = (transportConfig cfg) {serverALPN = Nothing}}
|
||||
|
||||
withXFTPServer :: HasCallStack => IO a -> IO a
|
||||
withXFTPServer = withXFTPServerCfg testXFTPServerConfig . const
|
||||
withXFTPServer :: HasCallStack => AFStoreType -> IO a -> IO a
|
||||
withXFTPServer fsType = withXFTPServerConfigOn (cfgFS fsType) . const
|
||||
|
||||
withXFTPServer2 :: HasCallStack => IO a -> IO a
|
||||
withXFTPServer2 = withXFTPServerCfg testXFTPServerConfig {xftpPort = xftpTestPort2, filesPath = xftpServerFiles2} . const
|
||||
withXFTPServer2 :: HasCallStack => AFStoreType -> IO a -> IO a
|
||||
withXFTPServer2 fsType = withXFTPServerConfigOn (cfgFS2 fsType) . const
|
||||
|
||||
withXFTPServerStoreLogOn :: HasCallStack => (HasCallStack => ThreadId -> IO a) -> IO a
|
||||
withXFTPServerStoreLogOn = withXFTPServerCfg testXFTPServerConfig {serverStoreCfg = XSCMemory (Just testXFTPLogFile), storeLogFile = Just testXFTPLogFile, serverStatsBackupFile = Just testXFTPStatsBackupFile}
|
||||
|
||||
@@ -31,7 +31,7 @@ import ServerTests (logSize)
|
||||
import Simplex.FileTransfer.Client
|
||||
import Simplex.FileTransfer.Description (kb)
|
||||
import Simplex.FileTransfer.Protocol (FileInfo (..), XFTPFileId, xftpBlockSize)
|
||||
import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..))
|
||||
import Simplex.FileTransfer.Server.Env (AFStoreType, XFTPServerConfig (..))
|
||||
import Simplex.FileTransfer.Transport (XFTPClientHandshake (..), XFTPClientHello (..), XFTPErrorType (..), XFTPRcvChunkSpec (..), XFTPServerHandshake (..), pattern VersionXFTP)
|
||||
import Simplex.Messaging.Client (ProtocolClientError (..))
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
@@ -52,7 +52,7 @@ import UnliftIO.STM
|
||||
import Util
|
||||
import XFTPClient
|
||||
|
||||
xftpServerTests :: SpecWith XFTPTestServer
|
||||
xftpServerTests :: SpecWith AFStoreType
|
||||
xftpServerTests =
|
||||
before_ (createDirectoryIfMissing False xftpServerFiles) . after_ (removeDirectoryRecursive xftpServerFiles) $ do
|
||||
describe "XFTP file chunk delivery" $ do
|
||||
@@ -103,10 +103,10 @@ createTestChunk fp = do
|
||||
readChunk :: XFTPFileId -> IO ByteString
|
||||
readChunk sId = B.readFile (xftpServerFiles </> B.unpack (B64.encode $ unEntityId sId))
|
||||
|
||||
testFileChunkDelivery :: XFTPTestServer -> Expectation
|
||||
testFileChunkDelivery :: AFStoreType -> Expectation
|
||||
testFileChunkDelivery = xftpTest $ \c -> runRight_ $ runTestFileChunkDelivery c c
|
||||
|
||||
testFileChunkDelivery2 :: XFTPTestServer -> Expectation
|
||||
testFileChunkDelivery2 :: AFStoreType -> Expectation
|
||||
testFileChunkDelivery2 = xftpTest2 $ \s r -> runRight_ $ runTestFileChunkDelivery s r
|
||||
|
||||
runTestFileChunkDelivery :: XFTPClient -> XFTPClient -> ExceptT XFTPClientError IO ()
|
||||
@@ -129,7 +129,7 @@ runTestFileChunkDelivery s r = do
|
||||
downloadXFTPChunk g r rpKey rId $ XFTPRcvChunkSpec "tests/tmp/received_chunk1" chSize digest
|
||||
liftIO $ B.readFile "tests/tmp/received_chunk1" `shouldReturn` bytes
|
||||
|
||||
testFileChunkDeliveryAddRecipients :: XFTPTestServer -> Expectation
|
||||
testFileChunkDeliveryAddRecipients :: AFStoreType -> Expectation
|
||||
testFileChunkDeliveryAddRecipients = xftpTest4 $ \s r1 r2 r3 -> runRight_ $ do
|
||||
g <- liftIO C.newRandom
|
||||
(sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
|
||||
@@ -150,10 +150,10 @@ testFileChunkDeliveryAddRecipients = xftpTest4 $ \s r1 r2 r3 -> runRight_ $ do
|
||||
testReceiveChunk r2 rpKey2 rId2 "tests/tmp/received_chunk2"
|
||||
testReceiveChunk r3 rpKey3 rId3 "tests/tmp/received_chunk3"
|
||||
|
||||
testFileChunkDelete :: XFTPTestServer -> Expectation
|
||||
testFileChunkDelete :: AFStoreType -> Expectation
|
||||
testFileChunkDelete = xftpTest $ \c -> runRight_ $ runTestFileChunkDelete c c
|
||||
|
||||
testFileChunkDelete2 :: XFTPTestServer -> Expectation
|
||||
testFileChunkDelete2 :: AFStoreType -> Expectation
|
||||
testFileChunkDelete2 = xftpTest2 $ \s r -> runRight_ $ runTestFileChunkDelete s r
|
||||
|
||||
runTestFileChunkDelete :: XFTPClient -> XFTPClient -> ExceptT XFTPClientError IO ()
|
||||
@@ -179,10 +179,10 @@ runTestFileChunkDelete s r = do
|
||||
deleteXFTPChunk s spKey sId
|
||||
`catchError` (liftIO . (`shouldBe` PCEProtocolError AUTH))
|
||||
|
||||
testFileChunkAck :: XFTPTestServer -> Expectation
|
||||
testFileChunkAck :: AFStoreType -> Expectation
|
||||
testFileChunkAck = xftpTest $ \c -> runRight_ $ runTestFileChunkAck c c
|
||||
|
||||
testFileChunkAck2 :: XFTPTestServer -> Expectation
|
||||
testFileChunkAck2 :: AFStoreType -> Expectation
|
||||
testFileChunkAck2 = xftpTest2 $ \s r -> runRight_ $ runTestFileChunkAck s r
|
||||
|
||||
runTestFileChunkAck :: XFTPClient -> XFTPClient -> ExceptT XFTPClientError IO ()
|
||||
@@ -206,7 +206,7 @@ runTestFileChunkAck s r = do
|
||||
ackXFTPChunk r rpKey rId
|
||||
`catchError` (liftIO . (`shouldBe` PCEProtocolError AUTH))
|
||||
|
||||
testWrongChunkSize :: XFTPTestServer -> Expectation
|
||||
testWrongChunkSize :: AFStoreType -> Expectation
|
||||
testWrongChunkSize = xftpTest $ \c -> do
|
||||
g <- C.newRandom
|
||||
(sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
|
||||
@@ -218,8 +218,8 @@ testWrongChunkSize = xftpTest $ \c -> do
|
||||
void (createXFTPChunk c spKey file [rcvKey] Nothing)
|
||||
`catchError` (liftIO . (`shouldBe` PCEProtocolError SIZE))
|
||||
|
||||
testFileChunkExpiration :: XFTPTestServer -> Expectation
|
||||
testFileChunkExpiration _ = withXFTPServerCfg testXFTPServerConfig {fileExpiration} $
|
||||
testFileChunkExpiration :: AFStoreType -> Expectation
|
||||
testFileChunkExpiration fsType = withXFTPServerConfigOn (updateXFTPCfg (cfgFS fsType) $ \c -> c {fileExpiration}) $
|
||||
\_ -> testXFTPClient $ \c -> runRight_ $ do
|
||||
g <- liftIO C.newRandom
|
||||
(sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
|
||||
@@ -242,8 +242,8 @@ testFileChunkExpiration _ = withXFTPServerCfg testXFTPServerConfig {fileExpirati
|
||||
where
|
||||
fileExpiration = Just ExpirationConfig {ttl = 1, checkInterval = 1}
|
||||
|
||||
testInactiveClientExpiration :: XFTPTestServer -> Expectation
|
||||
testInactiveClientExpiration _ = withXFTPServerCfg testXFTPServerConfig {inactiveClientExpiration} $ \_ -> runRight_ $ do
|
||||
testInactiveClientExpiration :: AFStoreType -> Expectation
|
||||
testInactiveClientExpiration fsType = withXFTPServerConfigOn (updateXFTPCfg (cfgFS fsType) $ \c -> c {inactiveClientExpiration}) $ \_ -> runRight_ $ do
|
||||
disconnected <- newEmptyTMVarIO
|
||||
ts <- liftIO getCurrentTime
|
||||
c <- ExceptT $ getXFTPClient (1, testXFTPServer, Nothing) testXFTPClientConfig [] ts (\_ -> atomically $ putTMVar disconnected ())
|
||||
@@ -258,8 +258,8 @@ testInactiveClientExpiration _ = withXFTPServerCfg testXFTPServerConfig {inactiv
|
||||
where
|
||||
inactiveClientExpiration = Just ExpirationConfig {ttl = 1, checkInterval = 1}
|
||||
|
||||
testFileStorageQuota :: XFTPTestServer -> Expectation
|
||||
testFileStorageQuota _ = withXFTPServerCfg testXFTPServerConfig {fileSizeQuota = Just $ chSize * 2} $
|
||||
testFileStorageQuota :: AFStoreType -> Expectation
|
||||
testFileStorageQuota fsType = withXFTPServerConfigOn (updateXFTPCfg (cfgFS fsType) $ \c -> c {fileSizeQuota = Just $ chSize * 2}) $
|
||||
\_ -> testXFTPClient $ \c -> runRight_ $ do
|
||||
g <- liftIO C.newRandom
|
||||
(sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
|
||||
@@ -286,7 +286,7 @@ testFileStorageQuota _ = withXFTPServerCfg testXFTPServerConfig {fileSizeQuota =
|
||||
uploadXFTPChunk c spKey sId3 chunkSpec
|
||||
download rId3
|
||||
|
||||
testFileLog :: XFTPTestServer -> Expectation
|
||||
testFileLog :: AFStoreType -> Expectation
|
||||
testFileLog _ = do
|
||||
g <- C.newRandom
|
||||
bytes <- liftIO $ createTestChunk testChunkPath
|
||||
@@ -378,9 +378,9 @@ testFileLog _ = do
|
||||
downloadXFTPChunk g c rpKey rId $ XFTPRcvChunkSpec "tests/tmp/received_chunk1" chSize digest
|
||||
liftIO $ B.readFile "tests/tmp/received_chunk1" `shouldReturn` bytes
|
||||
|
||||
testFileBasicAuth :: Bool -> Maybe BasicAuth -> Maybe BasicAuth -> Bool -> XFTPTestServer -> IO ()
|
||||
testFileBasicAuth allowNewFiles newFileBasicAuth clntAuth success _ =
|
||||
withXFTPServerCfg testXFTPServerConfig {allowNewFiles, newFileBasicAuth} $
|
||||
testFileBasicAuth :: Bool -> Maybe BasicAuth -> Maybe BasicAuth -> Bool -> AFStoreType -> IO ()
|
||||
testFileBasicAuth allowNewFiles newFileBasicAuth clntAuth success fsType =
|
||||
withXFTPServerConfigOn (updateXFTPCfg (cfgFS fsType) $ \c -> c {allowNewFiles, newFileBasicAuth}) $
|
||||
\_ -> testXFTPClient $ \c -> do
|
||||
g <- C.newRandom
|
||||
(sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
|
||||
@@ -400,9 +400,9 @@ testFileBasicAuth allowNewFiles newFileBasicAuth clntAuth success _ =
|
||||
void (createXFTPChunk c spKey file [rcvKey] clntAuth)
|
||||
`catchError` (liftIO . (`shouldBe` PCEProtocolError AUTH))
|
||||
|
||||
testFileSkipCommitted :: XFTPTestServer -> IO ()
|
||||
testFileSkipCommitted _ =
|
||||
withXFTPServerCfg testXFTPServerConfig $
|
||||
testFileSkipCommitted :: AFStoreType -> IO ()
|
||||
testFileSkipCommitted fsType =
|
||||
withXFTPServerConfigOn (cfgFS fsType) $
|
||||
\_ -> testXFTPClient $ \c -> do
|
||||
g <- C.newRandom
|
||||
(sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g
|
||||
|
||||
Reference in New Issue
Block a user