refactor: use C++ inheritance for tr_peer, tr_peerMsgs, and tr_webseed (#1877)
* refactor: use C++ inheritance for tr_peer, tr_peerMsgs, and tr_webseed
This commit is contained in:
parent
83f21b8e0e
commit
3b72a1feea
|
@ -86,7 +86,6 @@
|
|||
A209EBF91142FEEE002B02D1 /* InfoOptionsViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A209EBF81142FEEE002B02D1 /* InfoOptionsViewController.mm */; };
|
||||
A209EC12114301C6002B02D1 /* InfoOptionsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A209EC11114301C6002B02D1 /* InfoOptionsView.xib */; };
|
||||
A209ECA2114319C3002B02D1 /* InfoWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = A209ECA1114319C3002B02D1 /* InfoWindow.xib */; };
|
||||
A209EE5C1144B51E002B02D1 /* history.cc in Sources */ = {isa = PBXBuildFile; fileRef = A209EE5A1144B51E002B02D1 /* history.cc */; };
|
||||
A209EE5D1144B51E002B02D1 /* history.h in Headers */ = {isa = PBXBuildFile; fileRef = A209EE5B1144B51E002B02D1 /* history.h */; };
|
||||
A20BFFB70D091CC700CE5D2B /* ToolbarSegmentedCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = A20BFFB60D091CC700CE5D2B /* ToolbarSegmentedCell.mm */; };
|
||||
A21282A80CA6C66800EAEE0F /* StatusBarView.mm in Sources */ = {isa = PBXBuildFile; fileRef = A21282A60CA6C66800EAEE0F /* StatusBarView.mm */; };
|
||||
|
@ -599,7 +598,6 @@
|
|||
A209EBF81142FEEE002B02D1 /* InfoOptionsViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = InfoOptionsViewController.mm; sourceTree = "<group>"; };
|
||||
A209EC13114301C6002B02D1 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/InfoOptionsView.xib; sourceTree = "<group>"; };
|
||||
A209ECA1114319C3002B02D1 /* InfoWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InfoWindow.xib; sourceTree = "<group>"; };
|
||||
A209EE5A1144B51E002B02D1 /* history.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = history.cc; sourceTree = "<group>"; };
|
||||
A209EE5B1144B51E002B02D1 /* history.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = history.h; sourceTree = "<group>"; };
|
||||
A20BFFB50D091CC700CE5D2B /* ToolbarSegmentedCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ToolbarSegmentedCell.h; sourceTree = "<group>"; };
|
||||
A20BFFB60D091CC700CE5D2B /* ToolbarSegmentedCell.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ToolbarSegmentedCell.mm; sourceTree = "<group>"; };
|
||||
|
@ -1466,7 +1464,6 @@
|
|||
A21FBBA90EDA78C300BC3C51 /* bandwidth.h */,
|
||||
A21FBBAA0EDA78C300BC3C51 /* bandwidth.cc */,
|
||||
A209EE5B1144B51E002B02D1 /* history.h */,
|
||||
A209EE5A1144B51E002B02D1 /* history.cc */,
|
||||
A23547E011CD0B090046EAE6 /* cache.cc */,
|
||||
A23547E111CD0B090046EAE6 /* cache.h */,
|
||||
BEFC1E020C07861A00B0BB3C /* platform.h */,
|
||||
|
@ -2483,7 +2480,6 @@
|
|||
A25964A6106D73A800453B31 /* announcer.cc in Sources */,
|
||||
4D8017EA10BBC073008A4AF2 /* torrent-magnet.cc in Sources */,
|
||||
4D80185910BBC0B0008A4AF2 /* magnet.cc in Sources */,
|
||||
A209EE5C1144B51E002B02D1 /* history.cc in Sources */,
|
||||
A220EC5B118C8A060022B4BE /* tr-lpd.cc in Sources */,
|
||||
C1FEE57A1C3223CC00D62832 /* watchdir.cc in Sources */,
|
||||
A23547E211CD0B090046EAE6 /* cache.cc in Sources */,
|
||||
|
|
|
@ -27,7 +27,6 @@ set(PROJECT_FILES
|
|||
file-posix.cc
|
||||
file-win32.cc
|
||||
handshake.cc
|
||||
history.cc
|
||||
inout.cc
|
||||
log.cc
|
||||
magnet.cc
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2010-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h> /* memset() */
|
||||
|
||||
#include "transmission.h"
|
||||
#include "history.h"
|
||||
#include "utils.h"
|
||||
|
||||
void tr_historyAdd(tr_recentHistory* h, time_t now, unsigned int n)
|
||||
{
|
||||
if (h->slices[h->newest].date == now)
|
||||
{
|
||||
h->slices[h->newest].n += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (++h->newest == TR_RECENT_HISTORY_PERIOD_SEC)
|
||||
{
|
||||
h->newest = 0;
|
||||
}
|
||||
|
||||
h->slices[h->newest].date = now;
|
||||
h->slices[h->newest].n = n;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int tr_historyGet(tr_recentHistory const* h, time_t now, unsigned int sec)
|
||||
{
|
||||
unsigned int n = 0;
|
||||
time_t const cutoff = (now != 0 ? now : tr_time()) - sec;
|
||||
int i = h->newest;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (h->slices[i].date <= cutoff)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
n += h->slices[i].n;
|
||||
|
||||
if (--i == -1)
|
||||
{
|
||||
i = TR_RECENT_HISTORY_PERIOD_SEC - 1; /* circular history */
|
||||
}
|
||||
|
||||
if (i == h->newest)
|
||||
{
|
||||
break; /* we've come all the way around */
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
|
@ -12,47 +12,61 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include <time.h> /* time_t */
|
||||
|
||||
#include "tr-macros.h"
|
||||
#include <array>
|
||||
#include <cstddef> // size_t
|
||||
#include <ctime> // time_t
|
||||
#include <numeric> // std::accumulate
|
||||
|
||||
/**
|
||||
* A generic short-term memory object that remembers how many times
|
||||
* something happened over the last N seconds.
|
||||
*
|
||||
* For example, it could count how many are bytes transferred
|
||||
* to estimate the speed over the last N seconds.
|
||||
* A short-term memory object that remembers how many times something
|
||||
* happened over the last N seconds. tr_peer uses it to count how many
|
||||
* bytes transferred to estimate the speed over the last N seconds.
|
||||
*/
|
||||
|
||||
enum
|
||||
class tr_recentHistory
|
||||
{
|
||||
TR_RECENT_HISTORY_PERIOD_SEC = 60
|
||||
};
|
||||
|
||||
struct tr_recentHistory
|
||||
{
|
||||
/* these are PRIVATE IMPLEMENTATION details included for composition only.
|
||||
* Don't access these directly! */
|
||||
|
||||
int newest;
|
||||
|
||||
struct
|
||||
public:
|
||||
/**
|
||||
* @brief add a counter to the recent history object.
|
||||
* @param when the current time in sec, such as from tr_time()
|
||||
* @param n how many items to add to the history's counter
|
||||
*/
|
||||
void add(time_t now, size_t n)
|
||||
{
|
||||
unsigned int n;
|
||||
time_t date;
|
||||
} slices[TR_RECENT_HISTORY_PERIOD_SEC];
|
||||
if (slices[newest].time != now)
|
||||
{
|
||||
newest = (newest + 1) % TR_RECENT_HISTORY_PERIOD_SEC;
|
||||
slices[newest].time = now;
|
||||
}
|
||||
|
||||
slices[newest].n += n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief count how many events have occurred in the last N seconds.
|
||||
* @param when the current time in sec, such as from tr_time()
|
||||
* @param seconds how many seconds to count back through.
|
||||
*/
|
||||
size_t count(time_t now, unsigned int age_sec) const
|
||||
{
|
||||
time_t const oldest = now - age_sec;
|
||||
|
||||
return std::accumulate(
|
||||
std::begin(slices),
|
||||
std::end(slices),
|
||||
size_t{ 0 },
|
||||
[&oldest](size_t sum, auto const& slice) { return slice.time >= oldest ? sum + slice.n : sum; });
|
||||
}
|
||||
|
||||
private:
|
||||
inline auto static constexpr TR_RECENT_HISTORY_PERIOD_SEC = size_t{ 60 };
|
||||
|
||||
int newest = 0;
|
||||
|
||||
struct slice_t
|
||||
{
|
||||
size_t n = 0;
|
||||
time_t time = 0;
|
||||
};
|
||||
|
||||
std::array<slice_t, TR_RECENT_HISTORY_PERIOD_SEC> slices = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief add a counter to the recent history object.
|
||||
* @param when the current time in sec, such as from tr_time()
|
||||
* @param n how many items to add to the history's counter
|
||||
*/
|
||||
void tr_historyAdd(tr_recentHistory*, time_t when, unsigned int n);
|
||||
|
||||
/**
|
||||
* @brief count how many events have occurred in the last N seconds.
|
||||
* @param when the current time in sec, such as from tr_time()
|
||||
* @param seconds how many seconds to count back through.
|
||||
*/
|
||||
unsigned int tr_historyGet(tr_recentHistory const*, time_t when, unsigned int seconds);
|
||||
|
|
|
@ -22,16 +22,14 @@
|
|||
* @{
|
||||
*/
|
||||
|
||||
struct tr_peer;
|
||||
class tr_peer;
|
||||
struct tr_swarm;
|
||||
struct peer_atom;
|
||||
|
||||
enum
|
||||
{
|
||||
/* this is the maximum size of a block request.
|
||||
most bittorrent clients will reject requests
|
||||
larger than this size. */
|
||||
MAX_BLOCK_SIZE = (1024 * 16)
|
||||
};
|
||||
/* This is the maximum size of a block request.
|
||||
most bittorrent clients will reject requests
|
||||
larger than this size. */
|
||||
auto inline constexpr MAX_BLOCK_SIZE = 1024 * 16;
|
||||
|
||||
/**
|
||||
*** Peer Publish / Subscribe
|
||||
|
@ -66,22 +64,7 @@ struct tr_peer_event
|
|||
tr_port port; /* for GOT_PORT */
|
||||
};
|
||||
|
||||
using tr_peer_callback = void (*)(struct tr_peer* peer, tr_peer_event const* event, void* client_data);
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
using tr_peer_destruct_func = void (*)(struct tr_peer* peer);
|
||||
|
||||
using tr_peer_is_transferring_pieces_func =
|
||||
bool (*)(tr_peer const* peer, uint64_t now, tr_direction direction, unsigned int* Bps);
|
||||
|
||||
struct tr_peer_virtual_funcs
|
||||
{
|
||||
tr_peer_destruct_func destruct;
|
||||
tr_peer_is_transferring_pieces_func is_transferring_pieces;
|
||||
};
|
||||
using tr_peer_callback = void (*)(tr_peer* peer, tr_peer_event const* event, void* client_data);
|
||||
|
||||
/**
|
||||
* State information about a connected peer.
|
||||
|
@ -89,53 +72,55 @@ struct tr_peer_virtual_funcs
|
|||
* @see struct peer_atom
|
||||
* @see tr_peerMsgs
|
||||
*/
|
||||
struct tr_peer
|
||||
class tr_peer
|
||||
{
|
||||
public:
|
||||
tr_peer(tr_torrent const* tor, peer_atom* atom = nullptr);
|
||||
virtual ~tr_peer();
|
||||
|
||||
virtual bool is_transferring_pieces(uint64_t now, tr_direction direction, unsigned int* setme_Bps) const = 0;
|
||||
|
||||
/* whether or not we should free this peer soon.
|
||||
NOTE: private to peer-mgr.c */
|
||||
bool doPurge;
|
||||
bool doPurge = false;
|
||||
|
||||
/* number of bad pieces they've contributed to */
|
||||
uint8_t strikes;
|
||||
uint8_t strikes = 0;
|
||||
|
||||
/* how many requests the peer has made that we haven't responded to yet */
|
||||
int pendingReqsToClient;
|
||||
int pendingReqsToClient = 0;
|
||||
|
||||
/* how many requests we've made and are currently awaiting a response for */
|
||||
int pendingReqsToPeer;
|
||||
int pendingReqsToPeer = 0;
|
||||
|
||||
tr_session* const session;
|
||||
|
||||
/* Hook to private peer-mgr information */
|
||||
struct peer_atom* atom;
|
||||
peer_atom* const atom;
|
||||
|
||||
struct tr_swarm* swarm;
|
||||
tr_swarm* const swarm;
|
||||
|
||||
/** how complete the peer's copy of the torrent is. [0.0...1.0] */
|
||||
float progress;
|
||||
float progress = 0.0f;
|
||||
|
||||
struct tr_bitfield blame;
|
||||
struct tr_bitfield have;
|
||||
struct tr_bitfield blame = {};
|
||||
struct tr_bitfield have = {};
|
||||
|
||||
/* the client name.
|
||||
For BitTorrent peers, this is the app name derived from the `v' string in LTEP's handshake dictionary */
|
||||
tr_quark client;
|
||||
tr_quark client = TR_KEY_NONE;
|
||||
|
||||
tr_recentHistory blocksSentToClient;
|
||||
tr_recentHistory blocksSentToPeer;
|
||||
|
||||
tr_recentHistory cancelsSentToClient;
|
||||
tr_recentHistory cancelsSentToPeer;
|
||||
|
||||
struct tr_peer_virtual_funcs const* funcs;
|
||||
};
|
||||
|
||||
void tr_peerConstruct(struct tr_peer* peer, tr_torrent const* tor);
|
||||
|
||||
void tr_peerDestruct(struct tr_peer* peer);
|
||||
|
||||
/** Update the tr_peer.progress field based on the 'have' bitset. */
|
||||
void tr_peerUpdateProgress(tr_torrent* tor, struct tr_peer*);
|
||||
void tr_peerUpdateProgress(tr_torrent* tor, tr_peer*);
|
||||
|
||||
bool tr_peerIsSeed(struct tr_peer const* peer);
|
||||
bool tr_peerIsSeed(tr_peer const* peer);
|
||||
|
||||
/***
|
||||
****
|
||||
|
|
|
@ -227,60 +227,37 @@ struct tr_peerMgr
|
|||
*** tr_peer virtual functions
|
||||
**/
|
||||
|
||||
static bool tr_peerIsTransferringPieces(tr_peer const* peer, uint64_t now, tr_direction direction, unsigned int* Bps)
|
||||
{
|
||||
TR_ASSERT(peer != nullptr);
|
||||
TR_ASSERT(peer->funcs != nullptr);
|
||||
|
||||
return (*peer->funcs->is_transferring_pieces)(peer, now, direction, Bps);
|
||||
}
|
||||
|
||||
unsigned int tr_peerGetPieceSpeed_Bps(tr_peer const* peer, uint64_t now, tr_direction direction)
|
||||
{
|
||||
unsigned int Bps = 0;
|
||||
tr_peerIsTransferringPieces(peer, now, direction, &Bps);
|
||||
peer->is_transferring_pieces(now, direction, &Bps);
|
||||
return Bps;
|
||||
}
|
||||
|
||||
static void tr_peerFree(tr_peer* peer)
|
||||
tr_peer::tr_peer(tr_torrent const* tor, peer_atom* atom_in)
|
||||
: session{ tor->session }
|
||||
, atom{ atom_in }
|
||||
, swarm{ tor->swarm }
|
||||
{
|
||||
TR_ASSERT(peer != nullptr);
|
||||
TR_ASSERT(peer->funcs != nullptr);
|
||||
|
||||
(*peer->funcs->destruct)(peer);
|
||||
|
||||
tr_free(peer);
|
||||
}
|
||||
|
||||
void tr_peerConstruct(tr_peer* peer, tr_torrent const* tor)
|
||||
{
|
||||
TR_ASSERT(peer != nullptr);
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
|
||||
*peer = {};
|
||||
peer->client = TR_KEY_NONE;
|
||||
peer->swarm = tor->swarm;
|
||||
tr_bitfieldConstruct(&peer->have, tor->info.pieceCount);
|
||||
tr_bitfieldConstruct(&peer->blame, tor->blockCount);
|
||||
tr_bitfieldConstruct(&have, tor->info.pieceCount);
|
||||
tr_bitfieldConstruct(&blame, tor->blockCount);
|
||||
}
|
||||
|
||||
static void peerDeclinedAllRequests(tr_swarm*, tr_peer const*);
|
||||
|
||||
void tr_peerDestruct(tr_peer* peer)
|
||||
tr_peer::~tr_peer()
|
||||
{
|
||||
TR_ASSERT(peer != nullptr);
|
||||
|
||||
if (peer->swarm != nullptr)
|
||||
if (swarm != nullptr)
|
||||
{
|
||||
peerDeclinedAllRequests(peer->swarm, peer);
|
||||
peerDeclinedAllRequests(swarm, this);
|
||||
}
|
||||
|
||||
tr_bitfieldDestruct(&peer->have);
|
||||
tr_bitfieldDestruct(&peer->blame);
|
||||
tr_bitfieldDestruct(&have);
|
||||
tr_bitfieldDestruct(&blame);
|
||||
|
||||
if (peer->atom != nullptr)
|
||||
if (atom != nullptr)
|
||||
{
|
||||
peer->atom->peer = nullptr;
|
||||
atom->peer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -451,7 +428,7 @@ static void swarmFree(void* vs)
|
|||
TR_ASSERT(tr_ptrArrayEmpty(&s->outgoingHandshakes));
|
||||
TR_ASSERT(tr_ptrArrayEmpty(&s->peers));
|
||||
|
||||
tr_ptrArrayDestruct(&s->webseeds, (PtrArrayForeachFunc)tr_peerFree);
|
||||
tr_ptrArrayDestruct(&s->webseeds, [](void* peer) { delete static_cast<tr_peer*>(peer); });
|
||||
tr_ptrArrayDestruct(&s->pool, (PtrArrayForeachFunc)tr_free);
|
||||
tr_ptrArrayDestruct(&s->outgoingHandshakes, nullptr);
|
||||
tr_ptrArrayDestruct(&s->peers, nullptr);
|
||||
|
@ -471,14 +448,14 @@ static void rebuildWebseedArray(tr_swarm* s, tr_torrent* tor)
|
|||
tr_info const* inf = &tor->info;
|
||||
|
||||
/* clear the array */
|
||||
tr_ptrArrayDestruct(&s->webseeds, (PtrArrayForeachFunc)tr_peerFree);
|
||||
tr_ptrArrayDestruct(&s->webseeds, [](void* peer) { delete static_cast<tr_peer*>(peer); });
|
||||
s->webseeds = {};
|
||||
s->stats.activeWebseedCount = 0;
|
||||
|
||||
/* repopulate it */
|
||||
for (unsigned int i = 0; i < inf->webseedCount; ++i)
|
||||
{
|
||||
tr_webseed* w = tr_webseedNew(tor, inf->webseeds[i], peerCallbackFunc, s);
|
||||
tr_peer* w = tr_webseedNew(tor, inf->webseeds[i], peerCallbackFunc, s);
|
||||
tr_ptrArrayAppend(&s->webseeds, w);
|
||||
}
|
||||
}
|
||||
|
@ -781,11 +758,7 @@ static int countActiveWebseeds(tr_swarm* s)
|
|||
|
||||
for (int i = 0, n = tr_ptrArraySize(&s->webseeds); i < n; ++i)
|
||||
{
|
||||
if (tr_peerIsTransferringPieces(
|
||||
static_cast<tr_peer const*>(tr_ptrArrayNth(&s->webseeds, i)),
|
||||
now,
|
||||
TR_DOWN,
|
||||
nullptr))
|
||||
if (static_cast<tr_peer const*>(tr_ptrArrayNth(&s->webseeds, i))->is_transferring_pieces(now, TR_DOWN, nullptr))
|
||||
{
|
||||
++activeCount;
|
||||
}
|
||||
|
@ -1512,9 +1485,9 @@ static void refillUpkeep(evutil_socket_t fd, short what, void* vmgr)
|
|||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
struct block_request const* const request = &s->requests[i];
|
||||
tr_peerMsgs const* const msgs = PEER_MSGS(request->peer);
|
||||
auto const* const msgs = dynamic_cast<tr_peerMsgs const*>(request->peer);
|
||||
|
||||
if (msgs != nullptr && request->sentAt <= too_old && !tr_peerMsgsIsReadingBlock(msgs, request->block))
|
||||
if (msgs != nullptr && request->sentAt <= too_old && !msgs->is_reading_block(request->block))
|
||||
{
|
||||
TR_ASSERT(cancel != nullptr);
|
||||
TR_ASSERT(cancelCount < cancel_buflen);
|
||||
|
@ -1528,7 +1501,7 @@ static void refillUpkeep(evutil_socket_t fd, short what, void* vmgr)
|
|||
s->requests[keepCount] = *request;
|
||||
}
|
||||
|
||||
keepCount++;
|
||||
++keepCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1539,12 +1512,11 @@ static void refillUpkeep(evutil_socket_t fd, short what, void* vmgr)
|
|||
for (int i = 0; i < cancelCount; ++i)
|
||||
{
|
||||
struct block_request const* const request = &cancel[i];
|
||||
tr_peerMsgs* msgs = PEER_MSGS(request->peer);
|
||||
|
||||
auto* msgs = dynamic_cast<tr_peerMsgs*>(request->peer);
|
||||
if (msgs != nullptr)
|
||||
{
|
||||
tr_historyAdd(&request->peer->cancelsSentToPeer, now, 1);
|
||||
tr_peerMsgsCancel(msgs, request->block);
|
||||
request->peer->cancelsSentToPeer.add(now, 1);
|
||||
msgs->cancel_block_request(request->block);
|
||||
decrementPendingReqCount(request);
|
||||
}
|
||||
}
|
||||
|
@ -1671,12 +1643,15 @@ static void peerDeclinedAllRequests(tr_swarm* s, tr_peer const* peer)
|
|||
|
||||
static void cancelAllRequestsForBlock(tr_swarm* s, tr_block_index_t block, tr_peer* no_notify)
|
||||
{
|
||||
auto const now = tr_time();
|
||||
|
||||
for (auto* p : getBlockRequestPeers(s, block))
|
||||
{
|
||||
if (p != no_notify && tr_isPeerMsgs(p))
|
||||
auto* msgs = dynamic_cast<tr_peerMsgs*>(p);
|
||||
if ((msgs != nullptr) && (msgs != no_notify))
|
||||
{
|
||||
tr_historyAdd(&p->cancelsSentToPeer, tr_time(), 1);
|
||||
tr_peerMsgsCancel(PEER_MSGS(p), block);
|
||||
msgs->cancelsSentToPeer.add(now, 1);
|
||||
msgs->cancel_block_request(block);
|
||||
}
|
||||
|
||||
removeRequestFromTables(s, block, p);
|
||||
|
@ -1691,10 +1666,10 @@ void tr_peerMgrPieceCompleted(tr_torrent* tor, tr_piece_index_t p)
|
|||
/* walk through our peers */
|
||||
for (int i = 0, n = tr_ptrArraySize(&s->peers); i < n; ++i)
|
||||
{
|
||||
auto* peer = static_cast<tr_peer*>(tr_ptrArrayNth(&s->peers, i));
|
||||
auto* peer = static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, i));
|
||||
|
||||
/* notify the peer that we now have this piece */
|
||||
tr_peerMsgsHave(PEER_MSGS(peer), p);
|
||||
// notify the peer that we now have this piece
|
||||
peer->on_piece_completed(p);
|
||||
|
||||
if (!pieceCameFromPeers)
|
||||
{
|
||||
|
@ -1835,7 +1810,7 @@ static void peerCallbackFunc(tr_peer* peer, tr_peer_event const* e, void* vs)
|
|||
tr_piece_index_t const p = e->pieceIndex;
|
||||
tr_block_index_t const block = _tr_block(tor, p, e->offset);
|
||||
cancelAllRequestsForBlock(s, block, peer);
|
||||
tr_historyAdd(&peer->blocksSentToClient, tr_time(), 1);
|
||||
peer->blocksSentToClient.add(tr_time(), 1);
|
||||
pieceListResortPiece(s, pieceListLookup(s, p));
|
||||
tr_torrentGotBlock(tor, block);
|
||||
break;
|
||||
|
@ -1957,8 +1932,7 @@ static void createBitTorrentPeer(tr_torrent* tor, struct tr_peerIo* io, struct p
|
|||
|
||||
tr_swarm* swarm = tor->swarm;
|
||||
|
||||
tr_peer* peer = (tr_peer*)tr_peerMsgsNew(tor, io, peerCallbackFunc, swarm);
|
||||
peer->atom = atom;
|
||||
auto* peer = tr_peerMsgsNew(tor, atom, io, peerCallbackFunc, swarm);
|
||||
peer->client = client;
|
||||
atom->peer = peer;
|
||||
|
||||
|
@ -1969,9 +1943,10 @@ static void createBitTorrentPeer(tr_torrent* tor, struct tr_peerIo* io, struct p
|
|||
TR_ASSERT(swarm->stats.peerCount == tr_ptrArraySize(&swarm->peers));
|
||||
TR_ASSERT(swarm->stats.peerFromCount[atom->fromFirst] <= swarm->stats.peerCount);
|
||||
|
||||
tr_peerMsgs* msgs = PEER_MSGS(peer);
|
||||
tr_peerMsgsUpdateActive(msgs, TR_UP);
|
||||
tr_peerMsgsUpdateActive(msgs, TR_DOWN);
|
||||
// TODO is this needed?
|
||||
// isn't it already initialized in tr_peerMsgsImpl's ctor?
|
||||
peer->update_active(TR_UP);
|
||||
peer->update_active(TR_DOWN);
|
||||
}
|
||||
|
||||
/* FIXME: this is kind of a mess. */
|
||||
|
@ -2597,8 +2572,9 @@ void tr_peerMgrOnTorrentGotMetainfo(tr_torrent* tor)
|
|||
/* update the bittorrent peers' willingnes... */
|
||||
for (int i = 0; i < peerCount; ++i)
|
||||
{
|
||||
tr_peerMsgsUpdateActive(tr_peerMsgsCast(peers[i]), TR_UP);
|
||||
tr_peerMsgsUpdateActive(tr_peerMsgsCast(peers[i]), TR_DOWN);
|
||||
auto* msgs = static_cast<tr_peerMsgs*>(peers[i]);
|
||||
msgs->update_active(TR_UP);
|
||||
msgs->update_active(TR_DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2759,7 +2735,7 @@ double* tr_peerMgrWebSpeeds_KBps(tr_torrent const* tor)
|
|||
unsigned int Bps = 0;
|
||||
auto const* const peer = static_cast<tr_peer*>(tr_ptrArrayNth(&s->webseeds, i));
|
||||
|
||||
if (tr_peerIsTransferringPieces(peer, now, TR_DOWN, &Bps))
|
||||
if (peer->is_transferring_pieces(now, TR_DOWN, &Bps))
|
||||
{
|
||||
ret[i] = Bps / (double)tr_speed_K;
|
||||
}
|
||||
|
@ -2789,7 +2765,7 @@ struct tr_peer_stat* tr_peerMgrPeerStats(tr_torrent const* tor, int* setmeCount)
|
|||
{
|
||||
char* pch;
|
||||
tr_peer* peer = peers[i];
|
||||
tr_peerMsgs const* const msgs = PEER_MSGS(peer);
|
||||
auto const* const msgs = dynamic_cast<tr_peerMsgs const*>(peer);
|
||||
struct peer_atom const* atom = peer->atom;
|
||||
tr_peer_stat* stat = ret + i;
|
||||
|
||||
|
@ -2798,23 +2774,23 @@ struct tr_peer_stat* tr_peerMgrPeerStats(tr_torrent const* tor, int* setmeCount)
|
|||
stat->port = ntohs(peer->atom->port);
|
||||
stat->from = atom->fromFirst;
|
||||
stat->progress = peer->progress;
|
||||
stat->isUTP = tr_peerMsgsIsUtpConnection(msgs);
|
||||
stat->isEncrypted = tr_peerMsgsIsEncrypted(msgs);
|
||||
stat->isUTP = msgs->is_utp_connection();
|
||||
stat->isEncrypted = msgs->is_encrypted();
|
||||
stat->rateToPeer_KBps = toSpeedKBps(tr_peerGetPieceSpeed_Bps(peer, now_msec, TR_CLIENT_TO_PEER));
|
||||
stat->rateToClient_KBps = toSpeedKBps(tr_peerGetPieceSpeed_Bps(peer, now_msec, TR_PEER_TO_CLIENT));
|
||||
stat->peerIsChoked = tr_peerMsgsIsPeerChoked(msgs);
|
||||
stat->peerIsInterested = tr_peerMsgsIsPeerInterested(msgs);
|
||||
stat->clientIsChoked = tr_peerMsgsIsClientChoked(msgs);
|
||||
stat->clientIsInterested = tr_peerMsgsIsClientInterested(msgs);
|
||||
stat->isIncoming = tr_peerMsgsIsIncomingConnection(msgs);
|
||||
stat->isDownloadingFrom = tr_peerMsgsIsActive(msgs, TR_PEER_TO_CLIENT);
|
||||
stat->isUploadingTo = tr_peerMsgsIsActive(msgs, TR_CLIENT_TO_PEER);
|
||||
stat->peerIsChoked = msgs->is_peer_choked();
|
||||
stat->peerIsInterested = msgs->is_peer_interested();
|
||||
stat->clientIsChoked = msgs->is_client_choked();
|
||||
stat->clientIsInterested = msgs->is_client_interested();
|
||||
stat->isIncoming = msgs->is_incoming_connection();
|
||||
stat->isDownloadingFrom = msgs->is_active(TR_PEER_TO_CLIENT);
|
||||
stat->isUploadingTo = msgs->is_active(TR_CLIENT_TO_PEER);
|
||||
stat->isSeed = tr_peerIsSeed(peer);
|
||||
|
||||
stat->blocksToPeer = tr_historyGet(&peer->blocksSentToPeer, now, CANCEL_HISTORY_SEC);
|
||||
stat->blocksToClient = tr_historyGet(&peer->blocksSentToClient, now, CANCEL_HISTORY_SEC);
|
||||
stat->cancelsToPeer = tr_historyGet(&peer->cancelsSentToPeer, now, CANCEL_HISTORY_SEC);
|
||||
stat->cancelsToClient = tr_historyGet(&peer->cancelsSentToClient, now, CANCEL_HISTORY_SEC);
|
||||
stat->blocksToPeer = peer->blocksSentToPeer.count(now, CANCEL_HISTORY_SEC);
|
||||
stat->blocksToClient = peer->blocksSentToClient.count(now, CANCEL_HISTORY_SEC);
|
||||
stat->cancelsToPeer = peer->cancelsSentToPeer.count(now, CANCEL_HISTORY_SEC);
|
||||
stat->cancelsToClient = peer->cancelsSentToClient.count(now, CANCEL_HISTORY_SEC);
|
||||
|
||||
stat->pendingReqsToPeer = peer->pendingReqsToPeer;
|
||||
stat->pendingReqsToClient = peer->pendingReqsToClient;
|
||||
|
@ -2900,7 +2876,7 @@ void tr_peerMgrClearInterest(tr_torrent* tor)
|
|||
|
||||
for (int i = 0; i < peerCount; ++i)
|
||||
{
|
||||
tr_peerMsgsSetInterested(static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, i)), false);
|
||||
static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, i))->set_interested(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2936,7 +2912,7 @@ enum tr_rechoke_state
|
|||
|
||||
struct tr_rechoke_info
|
||||
{
|
||||
tr_peer* peer;
|
||||
tr_peerMsgs* peer;
|
||||
int salt;
|
||||
int rechoke_state;
|
||||
};
|
||||
|
@ -2998,8 +2974,8 @@ static void rechokeDownloads(tr_swarm* s)
|
|||
for (int i = 0; i < peerCount; ++i)
|
||||
{
|
||||
auto const* const peer = static_cast<tr_peer const*>(tr_ptrArrayNth(&s->peers, i));
|
||||
int const b = tr_historyGet(&peer->blocksSentToClient, now, CANCEL_HISTORY_SEC);
|
||||
int const c = tr_historyGet(&peer->cancelsSentToPeer, now, CANCEL_HISTORY_SEC);
|
||||
auto const b = peer->blocksSentToClient.count(now, CANCEL_HISTORY_SEC);
|
||||
auto const c = peer->cancelsSentToPeer.count(now, CANCEL_HISTORY_SEC);
|
||||
|
||||
if (b == 0) /* ignore unresponsive peers, as described above */
|
||||
{
|
||||
|
@ -3072,17 +3048,17 @@ static void rechokeDownloads(tr_swarm* s)
|
|||
/* decide WHICH peers to be interested in (based on their cancel-to-block ratio) */
|
||||
for (int i = 0; i < peerCount; ++i)
|
||||
{
|
||||
auto* const peer = static_cast<tr_peer*>(tr_ptrArrayNth(&s->peers, i));
|
||||
auto* const peer = static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, i));
|
||||
|
||||
if (!isPeerInteresting(s->tor, piece_is_interesting, peer))
|
||||
{
|
||||
tr_peerMsgsSetInterested(PEER_MSGS(peer), false);
|
||||
peer->set_interested(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_rechoke_state rechoke_state;
|
||||
int const blocks = tr_historyGet(&peer->blocksSentToClient, now, CANCEL_HISTORY_SEC);
|
||||
int const cancels = tr_historyGet(&peer->cancelsSentToPeer, now, CANCEL_HISTORY_SEC);
|
||||
auto const blocks = peer->blocksSentToClient.count(now, CANCEL_HISTORY_SEC);
|
||||
auto const cancels = peer->cancelsSentToPeer.count(now, CANCEL_HISTORY_SEC);
|
||||
|
||||
if (blocks == 0 && cancels == 0)
|
||||
{
|
||||
|
@ -3131,7 +3107,7 @@ static void rechokeDownloads(tr_swarm* s)
|
|||
|
||||
for (int i = 0; i < rechoke_count; ++i)
|
||||
{
|
||||
tr_peerMsgsSetInterested(PEER_MSGS(rechoke[i].peer), i < s->interestedCount);
|
||||
rechoke[i].peer->set_interested(i < s->interestedCount);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
|
@ -3178,7 +3154,7 @@ static int compareChoke(void const* va, void const* vb)
|
|||
/* is this a new connection? */
|
||||
static bool isNew(tr_peerMsgs const* msgs)
|
||||
{
|
||||
return msgs != nullptr && tr_peerMsgsGetConnectionAge(msgs) < 45;
|
||||
return msgs != nullptr && msgs->get_connection_age() < 45;
|
||||
}
|
||||
|
||||
/* get a rate for deciding which peers to choke and unchoke. */
|
||||
|
@ -3248,27 +3224,25 @@ static void rechokeUploads(tr_swarm* s, uint64_t const now)
|
|||
/* sort the peers by preference and rate */
|
||||
for (int i = 0; i < peerCount; ++i)
|
||||
{
|
||||
tr_peer* peer = peers[i];
|
||||
tr_peerMsgs* msgs = PEER_MSGS(peer);
|
||||
|
||||
struct peer_atom* atom = peer->atom;
|
||||
auto* const peer = dynamic_cast<tr_peerMsgs*>(peers[i]);
|
||||
struct peer_atom* const atom = peer->atom;
|
||||
|
||||
if (tr_peerIsSeed(peer))
|
||||
{
|
||||
/* choke seeds and partial seeds */
|
||||
tr_peerMsgsSetChoke(PEER_MSGS(peer), true);
|
||||
peer->set_choke(true);
|
||||
}
|
||||
else if (chokeAll)
|
||||
{
|
||||
/* choke everyone if we're not uploading */
|
||||
tr_peerMsgsSetChoke(PEER_MSGS(peer), true);
|
||||
peer->set_choke(true);
|
||||
}
|
||||
else if (msgs != s->optimistic)
|
||||
else if (peer != s->optimistic)
|
||||
{
|
||||
struct ChokeData* n = &choke[size++];
|
||||
n->msgs = msgs;
|
||||
n->isInterested = tr_peerMsgsIsPeerInterested(msgs);
|
||||
n->wasChoked = tr_peerMsgsIsPeerChoked(msgs);
|
||||
n->msgs = peer;
|
||||
n->isInterested = peer->is_peer_interested();
|
||||
n->wasChoked = peer->is_peer_choked();
|
||||
n->rate = getRate(s->tor, atom, now);
|
||||
n->salt = tr_rand_int_weak(INT_MAX);
|
||||
n->isChoked = true;
|
||||
|
@ -3338,7 +3312,7 @@ static void rechokeUploads(tr_swarm* s, uint64_t const now)
|
|||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
tr_peerMsgsSetChoke(choke[i].msgs, choke[i].isChoked);
|
||||
choke[i].msgs->set_choke(choke[i].isChoked);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
|
@ -3426,7 +3400,7 @@ static tr_peer** getPeersToClose(tr_swarm* s, time_t const now_sec, int* setmeSi
|
|||
|
||||
int peerCount;
|
||||
int outsize = 0;
|
||||
struct tr_peer** ret = nullptr;
|
||||
tr_peer** ret = nullptr;
|
||||
tr_peer** peers = (tr_peer**)tr_ptrArrayPeek(&s->peers, &peerCount);
|
||||
|
||||
for (int i = 0; i < peerCount; ++i)
|
||||
|
@ -3527,7 +3501,7 @@ static void removePeer(tr_swarm* s, tr_peer* peer)
|
|||
TR_ASSERT(s->stats.peerCount == tr_ptrArraySize(&s->peers));
|
||||
TR_ASSERT(s->stats.peerFromCount[atom->fromFirst] >= 0);
|
||||
|
||||
tr_peerFree(peer);
|
||||
delete peer;
|
||||
}
|
||||
|
||||
static void closePeer(tr_swarm* s, tr_peer* peer)
|
||||
|
@ -3570,7 +3544,7 @@ static void closeBadPeers(tr_swarm* s, time_t const now_sec)
|
|||
if (!tr_ptrArrayEmpty(&s->peers))
|
||||
{
|
||||
int peerCount;
|
||||
struct tr_peer** peers;
|
||||
tr_peer** peers;
|
||||
|
||||
peers = getPeersToClose(s, now_sec, &peerCount);
|
||||
|
||||
|
@ -3793,7 +3767,7 @@ static void pumpAllPeers(tr_peerMgr* mgr)
|
|||
|
||||
for (int j = 0, n = tr_ptrArraySize(&s->peers); j < n; ++j)
|
||||
{
|
||||
tr_peerMsgsPulse(static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, j)));
|
||||
static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, j))->pulse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
* @{
|
||||
*/
|
||||
|
||||
class tr_peerMsgs;
|
||||
struct UTPSocket;
|
||||
struct peer_atom;
|
||||
struct tr_peerIo;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,9 +15,9 @@
|
|||
#include <inttypes.h>
|
||||
#include "peer-common.h"
|
||||
|
||||
class tr_peer;
|
||||
struct tr_address;
|
||||
struct tr_bitfield;
|
||||
struct tr_peer;
|
||||
struct tr_peerIo;
|
||||
struct tr_torrent;
|
||||
|
||||
|
@ -26,47 +26,47 @@ struct tr_torrent;
|
|||
* @{
|
||||
*/
|
||||
|
||||
struct tr_peerMsgs;
|
||||
class tr_peerMsgs : public tr_peer
|
||||
{
|
||||
public:
|
||||
tr_peerMsgs(tr_torrent* torrent, peer_atom* atom_in)
|
||||
: tr_peer{ torrent, atom_in }
|
||||
{
|
||||
}
|
||||
|
||||
#define PEER_MSGS(o) (tr_peerMsgsCast(o))
|
||||
virtual ~tr_peerMsgs() override = default;
|
||||
|
||||
bool tr_isPeerMsgs(void const* msgs);
|
||||
virtual bool is_peer_choked() const = 0;
|
||||
virtual bool is_peer_interested() const = 0;
|
||||
virtual bool is_client_choked() const = 0;
|
||||
virtual bool is_client_interested() const = 0;
|
||||
|
||||
tr_peerMsgs* tr_peerMsgsCast(void* msgs);
|
||||
virtual bool is_utp_connection() const = 0;
|
||||
virtual bool is_encrypted() const = 0;
|
||||
virtual bool is_incoming_connection() const = 0;
|
||||
|
||||
tr_peerMsgs* tr_peerMsgsNew(struct tr_torrent* torrent, struct tr_peerIo* io, tr_peer_callback callback, void* callback_data);
|
||||
virtual bool is_active(tr_direction direction) const = 0;
|
||||
virtual void update_active(tr_direction direction) = 0;
|
||||
|
||||
bool tr_peerMsgsIsPeerChoked(tr_peerMsgs const* msgs);
|
||||
virtual time_t get_connection_age() const = 0;
|
||||
virtual bool is_reading_block(tr_block_index_t block) const = 0;
|
||||
|
||||
bool tr_peerMsgsIsPeerInterested(tr_peerMsgs const* msgs);
|
||||
virtual void cancel_block_request(tr_block_index_t block) = 0;
|
||||
|
||||
bool tr_peerMsgsIsClientChoked(tr_peerMsgs const* msgs);
|
||||
virtual void set_choke(bool peer_is_choked) = 0;
|
||||
virtual void set_interested(bool client_is_interested) = 0;
|
||||
|
||||
bool tr_peerMsgsIsClientInterested(tr_peerMsgs const* msgs);
|
||||
virtual void pulse() = 0;
|
||||
|
||||
bool tr_peerMsgsIsActive(tr_peerMsgs const* msgs, tr_direction direction);
|
||||
virtual void on_piece_completed(tr_piece_index_t) = 0;
|
||||
};
|
||||
|
||||
void tr_peerMsgsUpdateActive(tr_peerMsgs* msgs, tr_direction direction);
|
||||
|
||||
time_t tr_peerMsgsGetConnectionAge(tr_peerMsgs const* msgs);
|
||||
|
||||
bool tr_peerMsgsIsUtpConnection(tr_peerMsgs const* msgs);
|
||||
|
||||
bool tr_peerMsgsIsEncrypted(tr_peerMsgs const* msgs);
|
||||
|
||||
bool tr_peerMsgsIsIncomingConnection(tr_peerMsgs const* msgs);
|
||||
|
||||
void tr_peerMsgsSetChoke(tr_peerMsgs* msgs, bool peerIsChoked);
|
||||
|
||||
bool tr_peerMsgsIsReadingBlock(tr_peerMsgs const* msgs, tr_block_index_t block);
|
||||
|
||||
void tr_peerMsgsSetInterested(tr_peerMsgs* msgs, bool clientIsInterested);
|
||||
|
||||
void tr_peerMsgsHave(tr_peerMsgs* msgs, uint32_t pieceIndex);
|
||||
|
||||
void tr_peerMsgsPulse(tr_peerMsgs* msgs);
|
||||
|
||||
void tr_peerMsgsCancel(tr_peerMsgs* msgs, tr_block_index_t block);
|
||||
tr_peerMsgs* tr_peerMsgsNew(
|
||||
tr_torrent* torrent,
|
||||
peer_atom* atom,
|
||||
tr_peerIo* io,
|
||||
tr_peer_callback callback,
|
||||
void* callback_data);
|
||||
|
||||
size_t tr_generateAllowedSet(
|
||||
tr_piece_index_t* setmePieces,
|
||||
|
|
|
@ -24,6 +24,11 @@
|
|||
#include "web.h"
|
||||
#include "webseed.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct tr_webseed;
|
||||
|
||||
struct tr_webseed_task
|
||||
{
|
||||
bool dead;
|
||||
|
@ -40,36 +45,83 @@ struct tr_webseed_task
|
|||
long response_code;
|
||||
};
|
||||
|
||||
struct tr_webseed
|
||||
auto constexpr TR_IDLE_TIMER_MSEC = 2000;
|
||||
|
||||
auto constexpr FAILURE_RETRY_INTERVAL = 150;
|
||||
|
||||
auto constexpr MAX_CONSECUTIVE_FAILURES = 5;
|
||||
|
||||
auto constexpr MAX_WEBSEED_CONNECTIONS = 4;
|
||||
|
||||
void webseed_timer_func(evutil_socket_t fd, short what, void* vw);
|
||||
|
||||
struct tr_webseed : public tr_peer
|
||||
{
|
||||
tr_peer parent;
|
||||
tr_bandwidth bandwidth;
|
||||
tr_session* session;
|
||||
tr_peer_callback callback;
|
||||
void* callback_data;
|
||||
public:
|
||||
tr_webseed(struct tr_torrent* tor, std::string_view url, tr_peer_callback callback_in, void* callback_data_in)
|
||||
: tr_peer{ tor }
|
||||
, torrent_id{ tr_torrentId(tor) }
|
||||
, base_url{ url }
|
||||
, callback{ callback_in }
|
||||
, callback_data{ callback_data_in }
|
||||
{
|
||||
// init parent bits
|
||||
tr_bitfieldSetHasAll(&have);
|
||||
tr_peerUpdateProgress(tor, this);
|
||||
|
||||
file_urls.resize(tr_torrentInfo(tor)->fileCount);
|
||||
|
||||
tr_bandwidthConstruct(&bandwidth, &tor->bandwidth);
|
||||
timer = evtimer_new(session->event_base, webseed_timer_func, this);
|
||||
tr_timerAddMsec(timer, TR_IDLE_TIMER_MSEC);
|
||||
}
|
||||
|
||||
~tr_webseed() override
|
||||
{
|
||||
// flag all the pending tasks as dead
|
||||
std::for_each(std::begin(tasks), std::end(tasks), [](auto* task) { task->dead = true; });
|
||||
tasks.clear();
|
||||
|
||||
event_free(timer);
|
||||
tr_bandwidthDestruct(&bandwidth);
|
||||
}
|
||||
|
||||
bool is_transferring_pieces(uint64_t now, tr_direction direction, unsigned int* setme_Bps) const override
|
||||
{
|
||||
unsigned int Bps = 0;
|
||||
bool is_active = false;
|
||||
|
||||
if (direction == TR_DOWN)
|
||||
{
|
||||
is_active = !std::empty(tasks);
|
||||
Bps = tr_bandwidthGetPieceSpeed_Bps(&bandwidth, now, direction);
|
||||
}
|
||||
|
||||
if (setme_Bps != nullptr)
|
||||
{
|
||||
*setme_Bps = Bps;
|
||||
}
|
||||
|
||||
return is_active;
|
||||
}
|
||||
|
||||
int const torrent_id;
|
||||
std::string const base_url;
|
||||
tr_peer_callback const callback;
|
||||
void* const callback_data;
|
||||
|
||||
tr_bandwidth bandwidth = {};
|
||||
std::set<tr_webseed_task*> tasks;
|
||||
struct event* timer;
|
||||
char* base_url;
|
||||
size_t base_url_len;
|
||||
int torrent_id;
|
||||
int consecutive_failures;
|
||||
int retry_tickcount;
|
||||
int retry_challenge;
|
||||
int idle_connections;
|
||||
int active_transfers;
|
||||
char** file_urls;
|
||||
struct event* timer = nullptr;
|
||||
int consecutive_failures = 0;
|
||||
int retry_tickcount = 0;
|
||||
int retry_challenge = 0;
|
||||
int idle_connections = 0;
|
||||
int active_transfers = 0;
|
||||
std::vector<std::string> file_urls;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TR_IDLE_TIMER_MSEC = 2000,
|
||||
/* */
|
||||
FAILURE_RETRY_INTERVAL = 150,
|
||||
/* */
|
||||
MAX_CONSECUTIVE_FAILURES = 5,
|
||||
/* */
|
||||
MAX_WEBSEED_CONNECTIONS = 4
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/***
|
||||
****
|
||||
|
@ -79,7 +131,7 @@ static void publish(tr_webseed* w, tr_peer_event* e)
|
|||
{
|
||||
if (w->callback != nullptr)
|
||||
{
|
||||
(*w->callback)(&w->parent, e, w->callback_data);
|
||||
(*w->callback)(w, e, w->callback_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,8 +262,7 @@ static void connection_succeeded(void* vdata)
|
|||
tr_file_index_t file_index;
|
||||
|
||||
tr_ioFindFileLocation(tor, data->piece_index, data->piece_offset, &file_index, &file_offset);
|
||||
tr_free(w->file_urls[file_index]);
|
||||
w->file_urls[file_index] = data->real_url;
|
||||
w->file_urls[file_index].assign(data->real_url);
|
||||
data->real_url = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +279,7 @@ static void on_content_changed(struct evbuffer* buf, struct evbuffer_cb_info con
|
|||
{
|
||||
size_t const n_added = info->n_added;
|
||||
auto* task = static_cast<struct tr_webseed_task*>(vtask);
|
||||
tr_session* session = task->session;
|
||||
auto* session = task->session;
|
||||
|
||||
tr_sessionLock(session);
|
||||
|
||||
|
@ -256,7 +307,7 @@ static void on_content_changed(struct evbuffer* buf, struct evbuffer_cb_info con
|
|||
|
||||
/* processing this uses a tr_torrent pointer,
|
||||
so push the work to the libevent thread... */
|
||||
tr_runInEventThread(w->session, connection_succeeded, data);
|
||||
tr_runInEventThread(session, connection_succeeded, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,10 +315,10 @@ static void on_content_changed(struct evbuffer* buf, struct evbuffer_cb_info con
|
|||
{
|
||||
/* once we've got at least one full block, save it */
|
||||
|
||||
struct write_block_data* data;
|
||||
uint32_t const block_size = task->block_size;
|
||||
tr_block_index_t const completed = len / block_size;
|
||||
|
||||
struct write_block_data* data;
|
||||
data = tr_new(struct write_block_data, 1);
|
||||
data->webseed = task->webseed;
|
||||
data->piece_index = task->piece_index;
|
||||
|
@ -322,7 +373,7 @@ static void on_idle(tr_webseed* w)
|
|||
tr_block_index_t* blocks = nullptr;
|
||||
|
||||
blocks = tr_new(tr_block_index_t, want * 2);
|
||||
tr_peerMgrGetNextRequests(tor, &w->parent, want, blocks, &got, true);
|
||||
tr_peerMgrGetNextRequests(tor, w, want, blocks, &got, true);
|
||||
|
||||
w->idle_connections -= std::min(w->idle_connections, got);
|
||||
|
||||
|
@ -450,19 +501,21 @@ static void web_response_func(
|
|||
}
|
||||
}
|
||||
|
||||
static struct evbuffer* make_url(tr_webseed* w, tr_file const* file)
|
||||
static std::string make_url(tr_webseed* w, tr_file const* file)
|
||||
{
|
||||
struct evbuffer* buf = evbuffer_new();
|
||||
|
||||
evbuffer_add(buf, w->base_url, w->base_url_len);
|
||||
evbuffer_add(buf, std::data(w->base_url), std::size(w->base_url));
|
||||
|
||||
/* if url ends with a '/', add the torrent name */
|
||||
if (w->base_url[w->base_url_len - 1] == '/' && file->name != nullptr)
|
||||
if (*std::rbegin(w->base_url) == '/' && file->name != nullptr)
|
||||
{
|
||||
tr_http_escape(buf, file->name, strlen(file->name), false);
|
||||
}
|
||||
|
||||
return buf;
|
||||
auto const url = std::string{ (char const*)evbuffer_pullup(buf, -1), evbuffer_get_length(buf) };
|
||||
evbuffer_free(buf);
|
||||
return url;
|
||||
}
|
||||
|
||||
static void task_request_next_chunk(struct tr_webseed_task* t)
|
||||
|
@ -472,8 +525,7 @@ static void task_request_next_chunk(struct tr_webseed_task* t)
|
|||
|
||||
if (tor != nullptr)
|
||||
{
|
||||
char range[64];
|
||||
char** urls = t->webseed->file_urls;
|
||||
auto& urls = t->webseed->file_urls;
|
||||
|
||||
tr_info const* inf = tr_torrentInfo(tor);
|
||||
uint64_t const remain = t->length - t->blocks_done * tor->blockSize - evbuffer_get_length(t->content);
|
||||
|
@ -491,14 +543,15 @@ static void task_request_next_chunk(struct tr_webseed_task* t)
|
|||
file = &inf->files[file_index];
|
||||
this_pass = std::min(remain, file->length - file_offset);
|
||||
|
||||
if (urls[file_index] == nullptr)
|
||||
if (std::empty(urls[file_index]))
|
||||
{
|
||||
urls[file_index] = evbuffer_free_to_str(make_url(t->webseed, file), nullptr);
|
||||
urls[file_index] = make_url(t->webseed, file);
|
||||
}
|
||||
|
||||
char range[64];
|
||||
tr_snprintf(range, sizeof(range), "%" PRIu64 "-%" PRIu64, file_offset, file_offset + this_pass - 1);
|
||||
|
||||
t->web_task = tr_webRunWebseed(tor, urls[file_index], range, web_response_func, t, t->content);
|
||||
t->web_task = tr_webRunWebseed(tor, urls[file_index].c_str(), range, web_response_func, t, t->content);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -506,7 +559,10 @@ static void task_request_next_chunk(struct tr_webseed_task* t)
|
|||
****
|
||||
***/
|
||||
|
||||
static void webseed_timer_func(evutil_socket_t fd, short what, void* vw)
|
||||
namespace
|
||||
{
|
||||
|
||||
void webseed_timer_func(evutil_socket_t fd, short what, void* vw)
|
||||
{
|
||||
TR_UNUSED(fd);
|
||||
TR_UNUSED(what);
|
||||
|
@ -523,99 +579,9 @@ static void webseed_timer_func(evutil_socket_t fd, short what, void* vw)
|
|||
tr_timerAddMsec(w->timer, TR_IDLE_TIMER_MSEC);
|
||||
}
|
||||
|
||||
/***
|
||||
**** tr_peer virtual functions
|
||||
***/
|
||||
} // unnamed namespace
|
||||
|
||||
static bool webseed_is_transferring_pieces(tr_peer const* peer, uint64_t now, tr_direction direction, unsigned int* setme_Bps)
|
||||
tr_peer* tr_webseedNew(struct tr_torrent* torrent, std::string_view url, tr_peer_callback callback, void* callback_data)
|
||||
{
|
||||
unsigned int Bps = 0;
|
||||
bool is_active = false;
|
||||
|
||||
if (direction == TR_DOWN)
|
||||
{
|
||||
tr_webseed const* w = (tr_webseed const*)peer;
|
||||
is_active = !std::empty(w->tasks);
|
||||
Bps = tr_bandwidthGetPieceSpeed_Bps(&w->bandwidth, now, direction);
|
||||
}
|
||||
|
||||
if (setme_Bps != nullptr)
|
||||
{
|
||||
*setme_Bps = Bps;
|
||||
}
|
||||
|
||||
return is_active;
|
||||
}
|
||||
|
||||
static void webseed_destruct(tr_peer* peer)
|
||||
{
|
||||
tr_webseed* w = (tr_webseed*)peer;
|
||||
|
||||
/* flag all the pending tasks as dead */
|
||||
auto& src = w->tasks;
|
||||
std::for_each(std::begin(src), std::end(src), [](auto* task) { task->dead = true; });
|
||||
// Manually destructing is unfortunately necessary until we C++ify
|
||||
// the tr_peer / tr_peerMsgs / tr_webseed inheritance. Peers are
|
||||
// curently tr_free()d in tr_peerFree() so we can't new/delete them.
|
||||
using type = decltype(w->tasks);
|
||||
w->tasks.~type();
|
||||
|
||||
/* if we have an array of file URLs, free it */
|
||||
if (w->file_urls != nullptr)
|
||||
{
|
||||
tr_torrent const* const tor = tr_torrentFindFromId(w->session, w->torrent_id);
|
||||
tr_info const* const inf = tr_torrentInfo(tor);
|
||||
|
||||
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
|
||||
{
|
||||
tr_free(w->file_urls[i]);
|
||||
}
|
||||
|
||||
tr_free(w->file_urls);
|
||||
}
|
||||
|
||||
/* webseed destruct */
|
||||
event_free(w->timer);
|
||||
tr_bandwidthDestruct(&w->bandwidth);
|
||||
tr_free(w->base_url);
|
||||
|
||||
/* parent class destruct */
|
||||
tr_peerDestruct(&w->parent);
|
||||
}
|
||||
|
||||
static struct tr_peer_virtual_funcs const my_funcs = {
|
||||
webseed_destruct,
|
||||
webseed_is_transferring_pieces,
|
||||
};
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
tr_webseed* tr_webseedNew(struct tr_torrent* tor, char const* url, tr_peer_callback callback, void* callback_data)
|
||||
{
|
||||
tr_webseed* w = tr_new0(tr_webseed, 1);
|
||||
tr_peer* peer = &w->parent;
|
||||
tr_info const* inf = tr_torrentInfo(tor);
|
||||
|
||||
/* construct parent class */
|
||||
tr_peerConstruct(peer, tor);
|
||||
peer->client = TR_KEY_webseeds;
|
||||
peer->funcs = &my_funcs;
|
||||
tr_bitfieldSetHasAll(&peer->have);
|
||||
tr_peerUpdateProgress(tor, peer);
|
||||
|
||||
using type = decltype(w->tasks);
|
||||
new (&w->tasks) type;
|
||||
w->torrent_id = tr_torrentId(tor);
|
||||
w->session = tor->session;
|
||||
w->base_url_len = strlen(url);
|
||||
w->base_url = tr_strndup(url, w->base_url_len);
|
||||
w->callback = callback;
|
||||
w->callback_data = callback_data;
|
||||
w->file_urls = tr_new0(char*, inf->fileCount);
|
||||
tr_bandwidthConstruct(&w->bandwidth, &tor->bandwidth);
|
||||
w->timer = evtimer_new(w->session->event_base, webseed_timer_func, w);
|
||||
tr_timerAddMsec(w->timer, TR_IDLE_TIMER_MSEC);
|
||||
return w;
|
||||
return new tr_webseed(torrent, url, callback, callback_data);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
struct tr_webseed;
|
||||
#include <string_view>
|
||||
|
||||
#include "peer-common.h"
|
||||
|
||||
tr_webseed* tr_webseedNew(struct tr_torrent* torrent, char const* url, tr_peer_callback callback, void* callback_data);
|
||||
tr_peer* tr_webseedNew(struct tr_torrent* torrent, std::string_view, tr_peer_callback callback, void* callback_data);
|
||||
|
|
|
@ -15,13 +15,13 @@ TEST(History, recentHistory)
|
|||
{
|
||||
auto h = tr_recentHistory{};
|
||||
|
||||
tr_historyAdd(&h, 10000, 1);
|
||||
EXPECT_EQ(0, tr_historyGet(&h, 12000, 1000));
|
||||
EXPECT_EQ(1, tr_historyGet(&h, 12000, 3000));
|
||||
EXPECT_EQ(1, tr_historyGet(&h, 12000, 5000));
|
||||
tr_historyAdd(&h, 20000, 1);
|
||||
EXPECT_EQ(0, tr_historyGet(&h, 22000, 1000));
|
||||
EXPECT_EQ(1, tr_historyGet(&h, 22000, 3000));
|
||||
EXPECT_EQ(2, tr_historyGet(&h, 22000, 15000));
|
||||
EXPECT_EQ(2, tr_historyGet(&h, 22000, 20000));
|
||||
h.add(10000, 1);
|
||||
EXPECT_EQ(0, h.count(12000, 1000));
|
||||
EXPECT_EQ(1, h.count(12000, 3000));
|
||||
EXPECT_EQ(1, h.count(12000, 5000));
|
||||
h.add(20000, 1);
|
||||
EXPECT_EQ(0, h.count(22000, 1000));
|
||||
EXPECT_EQ(1, h.count(22000, 3000));
|
||||
EXPECT_EQ(2, h.count(22000, 15000));
|
||||
EXPECT_EQ(2, h.count(22000, 20000));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue