Changeset 12539


Ignore:
Timestamp:
Jul 10, 2011, 3:24:51 PM (10 years ago)
Author:
jordan
Message:

(trunk libT) #4338 "improved webseed support" -- patch by alexat

Location:
trunk/libtransmission
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/peer-mgr.c

    r12531 r12539  
    13051305                           int                    numwant,
    13061306                           tr_block_index_t     * setme,
    1307                            int                  * numgot )
     1307                           int                  * numgot,
     1308                           bool                   get_intervals )
    13081309{
    13091310    int i;
     
    13491350            tr_torGetPieceBlockRange( tor, p->index, &first, &last );
    13501351
    1351             for( b=first; b<=last && got<numwant; ++b )
     1352            for( b=first; b<=last && (got<numwant || (get_intervals && setme[2*got-1] == b-1)); ++b )
    13521353            {
    13531354                int peerCount;
     
    13841385
    13851386                /* update the caller's table */
    1386                 setme[got++] = b;
     1387                if( !get_intervals ) {
     1388                    setme[got++] = b;
     1389                }
     1390                /* if intervals are requested two array entries are necessarry:
     1391                   one for the interval's starting block and one for its end block */
     1392                else if( got && setme[2 * got - 1] == b - 1 && b != first ) {
     1393                    /* expand the last interval */
     1394                    ++setme[2 * got - 1];
     1395                }
     1396                else {
     1397                    /* begin a new interval */
     1398                    setme[2 * got] = setme[2 * got + 1] = b;
     1399                    ++got;
     1400                }
    13871401
    13881402                /* update our own tables */
  • trunk/libtransmission/peer-mgr.h

    r12254 r12539  
    164164                                int                   numwant,
    165165                                tr_block_index_t    * setme,
    166                                 int                 * numgot );
     166                                int                 * numgot,
     167                                bool                  get_intervals );
    167168
    168169bool tr_peerMgrDidPeerRequest( const tr_torrent  * torrent,
  • trunk/libtransmission/peer-msgs.c

    r12428 r12539  
    17501750        tr_block_index_t * blocks = alloca( sizeof( tr_block_index_t ) * numwant );
    17511751
    1752         tr_peerMgrGetNextRequests( msgs->torrent, msgs->peer, numwant, blocks, &n );
     1752        tr_peerMgrGetNextRequests( msgs->torrent, msgs->peer, numwant, blocks, &n, false );
    17531753
    17541754        for( i=0; i<n; ++i )
  • trunk/libtransmission/web.c

    r12506 r12539  
    7474    tr_web_done_func * done_func;
    7575    void * done_func_user_data;
     76    CURL * curl_easy;
    7677    struct tr_web_task * next;
    7778};
     
    156157    bool is_default_value;
    157158    const tr_address * addr;
    158     CURL * e = curl_easy_init( );
     159    CURL * e = task->curl_easy = curl_easy_init( );
    159160
    160161    task->timeout_secs = getTimeoutFromURL( task );
     
    220221****/
    221222
    222 void
     223struct tr_web_task *
    223224tr_webRun( tr_session         * session,
    224225           const char         * url,
     
    228229           void               * done_func_user_data )
    229230{
    230     tr_webRunWithBuffer( session, url, range, cookies,
    231                          done_func, done_func_user_data,
    232                          NULL );
    233 }
    234 
    235 void
     231    return tr_webRunWithBuffer( session, url, range, cookies,
     232                                done_func, done_func_user_data,
     233                                NULL );
     234}
     235
     236struct tr_web_task *
    236237tr_webRunWithBuffer( tr_session         * session,
    237238                     const char         * url,
     
    261262        web->tasks = task;
    262263        tr_lockUnlock( web->taskLock );
    263     }
     264        return task;
     265    }
     266    return NULL;
    264267}
    265268
     
    441444                tr_wait_msec( 100 );
    442445    }
     446}
     447
     448void
     449tr_webGetTaskInfo( struct tr_web_task * task, tr_web_task_info info, void * dst )
     450{
     451    curl_easy_getinfo( task->curl_easy, (CURLINFO) info, dst );
    443452}
    444453
  • trunk/libtransmission/web.h

    r12461 r12539  
    1414#define TR_HTTP_H
    1515
     16#include <curl/curl.h>
     17
    1618#ifdef __cplusplus
    1719extern "C" {
     
    1921
    2022struct tr_address;
     23struct tr_web_task;
     24
     25typedef enum
     26{
     27    TR_WEB_GET_CODE       = CURLINFO_RESPONSE_CODE,
     28    TR_WEB_GET_REDIRECTS  = CURLINFO_REDIRECT_COUNT,
     29    TR_WEB_GET_REAL_URL   = CURLINFO_EFFECTIVE_URL
     30}
     31tr_web_task_info;
    2132
    2233void tr_webInit( tr_session * session );
     
    4152const char * tr_webGetResponseStr( long response_code );
    4253
    43 void tr_webRun( tr_session        * session,
    44                 const char        * url,
    45                 const char        * range,
    46                 const char        * cookies,
    47                 tr_web_done_func    done_func,
    48                 void              * done_func_user_data );
     54struct tr_web_task * tr_webRun( tr_session        * session,
     55                                const char        * url,
     56                                const char        * range,
     57                                const char        * cookies,
     58                                tr_web_done_func    done_func,
     59                                void              * done_func_user_data );
    4960
    5061struct evbuffer;
    5162
    52 void tr_webRunWithBuffer( tr_session         * session,
    53                           const char         * url,
    54                           const char         * range,
    55                           const char         * cookies,
    56                           tr_web_done_func     done_func,
    57                           void               * done_func_user_data,
    58                           struct evbuffer    * buffer );
     63struct tr_web_task * tr_webRunWithBuffer( tr_session         * session,
     64                                          const char         * url,
     65                                          const char         * range,
     66                                          const char         * cookies,
     67                                          tr_web_done_func     done_func,
     68                                          void               * done_func_user_data,
     69                                          struct evbuffer    * buffer );
     70
     71void tr_webGetTaskInfo( struct tr_web_task * task, tr_web_task_info info, void * dst );
    5972
    6073void tr_http_escape( struct evbuffer *out, const char *str, int len, bool escape_slashes );
  • trunk/libtransmission/webseed.c

    r12471 r12539  
    2323#include "peer-mgr.h"
    2424#include "torrent.h"
     25#include "trevent.h" /* tr_runInEventThread() */
    2526#include "utils.h"
    2627#include "web.h"
     
    3637    uint32_t             piece_offset;
    3738    uint32_t             length;
     39    tr_block_index_t     blocks_done;
     40    uint32_t             block_size;
     41    struct tr_web_task * web_task;
     42    long                 response_code;
    3843    int                  torrent_id;
    3944};
     
    5358    bool                 is_stopping;
    5459    int                  consecutive_failures;
     60    int                  retry_tickcount;
     61    int                  retry_challenge;
     62    int                  idle_connections;
     63    int                  active_transfers;
     64    char              ** file_urls;
    5565};
    5666
     67struct tr_blockwrite_info
     68{
     69    struct tr_webseed  * webseed;
     70    struct evbuffer    * data;
     71    tr_piece_index_t     piece_index;
     72    tr_block_index_t     block_index;
     73    tr_block_index_t     count;
     74    uint32_t             block_offset;
     75};
     76
     77struct tr_http_info
     78{
     79    struct tr_webseed  * webseed;
     80    char               * redirect_url;
     81    tr_piece_index_t     piece_index;
     82    uint32_t             piece_offset;
     83};
     84
    5785enum
    5886{
    5987    TR_IDLE_TIMER_MSEC = 2000,
    6088
    61     MAX_CONSECUIVE_FAILURES = 5
     89    FAILURE_RETRY_INTERVAL = 150,
     90
     91    MAX_CONSECUTIVE_FAILURES = 5,
     92
     93    MAX_WEBSEED_CONNECTIONS = 4
    6294};
    6395
     
    6698{
    6799    tr_torrent * tor = tr_torrentFindFromId( w->session, w->torrent_id );
     100    const tr_info * inf = tr_torrentInfo( tor );
     101    tr_file_index_t i;
     102
     103    for( i = 0; i < inf->fileCount; i++ ) {
     104        if( w->file_urls[i] )
     105            tr_free( w->file_urls[i] );
     106    }
     107    tr_free( w->file_urls );
    68108
    69109    /* webseed destruct */
     
    90130
    91131static void
    92 fire_client_got_rej( tr_torrent * tor, tr_webseed * w, tr_block_index_t block )
    93 {
     132fire_client_got_rejs( tr_torrent * tor, tr_webseed * w,
     133                      tr_block_index_t block, tr_block_index_t count )
     134{
     135    tr_block_index_t i;
    94136    tr_peer_event e = TR_PEER_EVENT_INIT;
    95137    e.eventType = TR_PEER_CLIENT_GOT_REJ;
    96138    tr_torrentGetBlockLocation( tor, block, &e.pieceIndex, &e.offset, &e.length );
    97     publish( w, &e );
    98 }
    99 
    100 static void
    101 fire_client_got_block( tr_torrent * tor, tr_webseed * w, tr_block_index_t block )
    102 {
     139    for( i = 1; i <= count; i++ ) {
     140        if( i == count )
     141            e.length = tr_torBlockCountBytes( tor, block + count - 1 );
     142        publish( w, &e );
     143        e.offset += e.length;
     144    }
     145}
     146
     147static void
     148fire_client_got_blocks( tr_torrent * tor, tr_webseed * w,
     149                        tr_block_index_t block, tr_block_index_t count )
     150{
     151    tr_block_index_t i;
    103152    tr_peer_event e = TR_PEER_EVENT_INIT;
    104153    e.eventType = TR_PEER_CLIENT_GOT_BLOCK;
    105154    tr_torrentGetBlockLocation( tor, block, &e.pieceIndex, &e.offset, &e.length );
    106     publish( w, &e );
     155    for( i = 1; i <= count; i++ ) {
     156        if( i == count )
     157            e.length = tr_torBlockCountBytes( tor, block + count - 1 );
     158        publish( w, &e );
     159        e.offset += e.length;
     160    }
    107161}
    108162
     
    122176
    123177static void
    124 on_content_changed( struct evbuffer                * buf UNUSED,
     178write_block_func( void * vblock )
     179{
     180    struct tr_blockwrite_info * block = vblock;
     181    struct tr_webseed * w = block->webseed;
     182    struct evbuffer * buf = block->data;
     183    struct tr_torrent * tor;
     184
     185    tor = tr_torrentFindFromId( w->session, w->torrent_id );
     186    if( tor )
     187    {
     188        const uint32_t block_size = tor->blockSize;
     189        uint32_t len = evbuffer_get_length( buf );
     190        uint32_t offset_end = block->block_offset + len;
     191        tr_cache * cache = w->session->cache;
     192        tr_piece_index_t piece = block->piece_index;
     193
     194        while( true )
     195        {
     196            if( len > block_size) {
     197                tr_cacheWriteBlock( cache, tor, piece, offset_end - len,
     198                                    block_size, buf );
     199                len -= block_size;
     200            }
     201            else {
     202                tr_cacheWriteBlock( cache, tor, piece, offset_end - len,
     203                                    len, buf );
     204                break;
     205            }
     206        }
     207        fire_client_got_blocks( tor, w, block->block_index, block->count );
     208    }
     209
     210    evbuffer_free( buf );
     211    tr_free( block );
     212}
     213
     214static void
     215connection_succeeded( void * vinf )
     216{
     217    struct tr_http_info * inf = vinf;
     218    struct tr_webseed * w = inf->webseed;
     219    struct tr_torrent * tor;
     220
     221    if( ++w->active_transfers >= w->retry_challenge && w->retry_challenge )
     222        /* the server seems to be accepting more connections now */
     223        w->consecutive_failures = w->retry_tickcount = w->retry_challenge = 0;
     224
     225    if( inf->redirect_url &&
     226        (tor = tr_torrentFindFromId( w->session, w->torrent_id )))
     227    {
     228        uint64_t file_offset;
     229        tr_file_index_t file_index;
     230
     231        tr_ioFindFileLocation( tor, inf->piece_index, inf->piece_offset,
     232                               &file_index, &file_offset );
     233        tr_free( w->file_urls[file_index] );
     234        w->file_urls[file_index] = inf->redirect_url;
     235    }
     236}
     237
     238static void
     239on_content_changed( struct evbuffer                * buf,
    125240                    const struct evbuffer_cb_info  * info,
    126                     void                           * vw )
    127 {
    128     tr_webseed * w = vw;
    129 
    130     if( ( info->n_added > 0 ) && !w->is_stopping )
     241                    void                           * vtask )
     242{
     243    struct tr_webseed_task * task = vtask;
     244    struct tr_webseed * w = task->webseed;
     245    uint32_t len;
     246
     247    if( info->n_added <= 0 )
     248        return;
     249
     250    if( !w->is_stopping )
    131251    {
    132252        tr_bandwidthUsed( &w->bandwidth, TR_DOWN, info->n_added, true, tr_time_msec( ) );
    133253        fire_client_got_data( w, info->n_added );
    134254    }
     255
     256    if( !task->response_code ) {
     257        tr_webGetTaskInfo( task->web_task, TR_WEB_GET_CODE, &task->response_code );
     258
     259        if( task->response_code == 206 ) {
     260            struct tr_http_info * inf = tr_new( struct tr_http_info, 1 );
     261            long redirects;
     262
     263            inf->webseed = w;
     264            inf->piece_index = task->piece_index;
     265            inf->piece_offset = task->piece_offset;
     266            tr_webGetTaskInfo( task->web_task, TR_WEB_GET_REDIRECTS, &redirects );
     267            if( redirects ) {
     268                char * redirect_url;
     269                tr_webGetTaskInfo( task->web_task, TR_WEB_GET_REAL_URL, &redirect_url );
     270                inf->redirect_url = tr_strdup( redirect_url );
     271            }
     272            else
     273                inf->redirect_url = NULL;
     274            /* run this in the webseed thread to avoid tampering with mutexes and to
     275            not cost the web thrad too much time */
     276            tr_runInEventThread( task->session, connection_succeeded, inf );
     277        }
     278    }
     279
     280    len = evbuffer_get_length( buf );
     281
     282    if( task->response_code == 206 && len >= task->block_size )
     283    {
     284        /* one (ore more) block(s) received. write to hd */
     285        const uint32_t block_size = task->block_size;
     286        const tr_block_index_t completed = len / block_size;
     287        struct tr_blockwrite_info * b = tr_new( struct tr_blockwrite_info, 1 );
     288
     289        b->webseed = task->webseed;
     290        b->piece_index = task->piece_index;
     291        b->block_index = task->block + task->blocks_done;
     292        b->count = completed;
     293        b->block_offset = task->piece_offset + task->blocks_done * block_size;
     294        b->data = evbuffer_new( );
     295
     296        /* we don't use locking on this evbuffer so we must copy out the data
     297        that will be needed when writing the block in a different thread */
     298        evbuffer_remove_buffer( task->content, b->data,
     299                                block_size * completed );
     300
     301        tr_runInEventThread( task->session, write_block_func, b );
     302        task->blocks_done += completed;
     303    }
    135304}
    136305
     
    148317{
    149318    tr_torrent * tor = tr_torrentFindFromId( w->session, w->torrent_id );
     319    int want, running_tasks = tr_list_size( w->tasks );
     320
     321    if( w->consecutive_failures >= MAX_CONSECUTIVE_FAILURES ) {
     322        want = w->idle_connections;
     323
     324        if( w->retry_tickcount >= FAILURE_RETRY_INTERVAL ) {
     325            /* some time has passed since our connection attempts failed. try again */
     326            ++want;
     327            /* if this challenge is fulfilled we will reset consecutive_failures */
     328            w->retry_challenge = running_tasks + want;
     329        }
     330    }
     331    else {
     332        want = MAX_WEBSEED_CONNECTIONS - running_tasks;
     333        w->retry_challenge = running_tasks + w->idle_connections + 1;
     334    }
    150335
    151336    if( w->is_stopping && !webseed_has_tasks( w ) )
     
    156341                             && tor->isRunning
    157342                             && !tr_torrentIsSeed( tor )
    158                              && ( w->consecutive_failures < MAX_CONSECUIVE_FAILURES ) )
     343                             && want )
    159344    {
    160345        int i;
    161346        int got = 0;
    162         const int max = tor->blockCountInPiece;
    163         const int want = max - tr_list_size( w->tasks );
    164347        tr_block_index_t * blocks = NULL;
    165348
    166         if( want > 0 )
    167         {
    168             blocks = tr_new( tr_block_index_t, want );
    169             tr_peerMgrGetNextRequests( tor, &w->parent, want, blocks, &got );
    170         }
     349        blocks = tr_new( tr_block_index_t, want*2 );
     350        tr_peerMgrGetNextRequests( tor, &w->parent, want, blocks, &got, true );
     351
     352        w->idle_connections -= MIN( w->idle_connections, got );
     353        if( w->retry_tickcount >= FAILURE_RETRY_INTERVAL && got == want )
     354            w->retry_tickcount = 0;
    171355
    172356        for( i=0; i<got; ++i )
    173357        {
    174             const tr_block_index_t b = blocks[i];
     358            const tr_block_index_t b = blocks[i*2];
     359            const tr_block_index_t be = blocks[i*2+1];
    175360            struct tr_webseed_task * task = tr_new( struct tr_webseed_task, 1 );
    176361            task->webseed = w;
     
    181366            task->piece_offset = ( tor->blockSize * b )
    182367                                - ( tor->info.pieceSize * task->piece_index );
    183             task->length = tr_torBlockCountBytes( tor, b );
     368            task->length = (be - b) * tor->blockSize + tr_torBlockCountBytes( tor, be );
     369            task->blocks_done = 0;
     370            task->response_code = 0;
     371            task->block_size = tor->blockSize;
    184372            task->content = evbuffer_new( );
    185             evbuffer_add_cb( task->content, on_content_changed, w );
     373            evbuffer_add_cb( task->content, on_content_changed, task );
    186374            tr_list_append( &w->tasks, task );
    187375            task_request_next_chunk( task );
     
    207395    const int success = ( response_code == 206 );
    208396
    209     if( success )
    210         w->consecutive_failures = 0;
    211     else
    212         ++w->consecutive_failures;
    213 
    214397    if( tor )
    215398    {
     399        /* active_transfers was only increased if the connection was successful */
     400        if( t->response_code == 206 )
     401            --w->active_transfers;
     402
    216403        if( !success )
    217404        {
    218             fire_client_got_rej( tor, w, t->block );
     405            const tr_block_index_t blocks_remain = (t->length + tor->blockSize - 1)
     406                                                   / tor->blockSize - t->blocks_done;
     407
     408            if( blocks_remain )
     409                fire_client_got_rejs( tor, w, t->block + t->blocks_done, blocks_remain );
     410
     411            if( t->blocks_done )
     412                ++w->idle_connections;
     413            else if( ++w->consecutive_failures >= MAX_CONSECUTIVE_FAILURES && !w->retry_tickcount )
     414                /* now wait a while until retrying to establish a connection */
     415                ++w->retry_tickcount;
    219416
    220417            tr_list_remove_data( &w->tasks, t );
     
    224421        else
    225422        {
    226             if( evbuffer_get_length( t->content ) < t->length )
     423            const uint32_t bytes_done = t->blocks_done * tor->blockSize;
     424            const uint32_t buf_len = evbuffer_get_length( t->content );
     425
     426            if( bytes_done + buf_len < t->length )
    227427            {
     428                /* request finished successfully but there's still data missing. that
     429                means we've reached the end of a file and need to request the next one */
     430                t->response_code = 0;
    228431                task_request_next_chunk( t );
    229432            }
    230433            else
    231434            {
    232                 tr_cacheWriteBlock( session->cache, tor,
    233                                     t->piece_index, t->piece_offset, t->length,
    234                                     t->content );
    235                 fire_client_got_block( tor, w, t->block );
     435                if( buf_len ) {
     436                    /* on_content_changed() will not write a block if it is smaller than
     437                    the torrent's block size, i.e. the torrent's very last block */
     438                    tr_cacheWriteBlock( session->cache, tor,
     439                                        t->piece_index, t->piece_offset + bytes_done,
     440                                        buf_len, t->content );
     441
     442                    fire_client_got_blocks( tor, t->webseed,
     443                                            t->block + t->blocks_done, 1 );
     444                }
     445
     446                ++w->idle_connections;
    236447
    237448                tr_list_remove_data( &w->tasks, t );
     
    251462
    252463    evbuffer_add( buf, w->base_url, w->base_url_len );
    253     evbuffer_add( buf, "", 1 );
    254464
    255465    /* if url ends with a '/', add the torrent name */
     
    267477    {
    268478        char range[64];
    269         struct evbuffer * url;
     479        char ** urls = t->webseed->file_urls;
    270480
    271481        const tr_info * inf = tr_torrentInfo( tor );
    272         const uint32_t remain = t->length - evbuffer_get_length( t->content );
     482        const uint32_t remain = t->length - t->blocks_done * tor->blockSize
     483                                - evbuffer_get_length( t->content );
    273484
    274485        const uint64_t total_offset = inf->pieceSize * t->piece_index
    275                                     + t->piece_offset
    276                                     + evbuffer_get_length( t->content );
     486                                    + t->piece_offset + t->length - remain;
    277487        const tr_piece_index_t step_piece = total_offset / inf->pieceSize;
    278488        const uint32_t step_piece_offset
     
    289499        this_pass = MIN( remain, file->length - file_offset );
    290500
    291         url = make_url( t->webseed, file );
     501        if( !urls[file_index] )
     502            urls[file_index] = evbuffer_free_to_str( make_url( t->webseed, file ) );
     503
    292504        tr_snprintf( range, sizeof range, "%"PRIu64"-%"PRIu64,
    293505                     file_offset, file_offset + this_pass - 1 );
    294         tr_webRunWithBuffer( t->session, (char *) evbuffer_pullup( url, -1 ),
    295                              range, NULL, web_response_func, t, t->content );
    296         evbuffer_free( url );
     506        t->web_task = tr_webRunWithBuffer( t->session, urls[file_index],
     507                                           range, NULL, web_response_func, t, t->content );
    297508    }
    298509}
     
    321532{
    322533    tr_webseed * w = vw;
     534    if( w->retry_tickcount )
     535        ++w->retry_tickcount;
    323536    on_idle( w );
    324537    tr_timerAddMsec( w->timer, TR_IDLE_TIMER_MSEC );
     
    333546    tr_webseed * w = tr_new0( tr_webseed, 1 );
    334547    tr_peer * peer = &w->parent;
     548    const tr_info * inf = tr_torrentInfo( tor );
    335549
    336550    /* construct parent class */
     
    348562    w->callback = callback;
    349563    w->callback_data = callback_data;
     564    w->file_urls = tr_new0( char *, inf->fileCount );
    350565    //tr_rcConstruct( &w->download_rate );
    351566    tr_bandwidthConstruct( &w->bandwidth, tor->session, &tor->bandwidth );
Note: See TracChangeset for help on using the changeset viewer.