mirror of
https://github.com/transmission/transmission
synced 2024-12-25 17:17:31 +00:00
(libT) #1379: support "partial seeds" BEP 22
This commit is contained in:
parent
341b40f192
commit
d4ee0d714a
9 changed files with 105 additions and 35 deletions
|
@ -48,6 +48,7 @@ typedef enum
|
|||
TR_PEER_PEER_PROGRESS,
|
||||
TR_PEER_ERROR,
|
||||
TR_PEER_CANCEL,
|
||||
TR_PEER_UPLOAD_ONLY,
|
||||
TR_PEER_NEED_REQ
|
||||
}
|
||||
PeerEventType;
|
||||
|
@ -61,6 +62,7 @@ typedef struct
|
|||
float progress; /* for PEER_PROGRESS */
|
||||
int err; /* errno for GOT_ERROR */
|
||||
int wasPieceData; /* for GOT_DATA */
|
||||
tr_bool uploadOnly; /* for UPLOAD_ONLY */
|
||||
}
|
||||
tr_peer_event;
|
||||
|
||||
|
|
|
@ -89,6 +89,13 @@ enum
|
|||
***
|
||||
**/
|
||||
|
||||
enum
|
||||
{
|
||||
UPLOAD_ONLY_UKNOWN,
|
||||
UPLOAD_ONLY_YES,
|
||||
UPLOAD_ONLY_NO
|
||||
};
|
||||
|
||||
/* We keep one of these for every peer we know about, whether
|
||||
* it's connected or not, so the struct must be small.
|
||||
* When our current connections underperform, we dip back
|
||||
|
@ -96,12 +103,14 @@ enum
|
|||
struct peer_atom
|
||||
{
|
||||
uint8_t from;
|
||||
uint8_t flags; /* these match the added_f flags */
|
||||
uint8_t myflags; /* flags that aren't defined in added_f */
|
||||
uint8_t flags; /* these match the added_f flags */
|
||||
uint8_t myflags; /* flags that aren't defined in added_f */
|
||||
uint8_t uploadOnly; /* UPLOAD_ONLY_ */
|
||||
uint8_t partialSeed;
|
||||
tr_port port;
|
||||
uint16_t numFails;
|
||||
tr_address addr;
|
||||
time_t time; /* when the peer's connection status last changed */
|
||||
time_t time; /* when the peer's connection status last changed */
|
||||
time_t piece_data_time;
|
||||
};
|
||||
|
||||
|
@ -980,6 +989,14 @@ peerCallbackFunc( void * vpeer,
|
|||
|
||||
switch( e->eventType )
|
||||
{
|
||||
case TR_PEER_UPLOAD_ONLY:
|
||||
/* update our atom */
|
||||
if( peer ) {
|
||||
struct peer_atom * a = getExistingAtom( t, &peer->addr );
|
||||
a->uploadOnly = e->uploadOnly ? UPLOAD_ONLY_YES : UPLOAD_ONLY_NO;
|
||||
}
|
||||
break;
|
||||
|
||||
case TR_PEER_NEED_REQ:
|
||||
refillSoon( t );
|
||||
break;
|
||||
|
@ -1458,11 +1475,15 @@ tr_peerMgrGetPeers( tr_peerMgr * manager,
|
|||
for( i = 0; i < peerCount; ++i, ++walk )
|
||||
{
|
||||
const tr_peer * peer = peers[i];
|
||||
const struct peer_atom * atom = getExistingAtom( t, &peer->addr );
|
||||
|
||||
walk->addr = peer->addr;
|
||||
walk->port = peer->port;
|
||||
walk->flags = 0;
|
||||
if( peerPrefersCrypto( peer ) ) walk->flags |= ADDED_F_ENCRYPTION_FLAG;
|
||||
if( peer->progress >= 1.0 ) walk->flags |= ADDED_F_SEED_FLAG;
|
||||
if( peerPrefersCrypto( peer ) )
|
||||
walk->flags |= ADDED_F_ENCRYPTION_FLAG;
|
||||
if( ( atom->uploadOnly == UPLOAD_ONLY_YES ) || ( peer->progress >= 1.0 ) )
|
||||
walk->flags |= ADDED_F_SEED_FLAG;
|
||||
}
|
||||
|
||||
assert( ( walk - pex ) == peerCount );
|
||||
|
@ -1884,11 +1905,22 @@ rechoke( Torrent * t )
|
|||
for( i = 0, size = 0; i < peerCount; ++i )
|
||||
{
|
||||
tr_peer * peer = peers[i];
|
||||
struct peer_atom * atom = getExistingAtom( t, &peer->addr );
|
||||
|
||||
if( peer->progress >= 1.0 ) /* choke all seeds */
|
||||
{
|
||||
tr_peerMsgsSetChoke( peer->msgs, TRUE );
|
||||
else if( chokeAll )
|
||||
}
|
||||
else if( atom->uploadOnly == UPLOAD_ONLY_YES ) /* choke partial seeds */
|
||||
{
|
||||
tr_peerMsgsSetChoke( peer->msgs, TRUE );
|
||||
else {
|
||||
}
|
||||
else if( chokeAll ) /* choke everyone if we're not uploading */
|
||||
{
|
||||
tr_peerMsgsSetChoke( peer->msgs, TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
struct ChokeData * n = &choke[size++];
|
||||
n->peer = peer;
|
||||
n->isInterested = peer->peerIsInterested;
|
||||
|
@ -2175,7 +2207,8 @@ getPeerCandidates( Torrent * t,
|
|||
continue;
|
||||
|
||||
/* no need to connect if we're both seeds... */
|
||||
if( seed && ( atom->flags & ADDED_F_SEED_FLAG ) )
|
||||
if( seed && ( ( atom->flags & ADDED_F_SEED_FLAG ) ||
|
||||
( atom->uploadOnly == UPLOAD_ONLY_YES ) ) )
|
||||
continue;
|
||||
|
||||
/* don't reconnect too often */
|
||||
|
|
|
@ -513,7 +513,7 @@ protocolSendHaveNone( tr_peermsgs * msgs )
|
|||
*** EVENTS
|
||||
**/
|
||||
|
||||
static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0, 0 };
|
||||
static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0, 0, 0 };
|
||||
|
||||
static void
|
||||
publish( tr_peermsgs * msgs,
|
||||
|
@ -536,11 +536,19 @@ fireError( tr_peermsgs * msgs,
|
|||
publish( msgs, &e );
|
||||
}
|
||||
|
||||
static void
|
||||
fireUploadOnly( tr_peermsgs * msgs, tr_bool uploadOnly )
|
||||
{
|
||||
tr_peer_event e = blankEvent;
|
||||
e.eventType = TR_PEER_UPLOAD_ONLY;
|
||||
e.uploadOnly = uploadOnly;
|
||||
publish( msgs, &e );
|
||||
}
|
||||
|
||||
static void
|
||||
fireNeedReq( tr_peermsgs * msgs )
|
||||
{
|
||||
tr_peer_event e = blankEvent;
|
||||
|
||||
e.eventType = TR_PEER_NEED_REQ;
|
||||
publish( msgs, &e );
|
||||
}
|
||||
|
@ -1086,9 +1094,9 @@ sendLtepHandshake( tr_peermsgs * msgs )
|
|||
pex = 1;
|
||||
|
||||
tr_bencInitDict( &val, 4 );
|
||||
tr_bencDictAddInt( &val, "e",
|
||||
msgs->session->encryptionMode != TR_CLEAR_PREFERRED );
|
||||
tr_bencDictAddInt( &val, "e", msgs->session->encryptionMode != TR_CLEAR_PREFERRED );
|
||||
tr_bencDictAddInt( &val, "p", tr_sessionGetPeerPort( msgs->session ) );
|
||||
tr_bencDictAddInt( &val, "upload_only", tr_torrentIsSeed( msgs->torrent ) );
|
||||
tr_bencDictAddStr( &val, "v", TR_NAME " " USERAGENT_PREFIX );
|
||||
m = tr_bencDictAddDict( &val, "m", 1 );
|
||||
if( pex )
|
||||
|
@ -1145,6 +1153,10 @@ parseLtepHandshake( tr_peermsgs * msgs,
|
|||
}
|
||||
}
|
||||
|
||||
/* look for upload_only (BEP 21) */
|
||||
if( tr_bencDictFindInt( &val, "upload_only", &i ) )
|
||||
fireUploadOnly( msgs, i!=0 );
|
||||
|
||||
/* get peer's listening port */
|
||||
if( tr_bencDictFindInt( &val, "p", &i ) )
|
||||
{
|
||||
|
|
|
@ -291,6 +291,8 @@ addField( const tr_torrent * tor,
|
|||
tr_bencDictAddInt( d, key, st->doneDate );
|
||||
else if( !strcmp( key, "downloadedEver" ) )
|
||||
tr_bencDictAddInt( d, key, st->downloadedEver );
|
||||
else if( !strcmp( key, "downloaders" ) )
|
||||
tr_bencDictAddInt( d, key, st->downloaders );
|
||||
else if( !strcmp( key, "downloadLimitMode" ) )
|
||||
tr_bencDictAddInt( d, key, tr_torrentGetSpeedMode( tor, TR_DOWN ) );
|
||||
else if( !strcmp( key, "downloadLimit" ) )
|
||||
|
|
|
@ -783,9 +783,12 @@ tr_torrentStat( tr_torrent * tor )
|
|||
s->announceURL = ti ? ti->announce : NULL;
|
||||
s->scrapeURL = ti ? ti->scrape : NULL;
|
||||
tr_trackerStat( tc, s );
|
||||
|
||||
tr_trackerGetCounts( tc, &s->timesCompleted,
|
||||
&s->leechers,
|
||||
&s->seeders );
|
||||
&s->leechers,
|
||||
&s->seeders,
|
||||
&s->downloaders );
|
||||
|
||||
tr_peerMgrTorrentStats( tor->session->peerMgr,
|
||||
tor->info.hash,
|
||||
&s->peersKnown,
|
||||
|
|
|
@ -108,6 +108,7 @@ struct tr_tracker
|
|||
/* these are set from the latest tracker response... -1 is 'unknown' */
|
||||
int timesDownloaded;
|
||||
int seederCount;
|
||||
int downloaderCount;
|
||||
int leecherCount;
|
||||
char * trackerID;
|
||||
|
||||
|
@ -538,7 +539,7 @@ onScrapeResponse( tr_session * session,
|
|||
long responseCode,
|
||||
const void * response,
|
||||
size_t responseLen,
|
||||
void * torrent_hash )
|
||||
void * torrent_hash )
|
||||
{
|
||||
int success = FALSE;
|
||||
int retry;
|
||||
|
@ -583,9 +584,11 @@ onScrapeResponse( tr_session * session,
|
|||
if( ( tr_bencDictFindInt( tordict, "downloaded", &itmp ) ) )
|
||||
t->timesDownloaded = itmp;
|
||||
|
||||
if( ( tr_bencDictFindInt( tordict, "downloaders", &itmp ) ) )
|
||||
t->downloaderCount = itmp;
|
||||
|
||||
if( tr_bencDictFindDict( tordict, "flags", &flags ) )
|
||||
if( ( tr_bencDictFindInt( flags, "min_request_interval",
|
||||
&itmp ) ) )
|
||||
if( ( tr_bencDictFindInt( flags, "min_request_interval", &itmp ) ) )
|
||||
t->scrapeIntervalSec = i;
|
||||
|
||||
/* as per ticket #1045, safeguard against trackers returning
|
||||
|
@ -651,9 +654,9 @@ enum
|
|||
TR_REQ_STARTED,
|
||||
TR_REQ_COMPLETED,
|
||||
TR_REQ_STOPPED,
|
||||
TR_REQ_PAUSED, /* BEP 21 */
|
||||
TR_REQ_REANNOUNCE,
|
||||
TR_REQ_SCRAPE,
|
||||
TR_REQ_COUNT
|
||||
TR_REQ_SCRAPE
|
||||
};
|
||||
|
||||
struct tr_tracker_request
|
||||
|
@ -712,19 +715,23 @@ buildTrackerRequestURI( tr_tracker * t,
|
|||
}
|
||||
|
||||
static struct tr_tracker_request*
|
||||
createRequest( tr_session * session,
|
||||
tr_tracker * tracker,
|
||||
int reqtype )
|
||||
createRequest( tr_session * session,
|
||||
tr_tracker * tracker,
|
||||
int reqtype )
|
||||
{
|
||||
static const char* strings[] =
|
||||
{ "started", "completed", "stopped", "", "err" };
|
||||
const tr_torrent * torrent = tr_torrentFindFromHash(
|
||||
session, tracker->hash );
|
||||
const tr_tracker_info * address = getCurrentAddressFromTorrent(
|
||||
tracker, torrent );
|
||||
const int isStopping = reqtype == TR_REQ_STOPPED;
|
||||
static const char* strings[] = { "started", "completed", "stopped", "paused", "", "err" };
|
||||
const tr_torrent * torrent = tr_torrentFindFromHash( session, tracker->hash );
|
||||
const tr_tracker_info * address = getCurrentAddressFromTorrent( tracker, torrent );
|
||||
int isStopping;
|
||||
struct tr_tracker_request * req;
|
||||
struct evbuffer * url;
|
||||
struct evbuffer * url;
|
||||
|
||||
/* BEP 21: In order to tell the tracker that a peer is a partial seed, it MUST send
|
||||
* an event=paused parameter in every announce while it is a partial seed. */
|
||||
if( tr_cpGetStatus( torrent->completion ) == TR_PARTIAL_SEED )
|
||||
reqtype = TR_REQ_PAUSED;
|
||||
|
||||
isStopping = reqtype == TR_REQ_STOPPED;
|
||||
|
||||
url = evbuffer_new( );
|
||||
evbuffer_add_printf( url, "%s", address->announce );
|
||||
|
@ -980,6 +987,7 @@ tr_trackerNew( const tr_torrent * torrent )
|
|||
t->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
|
||||
t->timesDownloaded = -1;
|
||||
t->seederCount = -1;
|
||||
t->downloaderCount = -1;
|
||||
t->leecherCount = -1;
|
||||
t->lastAnnounceResponse = -1;
|
||||
t->lastScrapeResponse = -1;
|
||||
|
@ -1060,9 +1068,10 @@ tr_trackerCanManualAnnounce( const tr_tracker * t )
|
|||
|
||||
void
|
||||
tr_trackerGetCounts( const tr_tracker * t,
|
||||
int * setme_completedCount,
|
||||
int * setme_leecherCount,
|
||||
int * setme_seederCount )
|
||||
int * setme_completedCount,
|
||||
int * setme_leecherCount,
|
||||
int * setme_seederCount,
|
||||
int * setme_downloaderCount )
|
||||
{
|
||||
if( setme_completedCount )
|
||||
*setme_completedCount = t->timesDownloaded;
|
||||
|
@ -1072,6 +1081,9 @@ tr_trackerGetCounts( const tr_tracker * t,
|
|||
|
||||
if( setme_seederCount )
|
||||
*setme_seederCount = t->seederCount;
|
||||
|
||||
if( setme_downloaderCount )
|
||||
*setme_downloaderCount = t->downloaderCount;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -97,6 +97,7 @@ time_t tr_trackerGetManualAnnounceTime( const struct tr_tracker
|
|||
void tr_trackerGetCounts( const struct tr_tracker *,
|
||||
int * setme_completedCount,
|
||||
int * setme_leecherCount,
|
||||
int * setme_seederCount );
|
||||
int * setme_seederCount,
|
||||
int * setme_downloaderCount );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1323,6 +1323,11 @@ typedef struct tr_stat
|
|||
/** Number of leechers that the tracker says this torrent has */
|
||||
int leechers;
|
||||
|
||||
/** Number of downloaders that the tracker says this torrent has.
|
||||
This is a new key introduced in BEP 21 and may not be supported by some trackers.
|
||||
If the tracker doesn't support this key, the value here will be -1. */
|
||||
int downloaders;
|
||||
|
||||
/** Number of finished downloads that the tracker says torrent has */
|
||||
int timesCompleted;
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ struct tr_webseed
|
|||
****
|
||||
***/
|
||||
|
||||
static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0, 0 };
|
||||
static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0, 0, 0 };
|
||||
|
||||
static void
|
||||
publish( tr_webseed * w,
|
||||
|
|
Loading…
Reference in a new issue