source: trunk/libtransmission/webseed.c @ 12012

Last change on this file since 12012 was 12012, checked in by jordan, 11 years ago

(trunk libT) #4048 "use bitsets instead of bitfield in tr_completion" -- done.

Excuse the sprawl. Much of this didn't fit into self-contained commits.

  • Property svn:keywords set to Date Rev Author Id
File size: 9.8 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: webseed.c 12012 2011-02-23 03:54:04Z jordan $
11 */
12
13#include <string.h> /* strlen() */
14
15#include <event2/buffer.h>
16#include <event2/event.h>
17
18#include "transmission.h"
19#include "cache.h"
20#include "inout.h" /* tr_ioFindFileLocation() */
21#include "list.h"
22#include "ratecontrol.h"
23#include "peer-mgr.h"
24#include "torrent.h"
25#include "utils.h"
26#include "web.h"
27#include "webseed.h"
28
29struct tr_webseed_task
30{
31    tr_session         * session;
32    struct evbuffer    * content;
33    struct tr_webseed  * webseed;
34    tr_block_index_t     block;
35    tr_piece_index_t     piece_index;
36    uint32_t             piece_offset;
37    uint32_t             length;
38    int                  torrent_id;
39};
40
41struct tr_webseed
42{
43    tr_peer              parent;
44    tr_ratecontrol       download_rate;
45    tr_session         * session;
46    tr_peer_callback   * callback;
47    void               * callback_data;
48    tr_list            * tasks;
49    struct event       * timer;
50    char               * base_url;
51    size_t               base_url_len;
52    int                  torrent_id;
53    tr_bool              is_stopping;
54    int                  consecutive_failures;
55};
56
57enum
58{
59    TR_IDLE_TIMER_MSEC = 2000,
60
61    MAX_CONSECUIVE_FAILURES = 5
62};
63
64static void
65webseed_free( struct tr_webseed * w )
66{
67    tr_bitsetDestruct( &w->parent.have );
68    tr_free( w->parent.client );
69
70    event_free( w->timer );
71    tr_rcDestruct( &w->download_rate );
72    tr_free( w->base_url );
73    tr_free( w );
74}
75
76/***
77****
78***/
79
80static void
81publish( tr_webseed * w, tr_peer_event * e )
82{
83    if( w->callback != NULL )
84        w->callback( &w->parent, e, w->callback_data );
85}
86
87static void
88fire_client_got_rej( tr_torrent * tor, tr_webseed * w, tr_block_index_t block )
89{
90    tr_peer_event e = TR_PEER_EVENT_INIT;
91    e.eventType = TR_PEER_CLIENT_GOT_REJ;
92    tr_torrentGetBlockLocation( tor, block, &e.pieceIndex, &e.offset, &e.length );
93    publish( w, &e );
94}
95
96static void
97fire_client_got_block( tr_torrent * tor, tr_webseed * w, tr_block_index_t block )
98{
99    tr_peer_event e = TR_PEER_EVENT_INIT;
100    e.eventType = TR_PEER_CLIENT_GOT_BLOCK;
101    tr_torrentGetBlockLocation( tor, block, &e.pieceIndex, &e.offset, &e.length );
102    publish( w, &e );
103}
104
105static void
106fire_client_got_data( tr_webseed * w, uint32_t length )
107{
108    tr_peer_event e = TR_PEER_EVENT_INIT;
109    e.eventType = TR_PEER_CLIENT_GOT_DATA;
110    e.length = length;
111    e.wasPieceData = TRUE;
112    publish( w, &e );
113}
114
115/***
116****
117***/
118
119static void
120on_content_changed( struct evbuffer                * buf UNUSED,
121                    const struct evbuffer_cb_info  * info,
122                    void                           * vw )
123{
124    tr_webseed * w = vw;
125
126    if( ( info->n_added > 0 ) && !w->is_stopping )
127    {
128        tr_rcTransferred( &w->download_rate, info->n_added );
129        fire_client_got_data( w, info->n_added );
130    }
131}
132
133static void task_request_next_chunk( struct tr_webseed_task * task );
134
135static tr_bool
136webseed_has_tasks( const tr_webseed * w )
137{
138    return w->tasks != NULL;
139}
140
141
142static void
143on_idle( tr_webseed * w )
144{
145    tr_torrent * tor = tr_torrentFindFromId( w->session, w->torrent_id );
146
147    if( w->is_stopping && !webseed_has_tasks( w ) )
148    {
149        webseed_free( w );
150    }
151    else if( !w->is_stopping && tor
152                             && tor->isRunning
153                             && !tr_torrentIsSeed( tor )
154                             && ( w->consecutive_failures < MAX_CONSECUIVE_FAILURES ) )
155    {
156        int i;
157        int got = 0;
158        const int max = tor->blockCountInPiece;
159        const int want = max - tr_list_size( w->tasks );
160        tr_block_index_t * blocks = NULL;
161
162        if( want > 0 )
163        {
164            blocks = tr_new( tr_block_index_t, want );
165            tr_peerMgrGetNextRequests( tor, &w->parent, want, blocks, &got );
166        }
167
168        for( i=0; i<got; ++i )
169        {
170            const tr_block_index_t b = blocks[i];
171            struct tr_webseed_task * task = tr_new( struct tr_webseed_task, 1 );
172            task->webseed = w;
173            task->session = w->session;
174            task->torrent_id = w->torrent_id;
175            task->block = b;
176            task->piece_index = tr_torBlockPiece( tor, b );
177            task->piece_offset = ( tor->blockSize * b )
178                                - ( tor->info.pieceSize * task->piece_index );
179            task->length = tr_torBlockCountBytes( tor, b );
180            task->content = evbuffer_new( );
181            evbuffer_add_cb( task->content, on_content_changed, w );
182            tr_list_append( &w->tasks, task );
183            task_request_next_chunk( task );
184        }
185
186        tr_free( blocks );
187    }
188}
189
190
191static void
192web_response_func( tr_session    * session,
193                   tr_bool         did_connect UNUSED,
194                   tr_bool         did_timeout UNUSED,
195                   long            response_code,
196                   const void    * response UNUSED,
197                   size_t          response_byte_count UNUSED,
198                   void          * vtask )
199{
200    struct tr_webseed_task * t = vtask;
201    tr_webseed * w = t->webseed;
202    tr_torrent * tor = tr_torrentFindFromId( session, t->torrent_id );
203    const int success = ( response_code == 206 );
204
205    if( success )
206        w->consecutive_failures = 0;
207    else
208        ++w->consecutive_failures;
209
210    if( tor )
211    {
212        if( !success )
213        {
214            fire_client_got_rej( tor, w, t->block );
215
216            tr_list_remove_data( &w->tasks, t );
217            evbuffer_free( t->content );
218            tr_free( t );
219        }
220        else
221        {
222            if( evbuffer_get_length( t->content ) < t->length )
223            {
224                task_request_next_chunk( t );
225            }
226            else
227            {
228                tr_cacheWriteBlock( session->cache, tor,
229                                    t->piece_index, t->piece_offset, t->length,
230                                    t->content );
231                fire_client_got_block( tor, w, t->block );
232
233                tr_list_remove_data( &w->tasks, t );
234                evbuffer_free( t->content );
235                tr_free( t );
236
237                on_idle( w );
238            }
239        }
240    }
241}
242
243static char*
244make_url( tr_webseed * w, const tr_file * file )
245{
246    struct evbuffer * out = evbuffer_new( );
247
248    evbuffer_add( out, w->base_url, w->base_url_len );
249
250    /* if url ends with a '/', add the torrent name */
251    if( w->base_url[w->base_url_len - 1] == '/' && file->name )
252        tr_http_escape( out, file->name, strlen(file->name), FALSE );
253
254    return evbuffer_free_to_str( out );
255}
256
257static void
258task_request_next_chunk( struct tr_webseed_task * t )
259{
260    tr_torrent * tor = tr_torrentFindFromId( t->session, t->torrent_id );
261    if( tor != NULL )
262    {
263        char * url;
264        char range[64];
265
266        const tr_info * inf = tr_torrentInfo( tor );
267        const uint32_t remain = t->length - evbuffer_get_length( t->content );
268
269        const uint64_t total_offset = inf->pieceSize * t->piece_index
270                                    + t->piece_offset
271                                    + evbuffer_get_length( t->content );
272        const tr_piece_index_t step_piece = total_offset / inf->pieceSize;
273        const uint32_t step_piece_offset
274                               = total_offset - ( inf->pieceSize * step_piece );
275
276        tr_file_index_t file_index;
277        uint64_t file_offset;
278        const tr_file * file;
279        uint32_t this_pass;
280
281        tr_ioFindFileLocation( tor, step_piece, step_piece_offset,
282                                    &file_index, &file_offset );
283        file = &inf->files[file_index];
284        this_pass = MIN( remain, file->length - file_offset );
285
286        url = make_url( t->webseed, file );
287        tr_snprintf( range, sizeof range, "%"PRIu64"-%"PRIu64,
288                     file_offset, file_offset + this_pass - 1 );
289        tr_webRunWithBuffer( t->session, url, range,
290                             web_response_func, t, t->content );
291        tr_free( url );
292    }
293}
294
295tr_bool
296tr_webseedGetSpeed_Bps( const tr_webseed * w, uint64_t now, int * setme_Bps )
297{
298    const tr_bool is_active = webseed_has_tasks( w );
299    *setme_Bps = is_active ? tr_rcRate_Bps( &w->download_rate, now ) : 0;
300    return is_active;
301}
302
303tr_bool
304tr_webseedIsActive( const tr_webseed * w )
305{
306    int Bps = 0;
307    return tr_webseedGetSpeed_Bps( w, tr_time_msec(), &Bps ) && ( Bps > 0 );
308}
309
310/***
311****
312***/
313
314static void
315webseed_timer_func( evutil_socket_t foo UNUSED, short bar UNUSED, void * vw )
316{
317    tr_webseed * w = vw;
318    on_idle( w );
319    tr_timerAddMsec( w->timer, TR_IDLE_TIMER_MSEC );
320}
321
322tr_webseed*
323tr_webseedNew( struct tr_torrent * tor,
324               const char        * url,
325               tr_peer_callback  * callback,
326               void              * callback_data )
327{
328    tr_webseed * w = tr_new0( tr_webseed, 1 );
329    tr_peer * peer = &w->parent;
330
331    peer->peerIsChoked = TRUE;
332    peer->clientIsInterested = !tr_torrentIsSeed( tor );
333    peer->progress = 1.0;
334    peer->client = tr_strdup( "webseed" );
335    peer->have = TR_BITSET_INIT;
336    tr_bitsetSetHaveAll( &peer->have );
337
338    w->torrent_id = tr_torrentId( tor );
339    w->session = tor->session;
340
341    w->base_url_len = strlen( url );
342    w->base_url = tr_strndup( url, w->base_url_len );
343    w->callback = callback;
344    w->callback_data = callback_data;
345    tr_rcConstruct( &w->download_rate );
346    w->timer = evtimer_new( w->session->event_base, webseed_timer_func, w );
347    tr_timerAddMsec( w->timer, TR_IDLE_TIMER_MSEC );
348    return w;
349}
350
351void
352tr_webseedFree( tr_webseed * w )
353{
354    if( w )
355    {
356        if( webseed_has_tasks( w ) )
357            w->is_stopping = TRUE;
358        else
359            webseed_free( w );
360    }
361}
Note: See TracBrowser for help on using the repository browser.