source: trunk/libtransmission/webseed.c @ 9626

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

(trunk libT) resolve "noslashes" vs "keep_slashes" variable name inconsistency between the declaration and the definition of tr_http_escape()

  • Property svn:keywords set to Date Rev Author Id
File size: 6.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 9626 2009-11-29 08:05:47Z 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, 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#if 0
68    tr_peer_event e = blankEvent;
69    e.eventType = TR_PEER_NEED_REQ;
70    publish( w, &e );
71#endif
72}
73
74static void
75fireClientGotBlock( tr_webseed * w, uint32_t pieceIndex, uint32_t offset, uint32_t length )
76{
77    tr_peer_event e = blankEvent;
78    e.eventType = TR_PEER_CLIENT_GOT_BLOCK;
79    e.pieceIndex = pieceIndex;
80    e.offset = offset;
81    e.length = length;
82    publish( w, &e );
83}
84
85static void
86fireClientGotData( tr_webseed * w, uint32_t length )
87{
88    tr_peer_event e = blankEvent;
89    e.eventType = TR_PEER_CLIENT_GOT_DATA;
90    e.length = length;
91    e.wasPieceData = TRUE;
92    publish( w, &e );
93}
94
95/***
96****
97***/
98
99static char*
100makeURL( tr_webseed *    w,
101         const tr_file * file )
102{
103    char *            ret;
104    struct evbuffer * out = evbuffer_new( );
105    const char *      url = w->url;
106    const size_t      url_len = strlen( url );
107
108    evbuffer_add( out, url, url_len );
109
110    /* if url ends with a '/', add the torrent name */
111    if( url[url_len - 1] == '/' && file->name )
112        tr_http_escape( out, file->name, strlen(file->name), FALSE );
113
114    ret = tr_strndup( EVBUFFER_DATA( out ), EVBUFFER_LENGTH( out ) );
115    evbuffer_free( out );
116    return ret;
117}
118
119static void requestNextChunk( tr_webseed * w );
120
121static void
122webResponseFunc( tr_session    * session,
123                 long            response_code,
124                 const void    * response,
125                 size_t          response_byte_count,
126                 void          * vw )
127{
128    tr_webseed * w = vw;
129    tr_torrent * tor = tr_torrentFindFromHash( session, w->hash );
130    const int    success = ( response_code == 206 );
131
132/*fprintf( stderr, "server responded with code %ld and %lu bytes\n",
133  response_code, (unsigned long)response_byte_count );*/
134    if( !success )
135    {
136        /* FIXME */
137    }
138    else if( tor != NULL )
139    {
140        evbuffer_add( w->content, response, response_byte_count );
141        if( !w->dead )
142        {
143            fireClientGotData( w, response_byte_count );
144            tr_rcTransferred( &w->rateDown, response_byte_count );
145        }
146
147        if( EVBUFFER_LENGTH( w->content ) < w->byteCount )
148            requestNextChunk( w );
149        else {
150            tr_ioWrite( tor, w->pieceIndex, w->pieceOffset, w->byteCount, EVBUFFER_DATA(w->content) );
151            evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) );
152            w->busy = 0;
153            if( w->dead )
154                tr_webseedFree( w );
155            else  {
156                fireClientGotBlock( w, w->pieceIndex, w->pieceOffset, w->byteCount );
157                fireNeedReq( w );
158            }
159        }
160    }
161}
162
163static void
164requestNextChunk( tr_webseed * w )
165{
166    tr_torrent * tor = tr_torrentFindFromHash( w->session, w->hash );
167
168    if( tor != NULL )
169    {
170        const tr_info * inf = tr_torrentInfo( tor );
171        const uint32_t have = EVBUFFER_LENGTH( w->content );
172        const uint32_t left = w->byteCount - have;
173        const uint32_t pieceOffset = w->pieceOffset + have;
174        tr_file_index_t fileIndex;
175        uint64_t fileOffset;
176        uint32_t thisPass;
177        char * url;
178        char * range;
179
180        tr_ioFindFileLocation( tor, w->pieceIndex, pieceOffset,
181                               &fileIndex, &fileOffset );
182        thisPass = MIN( left, inf->files[fileIndex].length - fileOffset );
183
184        url = makeURL( w, &inf->files[fileIndex] );
185/*fprintf( stderr, "url is [%s]\n", url );*/
186        range = tr_strdup_printf( "%"PRIu64"-%"PRIu64, fileOffset, fileOffset + thisPass - 1 );
187/*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 );*/
188        tr_webRun( w->session, url, range, webResponseFunc, w );
189        tr_free( range );
190        tr_free( url );
191    }
192}
193
194tr_addreq_t
195tr_webseedAddRequest( tr_webseed  * w,
196                      uint32_t      pieceIndex,
197                      uint32_t      pieceOffset,
198                      uint32_t      byteCount )
199{
200    int ret;
201
202    if( w->busy || w->dead )
203    {
204        ret = TR_ADDREQ_FULL;
205    }
206    else
207    {
208        w->busy = 1;
209        w->pieceIndex = pieceIndex;
210        w->pieceOffset = pieceOffset;
211        w->byteCount = byteCount;
212        evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) );
213        requestNextChunk( w );
214        ret = TR_ADDREQ_OK;
215    }
216
217    return ret;
218}
219
220int
221tr_webseedIsActive( const tr_webseed * w )
222{
223    return w->busy != 0;
224}
225
226int
227tr_webseedGetSpeed( const tr_webseed * w, uint64_t now, float * setme_KiBs )
228{
229    const int isActive = tr_webseedIsActive( w );
230
231    *setme_KiBs = isActive ? tr_rcRate( &w->rateDown, now ) : 0.0f;
232    return isActive;
233}
234
235/***
236****
237***/
238
239tr_webseed*
240tr_webseedNew( struct tr_torrent * torrent,
241               const char *        url,
242               tr_delivery_func    callback,
243               void *              callback_userdata )
244{
245    tr_webseed * w = tr_new0( tr_webseed, 1 );
246
247    memcpy( w->hash, torrent->info.hash, SHA_DIGEST_LENGTH );
248    w->session = torrent->session;
249    w->content = evbuffer_new( );
250    w->url = tr_strdup( url );
251    w->callback = callback;
252    w->callback_userdata = callback_userdata;
253    tr_rcConstruct( &w->rateDown );
254/*fprintf( stderr, "w->callback_userdata is %p\n", w->callback_userdata );*/
255    return w;
256}
257
258void
259tr_webseedFree( tr_webseed * w )
260{
261    if( w )
262    {
263        if( w->busy )
264        {
265            w->dead = 1;
266        }
267        else
268        {
269            evbuffer_free( w->content );
270            tr_rcDestruct( &w->rateDown );
271            tr_free( w->url );
272            tr_free( w );
273        }
274    }
275}
Note: See TracBrowser for help on using the repository browser.