source: trunk/libtransmission/webseed.c @ 12204

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

(trunk) #4138 "use stdbool.h instead of tr_bool" -- done.

  • 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 12204 2011-03-22 15:19:54Z 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_bitsetSetHaveAll( &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.