2008-06-07 21:26:41 +00:00
/*
* This file Copyright ( C ) 2008 Charles Kerr < charles @ rebelbase . com >
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2 ( b )
* so that the bulk of its code can remain under the MIT license .
* This exemption does not extend to derived works not owned by
* the Transmission project .
*
* $ Id $
*/
# include <assert.h>
# include <string.h> /* strlen */
# include <event.h>
# include "transmission.h"
# include "inout.h"
# include "list.h"
2008-06-10 01:38:12 +00:00
# include "ratecontrol.h"
2008-06-07 21:26:41 +00:00
# include "torrent.h"
# include "utils.h"
# include "web.h"
# include "webseed.h"
2008-06-09 22:53:45 +00:00
# define MAX_QUEUE_SIZE 4
2008-06-07 21:26:41 +00:00
struct tr_webseed
{
unsigned int dead : 1 ;
tr_torrent * torrent ;
char * url ;
tr_delivery_func * callback ;
void * callback_userdata ;
2008-06-09 22:53:45 +00:00
uint64_t bytesSaved ;
tr_piece_index_t queue [ MAX_QUEUE_SIZE ] ;
int queueSize ;
2008-06-07 21:26:41 +00:00
2008-06-10 01:38:12 +00:00
tr_ratecontrol * rateDown ;
2008-06-07 21:26:41 +00:00
struct evbuffer * content ;
} ;
/***
* * * *
* * */
static const tr_peer_event blankEvent = { 0 , 0 , 0 , 0 , 0.0f , 0 } ;
static void
publish ( tr_webseed * w , tr_peer_event * e )
{
if ( w - > callback )
w - > callback ( NULL , e , w - > callback_userdata ) ;
}
static void
fireNeedReq ( tr_webseed * w )
{
tr_peer_event e = blankEvent ;
e . eventType = TR_PEER_NEED_REQ ;
publish ( w , & e ) ;
}
static void
fireClientGotBlock ( tr_webseed * w , uint32_t pieceIndex , uint32_t offset , uint32_t length )
{
tr_peer_event e = blankEvent ;
e . eventType = TR_PEER_CLIENT_GOT_BLOCK ;
e . pieceIndex = pieceIndex ;
e . offset = offset ;
e . length = length ;
publish ( w , & e ) ;
}
static void
fireClientGotData ( tr_webseed * w , uint32_t length )
{
tr_peer_event e = blankEvent ;
e . eventType = TR_PEER_CLIENT_GOT_DATA ;
e . length = length ;
publish ( w , & e ) ;
}
/***
* * * *
* * */
static char *
makeURL ( tr_webseed * w , const tr_file * file )
{
char * ret ;
struct evbuffer * out = evbuffer_new ( ) ;
const char * url = w - > url ;
const size_t url_len = strlen ( url ) ;
evbuffer_add ( out , url , url_len ) ;
/* if url ends with a '/', add the torrent name */
if ( url [ url_len - 1 ] = = ' / ' )
{
const char * str = file - > name ;
/* this is like curl_escape() but doesn't munge the
* ' / ' directory separators in the path */
while ( str & & * str )
{
switch ( * str ) {
case ' , ' : case ' - ' : case ' . ' : case ' / ' :
case ' 0 ' : case ' 1 ' : case ' 2 ' : case ' 3 ' : case ' 4 ' :
case ' 5 ' : case ' 6 ' : case ' 7 ' : case ' 8 ' : case ' 9 ' :
case ' a ' : case ' b ' : case ' c ' : case ' d ' : case ' e ' :
case ' f ' : case ' g ' : case ' h ' : case ' i ' : case ' j ' :
case ' k ' : case ' l ' : case ' m ' : case ' n ' : case ' o ' :
case ' p ' : case ' q ' : case ' r ' : case ' s ' : case ' t ' :
case ' u ' : case ' v ' : case ' w ' : case ' x ' : case ' y ' : case ' z ' :
case ' A ' : case ' B ' : case ' C ' : case ' D ' : case ' E ' :
case ' F ' : case ' G ' : case ' H ' : case ' I ' : case ' J ' :
case ' K ' : case ' L ' : case ' M ' : case ' N ' : case ' O ' :
case ' P ' : case ' Q ' : case ' R ' : case ' S ' : case ' T ' :
case ' U ' : case ' V ' : case ' W ' : case ' X ' : case ' Y ' : case ' Z ' :
evbuffer_add ( out , str , 1 ) ;
break ;
default :
evbuffer_add_printf ( out , " %%%02X " , * str ) ;
break ;
}
}
}
ret = tr_strndup ( EVBUFFER_DATA ( out ) , EVBUFFER_LENGTH ( out ) ) ;
evbuffer_free ( out ) ;
return ret ;
}
static void requestNextChunk ( tr_webseed * w ) ;
static void
webResponseFunc ( tr_handle * session UNUSED ,
long response_code ,
const void * response ,
size_t response_byte_count ,
void * vw )
{
tr_webseed * w = vw ;
const int success = ( response_code = = 206 ) ;
2008-06-09 22:53:45 +00:00
/*fprintf( stderr, "server responded with code %ld and %lu bytes\n", response_code, (unsigned long)response_byte_count );*/
2008-06-07 21:26:41 +00:00
if ( ! success )
{
/* FIXME */
}
2008-06-09 22:53:45 +00:00
else if ( w - > dead )
{
tr_webseedFree ( w ) ;
}
2008-06-07 21:26:41 +00:00
else
{
2008-06-09 22:53:45 +00:00
const tr_piece_index_t piece = w - > queue [ 0 ] ;
tr_block_index_t block ;
size_t len ;
2008-06-07 21:26:41 +00:00
evbuffer_add ( w - > content , response , response_byte_count ) ;
2008-06-09 22:53:45 +00:00
fireClientGotData ( w , response_byte_count ) ;
block = _tr_block ( w - > torrent , piece , w - > bytesSaved ) ;
len = tr_torBlockCountBytes ( w - > torrent , block ) ;
while ( EVBUFFER_LENGTH ( w - > content ) > = len )
{
/*fprintf( stderr, "saving piece index %lu, offset %lu, len %lu\n", (unsigned long)piece, (unsigned long)w->bytesSaved, (unsigned long)len );*/
/* save one block */
tr_ioWrite ( w - > torrent , piece , w - > bytesSaved , len ,
EVBUFFER_DATA ( w - > content ) ) ;
evbuffer_drain ( w - > content , len ) ;
2008-06-10 01:38:12 +00:00
tr_rcTransferred ( w - > rateDown , len ) ;
2008-06-09 22:53:45 +00:00
fireClientGotBlock ( w , piece , w - > bytesSaved , len ) ;
w - > bytesSaved + = len ;
/* march to the next one */
+ + block ;
len = tr_torBlockCountBytes ( w - > torrent , block ) ;
}
if ( w - > bytesSaved < tr_torPieceCountBytes ( w - > torrent , piece ) )
2008-06-07 21:26:41 +00:00
requestNextChunk ( w ) ;
else {
2008-06-09 22:53:45 +00:00
w - > bytesSaved = 0 ;
2008-06-07 21:26:41 +00:00
evbuffer_drain ( w - > content , EVBUFFER_LENGTH ( w - > content ) ) ;
2008-06-09 22:53:45 +00:00
/*fprintf( stderr, "w->callback_userdata is %p\n", w->callback_userdata );*/
memmove ( w - > queue , w - > queue + 1 , sizeof ( tr_piece_index_t ) * ( MAX_QUEUE_SIZE - 1 ) ) ;
/*fprintf( stderr, "w->callback_userdata is %p\n", w->callback_userdata );*/
if ( - - w - > queueSize )
requestNextChunk ( w ) ;
if ( w - > queueSize < ( MAX_QUEUE_SIZE / 2 ) )
2008-06-07 21:26:41 +00:00
fireNeedReq ( w ) ;
}
}
}
static void
requestNextChunk ( tr_webseed * w )
{
const tr_info * inf = tr_torrentInfo ( w - > torrent ) ;
2008-06-09 22:53:45 +00:00
const uint32_t have = w - > bytesSaved + EVBUFFER_LENGTH ( w - > content ) ;
const tr_piece_index_t piece = w - > queue [ 0 ] ;
const uint32_t left = tr_torPieceCountBytes ( w - > torrent , piece ) - have ;
const uint32_t pieceOffset = have ;
2008-06-07 21:26:41 +00:00
tr_file_index_t fileIndex ;
uint64_t fileOffset ;
uint32_t thisPass ;
char * url ;
char * range ;
2008-06-09 22:53:45 +00:00
tr_ioFindFileLocation ( w - > torrent , piece , pieceOffset ,
2008-06-07 21:26:41 +00:00
& fileIndex , & fileOffset ) ;
thisPass = MIN ( left , inf - > files [ fileIndex ] . length - fileOffset ) ;
url = makeURL ( w , & inf - > files [ fileIndex ] ) ;
range = tr_strdup_printf ( " % " PRIu64 " -% " PRIu64 , fileOffset , fileOffset + thisPass - 1 ) ;
2008-06-09 22:53:45 +00:00
/*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)tr_torPieceCountBytes(w->torrent,piece), (unsigned long)have, (unsigned long)left, (unsigned long)thisPass );*/
2008-06-07 21:26:41 +00:00
tr_webRun ( w - > torrent - > handle , url , range , webResponseFunc , w ) ;
tr_free ( range ) ;
tr_free ( url ) ;
}
2008-08-22 16:30:07 +00:00
tr_addreq_t
2008-06-09 22:53:45 +00:00
tr_webseedAddRequest ( tr_webseed * w ,
tr_piece_index_t piece )
2008-06-07 21:26:41 +00:00
{
int ret ;
2008-06-09 22:53:45 +00:00
if ( w - > dead | | w - > queueSize > = MAX_QUEUE_SIZE )
2008-06-07 21:26:41 +00:00
{
ret = TR_ADDREQ_FULL ;
}
else
{
2008-06-09 22:53:45 +00:00
int wasEmpty = w - > queueSize = = 0 ;
w - > queue [ w - > queueSize + + ] = piece ;
if ( wasEmpty )
requestNextChunk ( w ) ;
2008-06-07 21:26:41 +00:00
ret = TR_ADDREQ_OK ;
}
return ret ;
}
2008-06-10 02:36:52 +00:00
int
tr_webseedIsActive ( const tr_webseed * w )
{
return w - > queueSize > 0 ;
}
2008-06-10 01:38:12 +00:00
int
tr_webseedGetSpeed ( const tr_webseed * w ,
float * setme_KiBs )
{
2008-06-10 02:36:52 +00:00
const int isActive = tr_webseedIsActive ( w ) ;
* setme_KiBs = isActive ? tr_rcRate ( w - > rateDown ) : 0.0f ;
return isActive ;
2008-06-10 01:38:12 +00:00
}
2008-06-07 21:26:41 +00:00
/***
* * * *
* * */
tr_webseed *
tr_webseedNew ( struct tr_torrent * torrent ,
const char * url ,
tr_delivery_func callback ,
void * callback_userdata )
{
tr_webseed * w = tr_new0 ( tr_webseed , 1 ) ;
w - > content = evbuffer_new ( ) ;
2008-06-10 01:38:12 +00:00
w - > rateDown = tr_rcInit ( ) ;
2008-06-07 21:26:41 +00:00
w - > torrent = torrent ;
w - > url = tr_strdup ( url ) ;
w - > callback = callback ;
w - > callback_userdata = callback_userdata ;
2008-06-09 22:53:45 +00:00
/*fprintf( stderr, "w->callback_userdata is %p\n", w->callback_userdata );*/
2008-06-07 21:26:41 +00:00
return w ;
}
void
tr_webseedFree ( tr_webseed * w )
{
if ( w )
{
2008-06-09 22:53:45 +00:00
if ( w - > queueSize > 0 )
2008-06-07 21:26:41 +00:00
{
w - > dead = 1 ;
}
else
{
evbuffer_free ( w - > content ) ;
2008-06-10 01:38:12 +00:00
tr_rcClose ( w - > rateDown ) ;
2008-06-07 21:26:41 +00:00
tr_free ( w - > url ) ;
tr_free ( w ) ;
}
}
}