core: allow repeat receive after cancel for XFTP files (#2134)

This commit is contained in:
spaced4ndy 2023-04-03 16:31:18 +04:00 committed by GitHub
parent d3268e4a72
commit 1a7a79d504
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 9 deletions

View File

@ -107,6 +107,7 @@ tests:
- deepseq == 1.4.* - deepseq == 1.4.*
- hspec == 2.7.* - hspec == 2.7.*
- network == 3.1.* - network == 3.1.*
- silently == 1.2.*
- stm == 2.5.* - stm == 2.5.*
ghc-options: ghc-options:
- -threaded - -threaded

View File

@ -1,6 +1,6 @@
cabal-version: 1.12 cabal-version: 1.12
-- This file has been generated from package.yaml by hpack version 0.35.0. -- This file has been generated from package.yaml by hpack version 0.35.1.
-- --
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
@ -389,6 +389,7 @@ test-suite simplex-chat-test
, process ==1.6.* , process ==1.6.*
, random >=1.1 && <1.3 , random >=1.1 && <1.3
, record-hasfield ==1.0.* , record-hasfield ==1.0.*
, silently ==1.2.*
, simple-logger ==0.1.* , simple-logger ==0.1.*
, simplex-chat , simplex-chat
, simplexmq >=5.0 , simplexmq >=5.0

View File

@ -1401,13 +1401,27 @@ processChatCommand = \case
_ -> throwChatError $ CEFileInternal "invalid chat ref for file transfer" _ -> throwChatError $ CEFileInternal "invalid chat ref for file transfer"
ci <- withStore $ \db -> getChatItemByFileId db user fileId ci <- withStore $ \db -> getChatItemByFileId db user fileId
pure $ CRSndFileCancelled user ci ftm fts pure $ CRSndFileCancelled user ci ftm fts
FTRcv ftr@RcvFileTransfer {cancelled, fileStatus} FTRcv ftr@RcvFileTransfer {cancelled, fileStatus, xftpRcvFile}
| cancelled -> throwChatError $ CEFileCancel fileId "file already cancelled" | cancelled -> throwChatError $ CEFileCancel fileId "file already cancelled"
| rcvFileComplete fileStatus -> throwChatError $ CEFileCancel fileId "file transfer is complete" | rcvFileComplete fileStatus -> throwChatError $ CEFileCancel fileId "file transfer is complete"
| otherwise -> do | otherwise -> case xftpRcvFile of
Nothing -> do
cancelRcvFileTransfer user ftr >>= mapM_ (deleteAgentConnectionAsync user) cancelRcvFileTransfer user ftr >>= mapM_ (deleteAgentConnectionAsync user)
ci <- withStore $ \db -> getChatItemByFileId db user fileId ci <- withStore $ \db -> getChatItemByFileId db user fileId
pure $ CRRcvFileCancelled user ci ftr pure $ CRRcvFileCancelled user ci ftr
Just XFTPRcvFile {agentRcvFileId} -> do
forM_ (liveRcvFileTransferPath ftr) $ \filePath -> do
fsFilePath <- toFSFilePath filePath
removeFile fsFilePath `E.catch` \(_ :: E.SomeException) -> pure ()
forM_ agentRcvFileId $ \(AgentRcvFileId aFileId) ->
withAgent $ \a -> xftpDeleteRcvFile a (aUserId user) aFileId
ci <- withStore $ \db -> do
liftIO $ do
updateCIFileStatus db user fileId CIFSRcvInvitation
updateRcvFileStatus db fileId FSNew
updateRcvFileAgentId db fileId Nothing
getChatItemByFileId db user fileId
pure $ CRRcvFileCancelled user ci ftr
FileStatus fileId -> withUser $ \user -> do FileStatus fileId -> withUser $ \user -> do
fileStatus <- withStore $ \db -> getFileTransferProgress db user fileId fileStatus <- withStore $ \db -> getFileTransferProgress db user fileId
pure $ CRFileTransferStatus user fileStatus pure $ CRFileTransferStatus user fileStatus
@ -1808,7 +1822,7 @@ deleteFile' user CIFileInfo {filePath, fileId, fileStatus} sendCancel = do
delete :: m () delete :: m ()
delete = withFilesFolder $ \filesFolder -> delete = withFilesFolder $ \filesFolder ->
forM_ filePath $ \fPath -> do forM_ filePath $ \fPath -> do
let fsFilePath = filesFolder <> "/" <> fPath let fsFilePath = filesFolder </> fPath
removeFile fsFilePath `E.catch` \(_ :: E.SomeException) -> removeFile fsFilePath `E.catch` \(_ :: E.SomeException) ->
removePathForcibly fsFilePath `E.catch` \(_ :: E.SomeException) -> pure () removePathForcibly fsFilePath `E.catch` \(_ :: E.SomeException) -> pure ()
-- perform an action only if filesFolder is set (i.e. on mobile devices) -- perform an action only if filesFolder is set (i.e. on mobile devices)
@ -1925,7 +1939,7 @@ receiveViaCompleteFD user fileId RcvFileDescr {fileDescrText, fileDescrComplete}
rd <- parseRcvFileDescription fileDescrText rd <- parseRcvFileDescription fileDescrText
aFileId <- withAgent $ \a -> xftpReceiveFile a (aUserId user) rd aFileId <- withAgent $ \a -> xftpReceiveFile a (aUserId user) rd
startReceivingFile user fileId startReceivingFile user fileId
withStore' $ \db -> updateRcvFileAgentId db fileId (AgentRcvFileId aFileId) withStore' $ \db -> updateRcvFileAgentId db fileId (Just $ AgentRcvFileId aFileId)
startReceivingFile :: ChatMonad m => User -> FileTransferId -> m () startReceivingFile :: ChatMonad m => User -> FileTransferId -> m ()
startReceivingFile user fileId = do startReceivingFile user fileId = do

View File

@ -3074,7 +3074,7 @@ getRcvFileDescrByFileId_ db fileId =
toRcvFileDescr (fileDescrId, fileDescrText, fileDescrPartNo, fileDescrComplete) = toRcvFileDescr (fileDescrId, fileDescrText, fileDescrPartNo, fileDescrComplete) =
RcvFileDescr {fileDescrId, fileDescrText, fileDescrPartNo, fileDescrComplete} RcvFileDescr {fileDescrId, fileDescrText, fileDescrPartNo, fileDescrComplete}
updateRcvFileAgentId :: DB.Connection -> FileTransferId -> AgentRcvFileId -> IO () updateRcvFileAgentId :: DB.Connection -> FileTransferId -> Maybe AgentRcvFileId -> IO ()
updateRcvFileAgentId db fileId aFileId = do updateRcvFileAgentId db fileId aFileId = do
currentTs <- getCurrentTime currentTs <- getCurrentTime
DB.execute db "UPDATE rcv_files SET agent_rcv_file_id = ?, updated_at = ? WHERE file_id = ?" (aFileId, currentTs, fileId) DB.execute db "UPDATE rcv_files SET agent_rcv_file_id = ?, updated_at = ? WHERE file_id = ?" (aFileId, currentTs, fileId)

View File

@ -10,8 +10,11 @@ import Control.Concurrent.Async (concurrently_)
import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Char8 as B
import Simplex.Chat.Controller (ChatConfig (..), InlineFilesConfig (..), XFTPFileConfig (..), defaultInlineFilesConfig) import Simplex.Chat.Controller (ChatConfig (..), InlineFilesConfig (..), XFTPFileConfig (..), defaultInlineFilesConfig)
import Simplex.Chat.Options (ChatOpts (..)) import Simplex.Chat.Options (ChatOpts (..))
import Simplex.FileTransfer.Client.Main (xftpClientCLI)
import Simplex.Messaging.Util (unlessM) import Simplex.Messaging.Util (unlessM)
import System.Directory (copyFile, doesFileExist) import System.Directory (copyFile, doesFileExist)
import System.Environment (withArgs)
import System.IO.Silently (capture_)
import Test.Hspec import Test.Hspec
chatFileTests :: SpecWith FilePath chatFileTests :: SpecWith FilePath
@ -54,6 +57,7 @@ chatFileTests = do
it "with changed XFTP config: send and receive file" testXFTPWithChangedConfig it "with changed XFTP config: send and receive file" testXFTPWithChangedConfig
it "with relative paths: send and receive file" testXFTPWithRelativePaths it "with relative paths: send and receive file" testXFTPWithRelativePaths
it "continue receiving file after restart" testXFTPContinueRcv it "continue receiving file after restart" testXFTPContinueRcv
it "cancel receiving file, repeat receive" testXFTPCancelRcvRepeat
runTestFileTransfer :: HasCallStack => TestCC -> TestCC -> IO () runTestFileTransfer :: HasCallStack => TestCC -> TestCC -> IO ()
runTestFileTransfer alice bob = do runTestFileTransfer alice bob = do
@ -1088,6 +1092,41 @@ testXFTPContinueRcv tmp = do
where where
cfg = testCfg {xftpFileConfig = Just $ XFTPFileConfig {minFileSize = 0}, tempDir = Just "./tests/tmp"} cfg = testCfg {xftpFileConfig = Just $ XFTPFileConfig {minFileSize = 0}, tempDir = Just "./tests/tmp"}
testXFTPCancelRcvRepeat :: HasCallStack => FilePath -> IO ()
testXFTPCancelRcvRepeat =
testChatCfg2 cfg aliceProfile bobProfile $ \alice bob -> do
withXFTPServer $ do
xftpCLI ["rand", "./tests/tmp/testfile", "17mb"] `shouldReturn` ["File created: " <> "./tests/tmp/testfile"]
connectUsers alice bob
alice #> "/f @bob ./tests/tmp/testfile"
alice <## "use /fc 1 to cancel sending"
bob <# "alice> sends file testfile (17.0 MiB / 17825792 bytes)"
bob <## "use /fr 1 [<dir>/ | <path>] to receive it"
bob ##> "/fr 1 ./tests/tmp"
bob <## "saving file 1 from alice to ./tests/tmp/testfile_1"
-- alice <## "started sending file 1 (testfile) to bob" -- TODO "started uploading" ?
alice <## "uploaded file 1 (testfile) for bob"
bob <## "started receiving file 1 (testfile) from alice"
bob ##> "/fc 1"
bob <## "cancelled receiving file 1 (testfile) from alice"
bob ##> "/fr 1 ./tests/tmp"
bob <## "started receiving file 1 (testfile) from alice"
bob <## "saving file 1 from alice to ./tests/tmp/testfile_1"
bob <## "completed receiving file 1 (testfile) from alice"
src <- B.readFile "./tests/tmp/testfile"
dest <- B.readFile "./tests/tmp/testfile_1"
dest `shouldBe` src
where
cfg = testCfg {xftpFileConfig = Just $ XFTPFileConfig {minFileSize = 0}, tempDir = Just "./tests/tmp"}
xftpCLI :: [String] -> IO [String]
xftpCLI params = lines <$> capture_ (withArgs params xftpClientCLI)
startFileTransfer :: HasCallStack => TestCC -> TestCC -> IO () startFileTransfer :: HasCallStack => TestCC -> TestCC -> IO ()
startFileTransfer alice bob = startFileTransfer alice bob =
startFileTransfer' alice bob "test.jpg" "136.5 KiB / 139737 bytes" startFileTransfer' alice bob "test.jpg" "136.5 KiB / 139737 bytes"