source: trunk/libtransmission/webseed.c @ 7125

Last change on this file since 7125 was 7125, checked in by charles, 12 years ago

(libT) better possible fix for #1468: Speed display is very jumpy

  • Property svn:keywords set to Date Rev Author Id
File size: 9.0 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
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 7125 2008-11-17 04:00:57Z charles $
11 */
12
13#include <assert.h>
14#include <string.h> /* strlen */
15
16#include <event.h>
17
18#include "transmission.h"
19#include "inout.h"
20#include "list.h"
21#include "ratecontrol.h"
22#include "torrent.h"
23#include "utils.h"
24#include "web.h"
25#include "webseed.h"
26
27struct tr_webseed
28{
29    unsigned int        busy : 1;
30    unsigned int        dead : 1;
31
32    uint8_t             hash[SHA_DIGEST_LENGTH];
33
34    char              * url;
35
36    tr_delivery_func  * callback;
37    void *              callback_userdata;
38
39    tr_piece_index_t    pieceIndex;
40    uint32_t            pieceOffset;
41    uint32_t            byteCount;
42
43    tr_ratecontrol    * rateDown;
44
45    tr_session        * session;
46
47    struct evbuffer   * content;
48};
49
50/***
51****
52***/
53
54static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0, 0 };
55
56static void
57publish( tr_webseed *    w,
58         tr_peer_event * e )
59{
60    if( w->callback )
61        w->callback( NULL, e, w->callback_userdata );
62}
63
64static void
65fireNeedReq( tr_webseed * w )
66{
67    tr_peer_event e = blankEvent;
68
69    e.eventType = TR_PEER_NEED_REQ;
70    publish( w, &e );
71}
72
73static void
74fireClientGotBlock( tr_webseed * w,
75                    uint32_t     pieceIndex,
76                    uint32_t     offset,
77                    uint32_t     length )
78{
79    tr_peer_event e = blankEvent;
80
81    e.eventType = TR_PEER_CLIENT_GOT_BLOCK;
82    e.pieceIndex = pieceIndex;
83    e.offset = offset;
84    e.length = length;
85    publish( w, &e );
86}
87
88static void
89fireClientGotData( tr_webseed * w,
90                   uint32_t     length )
91{
92    tr_peer_event e = blankEvent;
93
94    e.eventType = TR_PEER_CLIENT_GOT_DATA;
95    e.length = length;
96    e.wasPieceData = TRUE;
97
98    publish( w, &e );
99}
100
101/***
102****
103***/
104
105static char*
106makeURL( tr_webseed *    w,
107         const tr_file * file )
108{
109    char *            ret;
110    struct evbuffer * out = evbuffer_new( );
111    const char *      url = w->url;
112    const size_t      url_len = strlen( url );
113
114    evbuffer_add( out, url, url_len );
115
116    /* if url ends with a '/', add the torrent name */
117    if( url[url_len - 1] == '/' )
118    {
119        const char * str = file->name;
120
121        /* this is like curl_escape() but doesn't munge the
122         * '/' directory separators in the path */
123        while( str && *str )
124        {
125            switch( *str )
126            {
127                case ',':
128                case '-':
129                case '.':
130                case '/':
131                case '0':
132                case '1':
133                case '2':
134                case '3':
135                case '4':
136                case '5':
137                case '6':
138                case '7':
139                case '8':
140                case '9':
141                case 'a':
142                case 'b':
143                case 'c':
144                case 'd':
145                case 'e':
146                case 'f':
147                case 'g':
148                case 'h':
149                case 'i':
150                case 'j':
151                case 'k':
152                case 'l':
153                case 'm':
154                case 'n':
155                case 'o':
156                case 'p':
157                case 'q':
158                case 'r':
159                case 's':
160                case 't':
161                case 'u':
162                case 'v':
163                case 'w':
164                case 'x':
165                case 'y':
166                case 'z':
167                case 'A':
168                case 'B':
169                case 'C':
170                case 'D':
171                case 'E':
172                case 'F':
173                case 'G':
174                case 'H':
175                case 'I':
176                case 'J':
177                case 'K':
178                case 'L':
179                case 'M':
180                case 'N':
181                case 'O':
182                case 'P':
183                case 'Q':
184                case 'R':
185                case 'S':
186                case 'T':
187                case 'U':
188                case 'V':
189                case 'W':
190                case 'X':
191                case 'Y':
192                case 'Z':
193                    evbuffer_add( out, str, 1 );
194                    break;
195
196                default:
197                    evbuffer_add_printf( out, "%%%02X", *str );
198                    break;
199            }
200            str++;
201        }
202    }
203
204    ret = tr_strndup( EVBUFFER_DATA( out ), EVBUFFER_LENGTH( out ) );
205    evbuffer_free( out );
206    return ret;
207}
208
209static void requestNextChunk( tr_webseed * w );
210
211static void
212webResponseFunc( tr_handle     * session,
213                 long            response_code,
214                 const void    * response,
215                 size_t          response_byte_count,
216                 void          * vw )
217{
218    tr_webseed * w = vw;
219    tr_torrent * tor = tr_torrentFindFromHash( session, w->hash );
220    const int    success = ( response_code == 206 );
221
222/*fprintf( stderr, "server responded with code %ld and %lu bytes\n",
223  response_code, (unsigned long)response_byte_count );*/
224    if( !success )
225    {
226        /* FIXME */
227    }
228    else if( tor != NULL )
229    {
230        evbuffer_add( w->content, response, response_byte_count );
231        if( !w->dead )
232        {
233            fireClientGotData( w, response_byte_count );
234            tr_rcTransferred( w->rateDown, response_byte_count );
235        }
236
237        if( EVBUFFER_LENGTH( w->content ) < w->byteCount )
238            requestNextChunk( w );
239        else {
240            tr_ioWrite( tor, w->pieceIndex, w->pieceOffset, w->byteCount, EVBUFFER_DATA(w->content) );
241            evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) );
242            w->busy = 0;
243            if( w->dead )
244                tr_webseedFree( w );
245            else  {
246                fireClientGotBlock( w, w->pieceIndex, w->pieceOffset, w->byteCount );
247                fireNeedReq( w );
248            }
249        }
250    }
251}
252
253static void
254requestNextChunk( tr_webseed * w )
255{
256    tr_torrent * tor = tr_torrentFindFromHash( w->session, w->hash );
257
258    if( tor != NULL )
259    {
260        const tr_info * inf = tr_torrentInfo( tor );
261        const uint32_t have = EVBUFFER_LENGTH( w->content );
262        const uint32_t left = w->byteCount - have;
263        const uint32_t pieceOffset = w->pieceOffset + have;
264        tr_file_index_t fileIndex;
265        uint64_t fileOffset;
266        uint32_t thisPass;
267        char * url;
268        char * range;
269
270        tr_ioFindFileLocation( tor, w->pieceIndex, pieceOffset,
271                               &fileIndex, &fileOffset );
272        thisPass = MIN( left, inf->files[fileIndex].length - fileOffset );
273
274        url = makeURL( w, &inf->files[fileIndex] );
275/*fprintf( stderr, "url is [%s]\n", url );*/
276        range = tr_strdup_printf( "%"PRIu64"-%"PRIu64, fileOffset, fileOffset + thisPass - 1 );
277/*fprintf( stderr, "range is [%s] ... we want %lu total, we have %lu, so %lu are left, and we're asking for %lu this time\n", range, (unsigned long)w->byteCount, (unsigned long)have, (unsigned long)left, (unsigned long)thisPass );*/
278        tr_webRun( w->session, url, range, webResponseFunc, w );
279        tr_free( range );
280        tr_free( url );
281    }
282}
283
284tr_addreq_t
285tr_webseedAddRequest( tr_webseed  * w,
286                      uint32_t      pieceIndex,
287                      uint32_t      pieceOffset,
288                      uint32_t      byteCount )
289{
290    int ret;
291
292    if( w->busy || w->dead )
293    {
294        ret = TR_ADDREQ_FULL;
295    }
296    else
297    {
298        w->busy = 1;
299        w->pieceIndex = pieceIndex;
300        w->pieceOffset = pieceOffset;
301        w->byteCount = byteCount;
302        evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) );
303        requestNextChunk( w );
304        ret = TR_ADDREQ_OK;
305    }
306
307    return ret;
308}
309
310int
311tr_webseedIsActive( const tr_webseed * w )
312{
313    return w->busy != 0;
314}
315
316int
317tr_webseedGetSpeed( const tr_webseed * w,
318                    float *            setme_KiBs )
319{
320    const int isActive = tr_webseedIsActive( w );
321
322    *setme_KiBs = isActive ? tr_rcRate( w->rateDown ) : 0.0f;
323    return isActive;
324}
325
326/***
327****
328***/
329
330tr_webseed*
331tr_webseedNew( struct tr_torrent * torrent,
332               const char *        url,
333               tr_delivery_func    callback,
334               void *              callback_userdata )
335{
336    tr_webseed * w = tr_new0( tr_webseed, 1 );
337
338    memcpy( w->hash, torrent->info.hash, SHA_DIGEST_LENGTH );
339    w->session = torrent->session;
340    w->content = evbuffer_new( );
341    w->rateDown = tr_rcInit( );
342    w->url = tr_strdup( url );
343    w->callback = callback;
344    w->callback_userdata = callback_userdata;
345/*fprintf( stderr, "w->callback_userdata is %p\n", w->callback_userdata );*/
346    return w;
347}
348
349void
350tr_webseedFree( tr_webseed * w )
351{
352    if( w )
353    {
354        if( w->busy )
355        {
356            w->dead = 1;
357        }
358        else
359        {
360            evbuffer_free( w->content );
361            tr_rcClose( w->rateDown );
362            tr_free( w->url );
363            tr_free( w );
364        }
365    }
366}
Note: See TracBrowser for help on using the repository browser.