mirror of
https://github.com/transmission/transmission
synced 2024-12-27 01:57:52 +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_PEER_PROGRESS,
|
||||||
TR_PEER_ERROR,
|
TR_PEER_ERROR,
|
||||||
TR_PEER_CANCEL,
|
TR_PEER_CANCEL,
|
||||||
|
TR_PEER_UPLOAD_ONLY,
|
||||||
TR_PEER_NEED_REQ
|
TR_PEER_NEED_REQ
|
||||||
}
|
}
|
||||||
PeerEventType;
|
PeerEventType;
|
||||||
|
@ -61,6 +62,7 @@ typedef struct
|
||||||
float progress; /* for PEER_PROGRESS */
|
float progress; /* for PEER_PROGRESS */
|
||||||
int err; /* errno for GOT_ERROR */
|
int err; /* errno for GOT_ERROR */
|
||||||
int wasPieceData; /* for GOT_DATA */
|
int wasPieceData; /* for GOT_DATA */
|
||||||
|
tr_bool uploadOnly; /* for UPLOAD_ONLY */
|
||||||
}
|
}
|
||||||
tr_peer_event;
|
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
|
/* We keep one of these for every peer we know about, whether
|
||||||
* it's connected or not, so the struct must be small.
|
* it's connected or not, so the struct must be small.
|
||||||
* When our current connections underperform, we dip back
|
* When our current connections underperform, we dip back
|
||||||
|
@ -98,6 +105,8 @@ struct peer_atom
|
||||||
uint8_t from;
|
uint8_t from;
|
||||||
uint8_t flags; /* these match the added_f flags */
|
uint8_t flags; /* these match the added_f flags */
|
||||||
uint8_t myflags; /* flags that aren't defined in added_f */
|
uint8_t myflags; /* flags that aren't defined in added_f */
|
||||||
|
uint8_t uploadOnly; /* UPLOAD_ONLY_ */
|
||||||
|
uint8_t partialSeed;
|
||||||
tr_port port;
|
tr_port port;
|
||||||
uint16_t numFails;
|
uint16_t numFails;
|
||||||
tr_address addr;
|
tr_address addr;
|
||||||
|
@ -980,6 +989,14 @@ peerCallbackFunc( void * vpeer,
|
||||||
|
|
||||||
switch( e->eventType )
|
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:
|
case TR_PEER_NEED_REQ:
|
||||||
refillSoon( t );
|
refillSoon( t );
|
||||||
break;
|
break;
|
||||||
|
@ -1458,11 +1475,15 @@ tr_peerMgrGetPeers( tr_peerMgr * manager,
|
||||||
for( i = 0; i < peerCount; ++i, ++walk )
|
for( i = 0; i < peerCount; ++i, ++walk )
|
||||||
{
|
{
|
||||||
const tr_peer * peer = peers[i];
|
const tr_peer * peer = peers[i];
|
||||||
|
const struct peer_atom * atom = getExistingAtom( t, &peer->addr );
|
||||||
|
|
||||||
walk->addr = peer->addr;
|
walk->addr = peer->addr;
|
||||||
walk->port = peer->port;
|
walk->port = peer->port;
|
||||||
walk->flags = 0;
|
walk->flags = 0;
|
||||||
if( peerPrefersCrypto( peer ) ) walk->flags |= ADDED_F_ENCRYPTION_FLAG;
|
if( peerPrefersCrypto( peer ) )
|
||||||
if( peer->progress >= 1.0 ) walk->flags |= ADDED_F_SEED_FLAG;
|
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 );
|
assert( ( walk - pex ) == peerCount );
|
||||||
|
@ -1884,11 +1905,22 @@ rechoke( Torrent * t )
|
||||||
for( i = 0, size = 0; i < peerCount; ++i )
|
for( i = 0, size = 0; i < peerCount; ++i )
|
||||||
{
|
{
|
||||||
tr_peer * peer = peers[i];
|
tr_peer * peer = peers[i];
|
||||||
|
struct peer_atom * atom = getExistingAtom( t, &peer->addr );
|
||||||
|
|
||||||
if( peer->progress >= 1.0 ) /* choke all seeds */
|
if( peer->progress >= 1.0 ) /* choke all seeds */
|
||||||
|
{
|
||||||
tr_peerMsgsSetChoke( peer->msgs, TRUE );
|
tr_peerMsgsSetChoke( peer->msgs, TRUE );
|
||||||
else if( chokeAll )
|
}
|
||||||
|
else if( atom->uploadOnly == UPLOAD_ONLY_YES ) /* choke partial seeds */
|
||||||
|
{
|
||||||
tr_peerMsgsSetChoke( peer->msgs, TRUE );
|
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++];
|
struct ChokeData * n = &choke[size++];
|
||||||
n->peer = peer;
|
n->peer = peer;
|
||||||
n->isInterested = peer->peerIsInterested;
|
n->isInterested = peer->peerIsInterested;
|
||||||
|
@ -2175,7 +2207,8 @@ getPeerCandidates( Torrent * t,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* no need to connect if we're both seeds... */
|
/* 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;
|
continue;
|
||||||
|
|
||||||
/* don't reconnect too often */
|
/* don't reconnect too often */
|
||||||
|
|
|
@ -513,7 +513,7 @@ protocolSendHaveNone( tr_peermsgs * msgs )
|
||||||
*** EVENTS
|
*** 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
|
static void
|
||||||
publish( tr_peermsgs * msgs,
|
publish( tr_peermsgs * msgs,
|
||||||
|
@ -536,11 +536,19 @@ fireError( tr_peermsgs * msgs,
|
||||||
publish( msgs, &e );
|
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
|
static void
|
||||||
fireNeedReq( tr_peermsgs * msgs )
|
fireNeedReq( tr_peermsgs * msgs )
|
||||||
{
|
{
|
||||||
tr_peer_event e = blankEvent;
|
tr_peer_event e = blankEvent;
|
||||||
|
|
||||||
e.eventType = TR_PEER_NEED_REQ;
|
e.eventType = TR_PEER_NEED_REQ;
|
||||||
publish( msgs, &e );
|
publish( msgs, &e );
|
||||||
}
|
}
|
||||||
|
@ -1086,9 +1094,9 @@ sendLtepHandshake( tr_peermsgs * msgs )
|
||||||
pex = 1;
|
pex = 1;
|
||||||
|
|
||||||
tr_bencInitDict( &val, 4 );
|
tr_bencInitDict( &val, 4 );
|
||||||
tr_bencDictAddInt( &val, "e",
|
tr_bencDictAddInt( &val, "e", msgs->session->encryptionMode != TR_CLEAR_PREFERRED );
|
||||||
msgs->session->encryptionMode != TR_CLEAR_PREFERRED );
|
|
||||||
tr_bencDictAddInt( &val, "p", tr_sessionGetPeerPort( msgs->session ) );
|
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 );
|
tr_bencDictAddStr( &val, "v", TR_NAME " " USERAGENT_PREFIX );
|
||||||
m = tr_bencDictAddDict( &val, "m", 1 );
|
m = tr_bencDictAddDict( &val, "m", 1 );
|
||||||
if( pex )
|
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 */
|
/* get peer's listening port */
|
||||||
if( tr_bencDictFindInt( &val, "p", &i ) )
|
if( tr_bencDictFindInt( &val, "p", &i ) )
|
||||||
{
|
{
|
||||||
|
|
|
@ -291,6 +291,8 @@ addField( const tr_torrent * tor,
|
||||||
tr_bencDictAddInt( d, key, st->doneDate );
|
tr_bencDictAddInt( d, key, st->doneDate );
|
||||||
else if( !strcmp( key, "downloadedEver" ) )
|
else if( !strcmp( key, "downloadedEver" ) )
|
||||||
tr_bencDictAddInt( d, key, st->downloadedEver );
|
tr_bencDictAddInt( d, key, st->downloadedEver );
|
||||||
|
else if( !strcmp( key, "downloaders" ) )
|
||||||
|
tr_bencDictAddInt( d, key, st->downloaders );
|
||||||
else if( !strcmp( key, "downloadLimitMode" ) )
|
else if( !strcmp( key, "downloadLimitMode" ) )
|
||||||
tr_bencDictAddInt( d, key, tr_torrentGetSpeedMode( tor, TR_DOWN ) );
|
tr_bencDictAddInt( d, key, tr_torrentGetSpeedMode( tor, TR_DOWN ) );
|
||||||
else if( !strcmp( key, "downloadLimit" ) )
|
else if( !strcmp( key, "downloadLimit" ) )
|
||||||
|
|
|
@ -783,9 +783,12 @@ tr_torrentStat( tr_torrent * tor )
|
||||||
s->announceURL = ti ? ti->announce : NULL;
|
s->announceURL = ti ? ti->announce : NULL;
|
||||||
s->scrapeURL = ti ? ti->scrape : NULL;
|
s->scrapeURL = ti ? ti->scrape : NULL;
|
||||||
tr_trackerStat( tc, s );
|
tr_trackerStat( tc, s );
|
||||||
|
|
||||||
tr_trackerGetCounts( tc, &s->timesCompleted,
|
tr_trackerGetCounts( tc, &s->timesCompleted,
|
||||||
&s->leechers,
|
&s->leechers,
|
||||||
&s->seeders );
|
&s->seeders,
|
||||||
|
&s->downloaders );
|
||||||
|
|
||||||
tr_peerMgrTorrentStats( tor->session->peerMgr,
|
tr_peerMgrTorrentStats( tor->session->peerMgr,
|
||||||
tor->info.hash,
|
tor->info.hash,
|
||||||
&s->peersKnown,
|
&s->peersKnown,
|
||||||
|
|
|
@ -108,6 +108,7 @@ struct tr_tracker
|
||||||
/* these are set from the latest tracker response... -1 is 'unknown' */
|
/* these are set from the latest tracker response... -1 is 'unknown' */
|
||||||
int timesDownloaded;
|
int timesDownloaded;
|
||||||
int seederCount;
|
int seederCount;
|
||||||
|
int downloaderCount;
|
||||||
int leecherCount;
|
int leecherCount;
|
||||||
char * trackerID;
|
char * trackerID;
|
||||||
|
|
||||||
|
@ -583,9 +584,11 @@ onScrapeResponse( tr_session * session,
|
||||||
if( ( tr_bencDictFindInt( tordict, "downloaded", &itmp ) ) )
|
if( ( tr_bencDictFindInt( tordict, "downloaded", &itmp ) ) )
|
||||||
t->timesDownloaded = itmp;
|
t->timesDownloaded = itmp;
|
||||||
|
|
||||||
|
if( ( tr_bencDictFindInt( tordict, "downloaders", &itmp ) ) )
|
||||||
|
t->downloaderCount = itmp;
|
||||||
|
|
||||||
if( tr_bencDictFindDict( tordict, "flags", &flags ) )
|
if( tr_bencDictFindDict( tordict, "flags", &flags ) )
|
||||||
if( ( tr_bencDictFindInt( flags, "min_request_interval",
|
if( ( tr_bencDictFindInt( flags, "min_request_interval", &itmp ) ) )
|
||||||
&itmp ) ) )
|
|
||||||
t->scrapeIntervalSec = i;
|
t->scrapeIntervalSec = i;
|
||||||
|
|
||||||
/* as per ticket #1045, safeguard against trackers returning
|
/* as per ticket #1045, safeguard against trackers returning
|
||||||
|
@ -651,9 +654,9 @@ enum
|
||||||
TR_REQ_STARTED,
|
TR_REQ_STARTED,
|
||||||
TR_REQ_COMPLETED,
|
TR_REQ_COMPLETED,
|
||||||
TR_REQ_STOPPED,
|
TR_REQ_STOPPED,
|
||||||
|
TR_REQ_PAUSED, /* BEP 21 */
|
||||||
TR_REQ_REANNOUNCE,
|
TR_REQ_REANNOUNCE,
|
||||||
TR_REQ_SCRAPE,
|
TR_REQ_SCRAPE
|
||||||
TR_REQ_COUNT
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tr_tracker_request
|
struct tr_tracker_request
|
||||||
|
@ -716,16 +719,20 @@ createRequest( tr_session * session,
|
||||||
tr_tracker * tracker,
|
tr_tracker * tracker,
|
||||||
int reqtype )
|
int reqtype )
|
||||||
{
|
{
|
||||||
static const char* strings[] =
|
static const char* strings[] = { "started", "completed", "stopped", "paused", "", "err" };
|
||||||
{ "started", "completed", "stopped", "", "err" };
|
const tr_torrent * torrent = tr_torrentFindFromHash( session, tracker->hash );
|
||||||
const tr_torrent * torrent = tr_torrentFindFromHash(
|
const tr_tracker_info * address = getCurrentAddressFromTorrent( tracker, torrent );
|
||||||
session, tracker->hash );
|
int isStopping;
|
||||||
const tr_tracker_info * address = getCurrentAddressFromTorrent(
|
|
||||||
tracker, torrent );
|
|
||||||
const int isStopping = reqtype == TR_REQ_STOPPED;
|
|
||||||
struct tr_tracker_request * req;
|
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( );
|
url = evbuffer_new( );
|
||||||
evbuffer_add_printf( url, "%s", address->announce );
|
evbuffer_add_printf( url, "%s", address->announce );
|
||||||
buildTrackerRequestURI( tracker, torrent, strings[reqtype], url );
|
buildTrackerRequestURI( tracker, torrent, strings[reqtype], url );
|
||||||
|
@ -980,6 +987,7 @@ tr_trackerNew( const tr_torrent * torrent )
|
||||||
t->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
|
t->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
|
||||||
t->timesDownloaded = -1;
|
t->timesDownloaded = -1;
|
||||||
t->seederCount = -1;
|
t->seederCount = -1;
|
||||||
|
t->downloaderCount = -1;
|
||||||
t->leecherCount = -1;
|
t->leecherCount = -1;
|
||||||
t->lastAnnounceResponse = -1;
|
t->lastAnnounceResponse = -1;
|
||||||
t->lastScrapeResponse = -1;
|
t->lastScrapeResponse = -1;
|
||||||
|
@ -1062,7 +1070,8 @@ void
|
||||||
tr_trackerGetCounts( const tr_tracker * t,
|
tr_trackerGetCounts( const tr_tracker * t,
|
||||||
int * setme_completedCount,
|
int * setme_completedCount,
|
||||||
int * setme_leecherCount,
|
int * setme_leecherCount,
|
||||||
int * setme_seederCount )
|
int * setme_seederCount,
|
||||||
|
int * setme_downloaderCount )
|
||||||
{
|
{
|
||||||
if( setme_completedCount )
|
if( setme_completedCount )
|
||||||
*setme_completedCount = t->timesDownloaded;
|
*setme_completedCount = t->timesDownloaded;
|
||||||
|
@ -1072,6 +1081,9 @@ tr_trackerGetCounts( const tr_tracker * t,
|
||||||
|
|
||||||
if( setme_seederCount )
|
if( setme_seederCount )
|
||||||
*setme_seederCount = t->seederCount;
|
*setme_seederCount = t->seederCount;
|
||||||
|
|
||||||
|
if( setme_downloaderCount )
|
||||||
|
*setme_downloaderCount = t->downloaderCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -97,6 +97,7 @@ time_t tr_trackerGetManualAnnounceTime( const struct tr_tracker
|
||||||
void tr_trackerGetCounts( const struct tr_tracker *,
|
void tr_trackerGetCounts( const struct tr_tracker *,
|
||||||
int * setme_completedCount,
|
int * setme_completedCount,
|
||||||
int * setme_leecherCount,
|
int * setme_leecherCount,
|
||||||
int * setme_seederCount );
|
int * setme_seederCount,
|
||||||
|
int * setme_downloaderCount );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1323,6 +1323,11 @@ typedef struct tr_stat
|
||||||
/** Number of leechers that the tracker says this torrent has */
|
/** Number of leechers that the tracker says this torrent has */
|
||||||
int leechers;
|
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 */
|
/** Number of finished downloads that the tracker says torrent has */
|
||||||
int timesCompleted;
|
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
|
static void
|
||||||
publish( tr_webseed * w,
|
publish( tr_webseed * w,
|
||||||
|
|
Loading…
Reference in a new issue