Changeset 10303


Ignore:
Timestamp:
Mar 6, 2010, 3:05:05 PM (13 years ago)
Author:
charles
Message:

(trunk libT) #2987 "push libcurl into its own worker thread" -- committed to trunk

Location:
trunk/libtransmission
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/session.c

    r10276 r10303  
    614614    tr_statsInit( session );
    615615
    616     session->web = tr_webInit( session );
     616    tr_webInit( session );
    617617
    618618    tr_sessionSet( session, &settings );
     
    714714    b.socket = -1;
    715715    session->public_ipv4 = tr_memdup( &b, sizeof( struct tr_bindinfo ) );
    716     tr_webSetInterface( session->web, &session->public_ipv4->addr );
     716    tr_webSetInterface( session, &session->public_ipv4->addr );
    717717
    718718    str = TR_PREFS_KEY_BIND_ADDRESS_IPV6;
     
    14571457tr_sessionGetRawSpeed( const tr_session * session, tr_direction dir )
    14581458{
    1459     return tr_isSession( session ) ? tr_bandwidthGetPieceSpeed( session->bandwidth, 0, dir ) : 0.0;
     1459    return tr_isSession( session ) ? tr_bandwidthGetRawSpeed( session->bandwidth, 0, dir ) : 0.0;
    14601460}
    14611461
     
    15251525    tr_statsClose( session );
    15261526    tr_peerMgrFree( session->peerMgr );
     1527    tr_webClose( session, TR_WEB_CLOSE_WHEN_IDLE );
    15271528
    15281529    closeBlocklists( session );
    1529     tr_webClose( &session->web );
    15301530
    15311531    tr_fdClose( session );
     
    15631563     * for a bit while they tell the router & tracker
    15641564     * that we're closing now */
    1565     while( ( session->shared
    1566            || session->announcer ) && !deadlineReached( deadline ) )
     1565    while( ( session->shared || session->web || session->announcer )
     1566           && !deadlineReached( deadline ) )
    15671567    {
    15681568        dbgmsg( "waiting on port unmap (%p) or announcer (%p)",
     
    15701570        tr_wait_msec( 100 );
    15711571    }
     1572
     1573    tr_webClose( session, TR_WEB_CLOSE_NOW );
    15721574
    15731575    /* close the libtransmission thread */
  • trunk/libtransmission/web.c

    r10298 r10303  
    1111 */
    1212
    13 #include <assert.h>
     13#include <sys/select.h>
    1414
    1515#include <curl/curl.h>
    1616#include <event.h>
    17 #include <evdns.h>
    1817
    1918#include "transmission.h"
    20 #include "net.h"
    21 #include "ptrarray.h"
     19#include "list.h"
     20#include "net.h" /* tr_address */
     21#include "platform.h" /* mutex */
    2222#include "session.h"
    23 #include "trevent.h"
     23#include "trevent.h" /* tr_runInEventThread() */
    2424#include "utils.h"
    25 #include "version.h"
     25#include "version.h" /* User-Agent */
    2626#include "web.h"
    2727
    2828enum
    2929{
    30     TR_MEMORY_TRASH = 0xCC,
    31 
    32     DEFAULT_TIMER_MSEC = 250, /* arbitrary */
    33 
    34     DNS_CACHE_FAIL_TTL = 120 /* seconds */
     30    THREADFUNC_MAX_SLEEP_MSEC = 1000,
    3531};
    3632
     
    5551struct tr_web
    5652{
    57     tr_bool closing;
     53    int close_mode;
    5854    tr_bool haveAddr;
    59     int taskCount;
    60     long timer_msec;
    61     CURLM * multi;
    62     tr_session * session;
     55    tr_list * tasks;
     56    tr_lock * taskLock;
    6357    tr_address addr;
    64     tr_ptrArray dns_cache;
    65     struct event timer_event;
    6658};
    6759
    68 struct dns_cache_item;
    69 static void dns_cache_item_free( struct dns_cache_item * );
    70 
    71 static void
    72 web_free( tr_web * g )
    73 {
    74     evdns_shutdown( TRUE );
    75     curl_multi_cleanup( g->multi );
    76     evtimer_del( &g->timer_event );
    77     tr_ptrArrayDestruct( &g->dns_cache, (PtrArrayForeachFunc)dns_cache_item_free );
    78     memset( g, TR_MEMORY_TRASH, sizeof( struct tr_web ) );
    79     tr_free( g );
    80 }
    8160
    8261/***
     
    8665struct tr_web_task
    8766{
    88     int port;
    89     unsigned long tag;
    90     struct curl_slist * slist;
     67    long code;
    9168    struct evbuffer * response;
    9269    char * url;
    93     char * resolved_url;
    94     char * host;
    95     const char * resolved_host;
    9670    char * range;
    9771    tr_session * session;
    9872    tr_web_done_func * done_func;
    9973    void * done_func_user_data;
    100     struct event timer_event;
    101     CURL * easy;
    102     CURLM * multi;
    103     tr_bool timer_event_isSet;
    10474};
    10575
     
    10777task_free( struct tr_web_task * task )
    10878{
    109     if( task->slist != NULL )
    110         curl_slist_free_all( task->slist );
    111     if( task->timer_event_isSet )
    112         evtimer_del( &task->timer_event );
    11379    evbuffer_free( task->response );
    114     tr_free( task->host );
    11580    tr_free( task->range );
    116     tr_free( task->resolved_url );
    11781    tr_free( task->url );
    118     memset( task, TR_MEMORY_TRASH, sizeof( struct tr_web_task ) );
    11982    tr_free( task );
    120 }
    121 
    122 /***
    123 ****
    124 ***/
    125 
    126 struct dns_cache_item
    127 {
    128     char * host;
    129     char * resolved_host;
    130     time_t expiration;
    131     tr_bool success;
    132 };
    133 
    134 static void
    135 dns_cache_item_free( struct dns_cache_item * item )
    136 {
    137     if( item != NULL )
    138     {
    139         tr_free( item->host );
    140         tr_free( item->resolved_host );
    141         memset( item, TR_MEMORY_TRASH, sizeof( struct dns_cache_item ) );
    142         tr_free( item );
    143     }
    144 }
    145 
    146 static int
    147 dns_cache_compare( const void * va, const void * vb )
    148 {
    149     const struct dns_cache_item * a = va;
    150     const struct dns_cache_item * b = vb;
    151     return strcmp( a->host, b->host );
    152 }
    153 
    154 typedef enum
    155 {
    156     TR_DNS_OK,
    157     TR_DNS_FAIL,
    158     TR_DNS_UNTESTED
    159 }
    160 tr_dns_result;
    161 
    162 static void
    163 dns_cache_clear_entry( struct tr_ptrArray * cache, const char * host )
    164 {
    165     struct dns_cache_item key;
    166     key.host = (char*) host;
    167     dns_cache_item_free( tr_ptrArrayRemoveSorted( cache, &key, dns_cache_compare ) );
    168 }
    169 
    170 static tr_dns_result
    171 dns_cache_lookup( struct tr_web_task * task, const char * host, const char ** resolved )
    172 {
    173     tr_dns_result result = TR_DNS_UNTESTED;
    174 
    175     if( task->session->web != NULL )
    176     {
    177         struct dns_cache_item key;
    178         struct dns_cache_item * item;
    179         tr_ptrArray * cache = &task->session->web->dns_cache;
    180 
    181         key.host = (char*) host;
    182         item = tr_ptrArrayFindSorted( cache, &key, dns_cache_compare );
    183 
    184         /* has the ttl expired? */
    185         if( ( item != NULL ) && ( item->expiration <= tr_time( ) ) )
    186         {
    187             dns_cache_clear_entry( cache, host );
    188             item = NULL;
    189         }
    190 
    191         if( item != NULL )
    192         {
    193             result = item->success ? TR_DNS_OK : TR_DNS_FAIL;
    194 
    195             if( result == TR_DNS_OK )
    196             {
    197                 *resolved = item->resolved_host;
    198            
    199                 dbgmsg( "found cached dns entry for \"%s\": %s", host, *resolved );
    200             }
    201         }
    202     }
    203 
    204     return result;
    205 }
    206 
    207 static void
    208 dns_cache_set_fail( struct tr_web_task * task, const char * host )
    209 {
    210     if( ( task->session->web != NULL ) && ( host != NULL ) )
    211     {
    212         struct dns_cache_item * item;
    213         tr_ptrArray * cache = &task->session->web->dns_cache;
    214 
    215         dns_cache_clear_entry( cache, host );
    216 
    217         item = tr_new( struct dns_cache_item, 1 );
    218         item->host = tr_strdup( host );
    219         item->resolved_host = NULL;
    220         item->expiration = tr_time( ) + DNS_CACHE_FAIL_TTL;
    221         item->success = FALSE;
    222         tr_ptrArrayInsertSorted( cache, item, dns_cache_compare );
    223     }
    224 }
    225 
    226 static const char*
    227 dns_cache_set_name( struct tr_web_task * task, const char * host,
    228                    const char * resolved, int ttl )
    229 {
    230     char * ret = NULL;
    231 
    232     if( task->session->web != NULL )
    233     {
    234         struct dns_cache_item * item;
    235         tr_ptrArray * cache = &task->session->web->dns_cache;
    236 
    237         dns_cache_clear_entry( cache, host );
    238 
    239         item = tr_new( struct dns_cache_item, 1 );
    240         item->host = tr_strdup( host );
    241         item->resolved_host = tr_strdup( resolved );
    242         item->expiration = tr_time( ) + ttl;
    243         item->success = TRUE;
    244         tr_ptrArrayInsertSorted( cache, item, dns_cache_compare );
    245         ret = item->resolved_host;
    246         dbgmsg( "adding dns cache entry for \"%s\": %s", host, resolved );
    247     }
    248     return ret;
    24983}
    25084
     
    291125}
    292126
    293 static int
     127static long
    294128getTimeoutFromURL( const char * url )
    295129{
    296     if( strstr( url, "scrape" ) != NULL ) return 30;
    297     if( strstr( url, "announce" ) != NULL ) return 90;
    298     return 240;
    299 }
    300 
    301 static void task_timeout_cb( int fd UNUSED, short what UNUSED, void * task );
    302 static void task_finish( struct tr_web_task * task, long response_code );
    303 
    304 static void
    305 addTask( void * vtask )
    306 {
    307     struct tr_web_task * task = vtask;
    308     const tr_session * session = task->session;
    309 
    310     if( ( session == NULL ) || ( session->web == NULL ) )
    311         return;
    312 
    313     if( !task->resolved_host )
    314     {
    315         dbgmsg( "couldn't resolve host for \"%s\"... task failed", task->url );
    316         task_finish( task, 0 );
    317     }
    318     else
    319     {
    320         CURL * e = curl_easy_init( );
    321         struct tr_web * web = session->web;
    322         const int timeout = getTimeoutFromURL( task->url );
    323         const long verbose = getenv( "TR_CURL_VERBOSE" ) != NULL;
    324         const char * user_agent = TR_NAME "/" SHORT_VERSION_STRING;
    325 
    326         /* insert the resolved host into the URL s.t. curl's DNS won't block
    327          * even if -- like on most OSes -- it wasn't built with C-Ares :(
    328          * "http://www.craptrackular.org/announce?key=val&key2=..." becomes
    329          * "http://127.0.0.1/announce?key=val&key2=..." */
    330         {
    331             char * host;
    332             struct evbuffer * buf = evbuffer_new( );
    333             char * pch = strstr( task->url, task->host );
    334             char * tail = pch + strlen( task->host );
    335             evbuffer_add( buf, task->url, pch - task->url );
    336             evbuffer_add_printf( buf, "%s", task->resolved_host );
    337             evbuffer_add_printf( buf, "%s", tail );
    338             task->resolved_url = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
    339             dbgmsg( "old url: \"%s\" -- new url: \"%s\"", task->url, task->resolved_url );
    340             evbuffer_free( buf );
    341 
    342             /* Manually add a Host: argument that refers to the true URL */
    343             if( ( ( task->port <= 0 ) ) ||
    344                 ( ( task->port == 80 ) && !strncmp( task->url, "http://", 7 ) ) ||
    345                 ( ( task->port == 443 ) && !strncmp( task->url, "https://", 8 ) ) )
    346                 host = tr_strdup_printf( "Host: %s", task->host );
    347             else
    348                 host = tr_strdup_printf( "Host: %s:%d", task->host, task->port );
    349 
    350             task->slist = curl_slist_append( NULL, host );
    351             task->slist = curl_slist_append( task->slist, "Accept:" );
    352             curl_easy_setopt( e, CURLOPT_HTTPHEADER, task->slist );
    353             tr_free( host );
    354         }
    355 
    356         dbgmsg( "adding task #%lu [%s]", task->tag, task->resolved_url ? task->resolved_url : task->url );
    357 
    358         if( !task->range && session->isProxyEnabled ) {
    359             curl_easy_setopt( e, CURLOPT_PROXY, session->proxy );
    360             curl_easy_setopt( e, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
    361             curl_easy_setopt( e, CURLOPT_PROXYPORT, session->proxyPort );
    362             curl_easy_setopt( e, CURLOPT_PROXYTYPE,
    363                                       getCurlProxyType( session->proxyType ) );
    364         }
    365         if( !task->range && session->isProxyAuthEnabled ) {
    366             char * str = tr_strdup_printf( "%s:%s", session->proxyUsername,
    367                                                     session->proxyPassword );
    368             curl_easy_setopt( e, CURLOPT_PROXYUSERPWD, str );
    369             tr_free( str );
    370         }
    371 
    372         task->easy = e;
    373         task->multi = web->multi;
    374 
    375         /* use our own timeout instead of CURLOPT_TIMEOUT because the latter
    376          * doesn't play nicely with curl_multi.  See curl bug #2501457 */
    377         task->timer_event_isSet = TRUE;
    378         evtimer_set( &task->timer_event, task_timeout_cb, task );
    379         tr_timerAdd( &task->timer_event, timeout, 0 );
    380 
    381         curl_easy_setopt( e, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
    382         curl_easy_setopt( e, CURLOPT_SOCKOPTFUNCTION, sockoptfunction );
    383         curl_easy_setopt( e, CURLOPT_SOCKOPTDATA, task );
    384         curl_easy_setopt( e, CURLOPT_WRITEDATA, task );
    385         curl_easy_setopt( e, CURLOPT_WRITEFUNCTION, writeFunc );
    386         curl_easy_setopt( e, CURLOPT_FOLLOWLOCATION, 1L );
    387         curl_easy_setopt( e, CURLOPT_AUTOREFERER, 1L );
    388         curl_easy_setopt( e, CURLOPT_FORBID_REUSE, 1L );
    389         curl_easy_setopt( e, CURLOPT_MAXREDIRS, -1L );
    390         curl_easy_setopt( e, CURLOPT_PRIVATE, task );
    391         curl_easy_setopt( e, CURLOPT_SSL_VERIFYHOST, 0L );
    392         curl_easy_setopt( e, CURLOPT_SSL_VERIFYPEER, 0L );
    393         curl_easy_setopt( e, CURLOPT_URL, task->resolved_url ? task->resolved_url : task->url );
    394         curl_easy_setopt( e, CURLOPT_USERAGENT, user_agent );
    395         curl_easy_setopt( e, CURLOPT_VERBOSE, verbose );
    396         if( web->haveAddr )
    397             curl_easy_setopt( e, CURLOPT_INTERFACE, tr_ntop_non_ts( &web->addr ) );
    398         if( task->range )
    399             curl_easy_setopt( e, CURLOPT_RANGE, task->range );
    400 
    401         if( curl_multi_add_handle( web->multi, e ) == CURLM_OK )
    402             ++web->taskCount;
    403     }
    404 }
    405 
    406 static void
    407 dns_ipv6_done_cb( int err, char type, int count, int ttl, void * addresses, void * vtask )
    408 {
    409     struct tr_web_task * task = vtask;
    410 
    411     if( !err && task->host && ( count>0 ) && ( ttl>=0 ) && ( type==DNS_IPv6_AAAA ) )
    412     {
    413         int i;
    414         char buf[INET6_ADDRSTRLEN+1];
    415         struct in6_addr *in6_addrs = addresses;
    416 
    417         for( i=0; i<count; ++i ) {
    418             const char * b = inet_ntop(AF_INET6, &in6_addrs[i], buf,sizeof(buf));
    419             if( b != NULL ) {
    420                 /* FIXME: is there a better way to tell which one to use if count > 1? */
    421                 task->resolved_host = dns_cache_set_name( task, task->host, b, ttl );
    422                 break;
    423             }
    424         }
    425     }
    426 
    427     if( task->resolved_host == NULL )
    428         dns_cache_set_fail( task, task->host );
    429 
    430     addTask( task );
    431 }
    432 
    433 static void
    434 dns_ipv4_done_cb( int err, char type, int count, int ttl, void * addresses, void * vtask )
    435 {
    436     struct tr_web_task * task = vtask;
    437 
    438     if( !err && task->host && ( count>0 ) && ( ttl>=0 ) && ( type==DNS_IPv4_A ) )
    439     {
    440         struct in_addr * in_addrs = addresses;
    441         const char * resolved = inet_ntoa( in_addrs[0] );
    442         task->resolved_host = dns_cache_set_name( task, task->host, resolved, ttl );
    443         /* FIXME: if count > 1, is there a way to decide which is best to use? */
    444     }
    445 
    446     if( ( task->resolved_host != NULL )
    447             || ( task->host == NULL )
    448             || evdns_resolve_ipv6( task->host, 0, dns_ipv6_done_cb, task ) )
    449         dns_ipv6_done_cb( DNS_ERR_UNKNOWN, DNS_IPv6_AAAA, 0, 0, NULL, task );
    450 }
    451 
    452 static void
    453 doDNS( void * vtask )
    454 {
    455     tr_address addr;
    456     int port = -1;
    457     char * host = NULL;
    458     struct tr_web_task * task = vtask;
    459     tr_dns_result lookup_result = TR_DNS_UNTESTED;
    460 
    461     assert( task->resolved_host == NULL );
    462 
    463     if( !tr_urlParse( task->url, -1, NULL, &host, &port, NULL ) )
    464     {
    465         task->port = port;
    466         task->host = host;
    467 
    468         /* If 'host' is an IPv4 or IPv6 address in text form, use it as-is.
    469          * Otherwise, see if its resolved name is in our DNS cache */
    470         if( tr_pton( task->host, &addr ) != NULL )
    471         {
    472             task->resolved_host = task->host;
    473             lookup_result = TR_DNS_OK;
    474         }
    475         else
    476         {
    477             lookup_result = dns_cache_lookup( task, host, &task->resolved_host );
    478         }
    479     }
    480 
    481     if( lookup_result != TR_DNS_UNTESTED )
    482     {
    483         addTask( task );
    484     }
    485     else if( !host || evdns_resolve_ipv4( host, 0, dns_ipv4_done_cb, task ) )
    486     {
    487         dns_ipv4_done_cb( DNS_ERR_UNKNOWN, DNS_IPv4_A, 0, 0, NULL, task );
    488     }
     130    if( strstr( url, "scrape" ) != NULL ) return 30L;
     131    if( strstr( url, "announce" ) != NULL ) return 90L;
     132    return 240L;
     133}
     134
     135static CURL *
     136createEasy( tr_session * s, struct tr_web * w, struct tr_web_task * task )
     137{
     138    CURL * e = curl_easy_init( );
     139    const long verbose = getenv( "TR_CURL_VERBOSE" ) != NULL;
     140
     141    if( !task->range && s->isProxyEnabled ) {
     142        const long proxyType = getCurlProxyType( s->proxyType );
     143        curl_easy_setopt( e, CURLOPT_PROXY, s->proxy );
     144        curl_easy_setopt( e, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
     145        curl_easy_setopt( e, CURLOPT_PROXYPORT, s->proxyPort );
     146        curl_easy_setopt( e, CURLOPT_PROXYTYPE, proxyType );
     147    }
     148
     149    if( !task->range && s->isProxyAuthEnabled ) {
     150        char * str = tr_strdup_printf( "%s:%s", s->proxyUsername,
     151                                                s->proxyPassword );
     152        curl_easy_setopt( e, CURLOPT_PROXYUSERPWD, str );
     153        tr_free( str );
     154    }
     155
     156    curl_easy_setopt( e, CURLOPT_AUTOREFERER, 1L );
     157    curl_easy_setopt( e, CURLOPT_ENCODING, "gzip;q=1.0, deflate, identity" );
     158    curl_easy_setopt( e, CURLOPT_FOLLOWLOCATION, 1L );
     159    curl_easy_setopt( e, CURLOPT_MAXREDIRS, -1L );
     160    curl_easy_setopt( e, CURLOPT_NOSIGNAL, 1L );
     161    curl_easy_setopt( e, CURLOPT_PRIVATE, task );
     162    curl_easy_setopt( e, CURLOPT_SOCKOPTFUNCTION, sockoptfunction );
     163    curl_easy_setopt( e, CURLOPT_SOCKOPTDATA, task );
     164    curl_easy_setopt( e, CURLOPT_SSL_VERIFYHOST, 0L );
     165    curl_easy_setopt( e, CURLOPT_SSL_VERIFYPEER, 0L );
     166    curl_easy_setopt( e, CURLOPT_TIMEOUT, getTimeoutFromURL( task->url ) );
     167    curl_easy_setopt( e, CURLOPT_URL, task->url );
     168    curl_easy_setopt( e, CURLOPT_USERAGENT, TR_NAME "/" SHORT_VERSION_STRING );
     169    curl_easy_setopt( e, CURLOPT_VERBOSE, verbose );
     170    curl_easy_setopt( e, CURLOPT_WRITEDATA, task );
     171    curl_easy_setopt( e, CURLOPT_WRITEFUNCTION, writeFunc );
     172    if( w->haveAddr )
     173        curl_easy_setopt( e, CURLOPT_INTERFACE, tr_ntop_non_ts( &w->addr ) );
     174    if( task->range )
     175        curl_easy_setopt( e, CURLOPT_RANGE, task->range );
     176
     177    return e;
    489178}
    490179
     
    494183
    495184static void
    496 task_finish( struct tr_web_task * task, long response_code )
    497 {
    498     dbgmsg( "finished web task %lu; got %ld", task->tag, response_code );
     185task_finish_func( void * vtask )
     186{
     187    struct tr_web_task * task = vtask;
     188    dbgmsg( "finished web task %p; got %ld", task, task->code );
    499189
    500190    if( task->done_func != NULL )
    501191        task->done_func( task->session,
    502                          response_code,
     192                         task->code,
    503193                         EVBUFFER_DATA( task->response ),
    504194                         EVBUFFER_LENGTH( task->response ),
    505195                         task->done_func_user_data );
     196
    506197    task_free( task );
    507 }
    508 
    509 static void
    510 remove_task( struct tr_web_task * task )
    511 {
    512     long code;
    513 
    514     curl_easy_getinfo( task->easy, CURLINFO_RESPONSE_CODE, &code );
    515     curl_multi_remove_handle( task->multi, task->easy );
    516     curl_easy_cleanup( task->easy );
    517     task_finish( task, code );
    518 }
    519 
    520 static void
    521 task_timeout_cb( int fd UNUSED, short what UNUSED, void * task )
    522 {
    523     remove_task( task );
    524 }
    525 
    526 static void
    527 remove_finished_tasks( tr_web * g )
    528 {
    529     CURLMsg * msg;
    530     int msgs_left;
    531 
    532     while(( msg = curl_multi_info_read( g->multi, &msgs_left ))) {
    533         if(( msg->msg == CURLMSG_DONE ) && ( msg->easy_handle != NULL )) {
    534             struct tr_web_task * task;
    535             CURL * e = msg->easy_handle;
    536             curl_easy_getinfo( e, CURLINFO_PRIVATE, (void*)&task );
    537             assert( e == task->easy );
    538             remove_task( task );
    539         }
    540     }
    541 }
    542 
    543 static void
    544 restart_timer( tr_web * g )
    545 {
    546     dbgmsg( "adding a timeout for %.1f seconds from now", g->timer_msec/1000.0 );
    547     evtimer_del( &g->timer_event );
    548     tr_timerAddMsec( &g->timer_event, g->timer_msec );
    549 }
    550 
    551 static void
    552 tr_multi_perform( tr_web * g, int fd, int curl_what )
    553 {
    554     CURLMcode m;
    555 
    556     dbgmsg( "check_run_count: %d taskCount", g->taskCount );
    557 
    558     /* invoke libcurl's processing */
    559     do
    560         m = curl_multi_socket_action( g->multi, fd, curl_what, &g->taskCount );
    561     while( m == CURLM_CALL_MULTI_SOCKET );
    562 
    563     remove_finished_tasks( g );
    564 
    565     if( g->closing && !g->taskCount )
    566         web_free( g );
    567     else
    568         restart_timer( g );
    569 }
    570 
    571 /* libevent says that sock is ready to be processed, so wake up libcurl */
    572 static void
    573 event_cb( int fd, short ev_what, void * g )
    574 {
    575     int curl_what = 0;
    576     if( ev_what & EV_READ ) curl_what |= CURL_POLL_IN;
    577     if( ev_what & EV_WRITE ) curl_what |= CURL_POLL_OUT;
    578     tr_multi_perform( g, fd, curl_what );
    579 }
    580 
    581 /* CURLMOPT_SOCKETFUNCTION */
    582 static int
    583 sock_cb( CURL * e UNUSED, curl_socket_t fd, int curl_what,
    584          void * vweb, void * vevent )
    585 {
    586     /*static int num_events = 0;*/
    587     struct tr_web * web = vweb;
    588     struct event * io_event = vevent;
    589     dbgmsg( "sock_cb: curl_what %d, fd %d, io_event %p",
    590             curl_what, (int)fd, io_event );
    591 
    592     if( io_event != NULL )
    593         event_del( io_event );
    594 
    595     if( curl_what & ( CURL_POLL_IN | CURL_POLL_OUT ) )
    596     {
    597         const short ev_what = EV_PERSIST
    598                            | (( curl_what & CURL_POLL_IN ) ? EV_READ : 0 )
    599                            | (( curl_what & CURL_POLL_OUT ) ? EV_WRITE : 0 );
    600 
    601         if( io_event == NULL ) {
    602             io_event = tr_new0( struct event, 1 );
    603             curl_multi_assign( web->multi, fd, io_event );
    604             /*fprintf( stderr, "+1 io_events to %d\n", ++num_events );*/
    605         }
    606 
    607         dbgmsg( "enabling (libevent %hd, libcurl %d) on io_event %p, fd %d",
    608                 ev_what, curl_what, io_event, fd );
    609         event_set( io_event, fd, ev_what, event_cb, web );
    610         assert( io_event->ev_base != NULL );
    611         event_add( io_event, NULL );
    612     }
    613 
    614     if( ( io_event != NULL ) && ( curl_what & CURL_POLL_REMOVE ) )
    615     {
    616         memset( io_event, TR_MEMORY_TRASH, sizeof( struct event ) );
    617         tr_free( io_event );
    618         /*fprintf( stderr, "-1 io_events to %d\n", --num_events );*/
    619     }
    620 
    621     return 0; /* libcurl documentation: "The callback MUST return 0." */
    622 }
    623 
    624 /* libevent says that timer_msec have passed, so wake up libcurl */
    625 static void
    626 libevent_timer_cb( int fd UNUSED, short what UNUSED, void * g )
    627 {
    628     dbgmsg( "libevent timer is done" );
    629     tr_multi_perform( g, CURL_SOCKET_TIMEOUT, 0 );
    630 }
    631 
    632 /* libcurl documentation: "If 0, it means you should proceed immediately
    633  * without waiting for anything. If it returns -1, there's no timeout at all
    634  * set ... (but) you must not wait too long (more than a few seconds perhaps)
    635  * before you call curl_multi_perform() again."  */
    636 static void
    637 multi_timer_cb( CURLM * multi UNUSED, long timer_msec, void * vg )
    638 {
    639     tr_web * g = vg;
    640 
    641     g->timer_msec = timer_msec > 0 ? timer_msec : DEFAULT_TIMER_MSEC;
    642 
    643     if( timer_msec < 1 )
    644         tr_multi_perform( g, CURL_SOCKET_TIMEOUT, 0 );
    645     else
    646         restart_timer( g );
    647198}
    648199
     
    658209           void               * done_func_user_data )
    659210{
    660     if( session->web != NULL )
     211    struct tr_web * web = session->web;
     212
     213    if( web != NULL )
    661214    {
    662         static unsigned long tag = 0;
    663215        struct tr_web_task * task = tr_new0( struct tr_web_task, 1 );
     216
    664217        task->session = session;
    665218        task->url = tr_strdup( url );
     
    667220        task->done_func = done_func;
    668221        task->done_func_user_data = done_func_user_data;
    669         task->tag = ++tag;
    670222        task->response = evbuffer_new( );
    671         tr_runInEventThread( session, doDNS, task );
     223
     224        tr_lockLock( web->taskLock );
     225        tr_list_append( &web->tasks, task );
     226        tr_lockUnlock( web->taskLock );
    672227    }
    673228}
    674229
    675230void
    676 tr_webSetInterface( tr_web * web, const tr_address * addr )
    677 {
    678     if(( web->haveAddr = ( addr != NULL )))
    679         web->addr = *addr;
    680 }
    681 
    682 tr_web*
    683 tr_webInit( tr_session * session )
    684 {
    685     tr_web * web;
     231tr_webSetInterface( tr_session * session, const tr_address * addr )
     232{
     233    struct tr_web * web = session->web;
     234
     235    if( web != NULL )
     236        if(( web->haveAddr = ( addr != NULL )))
     237            web->addr = *addr;
     238}
     239
     240static void
     241tr_webThreadFunc( void * vsession )
     242{
     243    int unused;
     244    CURLM * multi;
     245    struct tr_web * web;
     246    int taskCount = 0;
     247    tr_session * session = vsession;
    686248
    687249    /* try to enable ssl for https support; but if that fails,
     
    691253
    692254    web = tr_new0( struct tr_web, 1 );
    693     web->dns_cache = TR_PTR_ARRAY_INIT;
    694     web->session = session;
    695     web->timer_msec = DEFAULT_TIMER_MSEC; /* overwritten by multi_timer_cb() */
    696     evtimer_set( &web->timer_event, libevent_timer_cb, web );
    697 
    698     web->multi = curl_multi_init( );
    699 
    700     evdns_init( );
    701 
    702     curl_multi_setopt( web->multi, CURLMOPT_SOCKETDATA, web );
    703     curl_multi_setopt( web->multi, CURLMOPT_SOCKETFUNCTION, sock_cb );
    704     curl_multi_setopt( web->multi, CURLMOPT_TIMERDATA, web );
    705     curl_multi_setopt( web->multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb );
    706 
    707     return web;
     255    web->close_mode = ~0;
     256    web->taskLock = tr_lockNew( );
     257    web->tasks = NULL;
     258    multi = curl_multi_init( );
     259    session->web = web;
     260
     261    for( ;; )
     262    {
     263        long msec;
     264        CURLMsg * msg;
     265        CURLMcode mcode;
     266        struct tr_web_task * task;
     267
     268        if( web->close_mode == TR_WEB_CLOSE_NOW )
     269            break;
     270        if( ( web->close_mode == TR_WEB_CLOSE_WHEN_IDLE ) && !taskCount )
     271            break;
     272
     273        /* add tasks from the queue */
     274        tr_lockLock( web->taskLock );
     275        while(( task = tr_list_pop_front( &web->tasks )))
     276        {
     277            curl_multi_add_handle( multi, createEasy( session, web, task ));
     278            fprintf( stderr, "adding a task.. taskCount is now %d\n", taskCount );
     279            ++taskCount;
     280        }
     281        tr_lockUnlock( web->taskLock );
     282
     283        /* maybe wait a little while before calling curl_multi_perform() */
     284        msec = 0;
     285        curl_multi_timeout( multi, &msec );
     286        if( msec < 0 )
     287            msec = THREADFUNC_MAX_SLEEP_MSEC;
     288        if( msec > 0 )
     289        {
     290            int max_fd;
     291            struct timeval t;
     292            fd_set r_fd_set, w_fd_set, c_fd_set;
     293
     294            max_fd = 0;
     295            FD_ZERO( &r_fd_set );
     296            FD_ZERO( &w_fd_set );
     297            FD_ZERO( &c_fd_set );
     298            curl_multi_fdset( multi, &r_fd_set, &w_fd_set, &c_fd_set, &max_fd );
     299
     300            if( msec > THREADFUNC_MAX_SLEEP_MSEC )
     301                msec = THREADFUNC_MAX_SLEEP_MSEC;
     302            t.tv_sec = 0;
     303            t.tv_usec = msec * 1000;
     304
     305            select( max_fd+1, &r_fd_set, &w_fd_set, &c_fd_set, &t );
     306        }
     307
     308        /* call curl_multi_perform() */
     309        do {
     310            mcode = curl_multi_perform( multi, &unused );
     311        } while( mcode == CURLM_CALL_MULTI_PERFORM );
     312
     313        /* pump completed tasks from the multi */
     314        while(( msg = curl_multi_info_read( multi, &unused )))
     315        {
     316            if(( msg->msg == CURLMSG_DONE ) && ( msg->easy_handle != NULL ))
     317            {
     318                struct tr_web_task * task;
     319                CURL * e = msg->easy_handle;
     320                curl_easy_getinfo( e, CURLINFO_PRIVATE, (void*)&task );
     321                curl_easy_getinfo( e, CURLINFO_RESPONSE_CODE, &task->code );
     322                curl_multi_remove_handle( multi, e );
     323                curl_easy_cleanup( e );
     324fprintf( stderr, "removing a completed task.. taskCount is now %d (response code: %d, response len: %d)\n", taskCount, (int)task->code, EVBUFFER_LENGTH(task->response) );
     325                tr_runInEventThread( task->session, task_finish_func, task );
     326                --taskCount;
     327            }
     328        }
     329    }
     330
     331    /* cleanup */
     332    curl_multi_cleanup( multi );
     333    tr_lockFree( web->taskLock );
     334    tr_free( web );
     335    session->web = NULL;
    708336}
    709337
    710338void
    711 tr_webClose( tr_web ** web_in )
    712 {
    713     tr_web * web = *web_in;
    714     *web_in = NULL;
    715     if( web->taskCount < 1 )
    716         web_free( web );
    717     else
    718         web->closing = 1;
     339tr_webInit( tr_session * session )
     340{
     341    tr_threadNew( tr_webThreadFunc, session );
     342}
     343
     344void
     345tr_webClose( tr_session * session, tr_web_close_mode close_mode )
     346{
     347    if( session->web != NULL )
     348    {
     349        session->web->close_mode = close_mode;
     350
     351        if( close_mode == TR_WEB_CLOSE_NOW )
     352            while( session->web != NULL )
     353                tr_wait_msec( 100 );
     354    }
    719355}
    720356
     
    778414                const char * str, int len, tr_bool escape_slashes )
    779415{
    780     int i;
     416    const char * end;
    781417
    782418    if( ( len < 0 ) && ( str != NULL ) )
    783419        len = strlen( str );
    784420
    785     for( i = 0; i < len; i++ ) {
    786         if( str[i] == ',' || str[i] == '-' || str[i] == '.'
    787             || ( '0' <= str[i] && str[i] <= '9' )
    788             || ( 'A' <= str[i] && str[i] <= 'Z' )
    789             || ( 'a' <= str[i] && str[i] <= 'z' )
    790             || ( str[i] == '/' && !escape_slashes ) )
    791             evbuffer_add( out, &str[i], 1 );
     421    for( end=str+len; str!=end; ++str ) {
     422        if(    ( *str == ',' )
     423            || ( *str == '-' )
     424            || ( *str == '.' )
     425            || ( ( '0' <= *str ) && ( *str <= '9' ) )
     426            || ( ( 'A' <= *str ) && ( *str <= 'Z' ) )
     427            || ( ( 'a' <= *str ) && ( *str <= 'z' ) )
     428            || ( ( *str == '/' ) && ( !escape_slashes ) ) )
     429            evbuffer_add( out, str, 1 );
    792430        else
    793             evbuffer_add_printf( out, "%%%02X", (unsigned)(str[i]&0xFF) );
     431            evbuffer_add_printf( out, "%%%02X", (unsigned)(*str&0xFF) );
    794432    }
    795433}
  • trunk/libtransmission/web.h

    r9868 r10303  
    1515
    1616struct tr_address;
    17 typedef struct tr_web tr_web;
    1817
    19 tr_web*      tr_webInit( tr_session * session );
     18void tr_webInit( tr_session * session );
    2019
    21 void         tr_webClose( tr_web ** );
     20typedef enum
     21{
     22    TR_WEB_CLOSE_WHEN_IDLE,
     23    TR_WEB_CLOSE_NOW
     24}
     25tr_web_close_mode;
    2226
    23 void         tr_webSetInterface( tr_web * web, const struct tr_address * addr );
     27void tr_webClose( tr_session * session, tr_web_close_mode close_mode );
     28
     29void tr_webSetInterface( tr_session * session, const struct tr_address * addr );
    2430
    2531typedef void ( tr_web_done_func )( tr_session       * session,
Note: See TracChangeset for help on using the changeset viewer.