transmission: update from 2.13 to 2.22
[tomato.git] / release / src / router / transmission / libtransmission / webseed.c
blob7f4eb37ae4306eb5bbb9b66334736fb28aef9d71
1 /*
2 * This file Copyright (C) Mnemosyne LLC
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.
10 * $Id: webseed.c 12030 2011-02-24 15:33:50Z jordan $
13 #include <string.h> /* strlen() */
15 #include <event2/buffer.h>
16 #include <event2/event.h>
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"
29 struct tr_webseed_task
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;
41 struct tr_webseed
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;
57 enum
59 TR_IDLE_TIMER_MSEC = 2000,
61 MAX_CONSECUIVE_FAILURES = 5
64 static void
65 webseed_free( struct tr_webseed * w )
67 tr_bitsetDestructor( &w->parent.have );
68 tr_free( w->parent.client );
70 event_free( w->timer );
71 tr_rcDestruct( &w->download_rate );
72 tr_free( w->base_url );
73 tr_free( w );
76 /***
77 ****
78 ***/
80 static const tr_peer_event blank_event = { 0, 0, 0, 0, 0, 0, 0 };
82 static void
83 publish( tr_webseed * w, tr_peer_event * e )
85 if( w->callback != NULL )
86 w->callback( &w->parent, e, w->callback_data );
89 static void
90 fire_client_got_rej( tr_torrent * tor, tr_webseed * w, tr_block_index_t block )
92 tr_peer_event e = blank_event;
93 e.eventType = TR_PEER_CLIENT_GOT_REJ;
94 e.pieceIndex = tr_torBlockPiece( tor, block );
95 e.offset = tor->blockSize * block - tor->info.pieceSize * e.pieceIndex;
96 e.length = tr_torBlockCountBytes( tor, block );
97 publish( w, &e );
100 static void
101 fire_client_got_block( tr_torrent * tor, tr_webseed * w, tr_block_index_t block )
103 tr_peer_event e = blank_event;
104 e.eventType = TR_PEER_CLIENT_GOT_BLOCK;
105 e.pieceIndex = tr_torBlockPiece( tor, block );
106 e.offset = tor->blockSize * block - tor->info.pieceSize * e.pieceIndex;
107 e.length = tr_torBlockCountBytes( tor, block );
108 publish( w, &e );
111 static void
112 fire_client_got_data( tr_webseed * w, uint32_t length )
114 tr_peer_event e = blank_event;
115 e.eventType = TR_PEER_CLIENT_GOT_DATA;
116 e.length = length;
117 e.wasPieceData = TRUE;
118 publish( w, &e );
121 /***
122 ****
123 ***/
125 static void
126 on_content_changed( struct evbuffer * buf UNUSED,
127 const struct evbuffer_cb_info * info,
128 void * vw )
130 tr_webseed * w = vw;
132 if( ( info->n_added > 0 ) && !w->is_stopping )
134 tr_rcTransferred( &w->download_rate, info->n_added );
135 fire_client_got_data( w, info->n_added );
139 static void task_request_next_chunk( struct tr_webseed_task * task );
141 static tr_bool
142 webseed_has_tasks( const tr_webseed * w )
144 return w->tasks != NULL;
148 static void
149 on_idle( tr_webseed * w )
151 tr_torrent * tor = tr_torrentFindFromId( w->session, w->torrent_id );
153 if( w->is_stopping && !webseed_has_tasks( w ) )
155 webseed_free( w );
157 else if( !w->is_stopping && tor
158 && tor->isRunning
159 && !tr_torrentIsSeed( tor )
160 && ( w->consecutive_failures < MAX_CONSECUIVE_FAILURES ) )
162 int i;
163 int got = 0;
164 const int max = tor->blockCountInPiece;
165 const int want = max - tr_list_size( w->tasks );
166 tr_block_index_t * blocks = NULL;
168 if( want > 0 )
170 blocks = tr_new( tr_block_index_t, want );
171 tr_peerMgrGetNextRequests( tor, &w->parent, want, blocks, &got );
174 for( i=0; i<got; ++i )
176 const tr_block_index_t b = blocks[i];
177 struct tr_webseed_task * task = tr_new( struct tr_webseed_task, 1 );
178 task->webseed = w;
179 task->session = w->session;
180 task->torrent_id = w->torrent_id;
181 task->block = b;
182 task->piece_index = tr_torBlockPiece( tor, b );
183 task->piece_offset = ( tor->blockSize * b )
184 - ( tor->info.pieceSize * task->piece_index );
185 task->length = tr_torBlockCountBytes( tor, b );
186 task->content = evbuffer_new( );
187 evbuffer_add_cb( task->content, on_content_changed, w );
188 tr_list_append( &w->tasks, task );
189 task_request_next_chunk( task );
192 tr_free( blocks );
197 static void
198 web_response_func( tr_session * session,
199 long response_code,
200 const void * response UNUSED,
201 size_t response_byte_count UNUSED,
202 void * vtask )
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 );
209 if( success )
210 w->consecutive_failures = 0;
211 else
212 ++w->consecutive_failures;
214 if( tor )
216 if( !success )
218 fire_client_got_rej( tor, w, t->block );
220 tr_list_remove_data( &w->tasks, t );
221 evbuffer_free( t->content );
222 tr_free( t );
224 else
226 if( evbuffer_get_length( t->content ) < t->length )
228 task_request_next_chunk( t );
230 else
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 );
237 tr_list_remove_data( &w->tasks, t );
238 evbuffer_free( t->content );
239 tr_free( t );
241 on_idle( w );
247 static char*
248 make_url( tr_webseed * w, const tr_file * file )
250 struct evbuffer * out = evbuffer_new( );
252 evbuffer_add( out, w->base_url, w->base_url_len );
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 );
258 return evbuffer_free_to_str( out );
261 static void
262 task_request_next_chunk( struct tr_webseed_task * t )
264 tr_torrent * tor = tr_torrentFindFromId( t->session, t->torrent_id );
265 if( tor != NULL )
267 char * url;
268 char range[64];
270 const tr_info * inf = tr_torrentInfo( tor );
271 const uint32_t remain = t->length - evbuffer_get_length( t->content );
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 );
280 tr_file_index_t file_index;
281 uint64_t file_offset;
282 const tr_file * file;
283 uint32_t this_pass;
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 );
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,
294 web_response_func, t, t->content );
295 tr_free( url );
299 tr_bool
300 tr_webseedGetSpeed_Bps( const tr_webseed * w, uint64_t now, int * setme_Bps )
302 const tr_bool is_active = webseed_has_tasks( w );
303 *setme_Bps = is_active ? tr_rcRate_Bps( &w->download_rate, now ) : 0;
304 return is_active;
307 tr_bool
308 tr_webseedIsActive( const tr_webseed * w )
310 int Bps = 0;
311 return tr_webseedGetSpeed_Bps( w, tr_time_msec(), &Bps ) && ( Bps > 0 );
314 /***
315 ****
316 ***/
318 static void
319 webseed_timer_func( evutil_socket_t foo UNUSED, short bar UNUSED, void * vw )
321 tr_webseed * w = vw;
322 on_idle( w );
323 tr_timerAddMsec( w->timer, TR_IDLE_TIMER_MSEC );
326 tr_webseed*
327 tr_webseedNew( struct tr_torrent * tor,
328 const char * url,
329 tr_peer_callback * callback,
330 void * callback_data )
332 tr_webseed * w = tr_new0( tr_webseed, 1 );
333 tr_peer * peer = &w->parent;
335 peer->peerIsChoked = TRUE;
336 peer->clientIsInterested = !tr_torrentIsSeed( tor );
337 tr_bitsetConstructor( &peer->have, tor->info.pieceCount );
338 tr_bitsetSetHaveAll( &peer->have );
339 peer->progress = 1.0;
340 peer->client = tr_strdup( "webseed" );
342 w->torrent_id = tr_torrentId( tor );
343 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 w->timer = evtimer_new( w->session->event_base, webseed_timer_func, w );
351 tr_timerAddMsec( w->timer, TR_IDLE_TIMER_MSEC );
352 return w;
355 void
356 tr_webseedFree( tr_webseed * w )
358 if( w )
360 if( webseed_has_tasks( w ) )
361 w->is_stopping = TRUE;
362 else
363 webseed_free( w );