Ignore:
Timestamp:
Dec 2, 2006, 1:46:54 AM (15 years ago)
Author:
joshe
Message:

Merge scrape branch:

Automatically scrape trackers as needed.
If tracker supplies a trackerid then use it (untested).
Use tracker's min interval, clamped to the same range as interval.
Show total completed downloads in the MacOS X frontend.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/tracker.c

    r1059 r1149  
    3030
    3131    char         * id;
     32    char         * trackerid;
    3233
    3334    char           started;
     
    3637
    3738    int            interval;
     39    int            minInterval;
     40    int            scrapeInterval;
    3841    int            seeders;
    3942    int            leechers;
    4043    int            hasManyPeers;
     44    int            complete;
    4145
    4246    uint64_t       dateTry;
    4347    uint64_t       dateOk;
     48    uint64_t       dateScrape;
     49    int            lastScrapeFailed;
    4450
    4551#define TC_ATTEMPT_NOREACH 1
     
    4753#define TC_ATTEMPT_OK      4
    4854    char           lastAttempt;
     55    int            scrapeNeeded;
    4956
    5057    tr_http_t    * http;
     58    tr_http_t    * httpScrape;
    5159
    5260    int            bindPort;
     
    5462};
    5563
    56 static tr_http_t * getQuery   ( tr_tracker_t * tc );
    57 static void        readAnswer ( tr_tracker_t * tc, const char *, int );
     64static tr_http_t * getQuery         ( tr_tracker_t * tc );
     65static tr_http_t * getScrapeQuery   ( tr_tracker_t * tc );
     66static void        readAnswer       ( tr_tracker_t * tc, const char *, int );
     67static void        readScrapeAnswer ( tr_tracker_t * tc, const char *, int );
     68static void        killHttp         ( tr_http_t ** http, tr_fd_t * fdlimit );
    5869
    5970tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
     
    6172    tr_tracker_t * tc;
    6273
    63     tc           = calloc( 1, sizeof( tr_tracker_t ) );
    64     tc->tor      = tor;
    65     tc->id       = tor->id;
    66 
    67     tc->started  = 1;
    68 
    69     tc->interval = 300;
    70     tc->seeders  = -1;
    71     tc->leechers = -1;
    72 
    73     tc->lastAttempt = TC_ATTEMPT_NOREACH;
    74 
    75     tc->bindPort = *(tor->bindPort);
    76     tc->newPort  = -1;
     74    tc                 = calloc( 1, sizeof( tr_tracker_t ) );
     75    tc->tor            = tor;
     76    tc->id             = tor->id;
     77
     78    tc->started        = 1;
     79
     80    tc->interval       = 300;
     81    tc->scrapeInterval = 600;
     82    tc->seeders        = -1;
     83    tc->leechers       = -1;
     84    tc->complete       = -1;
     85
     86    tc->lastAttempt    = TC_ATTEMPT_NOREACH;
     87
     88    tc->bindPort       = *(tor->bindPort);
     89    tc->newPort        = -1;
    7790
    7891    return tc;
     
    8194static int shouldConnect( tr_tracker_t * tc )
    8295{
    83     uint64_t now = tr_date();
     96    tr_torrent_t * tor = tc->tor;
     97    uint64_t       now;
     98
     99    now = tr_date();
    84100
    85101    /* Unreachable tracker, try 10 seconds before trying again */
     
    113129    /* If there is quite a lot of people on this torrent, stress
    114130       the tracker a bit until we get a decent number of peers */
    115     if( tc->hasManyPeers )
    116     {
    117         if( tc->tor->peerCount < 5 && now > tc->dateOk + 10000 )
    118         {
    119             return 1;
    120         }
    121         if( tc->tor->peerCount < 10 && now > tc->dateOk + 20000 )
    122         {
    123             return 1;
    124         }
    125         if( tc->tor->peerCount < 15 && now > tc->dateOk + 30000 )
    126         {
    127             return 1;
     131    if( tc->hasManyPeers && !tr_cpIsSeeding( tor->completion ) )
     132    {
     133        /* reannounce in 10 seconds if we have less than 5 peers */
     134        if( tor->peerCount < 5 )
     135        {
     136            if( now > tc->dateOk + 1000 * MAX( 10, tc->minInterval ) )
     137            {
     138                return 1;
     139            }
     140        }
     141        /* reannounce in 20 seconds if we have less than 10 peers */
     142        else if( tor->peerCount < 10 )
     143        {
     144            if( now > tc->dateOk + 1000 * MAX( 20, tc->minInterval ) )
     145            {
     146                return 1;
     147            }
     148        }
     149        /* reannounce in 30 seconds if we have less than 15 peers */
     150        else if( tor->peerCount < 15 )
     151        {
     152            if( now > tc->dateOk + 1000 * MAX( 30, tc->minInterval ) )
     153            {
     154                return 1;
     155            }
    128156        }
    129157    }
    130158
    131159    return 0;
     160}
     161
     162static int shouldScrape( tr_tracker_t * tc )
     163{
     164    uint64_t now, interval;
     165
     166    /* scrape not supported */
     167    if( !tc->tor->scrape[0] )
     168    {
     169        return 0;
     170    }
     171
     172    now      = tr_date();
     173    interval = 1000 * MAX( tc->scrapeInterval, 60 );
     174
     175    /* scrape half as often if there is no need to */
     176    if( !tc->scrapeNeeded && !tc->lastScrapeFailed )
     177    {
     178        interval *= 2;
     179    }
     180
     181    return now > tc->dateScrape + interval;
    132182}
    133183
     
    167217        {
    168218            case TR_WAIT:
    169                 return 0;
     219                break;
    170220
    171221            case TR_ERROR:
    172                 tr_httpClose( tc->http );
    173                 tr_fdSocketClosed( tor->fdlimit, 1 );
    174                 tc->http    = NULL;
     222                killHttp( &tc->http, tor->fdlimit );
    175223                tc->dateTry = tr_date();
    176                 return 0;
     224                break;
    177225
    178226            case TR_OK:
    179227                readAnswer( tc, data, len );
    180                 tr_httpClose( tc->http );
    181                 tc->http = NULL;
    182                 tr_fdSocketClosed( tor->fdlimit, 1 );
     228                killHttp( &tc->http, tor->fdlimit );
     229                break;
     230        }
     231    }
     232   
     233    if( ( NULL == tc->httpScrape ) && shouldScrape( tc ) )
     234    {
     235        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
     236        {
     237            return 0;
     238        }
     239        tc->dateScrape = tr_date();
     240        tc->httpScrape = getScrapeQuery( tc );
     241        tr_inf( "Scrape: sent http request to %s:%d",
     242                    inf->trackerAddress, inf->trackerPort );
     243    }
     244
     245    if( NULL != tc->httpScrape )
     246    {
     247        switch( tr_httpPulse( tc->httpScrape, &data, &len ) )
     248        {
     249            case TR_WAIT:
     250                break;
     251
     252            case TR_ERROR:
     253                killHttp( &tc->httpScrape, tor->fdlimit );
     254                tc->lastScrapeFailed = 1;
     255                break;
     256
     257            case TR_OK:
     258                readScrapeAnswer( tc, data, len );
     259                killHttp( &tc->httpScrape, tor->fdlimit );
    183260                break;
    184261        }
     
    199276    tr_torrent_t * tor = tc->tor;
    200277
    201     if( NULL != tc->http )
    202     {
    203         /* If we are already sendy a query at the moment, we need to
    204            reconnect */
    205         tr_httpClose( tc->http );
    206         tc->http = NULL;
    207         tr_fdSocketClosed( tor->fdlimit, 1 );
    208     }
     278    /* If we are already sending a query at the moment, we need to
     279       reconnect */
     280    killHttp( &tc->http, tor->fdlimit );
    209281
    210282    tc->started   = 0;
     
    220292    tr_torrent_t * tor = tc->tor;
    221293
    222     if( NULL != tc->http )
    223     {
    224         tr_httpClose( tc->http );
    225         tr_fdSocketClosed( tor->fdlimit, 1 );
    226     }
     294    killHttp( &tc->http, tor->fdlimit );
     295    killHttp( &tc->httpScrape, tor->fdlimit );
     296    free( tc->trackerid );
    227297    free( tc );
    228298}
     
    233303    tr_info_t    * inf = &tor->info;
    234304
    235     char         * event;
     305    char         * event, * trackerid, * idparam;
    236306    uint64_t       left;
    237307    uint64_t       down;
    238308    uint64_t       up;
    239     char         * start;
     309    char           start;
    240310    int            numwant = 50;
    241311
    242312    down = tor->downloadedCur;
    243     up = tor->uploadedCur;
     313    up   = tor->uploadedCur;
    244314    if( tc->started )
    245315    {
    246316        event = "&event=started";
    247         down = up = 0;
    248        
     317        down  = 0;
     318        up    = 0;
     319
    249320        if( 0 < tc->newPort )
    250321        {
    251322            tc->bindPort = tc->newPort;
    252             tc->newPort = -1;
     323            tc->newPort  = -1;
    253324        }
    254325    }
     
    267338    }
    268339
    269     if( NULL == strchr( inf->trackerAnnounce, '?' ) )
    270     {
    271         start = "?";
     340    if( NULL == tc->trackerid )
     341    {
     342        trackerid = "";
     343        idparam   = "";
    272344    }
    273345    else
    274346    {
    275         start = "&";
    276     }
    277 
    278     left = tr_cpLeftBytes( tor->completion );
    279 
    280     return tr_httpClient( TR_HTTP_GET, inf->trackerAddress,
    281                           inf->trackerPort,
    282                           "%s%s"
     347        trackerid = tc->trackerid;
     348        idparam   = "&trackerid=";
     349    }
     350
     351    start = ( strchr( inf->trackerAnnounce, '?' ) ? '&' : '?' );
     352    left  = tr_cpLeftBytes( tor->completion );
     353
     354    return tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
     355                          "%s%c"
    283356                          "info_hash=%s&"
    284357                          "peer_id=%s&"
     
    290363                          "numwant=%d&"
    291364                          "key=%s"
     365                          "%s%s"
    292366                          "%s",
    293                           inf->trackerAnnounce, start, tor->hashString, tc->id,
    294                           tc->bindPort, up, down, left, numwant, tor->key, event );
     367                          inf->trackerAnnounce, start, tor->escapedHashString,
     368                          tc->id, tc->bindPort, up, down, left, numwant,
     369                          tor->key, idparam, trackerid, event );
     370}
     371
     372static tr_http_t * getScrapeQuery( tr_tracker_t * tc )
     373{
     374    tr_torrent_t * tor = tc->tor;
     375    tr_info_t    * inf = &tor->info;
     376
     377    char           start;
     378
     379    start = ( strchr( tor->scrape, '?' ) ? '&' : '?' );
     380
     381    return tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
     382                          "%s%c"
     383                          "info_hash=%s",
     384                          tor->scrape, start, tor->escapedHashString );
    295385}
    296386
     
    303393    benc_val_t * bePeers, * beFoo;
    304394    const uint8_t * body;
    305     int bodylen;
    306     int shouldfree;
     395    int bodylen, shouldfree, scrapeNeeded;
    307396
    308397    tc->dateTry = tr_date();
     
    332421        return;
    333422    }
    334     bodylen = len - (body - (const uint8_t*)data);
     423    bodylen = len - ( body - (const uint8_t*)data );
    335424
    336425    /* Find and load the dictionary */
     
    372461    tc->lastAttempt = TC_ATTEMPT_OK;
    373462
    374     if( !tc->interval )
    375     {
    376         /* Get the tracker interval, ignore it if it is not between
    377            10 sec and 5 mins */
    378         if( !( beFoo = tr_bencDictFind( &beAll, "interval" ) ) ||
    379             !( beFoo->type & TYPE_INT ) )
    380         {
    381             tr_err( "Tracker: no 'interval' field" );
    382             goto cleanup;
    383         }
    384 
    385         tc->interval = beFoo->val.i;
    386         tc->interval = MIN( tc->interval, 300 );
    387         tc->interval = MAX( 10, tc->interval );
    388 
    389         tr_inf( "Tracker: interval = %d seconds", tc->interval );
    390     }
    391 
    392     if( ( beFoo = tr_bencDictFind( &beAll, "complete" ) ) &&
    393         ( beFoo->type & TYPE_INT ) )
     463    /* Get the tracker interval, force to between
     464       10 sec and 5 mins */
     465    beFoo = tr_bencDictFind( &beAll, "interval" );
     466    if( !beFoo || TYPE_INT != beFoo->type )
     467    {
     468        tr_err( "Tracker: no 'interval' field" );
     469        goto cleanup;
     470    }
     471
     472    tc->interval = beFoo->val.i;
     473    tr_inf( "Tracker: interval = %d seconds", tc->interval );
     474
     475    tc->interval = MIN( tc->interval, 300 );
     476    tc->interval = MAX( 10, tc->interval );
     477
     478    /* Get the tracker minimum interval, force to between
     479       10 sec and 5 mins  */
     480    beFoo = tr_bencDictFind( &beAll, "min interval" );
     481    if( beFoo && TYPE_INT == beFoo->type )
     482    {
     483        tc->minInterval = beFoo->val.i;
     484        tr_inf( "Tracker: min interval = %d seconds", tc->minInterval );
     485
     486        tc->minInterval = MIN( tc->minInterval, 300 );
     487        tc->minInterval = MAX( 10, tc->minInterval );
     488
     489        if( tc->interval < tc->minInterval )
     490        {
     491            tc->interval = tc->minInterval;
     492            tr_inf( "Tracker: 'interval' less than 'min interval', "
     493                    "using 'min interval'" );
     494        }
     495    }
     496    else
     497    {
     498        tc->minInterval = 0;
     499        tr_inf( "Tracker: no 'min interval' field" );
     500    }
     501
     502    scrapeNeeded = 0;
     503    beFoo = tr_bencDictFind( &beAll, "complete" );
     504    if( beFoo && TYPE_INT == beFoo->type )
    394505    {
    395506        tc->seeders = beFoo->val.i;
    396507    }
    397     if( ( beFoo = tr_bencDictFind( &beAll, "incomplete" ) ) &&
    398         ( beFoo->type & TYPE_INT ) )
     508    else
     509    {
     510        scrapeNeeded = 1;
     511    }
     512
     513    beFoo = tr_bencDictFind( &beAll, "incomplete" );
     514    if( beFoo && TYPE_INT == beFoo->type )
    399515    {
    400516        tc->leechers = beFoo->val.i;
    401517    }
    402     if( tc->seeders + tc->leechers >= 50 )
    403     {
    404         tc->hasManyPeers = 1;
    405     }
    406 
    407     if( !( bePeers = tr_bencDictFind( &beAll, "peers" ) ) )
     518    else
     519    {
     520        scrapeNeeded = 1;
     521    }
     522
     523    tc->scrapeNeeded = scrapeNeeded;
     524    if( !scrapeNeeded )
     525    {
     526        tc->hasManyPeers = ( tc->seeders + tc->leechers >= 50 );
     527    }
     528
     529    beFoo = tr_bencDictFind( &beAll, "tracker id" );
     530    if( beFoo )
     531    {
     532        free( tc->trackerid );
     533        tc->trackerid = strdup( beFoo->val.s.s );
     534        tr_inf( "Tracker: tracker id = %s", tc->trackerid );
     535    }
     536
     537    bePeers = tr_bencDictFind( &beAll, "peers" );
     538    if( !bePeers )
    408539    {
    409540        if( tc->stopped || 0 < tc->newPort )
     
    494625}
    495626
    496 int tr_trackerScrape( tr_torrent_t * tor, int * seeders, int * leechers )
    497 {
    498     tr_info_t * inf = &tor->info;
    499 
    500     tr_http_t  * http;
    501     const char * data, * body;
    502     int          datalen, bodylen;
    503     int          code, ii;
    504     benc_val_t   scrape, * val1, * val2;
     627static void readScrapeAnswer( tr_tracker_t * tc, const char * data, int len )
     628{
     629    int code;
     630    const uint8_t * body;
     631    int bodylen, ii;
     632    benc_val_t scrape, * val1, * val2;
     633
     634    code = tr_httpResponseCode( data, len );
     635    if( 0 > code )
     636    {
     637        /* We don't have a valid HTTP status line */
     638        tr_inf( "Scrape: invalid HTTP status line" );
     639        tc->lastScrapeFailed = 1;
     640        return;
     641    }
     642
     643    if( !TR_HTTP_STATUS_OK( code ) )
     644    {
     645        /* we didn't get a 2xx status code */
     646        tr_err( "Scrape: invalid HTTP status code: %i", code );
     647        tc->lastScrapeFailed = 1;
     648        return;
     649    }
     650
     651    /* find the end of the http headers */
     652    body = (uint8_t *) tr_httpParse( data, len, NULL );
     653    if( NULL == body )
     654    {
     655        tr_err( "Scrape: could not find end of HTTP headers" );
     656        tc->lastScrapeFailed = 1;
     657        return;
     658    }
     659
     660    tc->lastScrapeFailed = 0;
     661    bodylen = len - ( body - (const uint8_t*)data );
     662
     663    for( ii = 0; ii < bodylen; ii++ )
     664    {
     665        if( !tr_bencLoad( body + ii, bodylen - ii, &scrape, NULL ) )
     666        {
     667            break;
     668        }
     669    }
     670    if( ii >= bodylen )
     671    {
     672        return;
     673    }
     674
     675    val1 = tr_bencDictFind( &scrape, "files" );
     676    if( !val1 )
     677    {
     678        tr_bencFree( &scrape );
     679        return;
     680    }
     681    val1 = &val1->val.l.vals[1];
     682    if( !val1 )
     683    {
     684        tr_bencFree( &scrape );
     685        return;
     686    }
     687   
     688    val2 = tr_bencDictFind( val1, "complete" );
     689    if( !val2 )
     690    {
     691        tr_bencFree( &scrape );
     692        return;
     693    }
     694    tc->seeders = val2->val.i;
     695   
     696    val2 = tr_bencDictFind( val1, "incomplete" );
     697    if( !val2 )
     698    {
     699        tr_bencFree( &scrape );
     700        return;
     701    }
     702    tc->leechers = val2->val.i;
     703   
     704    val2 = tr_bencDictFind( val1, "downloaded" );
     705    if( !val2 )
     706    {
     707        tr_bencFree( &scrape );
     708        return;
     709    }
     710    tc->complete = val2->val.i;
     711   
     712    val2 = tr_bencDictFind( val1, "flags" );
     713    if( val2 )
     714    {
     715        val2 = tr_bencDictFind( val2, "min_request_interval" );
     716        if( val2 )
     717        {
     718            tc->scrapeInterval = val2->val.i;
     719        }
     720    }
     721   
     722    tc->hasManyPeers = ( tc->seeders + tc->leechers >= 50 );
     723   
     724    tr_bencFree( &scrape );
     725}
     726
     727int tr_trackerSeeders( tr_tracker_t * tc )
     728{
     729    if( !tc )
     730    {
     731        return -1;
     732    }
     733    return tc->seeders;
     734}
     735
     736int tr_trackerLeechers( tr_tracker_t * tc )
     737{
     738    if( !tc )
     739    {
     740        return -1;
     741    }
     742    return tc->leechers;
     743}
     744
     745int tr_trackerDownloaded( tr_tracker_t * tc )
     746{
     747    if( !tc )
     748    {
     749        return -1;
     750    }
     751    return tc->complete;
     752}
     753
     754/* Blocking version */
     755int tr_trackerScrape( tr_torrent_t * tor, int * s, int * l, int * d )
     756{
     757    tr_tracker_t * tc;
     758    tr_http_t    * http;
     759    const char   * data;
     760    int            len;
     761    int            ret;
    505762
    506763    if( !tor->scrape[0] )
    507764    {
    508         /* scrape not supported */
    509765        return 1;
    510766    }
    511767
    512     http = tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
    513                           "%s?info_hash=%s", tor->scrape, tor->hashString );
    514 
    515     data = NULL;
    516     while( NULL == data )
    517     {
    518         switch( tr_httpPulse( http, &data, &datalen ) )
     768    tc = tr_trackerInit( tor );
     769    http = getScrapeQuery( tc );
     770
     771    for( data = NULL; !data; tr_wait( 10 ) )
     772    {
     773        switch( tr_httpPulse( http, &data, &len ) )
    519774        {
    520775            case TR_WAIT:
     
    522777
    523778            case TR_ERROR:
    524                 tr_httpClose( http );
    525                 return 1;
     779                goto scrapeDone;
    526780
    527781            case TR_OK:
    528                 if( NULL == data || 0 >= datalen )
    529                 {
    530                     tr_httpClose( http );
    531                     return 1;
    532                 }
    533                 break;
    534         }
    535         tr_wait( 10 );
    536     }
    537 
    538     code = tr_httpResponseCode( data, datalen );
    539     if( !TR_HTTP_STATUS_OK( code ) )
    540     {
    541         tr_httpClose( http );
    542         return 1;
    543     }
    544 
    545     body = tr_httpParse( data, datalen , NULL );
    546     if( NULL == body )
    547     {
    548         tr_httpClose( http );
    549         return 1;
    550     }
    551     bodylen = datalen - ( body - data );
    552 
    553     for( ii = 0; ii < bodylen - 8; ii++ )
    554     {
    555         if( !memcmp( body + ii, "d5:files", 8 ) )
    556         {
    557             break;
    558         }
    559     }
    560     if( ii >= bodylen - 8 )
    561     {
    562         tr_httpClose( http );
    563         return 1;
    564     }
    565     if( tr_bencLoad( body + ii, bodylen - ii, &scrape, NULL ) )
    566     {
    567         tr_httpClose( http );
    568         return 1;
    569     }
    570 
    571     val1 = tr_bencDictFind( &scrape, "files" );
    572     if( !val1 )
    573     {
    574         tr_bencFree( &scrape );
    575         tr_httpClose( http );
    576         return 1;
    577     }
    578     val1 = &val1->val.l.vals[1];
    579     if( !val1 )
    580     {
    581         tr_bencFree( &scrape );
    582         tr_httpClose( http );
    583         return 1;
    584     }
    585     val2 = tr_bencDictFind( val1, "complete" );
    586     if( !val2 )
    587     {
    588         tr_bencFree( &scrape );
    589         tr_httpClose( http );
    590         return 1;
    591     }
    592     *seeders = val2->val.i;
    593     val2 = tr_bencDictFind( val1, "incomplete" );
    594     if( !val2 )
    595     {
    596         tr_bencFree( &scrape );
    597         tr_httpClose( http );
    598         return 1;
    599     }
    600     *leechers = val2->val.i;
    601     tr_bencFree( &scrape );
     782                readScrapeAnswer( tc, data, len );
     783                goto scrapeDone;
     784        }
     785    }
     786
     787scrapeDone:
    602788    tr_httpClose( http );
    603789
    604     return 0;
    605 }
    606 
    607 int tr_trackerSeeders( tr_tracker_t * tc )
    608 {
    609     if( !tc )
    610     {
    611         return -1;
    612     }
    613     return tc->seeders;
    614 }
    615 
    616 int tr_trackerLeechers( tr_tracker_t * tc )
    617 {
    618     if( !tc )
    619     {
    620         return -1;
    621     }
    622     return tc->leechers;
    623 }
     790    ret = 1;
     791    if( tc->seeders > -1 && tc->leechers > -1 && tc->complete > -1 )
     792    {
     793        *s = tc->seeders;
     794        *l = tc->leechers;
     795        *d = tc->complete;
     796        ret = 0;
     797    }
     798
     799    tr_trackerClose( tc );
     800    return ret;
     801}
     802
     803static void killHttp( tr_http_t ** http, tr_fd_t * fdlimit )
     804{
     805    if( NULL != *http )
     806    {
     807        tr_httpClose( *http );
     808        tr_fdSocketClosed( fdlimit, 1 );
     809        *http = NULL;
     810    }
     811}
Note: See TracChangeset for help on using the changeset viewer.