source: trunk/libtransmission/webseed.c @ 11782

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

(trunk libT) memory cache should use evbuffers to avoid unnecessary calls to memcpy -- done.

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