source: trunk/libtransmission/webseed.c @ 10800

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

(trunk) #3256 "libtransmission/publish.[ch] should be replaced" -- apply publish.diff for 2.10

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