source: trunk/libtransmission/webseed.c @ 10500

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

(trunk libT) #3159 "better decision-making when choosing which peers to initiate new connections with" -- experimental commit

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