From: Jordan Lee Date: Thu, 6 Jan 2011 01:00:21 +0000 (+0000) Subject: (trunk libT) #3519 "webseeds don't work" -- fixed. X-Git-Tag: 2.20b1~99 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6dc03f471ebf1d84587373b03be27cfeffb50abd;p=transmission (trunk libT) #3519 "webseeds don't work" -- fixed. --- diff --git a/libtransmission/peer-common.h b/libtransmission/peer-common.h index 1eae05882..dbd6216d3 100644 --- a/libtransmission/peer-common.h +++ b/libtransmission/peer-common.h @@ -85,9 +85,9 @@ tr_peer_event; struct tr_peer; -typedef void tr_peer_callback( struct tr_peer * peer, - const tr_peer_event * event, - void * client_data ); +typedef void tr_peer_callback( struct tr_peer * peer, + const tr_peer_event * event, + void * client_data ); #ifdef WIN32 #define EMSGSIZE WSAEMSGSIZE diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c index 54d5f2014..b54772bdf 100644 --- a/libtransmission/peer-mgr.c +++ b/libtransmission/peer-mgr.c @@ -160,7 +160,7 @@ tr_isAtom( const struct peer_atom * atom ) static const char* tr_atomAddrStr( const struct peer_atom * atom ) { - return tr_peerIoAddrStr( &atom->addr, atom->port ); + return atom ? tr_peerIoAddrStr( &atom->addr, atom->port ) : "[no atom]"; } struct block_request @@ -1199,7 +1199,7 @@ refillUpkeep( int foo UNUSED, short bar UNUSED, void * vmgr ) for( it=t->requests, end=it+n; it!=end; ++it ) { - if( ( it->sentAt <= too_old ) && !tr_peerMsgsIsReadingBlock( it->peer->msgs, it->block ) ) + if( ( it->sentAt <= too_old ) && it->peer->msgs && !tr_peerMsgsIsReadingBlock( it->peer->msgs, it->block ) ) cancel[cancelCount++] = *it; else { @@ -1407,7 +1407,7 @@ peerCallbackFunc( tr_peer * peer, const tr_peer_event * e, void * vt ) tr_statsAddDownloaded( tor->session, e->length ); /* update our atom */ - if( peer && e->wasPieceData ) + if( peer && peer->atom && e->wasPieceData ) peer->atom->piece_data_time = now; break; @@ -1434,7 +1434,7 @@ peerCallbackFunc( tr_peer * peer, const tr_peer_event * e, void * vt ) requestListRemove( t, block, peer ); pieceListRemoveRequest( t, block ); - if( peer != NULL ) + if( peer && peer->blocksSentToClient ) tr_historyAdd( peer->blocksSentToClient, tr_time( ), 1 ); if( tr_cpBlockIsComplete( &tor->completion, block ) ) @@ -1482,7 +1482,7 @@ peerCallbackFunc( tr_peer * peer, const tr_peer_event * e, void * vt ) * content distributor, not the peers, it is the tracker's job * to manage the swarms, not the web server and does not fit * into the jurisdiction of the tracker." */ - if( peer != NULL ) { + if( peer->msgs != NULL ) { const uint32_t n = tr_torPieceCountBytes( tor, p ); tr_announcerAddBytes( tor, TR_ANN_DOWN, n ); } diff --git a/libtransmission/ratecontrol.c b/libtransmission/ratecontrol.c index af29ed10b..19066306c 100644 --- a/libtransmission/ratecontrol.c +++ b/libtransmission/ratecontrol.c @@ -73,8 +73,7 @@ tr_rcRate_Bps( const tr_ratecontrol * r, uint64_t now ) ***/ void -tr_rcTransferred( tr_ratecontrol * r, - size_t size ) +tr_rcTransferred( tr_ratecontrol * r, size_t size ) { const uint64_t now = tr_time_msec ( ); diff --git a/libtransmission/web.c b/libtransmission/web.c index 83899ecad..f716627f6 100644 --- a/libtransmission/web.c +++ b/libtransmission/web.c @@ -73,6 +73,7 @@ struct tr_web_task { long code; struct evbuffer * response; + struct evbuffer * freebuf; char * url; char * range; tr_session * session; @@ -83,7 +84,8 @@ struct tr_web_task static void task_free( struct tr_web_task * task ) { - evbuffer_free( task->response ); + if( task->freebuf ) + evbuffer_free( task->freebuf ); tr_free( task->range ); tr_free( task->url ); tr_free( task ); @@ -213,6 +215,19 @@ tr_webRun( tr_session * session, const char * range, tr_web_done_func done_func, void * done_func_user_data ) +{ + tr_webRunWithBuffer( session, url, range, + done_func, done_func_user_data, + NULL ); +} + +void +tr_webRunWithBuffer( tr_session * session, + const char * url, + const char * range, + tr_web_done_func done_func, + void * done_func_user_data, + struct evbuffer * buffer ) { struct tr_web * web = session->web; @@ -225,7 +240,8 @@ tr_webRun( tr_session * session, task->range = tr_strdup( range ); task->done_func = done_func; task->done_func_user_data = done_func_user_data; - task->response = evbuffer_new( ); + task->response = buffer ? buffer : evbuffer_new( ); + task->freebuf = buffer ? NULL : task->response; tr_lockLock( web->taskLock ); tr_list_append( &web->tasks, task ); diff --git a/libtransmission/web.h b/libtransmission/web.h index 252595cd5..d0e988687 100644 --- a/libtransmission/web.h +++ b/libtransmission/web.h @@ -50,14 +50,21 @@ typedef void ( tr_web_done_func )( tr_session * session, const char * tr_webGetResponseStr( long response_code ); -void tr_webRun( tr_session * session, - const char * url, - const char * range, - tr_web_done_func done_func, - void * done_func_user_data ); +void tr_webRun( tr_session * session, + const char * url, + const char * range, + tr_web_done_func done_func, + void * done_func_user_data ); struct evbuffer; +void tr_webRunWithBuffer( tr_session * session, + const char * url, + const char * range, + tr_web_done_func done_func, + void * done_func_user_data, + struct evbuffer * buffer ); + void tr_http_escape( struct evbuffer *out, const char *str, int len, tr_bool escape_slashes ); char* tr_http_unescape( const char * str, int len ); diff --git a/libtransmission/webseed.c b/libtransmission/webseed.c index d64f1da54..3549cf5c0 100644 --- a/libtransmission/webseed.c +++ b/libtransmission/webseed.c @@ -10,40 +10,60 @@ * $Id$ */ -#include /* strlen */ +#include /* strlen() */ #include +#include #include "transmission.h" -#include "inout.h" +#include "cache.h" +#include "inout.h" /* tr_ioFindFileLocation() */ +#include "list.h" #include "ratecontrol.h" +#include "peer-mgr.h" #include "torrent.h" #include "utils.h" #include "web.h" #include "webseed.h" -struct tr_webseed +struct tr_webseed_task { - tr_bool busy; - tr_bool dead; - - uint8_t hash[SHA_DIGEST_LENGTH]; - - char * url; - - tr_peer_callback * callback; - void * callback_data; - - tr_piece_index_t pieceIndex; - uint32_t pieceOffset; - uint32_t byteCount; + tr_session * session; + struct evbuffer * content; + struct tr_webseed * webseed; + tr_block_index_t block; + tr_piece_index_t piece_index; + uint32_t piece_offset; + uint32_t length; + int torrent_id; +}; - tr_ratecontrol rateDown; +struct tr_webseed +{ + tr_peer parent; + tr_ratecontrol download_rate; + tr_session * session; + tr_peer_callback * callback; + void * callback_data; + tr_list * tasks; + struct event * timer; + char * base_url; + size_t base_url_len; + int torrent_id; + tr_bool is_stopping; +}; - tr_session * session; +static void +webseed_free( struct tr_webseed * w ) +{ + tr_bitsetDestructor( &w->parent.have ); + tr_free( w->parent.client ); - struct evbuffer * content; -}; + event_free( w->timer ); + tr_rcDestruct( &w->download_rate ); + tr_free( w->base_url ); + tr_free( w ); +} /*** **** @@ -55,27 +75,17 @@ static void publish( tr_webseed * w, tr_peer_event * e ) { if( w->callback != NULL ) - w->callback( NULL, e, w->callback_data ); -} - -static void -fireNeedReq( tr_webseed * w ) -{ -#if 0 - tr_peer_event e = blankEvent; - e.eventType = TR_PEER_NEED_REQ; - publish( w, &e ); -#endif + w->callback( &w->parent, e, w->callback_data ); } static void -fireClientGotBlock( tr_webseed * w, uint32_t pieceIndex, uint32_t offset, uint32_t length ) +fireClientGotBlock( tr_torrent * tor, tr_webseed * w, tr_block_index_t block ) { tr_peer_event e = blankEvent; + e.pieceIndex = tr_torBlockPiece( tor, block ); + e.offset = tor->blockSize * block - tor->info.pieceSize * e.pieceIndex; + e.length = tr_torBlockCountBytes( tor, block ); e.eventType = TR_PEER_CLIENT_GOT_BLOCK; - e.pieceIndex = pieceIndex; - e.offset = offset; - e.length = length; publish( w, &e ); } @@ -93,157 +103,209 @@ fireClientGotData( tr_webseed * w, uint32_t length ) **** ***/ -static char* -makeURL( tr_webseed * w, - const tr_file * file ) +static void +on_content_changed( struct evbuffer * buf UNUSED, + const struct evbuffer_cb_info * info, + void * vw ) { - struct evbuffer * out = evbuffer_new( ); - const char * url = w->url; - const size_t url_len = strlen( url ); - - evbuffer_add( out, url, url_len ); - - /* if url ends with a '/', add the torrent name */ - if( url[url_len - 1] == '/' && file->name ) - tr_http_escape( out, file->name, strlen(file->name), FALSE ); + tr_webseed * w = vw; - return evbuffer_free_to_str( out ); + if( ( info->n_added > 0 ) && !w->is_stopping ) + { + tr_rcTransferred( &w->download_rate, info->n_added ); + fireClientGotData( w, info->n_added ); + } } -static void requestNextChunk( tr_webseed * w ); +static void task_request_next_chunk( struct tr_webseed_task * task ); static void -webResponseFunc( tr_session * session, - long response_code, - const void * response, - size_t response_byte_count, - void * vw ) +on_idle( tr_webseed * w ) { - tr_webseed * w = vw; - tr_torrent * tor = tr_torrentFindFromHash( session, w->hash ); - const int success = ( response_code == 206 ); + tr_torrent * tor = tr_torrentFindFromId( w->session, w->torrent_id ); -/*fprintf( stderr, "server responded with code %ld and %lu bytes\n", - response_code, (unsigned long)response_byte_count );*/ - if( !success ) + if( w->is_stopping && !tr_webseedIsActive( w ) ) { - /* FIXME */ + webseed_free( w ); } - else if( tor != NULL ) + else if( !w->is_stopping && tor && tor->isRunning && !tr_torrentIsSeed( tor ) ) { - evbuffer_add( w->content, response, response_byte_count ); - if( !w->dead ) + int i; + int got = 0; + const int max = tor->blockCountInPiece; + const int want = max - tr_list_size( w->tasks ); + tr_block_index_t * blocks = NULL; + + if( want > 0 ) { - fireClientGotData( w, response_byte_count ); - tr_rcTransferred( &w->rateDown, response_byte_count ); + blocks = tr_new( tr_block_index_t, want ); + tr_peerMgrGetNextRequests( tor, &w->parent, want, blocks, &got ); } - if( evbuffer_get_length( w->content ) < w->byteCount ) - requestNextChunk( w ); - else { - tr_ioWrite( tor, w->pieceIndex, w->pieceOffset, w->byteCount, evbuffer_pullup( w->content, -1 ) ); - evbuffer_drain( w->content, evbuffer_get_length( w->content ) ); - w->busy = 0; - if( w->dead ) - tr_webseedFree( w ); - else { - fireClientGotBlock( w, w->pieceIndex, w->pieceOffset, w->byteCount ); - fireNeedReq( w ); - } + for( i=0; iwebseed = w; + task->session = w->session; + task->torrent_id = w->torrent_id; + task->block = b; + task->piece_index = tr_torBlockPiece( tor, b ); + task->piece_offset = ( tor->blockSize * b ) + - ( tor->info.pieceSize * task->piece_index ); + task->length = tr_torBlockCountBytes( tor, b ); + task->content = evbuffer_new( ); + evbuffer_add_cb( task->content, on_content_changed, w ); + tr_list_append( &w->tasks, task ); + task_request_next_chunk( task ); } + + tr_free( blocks ); } } + static void -requestNextChunk( tr_webseed * w ) +web_response_func( tr_session * session, + long response_code, + const void * response UNUSED, + size_t response_byte_count UNUSED, + void * vtask ) { - tr_torrent * tor = tr_torrentFindFromHash( w->session, w->hash ); + struct tr_webseed_task * t = vtask; + tr_torrent * tor = tr_torrentFindFromId( session, t->torrent_id ); + const int success = ( response_code == 206 ); - if( tor != NULL ) + if( success && tor ) { - const tr_info * inf = tr_torrentInfo( tor ); - const uint32_t have = evbuffer_get_length( w->content ); - const uint32_t left = w->byteCount - have; - const uint32_t pieceOffset = w->pieceOffset + have; - tr_file_index_t fileIndex; - uint64_t fileOffset; - uint32_t thisPass; - char * url; - char * range; - - tr_ioFindFileLocation( tor, w->pieceIndex, pieceOffset, - &fileIndex, &fileOffset ); - thisPass = MIN( left, inf->files[fileIndex].length - fileOffset ); - - url = makeURL( w, &inf->files[fileIndex] ); -/*fprintf( stderr, "url is [%s]\n", url );*/ - range = tr_strdup_printf( "%"PRIu64"-%"PRIu64, fileOffset, fileOffset + thisPass - 1 ); -/*fprintf( stderr, "range is [%s] ... we want %lu total, we have %lu, so %lu are left, and we're asking for %lu this time\n", range, (unsigned long)w->byteCount, (unsigned long)have, (unsigned long)left, (unsigned long)thisPass );*/ - tr_webRun( w->session, url, range, webResponseFunc, w ); - tr_free( range ); - tr_free( url ); + if( evbuffer_get_length( t->content ) < t->length ) + { + task_request_next_chunk( t ); + } + else + { + tr_webseed * w = t->webseed; + + tr_cacheWriteBlock( session->cache, tor, + t->piece_index, t->piece_offset, t->length, + evbuffer_pullup( t->content, -1 ) ); + fireClientGotBlock( tor, w, t->block ); + + tr_list_remove_data( &w->tasks, t ); + evbuffer_free( t->content ); + tr_free( t ); + + on_idle( w ); + } } } -tr_addreq_t -tr_webseedAddRequest( tr_webseed * w, - uint32_t pieceIndex, - uint32_t pieceOffset, - uint32_t byteCount ) +static char* +make_url( tr_webseed * w, const tr_file * file ) { - int ret; + struct evbuffer * out = evbuffer_new( ); - if( w->busy || w->dead ) - { - ret = TR_ADDREQ_FULL; - } - else + evbuffer_add( out, w->base_url, w->base_url_len ); + + /* if url ends with a '/', add the torrent name */ + if( w->base_url[w->base_url_len - 1] == '/' && file->name ) + tr_http_escape( out, file->name, strlen(file->name), FALSE ); + + return evbuffer_free_to_str( out ); +} + +static void +task_request_next_chunk( struct tr_webseed_task * t ) +{ + tr_torrent * tor = tr_torrentFindFromId( t->session, t->torrent_id ); + if( tor != NULL ) { - w->busy = 1; - w->pieceIndex = pieceIndex; - w->pieceOffset = pieceOffset; - w->byteCount = byteCount; - evbuffer_drain( w->content, evbuffer_get_length( w->content ) ); - requestNextChunk( w ); - ret = TR_ADDREQ_OK; - } + char * url; + char range[64]; - return ret; + const tr_info * inf = tr_torrentInfo( tor ); + const uint32_t remain = t->length - evbuffer_get_length( t->content ); + + const uint64_t total_offset = inf->pieceSize * t->piece_index + + t->piece_offset + + evbuffer_get_length( t->content ); + const tr_piece_index_t step_piece = total_offset / inf->pieceSize; + const uint32_t step_piece_offset = total_offset - ( inf->pieceSize * step_piece ); + + tr_file_index_t file_index; + uint64_t file_offset; + const tr_file * file; + uint32_t this_pass; + + tr_ioFindFileLocation( tor, step_piece, step_piece_offset, + &file_index, &file_offset ); + file = &inf->files[file_index]; + this_pass = MIN( remain, file->length - file_offset ); + + url = make_url( t->webseed, file ); + tr_snprintf( range, sizeof range, "%"PRIu64"-%"PRIu64, + file_offset, file_offset + this_pass - 1 ); + tr_webRunWithBuffer( t->session, url, range, + web_response_func, t, t->content ); + tr_free( url ); + } } -int +tr_bool tr_webseedIsActive( const tr_webseed * w ) { - return w->busy != 0; + return w->tasks != NULL; } -int +tr_bool tr_webseedGetSpeed_Bps( const tr_webseed * w, uint64_t now, int * setme_Bps ) { - const int isActive = tr_webseedIsActive( w ); - *setme_Bps = isActive ? tr_rcRate_Bps( &w->rateDown, now ) : 0; - return isActive; + const tr_bool is_active = tr_webseedIsActive( w ); + *setme_Bps = is_active ? tr_rcRate_Bps( &w->download_rate, now ) : 0; + return is_active; } /*** **** ***/ +static void +webseed_timer_func( evutil_socket_t foo UNUSED, short bar UNUSED, void * vw ) +{ + tr_webseed * w = vw; + on_idle( w ); + tr_timerAddMsec( w->timer, 1000 ); +} + tr_webseed* -tr_webseedNew( struct tr_torrent * torrent, +tr_webseedNew( struct tr_torrent * tor, const char * url, tr_peer_callback * callback, void * callback_data ) { tr_webseed * w = tr_new0( tr_webseed, 1 ); - - memcpy( w->hash, torrent->info.hash, SHA_DIGEST_LENGTH ); - w->session = torrent->session; - w->content = evbuffer_new( ); - w->url = tr_strdup( url ); + tr_peer * peer = &w->parent; + + peer->peerIsChoked = TRUE; + peer->peerIsInterested = FALSE; + peer->clientIsChoked = FALSE; + peer->clientIsChoked = FALSE; + peer->clientIsInterested = !tr_torrentIsSeed( tor ); + tr_bitsetConstructor( &peer->have, tor->info.pieceCount ); + tr_bitsetSetHaveAll( &peer->have ); + peer->progress = 1.0; + peer->client = tr_strdup( "webseed" ); + + w->torrent_id = tr_torrentId( tor ); + w->session = tor->session; + + w->base_url_len = strlen( url ); + w->base_url = tr_strndup( url, w->base_url_len ); w->callback = callback; w->callback_data = callback_data; - tr_rcConstruct( &w->rateDown ); + tr_rcConstruct( &w->download_rate ); + w->timer = evtimer_new( w->session->event_base, webseed_timer_func, w ); + tr_timerAddMsec( w->timer, 1000 ); return w; } @@ -252,16 +314,9 @@ tr_webseedFree( tr_webseed * w ) { if( w ) { - if( w->busy ) - { - w->dead = 1; - } + if( tr_webseedIsActive( w ) ) + w->is_stopping = TRUE; else - { - evbuffer_free( w->content ); - tr_rcDestruct( &w->rateDown ); - tr_free( w->url ); - tr_free( w ); - } + webseed_free( w ); } } diff --git a/libtransmission/webseed.h b/libtransmission/webseed.h index a69e75745..5168c24c0 100644 --- a/libtransmission/webseed.h +++ b/libtransmission/webseed.h @@ -28,18 +28,13 @@ tr_webseed* tr_webseedNew( struct tr_torrent * torrent, void tr_webseedFree( tr_webseed * ); -tr_addreq_t tr_webseedAddRequest( tr_webseed * w, - uint32_t index, - uint32_t offset, - uint32_t length ); - /** @return true if a request is being processed, or false if idle */ -int tr_webseedGetSpeed_Bps( const tr_webseed * w, +tr_bool tr_webseedGetSpeed_Bps( const tr_webseed * w, uint64_t now, int * setme_Bps ); /** @return true if a request is being processed, or false if idle */ -int tr_webseedIsActive( const tr_webseed * w ); +tr_bool tr_webseedIsActive( const tr_webseed * w ); #endif