From d4ee0d714afb220a562847b9e6b2bf0a93f98e94 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 2 Dec 2008 19:46:51 +0000 Subject: [PATCH] (libT) #1379: support "partial seeds" BEP 22 --- libtransmission/peer-common.h | 2 ++ libtransmission/peer-mgr.c | 49 +++++++++++++++++++++++++++------ libtransmission/peer-msgs.c | 20 +++++++++++--- libtransmission/rpcimpl.c | 2 ++ libtransmission/torrent.c | 7 +++-- libtransmission/tracker.c | 50 +++++++++++++++++++++------------- libtransmission/tracker.h | 3 +- libtransmission/transmission.h | 5 ++++ libtransmission/webseed.c | 2 +- 9 files changed, 105 insertions(+), 35 deletions(-) diff --git a/libtransmission/peer-common.h b/libtransmission/peer-common.h index b5f0c52d4..15f3c9f0a 100644 --- a/libtransmission/peer-common.h +++ b/libtransmission/peer-common.h @@ -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; diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c index a452736d1..914ca7858 100644 --- a/libtransmission/peer-mgr.c +++ b/libtransmission/peer-mgr.c @@ -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 */ diff --git a/libtransmission/peer-msgs.c b/libtransmission/peer-msgs.c index 8bd462c26..7228b568b 100644 --- a/libtransmission/peer-msgs.c +++ b/libtransmission/peer-msgs.c @@ -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 ) ) { diff --git a/libtransmission/rpcimpl.c b/libtransmission/rpcimpl.c index 05f783c16..0b8d011d9 100644 --- a/libtransmission/rpcimpl.c +++ b/libtransmission/rpcimpl.c @@ -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" ) ) diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c index 35dc07330..525e177a7 100644 --- a/libtransmission/torrent.c +++ b/libtransmission/torrent.c @@ -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, diff --git a/libtransmission/tracker.c b/libtransmission/tracker.c index e6c6184d6..b93edcaa3 100644 --- a/libtransmission/tracker.c +++ b/libtransmission/tracker.c @@ -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 diff --git a/libtransmission/tracker.h b/libtransmission/tracker.h index 6e238f795..93bccfdcd 100644 --- a/libtransmission/tracker.h +++ b/libtransmission/tracker.h @@ -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 diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index cc912ad30..ba96ba506 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -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; diff --git a/libtransmission/webseed.c b/libtransmission/webseed.c index 9c238478d..54314c230 100644 --- a/libtransmission/webseed.c +++ b/libtransmission/webseed.c @@ -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,