Skip to content
Snippets Groups Projects
Commit 1bebb458 authored by Sébastien Blin's avatar Sébastien Blin Committed by Philippe Gorley
Browse files

datatransfer: add a 10 minutes timeout when awaiting peer


Currently, when a file is sent, the peer needs to accept the transfer.
So, connections with this peer will be opened until the peer accepts or
not the file. Since the multi-device support, it's a problem because
the peer has to accept/refuse the transfer on each devices.

To avoid to leave unused connections while we are using the turn,
we need to close these connections. This patch add a timeout to
outgoing file transfers. So, after 10 minutes with the status
awaiting peer, the connection is closed and the transfer canceled.

NOTE: the peer will still see the awaiting_host status even if the
connection is closed, but it's a known bug and it will need a major
change of the blocking startNewFile function in FtpServer.

Change-Id: Ic6e3a2bfebad92cc5c64f5ac2355c3c3765752c9
Reviewed-by: default avatarPhilippe Gorley <philippe.gorley@savoirfairelinux.com>
parent 55be1e14
Branches
Tags
No related merge requests found
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "map_utils.h" #include "map_utils.h"
#include "client/ring_signal.h" #include "client/ring_signal.h"
#include <thread>
#include <stdexcept> #include <stdexcept>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
...@@ -107,7 +108,7 @@ public: ...@@ -107,7 +108,7 @@ public:
protected: protected:
mutable std::mutex infoMutex_; mutable std::mutex infoMutex_;
mutable DRing::DataTransferInfo info_; mutable DRing::DataTransferInfo info_;
std::atomic_bool started_ {false}; mutable std::atomic_bool started_ {false};
std::atomic_bool wasStarted_ {false}; std::atomic_bool wasStarted_ {false};
}; };
...@@ -168,7 +169,7 @@ OptimisticMetaOutgoingInfo::updateInfo(const DRing::DataTransferInfo& info) cons ...@@ -168,7 +169,7 @@ OptimisticMetaOutgoingInfo::updateInfo(const DRing::DataTransferInfo& info) cons
DRing::DataTransferEventCode lastEvent { DRing::DataTransferEventCode::invalid }; DRing::DataTransferEventCode lastEvent { DRing::DataTransferEventCode::invalid };
{ {
std::lock_guard<std::mutex> lk {infoMutex_}; std::lock_guard<std::mutex> lk {infoMutex_};
if (info_.lastEvent > DRing::DataTransferEventCode::unjoinable_peer) { if (info_.lastEvent > DRing::DataTransferEventCode::timeout_expired) {
info_.lastEvent = DRing::DataTransferEventCode::invalid; info_.lastEvent = DRing::DataTransferEventCode::invalid;
} }
if (info.lastEvent >= DRing::DataTransferEventCode::created if (info.lastEvent >= DRing::DataTransferEventCode::created
...@@ -180,7 +181,7 @@ OptimisticMetaOutgoingInfo::updateInfo(const DRing::DataTransferInfo& info) cons ...@@ -180,7 +181,7 @@ OptimisticMetaOutgoingInfo::updateInfo(const DRing::DataTransferInfo& info) cons
} }
if (info.lastEvent >= DRing::DataTransferEventCode::closed_by_host if (info.lastEvent >= DRing::DataTransferEventCode::closed_by_host
&& info.lastEvent <= DRing::DataTransferEventCode::unjoinable_peer && info.lastEvent <= DRing::DataTransferEventCode::timeout_expired
&& info_.lastEvent < DRing::DataTransferEventCode::finished) { && info_.lastEvent < DRing::DataTransferEventCode::finished) {
// if not finished show error if all failed // if not finished show error if all failed
// if the transfer was ongoing and canceled, we should go to the best status // if the transfer was ongoing and canceled, we should go to the best status
...@@ -248,8 +249,10 @@ class SubOutgoingFileTransfer final : public DataTransfer ...@@ -248,8 +249,10 @@ class SubOutgoingFileTransfer final : public DataTransfer
{ {
public: public:
SubOutgoingFileTransfer(DRing::DataTransferId tid, const std::string& peerUri, std::shared_ptr<OptimisticMetaOutgoingInfo> metaInfo); SubOutgoingFileTransfer(DRing::DataTransferId tid, const std::string& peerUri, std::shared_ptr<OptimisticMetaOutgoingInfo> metaInfo);
~SubOutgoingFileTransfer();
void close() noexcept override; void close() noexcept override;
void closeAndEmit(DRing::DataTransferEventCode code) const noexcept;
bool read(std::vector<uint8_t>&) const override; bool read(std::vector<uint8_t>&) const override;
bool write(const std::vector<uint8_t>& buffer) override; bool write(const std::vector<uint8_t>& buffer) override;
void emit(DRing::DataTransferEventCode code) const override; void emit(DRing::DataTransferEventCode code) const override;
...@@ -263,6 +266,8 @@ private: ...@@ -263,6 +266,8 @@ private:
mutable bool headerSent_ {false}; mutable bool headerSent_ {false};
bool peerReady_ {false}; bool peerReady_ {false};
const std::string peerUri_; const std::string peerUri_;
mutable std::unique_ptr<std::thread> timeoutThread_;
mutable std::atomic_bool stopTimeout_ {false};
}; };
SubOutgoingFileTransfer::SubOutgoingFileTransfer(DRing::DataTransferId tid, SubOutgoingFileTransfer::SubOutgoingFileTransfer(DRing::DataTransferId tid,
...@@ -278,10 +283,23 @@ SubOutgoingFileTransfer::SubOutgoingFileTransfer(DRing::DataTransferId tid, ...@@ -278,10 +283,23 @@ SubOutgoingFileTransfer::SubOutgoingFileTransfer(DRing::DataTransferId tid,
metaInfo_->addLinkedTransfer(this); metaInfo_->addLinkedTransfer(this);
} }
SubOutgoingFileTransfer::~SubOutgoingFileTransfer() {
if (timeoutThread_ && timeoutThread_->joinable()) {
stopTimeout_ = true;
timeoutThread_->join();
}
}
void void
SubOutgoingFileTransfer::close() noexcept SubOutgoingFileTransfer::close() noexcept
{ {
DataTransfer::close(); closeAndEmit(DRing::DataTransferEventCode::closed_by_host);
}
void
SubOutgoingFileTransfer::closeAndEmit(DRing::DataTransferEventCode code) const noexcept
{
started_ = false; // NOTE: replace DataTransfer::close(); which is non const
input_.close(); input_.close();
// We don't need the connection anymore. Can close it. // We don't need the connection anymore. Can close it.
...@@ -289,7 +307,7 @@ SubOutgoingFileTransfer::close() noexcept ...@@ -289,7 +307,7 @@ SubOutgoingFileTransfer::close() noexcept
account->closePeerConnection(peerUri_, id); account->closePeerConnection(peerUri_, id);
if (info_.lastEvent < DRing::DataTransferEventCode::finished) if (info_.lastEvent < DRing::DataTransferEventCode::finished)
emit(DRing::DataTransferEventCode::closed_by_host); emit(code);
} }
bool bool
...@@ -366,6 +384,22 @@ SubOutgoingFileTransfer::emit(DRing::DataTransferEventCode code) const ...@@ -366,6 +384,22 @@ SubOutgoingFileTransfer::emit(DRing::DataTransferEventCode code) const
info_.lastEvent = code; info_.lastEvent = code;
} }
metaInfo_->updateInfo(info_); metaInfo_->updateInfo(info_);
if (code == DRing::DataTransferEventCode::wait_peer_acceptance) {
timeoutThread_ = std::move(std::unique_ptr<std::thread>(new std::thread([this]() {
const auto TEN_MIN = 1000 * 60 * 10;
const auto SLEEP_DURATION = 100;
for (auto i = 0; i < TEN_MIN / SLEEP_DURATION; ++i) {
// 10 min before timeout
std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_DURATION));
if (stopTimeout_.load())
return; // not waiting anymore
}
RING_WARN() << "FTP#" << this->getId() << ": timeout. Cancel";
this->closeAndEmit(DRing::DataTransferEventCode::timeout_expired);
})));
} else if (timeoutThread_) {
stopTimeout_ = true;
}
} }
/** /**
......
...@@ -46,6 +46,7 @@ enum class DataTransferEventCode : uint32_t ...@@ -46,6 +46,7 @@ enum class DataTransferEventCode : uint32_t
closed_by_peer, closed_by_peer,
invalid_pathname, invalid_pathname,
unjoinable_peer, unjoinable_peer,
timeout_expired,
}; };
enum class DataTransferError : uint32_t enum class DataTransferError : uint32_t
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment