more work on rpc. bug fixes, regression tests, and spec tweaks.
This commit is contained in:
parent
6aaa2fd39e
commit
bfd89b685d
|
@ -7,18 +7,18 @@
|
|||
|
||||
The JSON terminology in RFC 4627 is used. "array" is equivalent
|
||||
to a benc list; "object" is equivalent to a benc dictionary;
|
||||
an object's "strings" are the dictionary's keys,
|
||||
and an object's "members" are its string/value pairs.
|
||||
an object's "keys" are the dictionary's string keys,
|
||||
and an object's "members" are its key/value pairs.
|
||||
|
||||
2. Message Format
|
||||
|
||||
Messages are formatted in a subset of JSON that understands
|
||||
arrays, maps, strings, and whole numbers with no exponentials --
|
||||
in short, the subset of JSON easily represented in benc.
|
||||
floating-point numbers are represented as strings.
|
||||
booleans are represented as integers where 0 is false and 1 is true.
|
||||
in short, the subset of JSON easily represented as bencoded data.
|
||||
Floating-point numbers are represented as strings.
|
||||
Booleans are represented as integers where 0 is false and 1 is true.
|
||||
|
||||
Messages are represented as a JSON objects. There are two types:
|
||||
Messages are represented as JSON objects. There are two types:
|
||||
requests (described in 2.1) and responses (described in 2.2).
|
||||
|
||||
2.1. Requests
|
||||
|
@ -46,12 +46,12 @@
|
|||
Method names: "torrent-start", "torrent-stop",
|
||||
"torrent-remove", "torrent-verify"
|
||||
|
||||
Request arguments: "ids", a list of unique torrent ids, sha1 hash strings,
|
||||
Request arguments: "ids", a list of torrent id integers, sha1 hash strings,
|
||||
or both. These are the torrents that the request will
|
||||
be applied to. If "ids" is ommitted, the request is
|
||||
applied to all torrents.
|
||||
|
||||
Response arguments: none.
|
||||
Response arguments: none
|
||||
|
||||
3.2. Torrent Info Requests
|
||||
|
||||
|
@ -71,13 +71,13 @@
|
|||
{
|
||||
"arguments": { "ids": [ 7, 10 ] }
|
||||
"method": "torrent-info",
|
||||
"tag": 666
|
||||
"tag": 39693
|
||||
}
|
||||
|
||||
Example Response:
|
||||
|
||||
{
|
||||
"tag": 666
|
||||
"tag": 39693
|
||||
"result": "success",
|
||||
"arguments": {
|
||||
"info": [
|
||||
|
@ -108,11 +108,11 @@
|
|||
Request arguments: 3.1's optional "ids" argument.
|
||||
|
||||
Response arguments: "status", an array of objects based on
|
||||
libtransmission's tr_stat struct but which differ the following ways:
|
||||
libtransmission's tr_stat struct but which differ in the following ways:
|
||||
|
||||
(1) tr_stat's "tracker" field is omitted
|
||||
(2) a new string, "announce-url", is added
|
||||
(3) a new string, "scrape-url", is added
|
||||
(1) tr_stat's "tracker" field is omitted.
|
||||
(2) a new string, "announce-url", is added.
|
||||
(3) a new string, "scrape-url", is added.
|
||||
|
||||
3.4. Adding a Torrent
|
||||
|
||||
|
@ -122,15 +122,15 @@
|
|||
|
||||
string | value type & description
|
||||
-------------------+-------------------------------------------------
|
||||
"paused" | boolean if true, don't start the torrent
|
||||
"destination" | string path to download the torrent to
|
||||
"filename" | string location of the .torrent file
|
||||
"paused" | boolean if true, don't start the torrent
|
||||
"peer-limit" | int maximum number of peers
|
||||
|
||||
The "filename" argument is required; all others are optional.
|
||||
|
||||
Response arguments: on success, a "torrent-added" object in the
|
||||
form of one of 3.1's tr_info objects.
|
||||
form of one of 3.2's tr_info objects.
|
||||
|
||||
3.5. Other torrent settings
|
||||
|
||||
|
@ -172,16 +172,16 @@
|
|||
|
||||
3.6.1. Mutators
|
||||
|
||||
Method name: "torrent-set-file"
|
||||
Method name: "torrent-set-priorities"
|
||||
Request arguments: 3.1's "ids", plus one or more of 3.6's arguments
|
||||
Response arguments: none
|
||||
|
||||
3.6.2. Accessors
|
||||
|
||||
Method name: "torrent-get-file"
|
||||
Method name: "torrent-get-priorities"
|
||||
Request arguments: none
|
||||
Response arguments: A "torrents" list of objects containing all
|
||||
of 3.6's arguments plus the torrent's "id" int.
|
||||
Response arguments: A "torrents" list of objects containing all
|
||||
of 3.6's arguments plus the torrent's "id" int.
|
||||
|
||||
4. Session Status Requests
|
||||
|
||||
|
@ -191,9 +191,9 @@
|
|||
---------------------------+-------------------------------------------------
|
||||
"encryption" | string "required", "preferred", "tolerated"
|
||||
"peer-limit" | int maximum global number of peers
|
||||
"pex-allowed" | boolean true means allow pex in public torrents
|
||||
"port" | int port number
|
||||
"port-forwarding-enabled" | boolean true means enabled.
|
||||
"pex-allowed" | boolean true means allow pex in public torrents
|
||||
"speed-limit-down" | int max global download speed (in KiB/s)
|
||||
"speed-limit-down-enabled" | int max global download speed (in KiB/s)
|
||||
"speed-limit-up" | int max global upload speed (in KiB/s)
|
||||
|
|
|
@ -275,10 +275,8 @@ static int
|
|||
testRISONSnippet( const char * rison_str, const char * expected )
|
||||
{
|
||||
char * serialized = tr_rison2json( rison_str, -1 );
|
||||
#if 0
|
||||
fprintf( stderr, " expected: [%s]\n", expected );
|
||||
fprintf( stderr, " got: [%s]\n", serialized );
|
||||
#endif
|
||||
check( !strcmp( serialized, expected ) );
|
||||
tr_free( serialized );
|
||||
return 0;
|
||||
|
@ -296,6 +294,11 @@ testRISON( void )
|
|||
if(( val = testRISONSnippet( rison, expected )))
|
||||
return val;
|
||||
|
||||
rison = "(method:torrent-info)";
|
||||
expected = "{ \"method\": \"torrent-info\" }";
|
||||
if(( val = testRISONSnippet( rison, expected )))
|
||||
return val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -204,6 +204,7 @@ tr_rison2json( const char * str, int rison_len )
|
|||
case '\'': evbuffer_add_printf( out, "\"" ); mode = OTHER; break;
|
||||
case ':': evbuffer_add_printf( out, "\": "); mode = VAL_BEGIN; break;
|
||||
case '!': mode = ESCAPE_UNQUOTED_STRING; break;
|
||||
case ')': if( IN_OBJECT ) { evbuffer_add_printf( out, "\" }"); mode = OTHER; break; }
|
||||
case ',': if( IN_OBJECT ) { evbuffer_add_printf( out, "\", "); mode = STRING_BEGIN; break; }
|
||||
/* fallthrough */
|
||||
default: evbuffer_add_printf( out, "%c", *str ); break;
|
||||
|
|
|
@ -69,7 +69,8 @@ getTorrents( tr_handle * handle, tr_benc * args, int * setmeCount )
|
|||
|
||||
typedef void( *tor_func )( tr_torrent * tor );
|
||||
|
||||
static void callTorrentFunc( tr_handle * h, tr_benc * args_in, tor_func func )
|
||||
static void
|
||||
callTorrentFunc( tr_handle * h, tr_benc * args_in, tor_func func )
|
||||
{
|
||||
int i, torrentCount;
|
||||
tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
|
||||
|
@ -164,7 +165,8 @@ torrentStatus( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
|
|||
tr_bencDictAddInt( t, "nextScrapeTime", s->nextScrapeTime );
|
||||
tr_bencDictAddInt( t, "lastAnnounceTime", s->lastAnnounceTime );
|
||||
tr_bencDictAddInt( t, "nextAnnounceTime", s->nextAnnounceTime );
|
||||
tr_bencDictAddInt( t, "nextManualAnnounceTime", s->nextManualAnnounceTime );
|
||||
tr_bencDictAddInt( t, "nextManualAnnounceTime",
|
||||
s->nextManualAnnounceTime );
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
|
@ -172,6 +174,10 @@ torrentStatus( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
||||
static void
|
||||
addFiles( const tr_info * info, tr_benc * files )
|
||||
{
|
||||
|
@ -202,24 +208,24 @@ addTrackers( const tr_info * info, tr_benc * trackers )
|
|||
}
|
||||
|
||||
static void
|
||||
serializeInfo( const tr_torrent * tor, tr_benc * d )
|
||||
addInfo( const tr_torrent * tor, tr_benc * d )
|
||||
{
|
||||
const tr_info * inf = tr_torrentInfo( tor );
|
||||
tr_bencInitDict( d, 14 );
|
||||
tr_bencDictAddInt( d, "id", tor->uniqueId );
|
||||
tr_bencDictAddStr( d, "torrent", inf->torrent );
|
||||
tr_bencDictAddStr( d, "hashString", inf->hashString );
|
||||
tr_bencDictAddStr( d, "name", inf->name );
|
||||
tr_bencDictAddStr( d, "comment", inf->comment ? inf->comment : "" );
|
||||
tr_bencDictAddStr( d, "creator", inf->creator ? inf->creator : "" );
|
||||
tr_bencDictAddInt( d, "isPrivate", inf->isPrivate );
|
||||
tr_bencDictAddInt( d, "isMultifile", inf->isMultifile );
|
||||
tr_bencDictAddInt( d, "dateCreated", inf->dateCreated );
|
||||
tr_bencDictAddInt( d, "pieceSize", inf->pieceSize );
|
||||
tr_bencDictAddStr( d, "hashString", inf->hashString );
|
||||
tr_bencDictAddInt( d, "id", tor->uniqueId );
|
||||
tr_bencDictAddInt( d, "isMultifile", inf->isMultifile );
|
||||
tr_bencDictAddInt( d, "isPrivate", inf->isPrivate );
|
||||
tr_bencDictAddStr( d, "name", inf->name );
|
||||
tr_bencDictAddInt( d, "pieceCount", inf->pieceCount );
|
||||
tr_bencDictAddInt( d, "pieceSize", inf->pieceSize );
|
||||
tr_bencDictAddStr( d, "torrent", inf->torrent );
|
||||
tr_bencDictAddInt( d, "totalSize", inf->totalSize );
|
||||
addFiles ( inf, tr_bencDictAddList( d, "files", inf->fileCount ) );
|
||||
addTrackers( inf, tr_bencDictAddList( d, "files", inf->trackerCount ) );
|
||||
addTrackers( inf, tr_bencDictAddList( d, "trackers", inf->trackerCount ) );
|
||||
}
|
||||
|
||||
static const char*
|
||||
|
@ -227,15 +233,19 @@ torrentInfo( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
|
|||
{
|
||||
int i, torrentCount;
|
||||
tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
|
||||
tr_benc * list = tr_bencDictAddList( args_out, "status", torrentCount );
|
||||
tr_benc * list = tr_bencDictAddList( args_out, "info", torrentCount );
|
||||
|
||||
for( i=0; i<torrentCount; ++i )
|
||||
serializeInfo( torrents[i], tr_bencListAdd( list ) );
|
||||
addInfo( torrents[i], tr_bencListAdd( list ) );
|
||||
|
||||
tr_free( torrents );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
||||
static const char*
|
||||
torrentGet( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
|
||||
{
|
||||
|
@ -345,7 +355,7 @@ buildFileList( const tr_torrent * tor, tr_benc * dict,
|
|||
}
|
||||
|
||||
static const char*
|
||||
torrentGetFile( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
|
||||
torrentGetPriorities( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
|
||||
{
|
||||
int i, torrentCount;
|
||||
tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
|
||||
|
@ -356,11 +366,11 @@ torrentGetFile( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
|
|||
const tr_torrent * tor = torrents[i];
|
||||
tr_benc * d = tr_bencListAddDict( list, 6 );
|
||||
tr_bencDictAddInt( d, "id", tor->uniqueId );
|
||||
buildFileList( tor, d, "priority-high", testFileHigh );
|
||||
buildFileList( tor, d, "priority-low", testFileLow );
|
||||
buildFileList( tor, d, "priority-normal", testFileNormal );
|
||||
buildFileList( tor, d, "download", testFileDownload );
|
||||
buildFileList( tor, d, "no-download", testFileDND );
|
||||
buildFileList( tor, d, "priority-low", testFileLow );
|
||||
buildFileList( tor, d, "priority-normal", testFileNormal );
|
||||
buildFileList( tor, d, "priority-high", testFileHigh );
|
||||
}
|
||||
|
||||
tr_free( torrents );
|
||||
|
@ -370,10 +380,10 @@ torrentGetFile( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
|
|||
static void
|
||||
setFilePriorities( tr_torrent * tor, int priority, tr_benc * list )
|
||||
{
|
||||
const int n = tr_bencListSize( list );
|
||||
int i;
|
||||
int64_t tmp;
|
||||
int fileCount = 0;
|
||||
const int n = tr_bencListSize( list );
|
||||
tr_file_index_t * files = tr_new0( tr_file_index_t, n );
|
||||
|
||||
for( i=0; i<n; ++i )
|
||||
|
@ -389,10 +399,10 @@ setFilePriorities( tr_torrent * tor, int priority, tr_benc * list )
|
|||
static void
|
||||
setFileDLs( tr_torrent * tor, int do_download, tr_benc * list )
|
||||
{
|
||||
const int n = tr_bencListSize( list );
|
||||
int i;
|
||||
int64_t tmp;
|
||||
int fileCount = 0;
|
||||
const int n = tr_bencListSize( list );
|
||||
tr_file_index_t * files = tr_new0( tr_file_index_t, n );
|
||||
|
||||
for( i=0; i<n; ++i )
|
||||
|
@ -406,7 +416,7 @@ setFileDLs( tr_torrent * tor, int do_download, tr_benc * list )
|
|||
}
|
||||
|
||||
static const char*
|
||||
torrentSetFile( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
|
||||
torrentSetPriorities( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
|
||||
{
|
||||
int i, torrentCount;
|
||||
tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
|
||||
|
@ -451,17 +461,17 @@ torrentAdd( tr_handle * h, tr_benc * args_in, tr_benc * args_out )
|
|||
|
||||
ctor = tr_ctorNew( h );
|
||||
tr_ctorSetMetainfoFromFile( ctor, filename );
|
||||
if( tr_bencDictFindStr( args_in, "destination", &str ) )
|
||||
tr_ctorSetDestination( ctor, TR_FORCE, str );
|
||||
if( tr_bencDictFindInt( args_in, "paused", &i ) )
|
||||
tr_ctorSetPaused( ctor, TR_FORCE, i );
|
||||
if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
|
||||
tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
|
||||
if( tr_bencDictFindStr( args_in, "destination", &str ) )
|
||||
tr_ctorSetDestination( ctor, TR_FORCE, str );
|
||||
tor = tr_torrentNew( h, ctor, &err );
|
||||
tr_ctorFree( ctor );
|
||||
|
||||
if( tor )
|
||||
serializeInfo( tor, tr_bencDictAdd( args_out, "torrent-added" ) );
|
||||
addInfo( tor, tr_bencDictAdd( args_out, "torrent-added" ) );
|
||||
else if( err == TR_EDUPLICATE )
|
||||
return "duplicate torrent";
|
||||
else if( err == TR_EINVALID )
|
||||
|
@ -483,12 +493,12 @@ sessionSet( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
|
|||
|
||||
if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
|
||||
tr_sessionSetPeerLimit( h, i );
|
||||
if( tr_bencDictFindInt( args_in, "pex-allowed", &i ) )
|
||||
tr_sessionSetPexEnabled( h, i );
|
||||
if( tr_bencDictFindInt( args_in, "port", &i ) )
|
||||
tr_sessionSetPublicPort( h, i );
|
||||
if( tr_bencDictFindInt( args_in, "port-forwarding-enabled", &i ) )
|
||||
tr_sessionSetPortForwardingEnabled( h, i );
|
||||
if( tr_bencDictFindInt( args_in, "pex-allowed", &i ) )
|
||||
tr_sessionSetPexEnabled( h, i );
|
||||
if( tr_bencDictFindInt( args_in, "speed-limit-down", &i ) )
|
||||
tr_sessionSetSpeedLimit( h, TR_DOWN, i );
|
||||
if( tr_bencDictFindInt( args_in, "speed-limit-down-enabled", &i ) )
|
||||
|
@ -517,12 +527,12 @@ sessionGet( tr_handle * h, tr_benc * args_in UNUSED, tr_benc * args_out )
|
|||
|
||||
tr_bencDictAddInt( d, "peer-limit",
|
||||
tr_sessionGetPeerLimit( h ) );
|
||||
tr_bencDictAddInt( d, "pex-allowed",
|
||||
tr_sessionIsPexEnabled( h ) );
|
||||
tr_bencDictAddInt( d, "port",
|
||||
tr_sessionGetPublicPort( h ) );
|
||||
tr_bencDictAddInt( d, "port-forwarding-enabled",
|
||||
tr_sessionIsPortForwardingEnabled( h ) );
|
||||
tr_bencDictAddInt( d, "pex-allowed",
|
||||
tr_sessionIsPexEnabled( h ) );
|
||||
tr_bencDictAddInt( d, "speed-limit-up",
|
||||
tr_sessionGetSpeedLimit( h, TR_UP ) );
|
||||
tr_bencDictAddInt( d, "speed-limit-up-enabled",
|
||||
|
@ -549,11 +559,11 @@ sessionGet( tr_handle * h, tr_benc * args_in UNUSED, tr_benc * args_out )
|
|||
|
||||
typedef const char* (handler)( tr_handle*, tr_benc*, tr_benc* );
|
||||
|
||||
struct request_handler
|
||||
struct method
|
||||
{
|
||||
const char * method;
|
||||
const char * name;
|
||||
handler * func;
|
||||
} request_handlers[] = {
|
||||
} methods[] = {
|
||||
{ "torrent-start", torrentStart },
|
||||
{ "torrent-stop", torrentStop },
|
||||
{ "torrent-close", torrentClose },
|
||||
|
@ -563,8 +573,8 @@ struct request_handler
|
|||
{ "torrent-add", torrentAdd },
|
||||
{ "torrent-set", torrentSet },
|
||||
{ "torrent-get", torrentGet },
|
||||
{ "torrent-set-file", torrentSetFile },
|
||||
{ "torrent-get-file", torrentGetFile },
|
||||
{ "torrent-set-priorities", torrentSetPriorities },
|
||||
{ "torrent-get-priorities", torrentGetPriorities },
|
||||
{ "session-set", sessionSet },
|
||||
{ "session-get", sessionGet }
|
||||
};
|
||||
|
@ -592,13 +602,13 @@ request_exec( struct tr_handle * handle,
|
|||
if( !tr_bencDictFindStr( request, "method", &str ) )
|
||||
result = "no method name";
|
||||
else {
|
||||
const int n = TR_N_ELEMENTS( request_handlers );
|
||||
const int n = TR_N_ELEMENTS( methods );
|
||||
for( i=0; i<n; ++i )
|
||||
if( !strcmp( str, request_handlers[i].method ) )
|
||||
if( !strcmp( str, methods[i].name ) )
|
||||
break;
|
||||
result = i==n
|
||||
? "method name not recognized"
|
||||
: (*request_handlers[i].func)( handle, args_in, args_out );
|
||||
: (*methods[i].func)( handle, args_in, args_out );
|
||||
}
|
||||
|
||||
/* serialize & return the response */
|
||||
|
@ -639,8 +649,57 @@ tr_rpc_request_exec_rison( struct tr_handle * handle,
|
|||
int request_len,
|
||||
int * response_len )
|
||||
{
|
||||
char * json = tr_rison2json( request_rison, request_len );
|
||||
char * ret = tr_rpc_request_exec_json( handle, json, -1, response_len );
|
||||
tr_free( json );
|
||||
char * ret = NULL;
|
||||
tr_benc top;
|
||||
int have_content;
|
||||
char * request = tr_strndup( request_rison, request_len );;
|
||||
|
||||
fprintf( stderr, "passed in: --> %s <--\n", request );
|
||||
/* possibly convert o-rison to rison */
|
||||
if( request && ( *request != '(' ) )
|
||||
{
|
||||
const int n = strlen( request );
|
||||
char * tmp = tr_new( char, n + 3 );
|
||||
tmp[0] = '(';
|
||||
memcpy( tmp+1, request, n );
|
||||
tmp[n+1] = ')';
|
||||
tmp[n+2] = '\0';
|
||||
tr_free( request );
|
||||
request = tmp;
|
||||
}
|
||||
fprintf( stderr, "after o-rison conversion: --> %s <--\n", request );
|
||||
|
||||
/* convert rison to json */
|
||||
{
|
||||
char * tmp = tr_rison2json( request, strlen( request ) );
|
||||
tr_free( request );
|
||||
request = tmp;
|
||||
}
|
||||
fprintf( stderr, "after json conversion: --> %s <--\n", request );
|
||||
|
||||
/* parse the json */
|
||||
have_content = !tr_jsonParse( request, request+strlen(request), &top, NULL );
|
||||
if( have_content )
|
||||
{
|
||||
/* for convenience' sake, our URI rpc notation allows the
|
||||
* `args' object to be declared implicitly... */
|
||||
tr_benc tmp, *args;
|
||||
int64_t i;
|
||||
const char * str;
|
||||
tr_bencInitDict( &tmp, 3 );
|
||||
if( tr_bencDictFindInt( &top, "tag", &i ) )
|
||||
tr_bencDictAddInt( &tmp, "tag", i );
|
||||
if( tr_bencDictFindStr( &top, "method", &str ) )
|
||||
tr_bencDictAddStr( &tmp, "method", str );
|
||||
args = tr_bencDictAdd( &tmp, "args" );
|
||||
*args = top;
|
||||
tr_bencPrint( &tmp);
|
||||
ret = request_exec( handle, &tmp, response_len );
|
||||
tr_bencInitInt( args, 0 );
|
||||
tr_bencFree( &tmp );
|
||||
tr_bencFree( &top );
|
||||
}
|
||||
|
||||
tr_free( request );
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue