source: trunk/libtransmission/webseed.c @ 7824

Last change on this file since 7824 was 7824, checked in by charles, 13 years ago

(trunk libT) #1748: possible fix for the kqueue corruption errors by consolidating the three per-torrent libevent timers into three session-wide timers. Since most people reporting this error have lots of torrents loaded, consider a hypothetical example: if you had 500 torrents, this patch will reduce 1,500 libevent timers down to just three timers. On top of that, those three have simpler life cycles too...

  • Property svn:keywords set to Date Rev Author Id
File size: 7.9 KB
Line 
1/*
2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.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 7824 2009-02-04 16:58:52Z 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    tr_bool             busy;
30    tr_bool             dead;
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, 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
65fireClientGotBlock( tr_webseed * w, uint32_t pieceIndex, uint32_t offset, uint32_t length )
66{
67    tr_peer_event e = blankEvent;
68    e.eventType = TR_PEER_CLIENT_GOT_BLOCK;
69    e.pieceIndex = pieceIndex;
70    e.offset = offset;
71    e.length = length;
72    publish( w, &e );
73}
74
75static void
76fireClientGotData( tr_webseed * w, uint32_t length )
77{
78    tr_peer_event e = blankEvent;
79    e.eventType = TR_PEER_CLIENT_GOT_DATA;
80    e.length = length;
81    e.wasPieceData = TRUE;
82    publish( w, &e );
83}
84
85/***
86****
87***/
88
89static char*
90makeURL( tr_webseed *    w,
91         const tr_file * file )
92{
93    char *            ret;
94    struct evbuffer * out = tr_getBuffer( );
95    const char *      url = w->url;
96    const size_t      url_len = strlen( url );
97
98    evbuffer_add( out, url, url_len );
99
100    /* if url ends with a '/', add the torrent name */
101    if( url[url_len - 1] == '/' )
102    {
103        const char * str = file->name;
104
105        /* this is like curl_escape() but doesn't munge the
106         * '/' directory separators in the path */
107        while( str && *str )
108        {
109            switch( *str )
110            {
111                case ',': case '-': case '.': case '/':
112                case '0': case '1': case '2': case '3': case '4':
113                case '5': case '6': case '7': case '8': case '9':
114                case 'a': case 'b': case 'c': case 'd': case 'e':
115                case 'f': case 'g': case 'h': case 'i': case 'j':
116                case 'k': case 'l': case 'm': case 'n': case 'o':
117                case 'p': case 'q': case 'r': case 's': case 't':
118                case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
119                case 'A': case 'B': case 'C': case 'D': case 'E':
120                case 'F': case 'G': case 'H': case 'I': case 'J':
121                case 'K': case 'L': case 'M': case 'N': case 'O':
122                case 'P': case 'Q': case 'R': case 'S': case 'T':
123                case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
124                    evbuffer_add( out, str, 1 );
125                    break;
126                default:
127                    evbuffer_add_printf( out, "%%%02X", *str );
128                    break;
129            }
130            str++;
131        }
132    }
133
134    ret = tr_strndup( EVBUFFER_DATA( out ), EVBUFFER_LENGTH( out ) );
135    tr_releaseBuffer( out );
136    return ret;
137}
138
139static void requestNextChunk( tr_webseed * w );
140
141static void
142webResponseFunc( tr_session    * session,
143                 long            response_code,
144                 const void    * response,
145                 size_t          response_byte_count,
146                 void          * vw )
147{
148    tr_webseed * w = vw;
149    tr_torrent * tor = tr_torrentFindFromHash( session, w->hash );
150    const int    success = ( response_code == 206 );
151
152/*fprintf( stderr, "server responded with code %ld and %lu bytes\n",
153  response_code, (unsigned long)response_byte_count );*/
154    if( !success )
155    {
156        /* FIXME */
157    }
158    else if( tor != NULL )
159    {
160        evbuffer_add( w->content, response, response_byte_count );
161        if( !w->dead )
162        {
163            fireClientGotData( w, response_byte_count );
164            tr_rcTransferred( &w->rateDown, response_byte_count );
165        }
166
167        if( EVBUFFER_LENGTH( w->content ) < w->byteCount )
168            requestNextChunk( w );
169        else {
170            tr_ioWrite( tor, w->pieceIndex, w->pieceOffset, w->byteCount, EVBUFFER_DATA(w->content) );
171            evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) );
172            w->busy = 0;
173            if( w->dead )
174                tr_webseedFree( w );
175            else
176                fireClientGotBlock( w, w->pieceIndex, w->pieceOffset, w->byteCount );
177        }
178    }
179}
180
181static void
182requestNextChunk( tr_webseed * w )
183{
184    tr_torrent * tor = tr_torrentFindFromHash( w->session, w->hash );
185
186    if( tor != NULL )
187    {
188        const tr_info * inf = tr_torrentInfo( tor );
189        const uint32_t have = EVBUFFER_LENGTH( w->content );
190        const uint32_t left = w->byteCount - have;
191        const uint32_t pieceOffset = w->pieceOffset + have;
192        tr_file_index_t fileIndex;
193        uint64_t fileOffset;
194        uint32_t thisPass;
195        char * url;
196        char * range;
197
198        tr_ioFindFileLocation( tor, w->pieceIndex, pieceOffset,
199                               &fileIndex, &fileOffset );
200        thisPass = MIN( left, inf->files[fileIndex].length - fileOffset );
201
202        url = makeURL( w, &inf->files[fileIndex] );
203/*fprintf( stderr, "url is [%s]\n", url );*/
204        range = tr_strdup_printf( "%"PRIu64"-%"PRIu64, fileOffset, fileOffset + thisPass - 1 );
205/*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 );*/
206        tr_webRun( w->session, url, range, webResponseFunc, w );
207        tr_free( range );
208        tr_free( url );
209    }
210}
211
212tr_addreq_t
213tr_webseedAddRequest( tr_webseed  * w,
214                      uint32_t      pieceIndex,
215                      uint32_t      pieceOffset,
216                      uint32_t      byteCount )
217{
218    int ret;
219
220    if( w->busy || w->dead )
221    {
222        ret = TR_ADDREQ_FULL;
223    }
224    else
225    {
226        w->busy = 1;
227        w->pieceIndex = pieceIndex;
228        w->pieceOffset = pieceOffset;
229        w->byteCount = byteCount;
230        evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) );
231        requestNextChunk( w );
232        ret = TR_ADDREQ_OK;
233    }
234
235    return ret;
236}
237
238int
239tr_webseedIsActive( const tr_webseed * w )
240{
241    return w->busy != 0;
242}
243
244int
245tr_webseedGetSpeed( const tr_webseed * w, uint64_t now, float * setme_KiBs )
246{
247    const int isActive = tr_webseedIsActive( w );
248
249    *setme_KiBs = isActive ? tr_rcRate( &w->rateDown, now ) : 0.0f;
250    return isActive;
251}
252
253/***
254****
255***/
256
257tr_webseed*
258tr_webseedNew( struct tr_torrent * torrent,
259               const char *        url,
260               tr_delivery_func    callback,
261               void *              callback_userdata )
262{
263    tr_webseed * w = tr_new0( tr_webseed, 1 );
264
265    memcpy( w->hash, torrent->info.hash, SHA_DIGEST_LENGTH );
266    w->session = torrent->session;
267    w->content = evbuffer_new( );
268    w->url = tr_strdup( url );
269    w->callback = callback;
270    w->callback_userdata = callback_userdata;
271    tr_rcConstruct( &w->rateDown );
272/*fprintf( stderr, "w->callback_userdata is %p\n", w->callback_userdata );*/
273    return w;
274}
275
276void
277tr_webseedFree( tr_webseed * w )
278{
279    if( w )
280    {
281        if( w->busy )
282        {
283            w->dead = 1;
284        }
285        else
286        {
287            evbuffer_free( w->content );
288            tr_rcDestruct( &w->rateDown );
289            tr_free( w->url );
290            tr_free( w );
291        }
292    }
293}
Note: See TracBrowser for help on using the repository browser.