Changeset 10303
- Timestamp:
- Mar 6, 2010, 3:05:05 PM (13 years ago)
- Location:
- trunk/libtransmission
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/libtransmission/session.c
r10276 r10303 614 614 tr_statsInit( session ); 615 615 616 session->web =tr_webInit( session );616 tr_webInit( session ); 617 617 618 618 tr_sessionSet( session, &settings ); … … 714 714 b.socket = -1; 715 715 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 ); 717 717 718 718 str = TR_PREFS_KEY_BIND_ADDRESS_IPV6; … … 1457 1457 tr_sessionGetRawSpeed( const tr_session * session, tr_direction dir ) 1458 1458 { 1459 return tr_isSession( session ) ? tr_bandwidthGet PieceSpeed( session->bandwidth, 0, dir ) : 0.0;1459 return tr_isSession( session ) ? tr_bandwidthGetRawSpeed( session->bandwidth, 0, dir ) : 0.0; 1460 1460 } 1461 1461 … … 1525 1525 tr_statsClose( session ); 1526 1526 tr_peerMgrFree( session->peerMgr ); 1527 tr_webClose( session, TR_WEB_CLOSE_WHEN_IDLE ); 1527 1528 1528 1529 closeBlocklists( session ); 1529 tr_webClose( &session->web );1530 1530 1531 1531 tr_fdClose( session ); … … 1563 1563 * for a bit while they tell the router & tracker 1564 1564 * 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 ) ) 1567 1567 { 1568 1568 dbgmsg( "waiting on port unmap (%p) or announcer (%p)", … … 1570 1570 tr_wait_msec( 100 ); 1571 1571 } 1572 1573 tr_webClose( session, TR_WEB_CLOSE_NOW ); 1572 1574 1573 1575 /* close the libtransmission thread */ -
trunk/libtransmission/web.c
r10298 r10303 11 11 */ 12 12 13 #include < assert.h>13 #include <sys/select.h> 14 14 15 15 #include <curl/curl.h> 16 16 #include <event.h> 17 #include <evdns.h>18 17 19 18 #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 */ 22 22 #include "session.h" 23 #include "trevent.h" 23 #include "trevent.h" /* tr_runInEventThread() */ 24 24 #include "utils.h" 25 #include "version.h" 25 #include "version.h" /* User-Agent */ 26 26 #include "web.h" 27 27 28 28 enum 29 29 { 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, 35 31 }; 36 32 … … 55 51 struct tr_web 56 52 { 57 tr_bool closing;53 int close_mode; 58 54 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; 63 57 tr_address addr; 64 tr_ptrArray dns_cache;65 struct event timer_event;66 58 }; 67 59 68 struct dns_cache_item;69 static void dns_cache_item_free( struct dns_cache_item * );70 71 static void72 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 }81 60 82 61 /*** … … 86 65 struct tr_web_task 87 66 { 88 int port; 89 unsigned long tag; 90 struct curl_slist * slist; 67 long code; 91 68 struct evbuffer * response; 92 69 char * url; 93 char * resolved_url;94 char * host;95 const char * resolved_host;96 70 char * range; 97 71 tr_session * session; 98 72 tr_web_done_func * done_func; 99 73 void * done_func_user_data; 100 struct event timer_event;101 CURL * easy;102 CURLM * multi;103 tr_bool timer_event_isSet;104 74 }; 105 75 … … 107 77 task_free( struct tr_web_task * task ) 108 78 { 109 if( task->slist != NULL )110 curl_slist_free_all( task->slist );111 if( task->timer_event_isSet )112 evtimer_del( &task->timer_event );113 79 evbuffer_free( task->response ); 114 tr_free( task->host );115 80 tr_free( task->range ); 116 tr_free( task->resolved_url );117 81 tr_free( task->url ); 118 memset( task, TR_MEMORY_TRASH, sizeof( struct tr_web_task ) );119 82 tr_free( task ); 120 }121 122 /***123 ****124 ***/125 126 struct dns_cache_item127 {128 char * host;129 char * resolved_host;130 time_t expiration;131 tr_bool success;132 };133 134 static void135 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 int147 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 enum155 {156 TR_DNS_OK,157 TR_DNS_FAIL,158 TR_DNS_UNTESTED159 }160 tr_dns_result;161 162 static void163 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_result171 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 void208 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;249 83 } 250 84 … … 291 125 } 292 126 293 static int127 static long 294 128 getTimeoutFromURL( const char * url ) 295 129 { 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 135 static CURL * 136 createEasy( 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; 489 178 } 490 179 … … 494 183 495 184 static 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 ); 185 task_finish_func( void * vtask ) 186 { 187 struct tr_web_task * task = vtask; 188 dbgmsg( "finished web task %p; got %ld", task, task->code ); 499 189 500 190 if( task->done_func != NULL ) 501 191 task->done_func( task->session, 502 response_code,192 task->code, 503 193 EVBUFFER_DATA( task->response ), 504 194 EVBUFFER_LENGTH( task->response ), 505 195 task->done_func_user_data ); 196 506 197 task_free( task ); 507 }508 509 static void510 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 void521 task_timeout_cb( int fd UNUSED, short what UNUSED, void * task )522 {523 remove_task( task );524 }525 526 static void527 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 void544 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 void552 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 do560 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 else568 restart_timer( g );569 }570 571 /* libevent says that sock is ready to be processed, so wake up libcurl */572 static void573 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 int583 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_PERSIST598 | (( 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 void626 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 immediately633 * without waiting for anything. If it returns -1, there's no timeout at all634 * set ... (but) you must not wait too long (more than a few seconds perhaps)635 * before you call curl_multi_perform() again." */636 static void637 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 else646 restart_timer( g );647 198 } 648 199 … … 658 209 void * done_func_user_data ) 659 210 { 660 if( session->web != NULL ) 211 struct tr_web * web = session->web; 212 213 if( web != NULL ) 661 214 { 662 static unsigned long tag = 0;663 215 struct tr_web_task * task = tr_new0( struct tr_web_task, 1 ); 216 664 217 task->session = session; 665 218 task->url = tr_strdup( url ); … … 667 220 task->done_func = done_func; 668 221 task->done_func_user_data = done_func_user_data; 669 task->tag = ++tag;670 222 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 ); 672 227 } 673 228 } 674 229 675 230 void 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; 231 tr_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 240 static void 241 tr_webThreadFunc( void * vsession ) 242 { 243 int unused; 244 CURLM * multi; 245 struct tr_web * web; 246 int taskCount = 0; 247 tr_session * session = vsession; 686 248 687 249 /* try to enable ssl for https support; but if that fails, … … 691 253 692 254 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 ); 324 fprintf( 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; 708 336 } 709 337 710 338 void 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; 339 tr_webInit( tr_session * session ) 340 { 341 tr_threadNew( tr_webThreadFunc, session ); 342 } 343 344 void 345 tr_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 } 719 355 } 720 356 … … 778 414 const char * str, int len, tr_bool escape_slashes ) 779 415 { 780 int i;416 const char * end; 781 417 782 418 if( ( len < 0 ) && ( str != NULL ) ) 783 419 len = strlen( str ); 784 420 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 ); 792 430 else 793 evbuffer_add_printf( out, "%%%02X", (unsigned)( str[i]&0xFF) );431 evbuffer_add_printf( out, "%%%02X", (unsigned)(*str&0xFF) ); 794 432 } 795 433 } -
trunk/libtransmission/web.h
r9868 r10303 15 15 16 16 struct tr_address; 17 typedef struct tr_web tr_web;18 17 19 tr_web*tr_webInit( tr_session * session );18 void tr_webInit( tr_session * session ); 20 19 21 void tr_webClose( tr_web ** ); 20 typedef enum 21 { 22 TR_WEB_CLOSE_WHEN_IDLE, 23 TR_WEB_CLOSE_NOW 24 } 25 tr_web_close_mode; 22 26 23 void tr_webSetInterface( tr_web * web, const struct tr_address * addr ); 27 void tr_webClose( tr_session * session, tr_web_close_mode close_mode ); 28 29 void tr_webSetInterface( tr_session * session, const struct tr_address * addr ); 24 30 25 31 typedef void ( tr_web_done_func )( tr_session * session,
Note: See TracChangeset
for help on using the changeset viewer.