source: trunk/libtransmission/webseed.c @ 12248

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

(trunk libT) break the mac build and introduce new crashes.

This is partially to address #4145 "Downloads stuck at 100%" by refactoring the bitset, bitfield, and tr_completion; however, the ripple effect is larger than usual so things may get worse in the short term before getting better.

livings124: to fix the mac build, remove bitset.[ch] from xcode

  • Property svn:keywords set to Date Rev Author Id
File size: 10.0 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 12248 2011-03-28 16:31:05Z 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 "bandwidth.h"
20#include "cache.h"
21#include "inout.h" /* tr_ioFindFileLocation() */
22#include "list.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_bandwidth         bandwidth;
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    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_torrent * tor = tr_torrentFindFromId( w->session, w->torrent_id );
68
69    /* webseed destruct */
70    event_free( w->timer );
71    tr_bandwidthDestruct( &w->bandwidth );
72    tr_free( w->base_url );
73
74    /* parent class destruct */
75    tr_peerDestruct( tor, &w->parent );
76
77    tr_free( w );
78}
79
80/***
81****
82***/
83
84static void
85publish( tr_webseed * w, tr_peer_event * e )
86{
87    if( w->callback != NULL )
88        w->callback( &w->parent, e, w->callback_data );
89}
90
91static void
92fire_client_got_rej( tr_torrent * tor, tr_webseed * w, tr_block_index_t block )
93{
94    tr_peer_event e = TR_PEER_EVENT_INIT;
95    e.eventType = TR_PEER_CLIENT_GOT_REJ;
96    tr_torrentGetBlockLocation( tor, block, &e.pieceIndex, &e.offset, &e.length );
97    publish( w, &e );
98}
99
100static void
101fire_client_got_block( tr_torrent * tor, tr_webseed * w, tr_block_index_t block )
102{
103    tr_peer_event e = TR_PEER_EVENT_INIT;
104    e.eventType = TR_PEER_CLIENT_GOT_BLOCK;
105    tr_torrentGetBlockLocation( tor, block, &e.pieceIndex, &e.offset, &e.length );
106    publish( w, &e );
107}
108
109static void
110fire_client_got_data( tr_webseed * w, uint32_t length )
111{
112    tr_peer_event e = TR_PEER_EVENT_INIT;
113    e.eventType = TR_PEER_CLIENT_GOT_DATA;
114    e.length = length;
115    e.wasPieceData = true;
116    publish( w, &e );
117}
118
119/***
120****
121***/
122
123static void
124on_content_changed( struct evbuffer                * buf UNUSED,
125                    const struct evbuffer_cb_info  * info,
126                    void                           * vw )
127{
128    tr_webseed * w = vw;
129
130    if( ( info->n_added > 0 ) && !w->is_stopping )
131    {
132        tr_bandwidthUsed( &w->bandwidth, TR_DOWN, info->n_added, true, tr_time_msec( ) );
133        fire_client_got_data( w, info->n_added );
134    }
135}
136
137static void task_request_next_chunk( struct tr_webseed_task * task );
138
139static bool
140webseed_has_tasks( const tr_webseed * w )
141{
142    return w->tasks != NULL;
143}
144
145
146static void
147on_idle( tr_webseed * w )
148{
149    tr_torrent * tor = tr_torrentFindFromId( w->session, w->torrent_id );
150
151    if( w->is_stopping && !webseed_has_tasks( w ) )
152    {
153        webseed_free( w );
154    }
155    else if( !w->is_stopping && tor
156                             && tor->isRunning
157                             && !tr_torrentIsSeed( tor )
158                             && ( w->consecutive_failures < MAX_CONSECUIVE_FAILURES ) )
159    {
160        int i;
161        int got = 0;
162        const int max = tor->blockCountInPiece;
163        const int want = max - tr_list_size( w->tasks );
164        tr_block_index_t * blocks = NULL;
165
166        if( want > 0 )
167        {
168            blocks = tr_new( tr_block_index_t, want );
169            tr_peerMgrGetNextRequests( tor, &w->parent, want, blocks, &got );
170        }
171
172        for( i=0; i<got; ++i )
173        {
174            const tr_block_index_t b = blocks[i];
175            struct tr_webseed_task * task = tr_new( struct tr_webseed_task, 1 );
176            task->webseed = w;
177            task->session = w->session;
178            task->torrent_id = w->torrent_id;
179            task->block = b;
180            task->piece_index = tr_torBlockPiece( tor, b );
181            task->piece_offset = ( tor->blockSize * b )
182                                - ( tor->info.pieceSize * task->piece_index );
183            task->length = tr_torBlockCountBytes( tor, b );
184            task->content = evbuffer_new( );
185            evbuffer_add_cb( task->content, on_content_changed, w );
186            tr_list_append( &w->tasks, task );
187            task_request_next_chunk( task );
188        }
189
190        tr_free( blocks );
191    }
192}
193
194
195static void
196web_response_func( tr_session    * session,
197                   bool            did_connect UNUSED,
198                   bool            did_timeout UNUSED,
199                   long            response_code,
200                   const void    * response UNUSED,
201                   size_t          response_byte_count UNUSED,
202                   void          * vtask )
203{
204    struct tr_webseed_task * t = vtask;
205    tr_webseed * w = t->webseed;
206    tr_torrent * tor = tr_torrentFindFromId( session, t->torrent_id );
207    const int success = ( response_code == 206 );
208
209    if( success )
210        w->consecutive_failures = 0;
211    else
212        ++w->consecutive_failures;
213
214    if( tor )
215    {
216        if( !success )
217        {
218            fire_client_got_rej( tor, w, t->block );
219
220            tr_list_remove_data( &w->tasks, t );
221            evbuffer_free( t->content );
222            tr_free( t );
223        }
224        else
225        {
226            if( evbuffer_get_length( t->content ) < t->length )
227            {
228                task_request_next_chunk( t );
229            }
230            else
231            {
232                tr_cacheWriteBlock( session->cache, tor,
233                                    t->piece_index, t->piece_offset, t->length,
234                                    t->content );
235                fire_client_got_block( tor, w, t->block );
236
237                tr_list_remove_data( &w->tasks, t );
238                evbuffer_free( t->content );
239                tr_free( t );
240
241                on_idle( w );
242            }
243        }
244    }
245}
246
247static char*
248make_url( tr_webseed * w, const tr_file * file )
249{
250    struct evbuffer * out = evbuffer_new( );
251
252    evbuffer_add( out, w->base_url, w->base_url_len );
253
254    /* if url ends with a '/', add the torrent name */
255    if( w->base_url[w->base_url_len - 1] == '/' && file->name )
256        tr_http_escape( out, file->name, strlen(file->name), false );
257
258    return evbuffer_free_to_str( out );
259}
260
261static void
262task_request_next_chunk( struct tr_webseed_task * t )
263{
264    tr_torrent * tor = tr_torrentFindFromId( t->session, t->torrent_id );
265    if( tor != NULL )
266    {
267        char * url;
268        char range[64];
269
270        const tr_info * inf = tr_torrentInfo( tor );
271        const uint32_t remain = t->length - evbuffer_get_length( t->content );
272
273        const uint64_t total_offset = inf->pieceSize * t->piece_index
274                                    + t->piece_offset
275                                    + evbuffer_get_length( t->content );
276        const tr_piece_index_t step_piece = total_offset / inf->pieceSize;
277        const uint32_t step_piece_offset
278                               = total_offset - ( inf->pieceSize * step_piece );
279
280        tr_file_index_t file_index;
281        uint64_t file_offset;
282        const tr_file * file;
283        uint32_t this_pass;
284
285        tr_ioFindFileLocation( tor, step_piece, step_piece_offset,
286                                    &file_index, &file_offset );
287        file = &inf->files[file_index];
288        this_pass = MIN( remain, file->length - file_offset );
289
290        url = make_url( t->webseed, file );
291        tr_snprintf( range, sizeof range, "%"PRIu64"-%"PRIu64,
292                     file_offset, file_offset + this_pass - 1 );
293        tr_webRunWithBuffer( t->session, url, range, NULL,
294                             web_response_func, t, t->content );
295        tr_free( url );
296    }
297}
298
299bool
300tr_webseedGetSpeed_Bps( const tr_webseed * w, uint64_t now, int * setme_Bps )
301{
302    const bool is_active = webseed_has_tasks( w );
303    *setme_Bps = is_active ? tr_bandwidthGetPieceSpeed_Bps( &w->bandwidth, now, TR_DOWN ) : 0;
304    return is_active;
305}
306
307bool
308tr_webseedIsActive( const tr_webseed * w )
309{
310    int Bps = 0;
311    return tr_webseedGetSpeed_Bps( w, tr_time_msec(), &Bps ) && ( Bps > 0 );
312}
313
314/***
315****
316***/
317
318static void
319webseed_timer_func( evutil_socket_t foo UNUSED, short bar UNUSED, void * vw )
320{
321    tr_webseed * w = vw;
322    on_idle( w );
323    tr_timerAddMsec( w->timer, TR_IDLE_TIMER_MSEC );
324}
325
326tr_webseed*
327tr_webseedNew( struct tr_torrent  * tor,
328               const char         * url,
329               tr_peer_callback   * callback,
330               void               * callback_data )
331{
332    tr_webseed * w = tr_new0( tr_webseed, 1 );
333    tr_peer * peer = &w->parent;
334
335    /* construct parent class */
336    tr_peerConstruct( peer );
337    peer->peerIsChoked = true;
338    peer->clientIsInterested = !tr_torrentIsSeed( tor );
339    peer->client = tr_strdup( "webseed" );
340    tr_bitfieldSetHasAll( &peer->have );
341    tr_peerUpdateProgress( tor, peer );
342
343    w->torrent_id = tr_torrentId( tor );
344    w->session = tor->session;
345    w->base_url_len = strlen( url );
346    w->base_url = tr_strndup( url, w->base_url_len );
347    w->callback = callback;
348    w->callback_data = callback_data;
349    //tr_rcConstruct( &w->download_rate );
350    tr_bandwidthConstruct( &w->bandwidth, tor->session, tor->bandwidth );
351    w->timer = evtimer_new( w->session->event_base, webseed_timer_func, w );
352    tr_timerAddMsec( w->timer, TR_IDLE_TIMER_MSEC );
353    return w;
354}
355
356void
357tr_webseedFree( tr_webseed * w )
358{
359    if( w )
360    {
361        if( webseed_has_tasks( w ) )
362            w->is_stopping = true;
363        else
364            webseed_free( w );
365    }
366}
Note: See TracBrowser for help on using the repository browser.