source: trunk/libtransmission/webseed.c @ 9846

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

(trunk libT) fix <assert.h> #includes

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