mirror of
https://github.com/transmission/transmission.git
synced 2025-12-20 02:18:42 +00:00
refactor: local peer discovery (#3696)
This commit is contained in:
@@ -20,6 +20,7 @@ include(CheckIncludeFile)
|
|||||||
include(CheckIncludeFiles)
|
include(CheckIncludeFiles)
|
||||||
include(CheckFunctionExists)
|
include(CheckFunctionExists)
|
||||||
include(CheckLibraryExists)
|
include(CheckLibraryExists)
|
||||||
|
include(CheckSymbolExists)
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
include(TrMacros)
|
include(TrMacros)
|
||||||
@@ -611,6 +612,8 @@ if(HAVE_LIBM)
|
|||||||
set(LIBM_LIBRARY m)
|
set(LIBM_LIBRARY m)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
check_symbol_exists(SO_REUSEPORT "sys/types.h;sys/socket.h" HAVE_SO_REUSEPORT)
|
||||||
|
|
||||||
set(TR_NETWORK_LIBRARIES)
|
set(TR_NETWORK_LIBRARIES)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND TR_NETWORK_LIBRARIES iphlpapi ws2_32)
|
list(APPEND TR_NETWORK_LIBRARIES iphlpapi ws2_32)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Checks: >
|
|||||||
clang-analyzer-core.*,
|
clang-analyzer-core.*,
|
||||||
clang-analyzer-cplusplus.*,
|
clang-analyzer-cplusplus.*,
|
||||||
clang-analyzer-deadcode.*,
|
clang-analyzer-deadcode.*,
|
||||||
|
clang-analyzer-nullability.*,
|
||||||
clang-analyzer-optin.cplusplus.*,
|
clang-analyzer-optin.cplusplus.*,
|
||||||
clang-analyzer-security.*,
|
clang-analyzer-security.*,
|
||||||
clang-analyzer-valist.*,
|
clang-analyzer-valist.*,
|
||||||
@@ -28,6 +29,7 @@ Checks: >
|
|||||||
-modernize-avoid-c-arrays,
|
-modernize-avoid-c-arrays,
|
||||||
-modernize-use-trailing-return-type,
|
-modernize-use-trailing-return-type,
|
||||||
performance-*,
|
performance-*,
|
||||||
|
portability-*,
|
||||||
readability-*,
|
readability-*,
|
||||||
-readability-function-cognitive-complexity,
|
-readability-function-cognitive-complexity,
|
||||||
-readability-identifier-length,
|
-readability-identifier-length,
|
||||||
|
|||||||
@@ -254,6 +254,10 @@ if(POLARSSL_IS_MBEDTLS)
|
|||||||
add_definitions(-DPOLARSSL_IS_MBEDTLS)
|
add_definitions(-DPOLARSSL_IS_MBEDTLS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(HAVE_SO_REUSEPORT)
|
||||||
|
add_definitions(-DHAVE_SO_REUSEPORT=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${PROJECT_SOURCE_DIR}
|
${PROJECT_SOURCE_DIR}
|
||||||
${PROJECT_BINARY_DIR}
|
${PROJECT_BINARY_DIR}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ std::string tr_base64_decode(std::string_view input);
|
|||||||
std::string tr_sha1_to_string(tr_sha1_digest_t const&);
|
std::string tr_sha1_to_string(tr_sha1_digest_t const&);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generate a sha256 digest from a hex string.
|
* @brief Generate a sha1 digest from a hex string.
|
||||||
*/
|
*/
|
||||||
std::optional<tr_sha1_digest_t> tr_sha1_from_string(std::string_view hex);
|
std::optional<tr_sha1_digest_t> tr_sha1_from_string(std::string_view hex);
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,58 @@ tr_peer_id_t tr_peerIdInit()
|
|||||||
****
|
****
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
bool tr_session::LpdMediator::onPeerFound(std::string_view info_hash_str, tr_address address, tr_port port)
|
||||||
|
{
|
||||||
|
auto const digest = tr_sha1_from_string(info_hash_str);
|
||||||
|
if (!digest)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr_torrent* const tor = session_.torrents_.get(*digest);
|
||||||
|
if (!tr_isTorrent(tor) || !tor->allowsLpd())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we found a suitable peer, add it to the torrent
|
||||||
|
auto pex = tr_pex{ address, port };
|
||||||
|
tr_peerMgrAddPex(tor, TR_PEER_FROM_LPD, &pex, 1U);
|
||||||
|
tr_logAddDebugTor(tor, fmt::format(FMT_STRING("Found a local peer from LPD ({:s})"), address.readable(port)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<tr_lpd::Mediator::TorrentInfo> tr_session::LpdMediator::torrents() const
|
||||||
|
{
|
||||||
|
auto ret = std::vector<tr_lpd::Mediator::TorrentInfo>{};
|
||||||
|
ret.reserve(std::size(session_.torrents()));
|
||||||
|
for (auto const* const tor : session_.torrents())
|
||||||
|
{
|
||||||
|
auto info = tr_lpd::Mediator::TorrentInfo{};
|
||||||
|
info.info_hash_str = tor->infoHashString();
|
||||||
|
info.activity = tr_torrentGetActivity(tor);
|
||||||
|
info.allows_lpd = tor->allowsLpd();
|
||||||
|
info.announce_after = tor->lpdAnnounceAt;
|
||||||
|
ret.emplace_back(info);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tr_session::LpdMediator::setNextAnnounceTime(std::string_view info_hash_str, time_t announce_after)
|
||||||
|
{
|
||||||
|
if (auto digest = tr_sha1_from_string(info_hash_str); digest)
|
||||||
|
{
|
||||||
|
if (tr_torrent* const tor = session_.torrents_.get(*digest); tr_isTorrent(tor))
|
||||||
|
{
|
||||||
|
tor->lpdAnnounceAt = announce_after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
****
|
||||||
|
***/
|
||||||
|
|
||||||
std::optional<std::string> tr_session::WebMediator::cookieFile() const
|
std::optional<std::string> tr_session::WebMediator::cookieFile() const
|
||||||
{
|
{
|
||||||
auto const path = tr_pathbuf{ session_->configDir(), "/cookies.txt"sv };
|
auto const path = tr_pathbuf{ session_->configDir(), "/cookies.txt"sv };
|
||||||
@@ -700,7 +752,7 @@ void tr_session::initImpl(init_data& data)
|
|||||||
|
|
||||||
if (this->allowsLPD())
|
if (this->allowsLPD())
|
||||||
{
|
{
|
||||||
tr_lpdInit(this, &this->bind_ipv4.addr_);
|
this->lpd_ = tr_lpd::create(lpd_mediator_, timerMaker(), eventBase());
|
||||||
}
|
}
|
||||||
|
|
||||||
tr_utpInit(this);
|
tr_utpInit(this);
|
||||||
@@ -1743,10 +1795,7 @@ void tr_session::closeImplStart()
|
|||||||
{
|
{
|
||||||
is_closing_ = true;
|
is_closing_ = true;
|
||||||
|
|
||||||
if (this->allowsLPD())
|
lpd_.reset();
|
||||||
{
|
|
||||||
tr_lpdUninit(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr_dhtUninit(this);
|
tr_dhtUninit(this);
|
||||||
|
|
||||||
@@ -2082,16 +2131,11 @@ void tr_sessionSetLPDEnabled(tr_session* session, bool enabled)
|
|||||||
session,
|
session,
|
||||||
[session, enabled]()
|
[session, enabled]()
|
||||||
{
|
{
|
||||||
if (session->allowsLPD())
|
session->lpd_.reset();
|
||||||
{
|
|
||||||
tr_lpdUninit(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
session->is_lpd_enabled_ = enabled;
|
session->is_lpd_enabled_ = enabled;
|
||||||
|
if (enabled)
|
||||||
if (session->allowsLPD())
|
|
||||||
{
|
{
|
||||||
tr_lpdInit(session, &session->bind_ipv4.addr_);
|
session->lpd_ = tr_lpd::create(session->lpd_mediator_, session->timerMaker(), session->eventBase());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "torrents.h"
|
#include "torrents.h"
|
||||||
|
#include "tr-lpd.h"
|
||||||
#include "web.h"
|
#include "web.h"
|
||||||
|
|
||||||
enum tr_auto_switch_state_t
|
enum tr_auto_switch_state_t
|
||||||
@@ -53,6 +54,7 @@ struct evdns_base;
|
|||||||
|
|
||||||
class tr_rpc_server;
|
class tr_rpc_server;
|
||||||
class tr_web;
|
class tr_web;
|
||||||
|
class tr_lpd;
|
||||||
struct BlocklistFile;
|
struct BlocklistFile;
|
||||||
struct struct_utp_context;
|
struct struct_utp_context;
|
||||||
struct tr_announcer;
|
struct tr_announcer;
|
||||||
@@ -503,6 +505,7 @@ public:
|
|||||||
std::unique_ptr<Cache> cache;
|
std::unique_ptr<Cache> cache;
|
||||||
|
|
||||||
std::unique_ptr<tr_web> web;
|
std::unique_ptr<tr_web> web;
|
||||||
|
std::unique_ptr<tr_lpd> lpd_;
|
||||||
|
|
||||||
struct tr_announcer* announcer = nullptr;
|
struct tr_announcer* announcer = nullptr;
|
||||||
struct tr_announcer_udp* announcer_udp = nullptr;
|
struct tr_announcer_udp* announcer_udp = nullptr;
|
||||||
@@ -839,6 +842,37 @@ private:
|
|||||||
|
|
||||||
WebMediator web_mediator_{ this };
|
WebMediator web_mediator_{ this };
|
||||||
|
|
||||||
|
class LpdMediator final : public tr_lpd::Mediator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LpdMediator(tr_session& session)
|
||||||
|
: session_{ session }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~LpdMediator() override = default;
|
||||||
|
|
||||||
|
[[nodiscard]] tr_port port() const override
|
||||||
|
{
|
||||||
|
return session_.peerPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool allowsLPD() const override
|
||||||
|
{
|
||||||
|
return session_.allowsLPD();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<TorrentInfo> torrents() const override;
|
||||||
|
|
||||||
|
bool onPeerFound(std::string_view info_hash_str, tr_address address, tr_port port) override;
|
||||||
|
|
||||||
|
void setNextAnnounceTime(std::string_view info_hash_str, time_t announce_after) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
tr_session& session_;
|
||||||
|
};
|
||||||
|
|
||||||
|
LpdMediator lpd_mediator_{ *this };
|
||||||
|
|
||||||
std::shared_ptr<event_base> const event_base_;
|
std::shared_ptr<event_base> const event_base_;
|
||||||
std::shared_ptr<evdns_base> const evdns_base_;
|
std::shared_ptr<evdns_base> const evdns_base_;
|
||||||
std::unique_ptr<libtransmission::TimerMaker> const timer_maker_;
|
std::unique_ptr<libtransmission::TimerMaker> const timer_maker_;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "transmission.h"
|
#include "transmission.h"
|
||||||
|
|
||||||
|
#include "file.h"
|
||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
#include "tr-strbuf.h"
|
#include "tr-strbuf.h"
|
||||||
#include "utils.h" // for tr_getRatio(), tr_time()
|
#include "utils.h" // for tr_getRatio(), tr_time()
|
||||||
@@ -18,14 +19,14 @@ tr_session_stats tr_stats::loadOldStats(std::string_view config_dir)
|
|||||||
|
|
||||||
auto top = tr_variant{};
|
auto top = tr_variant{};
|
||||||
auto filename = tr_pathbuf{ config_dir, "/stats.json"sv };
|
auto filename = tr_pathbuf{ config_dir, "/stats.json"sv };
|
||||||
bool loaded = tr_variantFromFile(&top, TR_VARIANT_PARSE_JSON, filename.sv(), nullptr);
|
bool loaded = tr_sys_path_exists(filename) && tr_variantFromFile(&top, TR_VARIANT_PARSE_JSON, filename.sv(), nullptr);
|
||||||
|
|
||||||
if (!loaded)
|
if (!loaded)
|
||||||
{
|
{
|
||||||
// maybe the user just upgraded from an old version of Transmission
|
// maybe the user just upgraded from an old version of Transmission
|
||||||
// that was still using stats.benc
|
// that was still using stats.benc
|
||||||
filename.assign(config_dir, "/stats.benc");
|
filename.assign(config_dir, "/stats.benc");
|
||||||
loaded = tr_variantFromFile(&top, TR_VARIANT_PARSE_BENC, filename.sv(), nullptr);
|
loaded = tr_sys_path_exists(filename) && tr_variantFromFile(&top, TR_VARIANT_PARSE_BENC, filename.sv(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loaded)
|
if (loaded)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,9 +8,48 @@
|
|||||||
#error only libtransmission should #include this header.
|
#error only libtransmission should #include this header.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int tr_lpdInit(tr_session*, tr_address*);
|
#include <ctime>
|
||||||
void tr_lpdUninit(tr_session*);
|
#include <memory>
|
||||||
bool tr_lpdSendAnnounce(tr_torrent const*);
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
/**
|
#include "transmission.h"
|
||||||
* @} */
|
|
||||||
|
#include "net.h" // for tr_address, tr_port
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
class tr_torrents;
|
||||||
|
struct tr_session;
|
||||||
|
struct event_base;
|
||||||
|
|
||||||
|
class tr_lpd
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class Mediator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct TorrentInfo
|
||||||
|
{
|
||||||
|
std::string_view info_hash_str;
|
||||||
|
tr_torrent_activity activity;
|
||||||
|
bool allows_lpd;
|
||||||
|
time_t announce_after;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~Mediator() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual tr_port port() const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual bool allowsLPD() const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual std::vector<TorrentInfo> torrents() const = 0;
|
||||||
|
|
||||||
|
virtual void setNextAnnounceTime(std::string_view info_hash_str, time_t announce_at) = 0;
|
||||||
|
|
||||||
|
// returns true if info was used
|
||||||
|
virtual bool onPeerFound(std::string_view info_hash_str, tr_address address, tr_port port) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~tr_lpd() = default;
|
||||||
|
static std::unique_ptr<tr_lpd> create(Mediator& mediator, libtransmission::TimerMaker&, event_base* event_base);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1135,6 +1135,7 @@ template std::optional<char> tr_parseNum(std::string_view& sv, int base);
|
|||||||
template std::optional<unsigned long long> tr_parseNum(std::string_view& sv, int base);
|
template std::optional<unsigned long long> tr_parseNum(std::string_view& sv, int base);
|
||||||
template std::optional<unsigned long> tr_parseNum(std::string_view& sv, int base);
|
template std::optional<unsigned long> tr_parseNum(std::string_view& sv, int base);
|
||||||
template std::optional<unsigned int> tr_parseNum(std::string_view& sv, int base);
|
template std::optional<unsigned int> tr_parseNum(std::string_view& sv, int base);
|
||||||
|
template std::optional<unsigned short> tr_parseNum(std::string_view& sv, int base);
|
||||||
template std::optional<unsigned char> tr_parseNum(std::string_view& sv, int base);
|
template std::optional<unsigned char> tr_parseNum(std::string_view& sv, int base);
|
||||||
|
|
||||||
template<typename T, std::enable_if_t<std::is_floating_point<T>::value, bool>>
|
template<typename T, std::enable_if_t<std::is_floating_point<T>::value, bool>>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ add_executable(libtransmission-test
|
|||||||
handshake-test.cc
|
handshake-test.cc
|
||||||
history-test.cc
|
history-test.cc
|
||||||
json-test.cc
|
json-test.cc
|
||||||
|
lpd-test.cc
|
||||||
magnet-metainfo-test.cc
|
magnet-metainfo-test.cc
|
||||||
makemeta-test.cc
|
makemeta-test.cc
|
||||||
move-test.cc
|
move-test.cc
|
||||||
|
|||||||
202
tests/libtransmission/lpd-test.cc
Normal file
202
tests/libtransmission/lpd-test.cc
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
// This file Copyright (C) 2022 Mnemosyne LLC.
|
||||||
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
||||||
|
// or any future license endorsed by Mnemosyne LLC.
|
||||||
|
// License text can be found in the licenses/ folder.
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "transmission.h"
|
||||||
|
|
||||||
|
#include "crypto-utils.h"
|
||||||
|
#include "session.h"
|
||||||
|
#include "tr-lpd.h"
|
||||||
|
|
||||||
|
#include "test-fixtures.h"
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
namespace libtransmission
|
||||||
|
{
|
||||||
|
namespace test
|
||||||
|
{
|
||||||
|
|
||||||
|
using LpdTest = SessionTest;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
class MyMediator final : public tr_lpd::Mediator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyMediator() = default;
|
||||||
|
~MyMediator() override = default;
|
||||||
|
|
||||||
|
[[nodiscard]] tr_port port() const override
|
||||||
|
{
|
||||||
|
return port_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool allowsLPD() const override
|
||||||
|
{
|
||||||
|
return allows_lpd_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<TorrentInfo> torrents() const override
|
||||||
|
{
|
||||||
|
return torrents_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNextAnnounceTime(std::string_view info_hash_str, time_t announce_after) override
|
||||||
|
{
|
||||||
|
for (auto& tor : torrents_)
|
||||||
|
{
|
||||||
|
if (tor.info_hash_str == info_hash_str)
|
||||||
|
{
|
||||||
|
tor.announce_after = announce_after;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onPeerFound(std::string_view info_hash_str, tr_address /*address*/, tr_port /*port*/) override
|
||||||
|
{
|
||||||
|
found_.insert(std::string{ info_hash_str });
|
||||||
|
return found_returns_;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr_port port_ = tr_port::fromHost(51413);
|
||||||
|
bool allows_lpd_ = true;
|
||||||
|
std::vector<TorrentInfo> torrents_;
|
||||||
|
std::set<std::string> found_;
|
||||||
|
bool found_returns_ = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto makeRandomHash()
|
||||||
|
{
|
||||||
|
auto buf = std::array<char, 256>{};
|
||||||
|
tr_rand_buffer(std::data(buf), std::size(buf));
|
||||||
|
return tr_sha1::digest(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto makeRandomHashString()
|
||||||
|
{
|
||||||
|
return tr_strupper(tr_sha1_to_string(makeRandomHash()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_F(LpdTest, HelloWorld)
|
||||||
|
{
|
||||||
|
auto mediator = MyMediator{};
|
||||||
|
auto lpd = tr_lpd::create(mediator, session_->timerMaker(), session_->eventBase());
|
||||||
|
EXPECT_TRUE(lpd);
|
||||||
|
EXPECT_EQ(0U, std::size(mediator.found_));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LpdTest, CanAnnounceAndRead)
|
||||||
|
{
|
||||||
|
auto mediator_a = MyMediator{};
|
||||||
|
auto lpd_a = tr_lpd::create(mediator_a, session_->timerMaker(), session_->eventBase());
|
||||||
|
EXPECT_TRUE(lpd_a);
|
||||||
|
|
||||||
|
auto const info_hash_str = makeRandomHashString();
|
||||||
|
auto info = tr_lpd::Mediator::TorrentInfo{};
|
||||||
|
info.info_hash_str = info_hash_str;
|
||||||
|
info.activity = TR_STATUS_SEED;
|
||||||
|
info.allows_lpd = true;
|
||||||
|
info.announce_after = 0; // never announced
|
||||||
|
|
||||||
|
auto mediator_b = MyMediator{};
|
||||||
|
mediator_b.torrents_.push_back(info);
|
||||||
|
auto lpd_b = tr_lpd::create(mediator_b, session_->timerMaker(), session_->eventBase());
|
||||||
|
|
||||||
|
waitFor([&mediator_a]() { return !std::empty(mediator_a.found_); }, 1s);
|
||||||
|
EXPECT_EQ(1U, mediator_a.found_.count(info_hash_str));
|
||||||
|
EXPECT_EQ(0U, mediator_b.found_.count(info_hash_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LpdTest, canMultiAnnounce)
|
||||||
|
{
|
||||||
|
auto mediator_a = MyMediator{};
|
||||||
|
auto lpd_a = tr_lpd::create(mediator_a, session_->timerMaker(), session_->eventBase());
|
||||||
|
EXPECT_TRUE(lpd_a);
|
||||||
|
|
||||||
|
auto info_hash_strings = std::array<std::string, 2>{};
|
||||||
|
auto infos = std::array<tr_lpd::Mediator::TorrentInfo, 2>{};
|
||||||
|
auto mediator_b = MyMediator{};
|
||||||
|
for (size_t i = 0; i < std::size(info_hash_strings); ++i)
|
||||||
|
{
|
||||||
|
auto& info_hash_string = info_hash_strings[i];
|
||||||
|
auto& info = infos[i];
|
||||||
|
|
||||||
|
info_hash_string = makeRandomHashString();
|
||||||
|
|
||||||
|
info.info_hash_str = info_hash_string;
|
||||||
|
info.activity = TR_STATUS_SEED;
|
||||||
|
info.allows_lpd = true;
|
||||||
|
info.announce_after = 0; // never announced
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& info : infos)
|
||||||
|
{
|
||||||
|
mediator_b.torrents_.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lpd_b = tr_lpd::create(mediator_b, session_->timerMaker(), session_->eventBase());
|
||||||
|
waitFor([&mediator_a]() { return !std::empty(mediator_a.found_); }, 1s);
|
||||||
|
|
||||||
|
for (auto const& info : infos)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(1U, mediator_a.found_.count(std::string{ info.info_hash_str }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LpdTest, DoesNotReannounceTooSoon)
|
||||||
|
{
|
||||||
|
auto mediator_a = MyMediator{};
|
||||||
|
auto lpd_a = tr_lpd::create(mediator_a, session_->timerMaker(), session_->eventBase());
|
||||||
|
EXPECT_TRUE(lpd_a);
|
||||||
|
|
||||||
|
// similar to canMultiAnnounce...
|
||||||
|
auto info_hash_strings = std::array<std::string, 2>{};
|
||||||
|
auto infos = std::array<tr_lpd::Mediator::TorrentInfo, 2>{};
|
||||||
|
auto mediator_b = MyMediator{};
|
||||||
|
for (size_t i = 0; i < std::size(info_hash_strings); ++i)
|
||||||
|
{
|
||||||
|
auto& info_hash_string = info_hash_strings[i];
|
||||||
|
auto& info = infos[i];
|
||||||
|
|
||||||
|
info_hash_string = makeRandomHashString();
|
||||||
|
|
||||||
|
info.info_hash_str = info_hash_string;
|
||||||
|
info.activity = TR_STATUS_SEED;
|
||||||
|
info.allows_lpd = true;
|
||||||
|
info.announce_after = 0; // never announced
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...except one torrent has already been announced
|
||||||
|
// and doesn't need to be reannounced until later
|
||||||
|
auto const now = time(nullptr);
|
||||||
|
infos[0].announce_after = now + 60;
|
||||||
|
|
||||||
|
for (auto const& info : infos)
|
||||||
|
{
|
||||||
|
mediator_b.torrents_.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lpd_b = tr_lpd::create(mediator_b, session_->timerMaker(), session_->eventBase());
|
||||||
|
waitFor([&mediator_a]() { return !std::empty(mediator_a.found_); }, 1s);
|
||||||
|
|
||||||
|
for (auto& info : infos)
|
||||||
|
{
|
||||||
|
auto const expected_count = info.announce_after <= now ? 1U : 0U;
|
||||||
|
EXPECT_EQ(expected_count, mediator_a.found_.count(std::string{ info.info_hash_str }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace libtransmission
|
||||||
@@ -33,7 +33,7 @@ class RenameTest : public SessionTest
|
|||||||
static auto constexpr MaxWaitMsec = 3000;
|
static auto constexpr MaxWaitMsec = 3000;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void torrentRemoveAndWait(tr_torrent* tor, int expected_torrent_count)
|
void torrentRemoveAndWait(tr_torrent* tor, size_t expected_torrent_count)
|
||||||
{
|
{
|
||||||
tr_torrentRemove(tor, false, nullptr);
|
tr_torrentRemove(tor, false, nullptr);
|
||||||
auto const test = [this, expected_torrent_count]()
|
auto const test = [this, expected_torrent_count]()
|
||||||
|
|||||||
@@ -71,10 +71,9 @@ static void depthFirstWalk(char const* path, file_func_t func)
|
|||||||
func(path);
|
func(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool waitFor(std::function<bool()> const& test, int msec)
|
inline bool waitFor(std::function<bool()> const& test, std::chrono::milliseconds msec)
|
||||||
{
|
{
|
||||||
auto const deadline = std::chrono::milliseconds{ msec };
|
auto const deadline = std::chrono::steady_clock::now() + msec;
|
||||||
auto const begin = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@@ -83,7 +82,7 @@ inline bool waitFor(std::function<bool()> const& test, int msec)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((std::chrono::steady_clock::now() - begin) >= deadline)
|
if (std::chrono::steady_clock::now() > deadline)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -92,6 +91,11 @@ inline bool waitFor(std::function<bool()> const& test, int msec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool waitFor(std::function<bool()> const& test, int msec)
|
||||||
|
{
|
||||||
|
return waitFor(test, std::chrono::milliseconds{ msec });
|
||||||
|
}
|
||||||
|
|
||||||
class Sandbox
|
class Sandbox
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
Reference in New Issue
Block a user