source: branches/1.4x/libtransmission/webseed.c @ 7455

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

(1.4x libT) backport handshake, peer, bandwidth, peer-io to 1.4x.

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