Don't unnecessarily de-/serialize JSON data if local session is used.
{
if (tr_urlIsValidTracker (url))
{
- char * json = g_strdup_printf (
- "{\n"
- " \"method\": \"torrent-set\",\n"
- " \"arguments\": { \"id\": %d, \"trackerAdd\": [ \"%s\" ] }\n"
- "}\n",
- torrent_id, url);
- gtr_core_exec_json (di->core, json);
+ tr_variant top, * args, * trackers;
+
+ tr_variantInitDict (&top, 2);
+ tr_variantDictAddStr (&top, TR_KEY_method, "torrent-set");
+ args = tr_variantDictAddDict (&top, TR_KEY_arguments, 2);
+ tr_variantDictAddInt (args, TR_KEY_id, torrent_id);
+ trackers = tr_variantDictAddList (args, TR_KEY_trackerAdd, 1);
+ tr_variantListAddStr (trackers, url);
+
+ gtr_core_exec (di->core, &top);
refresh (di);
- g_free (json);
+
+ tr_variantFree (&top);
}
else
{
if (gtk_tree_selection_get_selected (sel, &model, &iter))
{
- char * json;
int torrent_id;
int tracker_id;
+ tr_variant top, * args, * trackers;
+
gtk_tree_model_get (model, &iter, TRACKER_COL_TRACKER_ID, &tracker_id,
TRACKER_COL_TORRENT_ID, &torrent_id,
-1);
- json = g_strdup_printf ("{\n"
- " \"method\": \"torrent-set\",\n"
- " \"arguments\": { \"id\": %d, \"trackerRemove\": [ %d ] }\n"
- "}\n",
- torrent_id, tracker_id);
- gtr_core_exec_json (di->core, json);
+
+ tr_variantInitDict (&top, 2);
+ tr_variantDictAddStr (&top, TR_KEY_method, "torrent-set");
+ args = tr_variantDictAddDict (&top, TR_KEY_arguments, 2);
+ tr_variantDictAddInt (args, TR_KEY_id, torrent_id);
+ trackers = tr_variantDictAddList (args, TR_KEY_trackerRemove, 1);
+ tr_variantListAddInt (trackers, tracker_id);
+
+ gtr_core_exec (di->core, &top);
refresh (di);
- g_free (json);
+
+ tr_variantFree (&top);
}
}
if (tr_variantListSize (ids) != 0)
{
- int json_len;
- char * json = tr_variantToStr (&top, TR_VARIANT_FMT_JSON_LEAN, &json_len);
- tr_rpc_request_exec_json (session, json, json_len, NULL, NULL);
- g_free (json);
+ tr_rpc_request_exec_json (session, &top, NULL, NULL);
invoked = TRUE;
}
start_all_torrents (struct cbdata * data)
{
tr_session * session = gtr_core_session (data->core);
- const char * cmd = "{ \"method\": \"torrent-start\" }";
- tr_rpc_request_exec_json (session, cmd, strlen (cmd), NULL, NULL);
+ tr_variant request;
+
+ tr_variantInitDict (&request, 1);
+ tr_variantDictAddStr (&request, TR_KEY_method, "torrent-start");
+ tr_rpc_request_exec_json (session, &request, NULL, NULL);
+ tr_variantFree (&request);
}
static void
pause_all_torrents (struct cbdata * data)
{
tr_session * session = gtr_core_session (data->core);
- const char * cmd = "{ \"method\": \"torrent-stop\" }";
- tr_rpc_request_exec_json (session, cmd, strlen (cmd), NULL, NULL);
+ tr_variant request;
+
+ tr_variantInitDict (&request, 1);
+ tr_variantDictAddStr (&request, TR_KEY_method, "torrent-stop");
+ tr_rpc_request_exec_json (session, &request, NULL, NULL);
+ tr_variantFree (&request);
}
static tr_torrent*
static gboolean
core_read_rpc_response_idle (void * vresponse)
{
- tr_variant top;
int64_t intVal;
- struct evbuffer * response = vresponse;
+ tr_variant * response = vresponse;
- tr_variantFromJson (&top, evbuffer_pullup (response, -1), evbuffer_get_length (response));
-
- if (tr_variantDictFindInt (&top, TR_KEY_tag, &intVal))
+ if (tr_variantDictFindInt (response, TR_KEY_tag, &intVal))
{
const int tag = (int)intVal;
struct pending_request_data * data = g_hash_table_lookup (pendingRequests, &tag);
if (data)
{
if (data->response_func)
- (*data->response_func)(data->core, &top, data->response_func_user_data);
+ (*data->response_func)(data->core, response, data->response_func_user_data);
g_hash_table_remove (pendingRequests, &tag);
}
}
- tr_variantFree (&top);
- evbuffer_free (response);
+ tr_variantFree (response);
+ tr_free (response);
return G_SOURCE_REMOVE;
}
static void
-core_read_rpc_response (tr_session * session UNUSED,
- struct evbuffer * response,
- void * unused UNUSED)
+core_read_rpc_response (tr_session * session UNUSED,
+ tr_variant * response,
+ void * unused UNUSED)
{
- struct evbuffer * buf = evbuffer_new ();
- evbuffer_add_buffer (buf, response);
- gdk_threads_add_idle (core_read_rpc_response_idle, buf);
+ tr_variant * response_copy = tr_new (tr_variant, 1);
+
+ *response_copy = *response;
+ tr_variantInitBool (response, false);
+
+ gdk_threads_add_idle (core_read_rpc_response_idle, response_copy);
}
static void
-core_send_rpc_request (TrCore * core, const char * json, int tag,
+core_send_rpc_request (TrCore * core, const tr_variant * request, int tag,
server_response_func * response_func,
void * response_func_user_data)
{
/* make the request */
#ifdef DEBUG_RPC
- g_message ("request: [%s]", json);
+ {
+ struct evbuffer * buf = tr_variantToBuf (request, TR_VARIANT_FMT_JSON_LEAN);
+ const size_t buf_len = evbuffer_get_length (buf);
+ g_message ("request: [%*.*s]", (int) buf_len, (int) buf_len, evbuffer_pullup (buf, -1));
+ evbuffer_free (buf);
+ }
#endif
- tr_rpc_request_exec_json (session, json, strlen (json), core_read_rpc_response, GINT_TO_POINTER (tag));
+
+ tr_rpc_request_exec_json (session, request, core_read_rpc_response, GINT_TO_POINTER (tag));
}
}
void
gtr_core_port_test (TrCore * core)
{
- char buf[64];
const int tag = nextTag++;
- g_snprintf (buf, sizeof (buf), "{ \"method\": \"port-test\", \"tag\": %d }", tag);
- core_send_rpc_request (core, buf, tag, on_port_test_response, NULL);
+ tr_variant request;
+
+ tr_variantInitDict (&request, 2);
+ tr_variantDictAddStr (&request, TR_KEY_method, "port-test");
+ tr_variantDictAddInt (&request, TR_KEY_tag, tag);
+ core_send_rpc_request (core, &request, tag, on_port_test_response, NULL);
+ tr_variantFree (&request);
}
/***
void
gtr_core_blocklist_update (TrCore * core)
{
- char buf[64];
const int tag = nextTag++;
- g_snprintf (buf, sizeof (buf), "{ \"method\": \"blocklist-update\", \"tag\": %d }", tag);
- core_send_rpc_request (core, buf, tag, on_blocklist_response, NULL);
+ tr_variant request;
+
+ tr_variantInitDict (&request, 2);
+ tr_variantDictAddStr (&request, TR_KEY_method, "blocklist-update");
+ tr_variantDictAddInt (&request, TR_KEY_tag, tag);
+ core_send_rpc_request (core, &request, tag, on_blocklist_response, NULL);
+ tr_variantFree (&request);
}
/***
****
***/
-void
-gtr_core_exec_json (TrCore * core, const char * json)
-{
- const int tag = nextTag++;
- core_send_rpc_request (core, json, tag, NULL, NULL);
-}
-
void
gtr_core_exec (TrCore * core, const tr_variant * top)
{
- char * json = tr_variantToStr (top, TR_VARIANT_FMT_JSON_LEAN, NULL);
- gtr_core_exec_json (core, json);
- tr_free (json);
+ const int tag = nextTag++;
+ core_send_rpc_request (core, top, tag, NULL, NULL);
}
/***
void gtr_core_exec (TrCore * core, const tr_variant * benc);
-void gtr_core_exec_json (TrCore * core, const char * json);
-
void gtr_core_open_folder (TrCore * core, int torrent_id);
}
if (have_source)
- {
- struct evbuffer * json = tr_variantToBuf (&top, TR_VARIANT_FMT_JSON);
- tr_rpc_request_exec_json (server->session,
- evbuffer_pullup (json, -1),
- evbuffer_get_length (json),
- NULL, NULL);
- evbuffer_free (json);
- }
+ tr_rpc_request_exec_json (server->session, &top, NULL, NULL);
tr_variantFree (&top);
}
};
static void
-rpc_response_func (tr_session * session UNUSED,
- struct evbuffer * response,
- void * user_data)
+rpc_response_func (tr_session * session UNUSED,
+ tr_variant * response,
+ void * user_data)
{
struct rpc_response_data * data = user_data;
+ struct evbuffer * response_buf = tr_variantToBuf (response, TR_VARIANT_FMT_JSON_LEAN);
struct evbuffer * buf = evbuffer_new ();
- add_response (data->req, data->server, buf, response);
+ add_response (data->req, data->server, buf, response_buf);
evhttp_add_header (data->req->output_headers,
"Content-Type", "application/json; charset=UTF-8");
evhttp_send_reply (data->req, HTTP_OK, "OK", buf);
evbuffer_free (buf);
+ evbuffer_free (response_buf);
tr_free (data);
}
const char * json,
size_t json_len)
{
+ tr_variant top;
+ bool have_content = tr_variantFromJson (&top, json, json_len) == 0;
struct rpc_response_data * data;
data = tr_new0 (struct rpc_response_data, 1);
data->req = req;
data->server = server;
- tr_rpc_request_exec_json (server->session, json, json_len, rpc_response_func, data);
+ tr_rpc_request_exec_json (server->session, have_content ? &top : NULL, rpc_response_func, data);
+
+ if (have_content)
+ tr_variantFree (&top);
}
static void
* $Id$
*/
-#include <string.h>
-
-#include <event2/buffer.h>
-
#include "transmission.h"
#include "rpcimpl.h"
#include "utils.h"
***/
static void
-rpc_response_func (tr_session * session UNUSED,
- struct evbuffer * response,
- void * setme)
+rpc_response_func (tr_session * session UNUSED,
+ tr_variant * response,
+ void * setme)
{
- tr_variantFromBuf (setme, TR_VARIANT_FMT_JSON, evbuffer_pullup(response,-1), evbuffer_get_length(response), NULL, NULL);
+ *(tr_variant *) setme = *response;
+ tr_variantInitBool (response, false);
}
static int
test_session_get_and_set (void)
{
tr_session * session;
- const char * json;
+ tr_variant request;
tr_variant response;
tr_variant * args;
tr_torrent * tor;
tor= libttest_zero_torrent_init (session);
check (tor != NULL);
- json = "{\"method\":\"session-get\"}";
- tr_rpc_request_exec_json (session, json, strlen(json), rpc_response_func, &response);
+ tr_variantInitDict (&request, 1);
+ tr_variantDictAddStr (&request, TR_KEY_method, "session-get");
+ tr_rpc_request_exec_json (session, &request, rpc_response_func, &response);
+ tr_variantFree (&request);
- check (tr_variantIsDict(&response));
check (tr_variantIsDict(&response));
check (tr_variantDictFindDict (&response, TR_KEY_arguments, &args));
check (tr_variantDictFind (args, TR_KEY_alt_speed_down) != NULL);
* when the task is complete */
struct tr_rpc_idle_data
{
- tr_session * session;
- tr_variant * response;
- tr_variant * args_out;
- tr_rpc_response_func callback;
- void * callback_user_data;
+ tr_session * session;
+ tr_variant * response;
+ tr_variant * args_out;
+ tr_rpc_response_func callback;
+ void * callback_user_data;
};
static void
tr_idle_function_done (struct tr_rpc_idle_data * data, const char * result)
{
- struct evbuffer * buf;
+ if (result == NULL)
+ result = "success";
+ tr_variantDictAddStr (data->response, TR_KEY_result, result);
- if (result == NULL)
- result = "success";
- tr_variantDictAddStr (data->response, TR_KEY_result, result);
+ (*data->callback)(data->session, data->response, data->callback_user_data);
- buf = tr_variantToBuf (data->response, TR_VARIANT_FMT_JSON_LEAN);
- (*data->callback)(data->session, buf, data->callback_user_data);
- evbuffer_free (buf);
-
- tr_variantFree (data->response);
- tr_free (data->response);
- tr_free (data);
+ tr_variantFree (data->response);
+ tr_free (data->response);
+ tr_free (data);
}
/***
tr_snprintf (result, sizeof (result), "success");
}
- tr_idle_function_done (data, result);
+ tr_idle_function_done (data, result);
}
static const char*
};
static void
-noop_response_callback (tr_session * session UNUSED,
- struct evbuffer * response UNUSED,
- void * user_data UNUSED)
+noop_response_callback (tr_session * session UNUSED,
+ tr_variant * response UNUSED,
+ void * user_data UNUSED)
{
}
-static void
-request_exec (tr_session * session,
- tr_variant * request,
- tr_rpc_response_func callback,
- void * callback_user_data)
+void
+tr_rpc_request_exec_json (tr_session * session,
+ const tr_variant * request,
+ tr_rpc_response_func callback,
+ void * callback_user_data)
{
int i;
const char * str;
- tr_variant * args_in = tr_variantDictFind (request, TR_KEY_arguments);
+ tr_variant * const mutable_request = (tr_variant *) request;
+ tr_variant * args_in = tr_variantDictFind (mutable_request, TR_KEY_arguments);
const char * result = NULL;
if (callback == NULL)
callback = noop_response_callback;
/* parse the request */
- if (!tr_variantDictFindStr (request, TR_KEY_method, &str, NULL))
+ if (!tr_variantDictFindStr (mutable_request, TR_KEY_method, &str, NULL))
{
result = "no method name";
}
{
int64_t tag;
tr_variant response;
- struct evbuffer * buf;
tr_variantInitDict (&response, 3);
tr_variantDictAddDict (&response, TR_KEY_arguments, 0);
tr_variantDictAddStr (&response, TR_KEY_result, result);
- if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
+ if (tr_variantDictFindInt (mutable_request, TR_KEY_tag, &tag))
tr_variantDictAddInt (&response, TR_KEY_tag, tag);
- buf = tr_variantToBuf (&response, TR_VARIANT_FMT_JSON_LEAN);
- (*callback)(session, buf, callback_user_data);
- evbuffer_free (buf);
+ (*callback)(session, &response, callback_user_data);
tr_variantFree (&response);
}
int64_t tag;
tr_variant response;
tr_variant * args_out;
- struct evbuffer * buf;
tr_variantInitDict (&response, 3);
args_out = tr_variantDictAddDict (&response, TR_KEY_arguments, 0);
if (result == NULL)
result = "success";
tr_variantDictAddStr (&response, TR_KEY_result, result);
- if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
+ if (tr_variantDictFindInt (mutable_request, TR_KEY_tag, &tag))
tr_variantDictAddInt (&response, TR_KEY_tag, tag);
- buf = tr_variantToBuf (&response, TR_VARIANT_FMT_JSON_LEAN);
- (*callback)(session, buf, callback_user_data);
- evbuffer_free (buf);
+ (*callback)(session, &response, callback_user_data);
tr_variantFree (&response);
}
data->session = session;
data->response = tr_new0 (tr_variant, 1);
tr_variantInitDict (data->response, 3);
- if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
+ if (tr_variantDictFindInt (mutable_request, TR_KEY_tag, &tag))
tr_variantDictAddInt (data->response, TR_KEY_tag, tag);
data->args_out = tr_variantDictAddDict (data->response, TR_KEY_arguments, 0);
data->callback = callback;
}
}
-void
-tr_rpc_request_exec_json (tr_session * session,
- const void * request_json,
- int request_len,
- tr_rpc_response_func callback,
- void * callback_user_data)
-{
- tr_variant top;
- int have_content;
-
- if (request_len < 0)
- request_len = strlen (request_json);
-
- have_content = !tr_variantFromJson (&top, request_json, request_len);
- request_exec (session, have_content ? &top : NULL, callback, callback_user_data);
-
- if (have_content)
- tr_variantFree (&top);
-}
-
/**
* Munge the URI into a usable form.
*
pch = next ? next + 1 : NULL;
}
- request_exec (session, &top, callback, callback_user_data);
+ tr_rpc_request_exec_json (session, &top, callback, callback_user_data);
/* cleanup */
tr_variantFree (&top);
**** RPC processing
***/
-struct evbuffer;
+typedef void (*tr_rpc_response_func)(tr_session * session,
+ tr_variant * response,
+ void * user_data);
-typedef void (*tr_rpc_response_func)(tr_session * session,
- struct evbuffer * response,
- void * user_data);
/* http://www.json.org/ */
void tr_rpc_request_exec_json (tr_session * session,
- const void * request_json,
- int request_len,
+ const tr_variant * request,
tr_rpc_response_func callback,
void * callback_user_data);
#define REQUEST_DATA_PROPERTY_KEY "requestData"
+namespace
+{
+ void
+ destroyVariant (tr_variant * json)
+ {
+ tr_variantFree (json);
+ tr_free (json);
+ }
+
+ TrVariantPtr
+ createVariant ()
+ {
+ return TrVariantPtr (tr_new0 (tr_variant, 1), &destroyVariant);
+ }
+}
+
RpcClient::RpcClient (QObject * parent):
QObject (parent),
mySession (nullptr),
myNAM (nullptr)
{
- connect (this, SIGNAL (responseReceived (QByteArray)),
- this, SLOT (parseResponse (QByteArray)));
+ qRegisterMetaType<TrVariantPtr> ("TrVariantPtr");
+
+ connect (this, SIGNAL (responseReceived (TrVariantPtr)),
+ this, SLOT (parseResponse (TrVariantPtr)));
}
void
void
RpcClient::exec (const char* method, tr_variant * args, int64_t tag)
{
- tr_variant top;
- tr_variantInitDict (&top, 3);
- tr_variantDictAddStr (&top, TR_KEY_method, method);
+ TrVariantPtr json = createVariant ();
+ tr_variantInitDict (json.get (), 3);
+ tr_variantDictAddStr (json.get (), TR_KEY_method, method);
if (tag >= 0)
- tr_variantDictAddInt (&top, TR_KEY_tag, tag);
+ tr_variantDictAddInt (json.get (), TR_KEY_tag, tag);
if (args != nullptr)
- tr_variantDictSteal (&top, TR_KEY_arguments, args);
-
- int length;
- char * str = tr_variantToStr (&top, TR_VARIANT_FMT_JSON_LEAN, &length);
- sendRequest (QByteArray (str, length));
- tr_free (str);
+ tr_variantDictSteal (json.get (), TR_KEY_arguments, args);
- tr_variantFree (&top);
+ sendRequest (json);
}
void
-RpcClient::sendRequest (const QByteArray& json)
+RpcClient::sendRequest (TrVariantPtr json)
{
if (mySession != nullptr)
{
- tr_rpc_request_exec_json (mySession, json.constData (), json.size (), localSessionCallback, this);
+ tr_rpc_request_exec_json (mySession, json.get (), localSessionCallback, this);
}
else if (!myUrl.isEmpty ())
{
if (!mySessionId.isEmpty ())
request.setRawHeader (TR_RPC_SESSION_ID_HEADER, mySessionId.toUtf8 ());
- QNetworkReply * reply = networkAccessManager ()->post (request, json);
- reply->setProperty (REQUEST_DATA_PROPERTY_KEY, json);
+ int rawJsonDataLength;
+ char * rawJsonData = tr_variantToStr (json.get (), TR_VARIANT_FMT_JSON_LEAN, &rawJsonDataLength);
+ QByteArray jsonData (rawJsonData, rawJsonDataLength);
+ tr_free (rawJsonData);
+
+ QNetworkReply * reply = networkAccessManager ()->post (request, jsonData);
+ reply->setProperty (REQUEST_DATA_PROPERTY_KEY, QVariant::fromValue (json));
connect (reply, SIGNAL (downloadProgress (qint64, qint64)), this, SIGNAL (dataReadProgress ()));
connect (reply, SIGNAL (uploadProgress (qint64, qint64)), this, SIGNAL (dataSendProgress ()));
connect (reply, SIGNAL (error (QNetworkReply::NetworkError)), this, SIGNAL (error (QNetworkReply::NetworkError)));
<< ": "
<< request.rawHeader (b).constData ()
<< std::endl;
- std::cerr << "Body:\n" << json.constData () << std::endl;
+ std::cerr << "Body:\n" << jsonData.constData () << std::endl;
#endif
}
}
}
void
-RpcClient::localSessionCallback (tr_session * s, evbuffer * json, void * vself)
+RpcClient::localSessionCallback (tr_session * s, tr_variant * response, void * vself)
{
Q_UNUSED (s);
RpcClient * self = static_cast<RpcClient*> (vself);
+ TrVariantPtr json = createVariant ();
+ *json = *response;
+ tr_variantInitBool (response, false);
+
// this callback is invoked in the libtransmission thread, so we don't want
// to process the response here... let's push it over to the Qt thread.
- self->responseReceived (QByteArray (reinterpret_cast<const char*> (evbuffer_pullup (json, -1)),
- static_cast<int> (evbuffer_get_length (json))).trimmed ());
+ self->responseReceived (json);
}
void
// we got a 409 telling us our session id has expired.
// update it and resubmit the request.
mySessionId = QString::fromUtf8 (reply->rawHeader (TR_RPC_SESSION_ID_HEADER));
- sendRequest (reply->property (REQUEST_DATA_PROPERTY_KEY).toByteArray ());
+ sendRequest (reply->property (REQUEST_DATA_PROPERTY_KEY).value<TrVariantPtr> ());
}
else if (reply->error () != QNetworkReply::NoError)
{
}
else
{
- parseResponse (reply->readAll ().trimmed ());
+ const QByteArray jsonData = reply->readAll ().trimmed ();
+
+ TrVariantPtr json = createVariant ();
+ if (tr_variantFromJson (json.get (), jsonData.constData (), jsonData.size ()) == 0)
+ parseResponse (json);
+
emit error (QNetworkReply::NoError);
}
}
void
-RpcClient::parseResponse (const QByteArray& json)
+RpcClient::parseResponse (TrVariantPtr json)
{
- tr_variant top;
- if (tr_variantFromJson (&top, json.constData (), json.size ()) != 0)
- return;
-
int64_t tag;
- if (!tr_variantDictFindInt (&top, TR_KEY_tag, &tag))
+ if (!tr_variantDictFindInt (json.get (), TR_KEY_tag, &tag))
tag = -1;
const char * result;
- if (!tr_variantDictFindStr (&top, TR_KEY_result, &result, nullptr))
+ if (!tr_variantDictFindStr (json.get (), TR_KEY_result, &result, nullptr))
result = nullptr;
tr_variant * args;
- if (!tr_variantDictFindDict (&top, TR_KEY_arguments, &args))
+ if (!tr_variantDictFindDict (json.get (), TR_KEY_arguments, &args))
args = nullptr;
emit executed (tag, result == nullptr ? QString () : QString::fromUtf8 (result), args);
-
- tr_variantFree (&top);
}
#ifndef QTR_RPC_CLIENT_H
#define QTR_RPC_CLIENT_H
+#include <memory>
+
#include <QNetworkReply>
#include <QObject>
#include <QString>
#include <libtransmission/transmission.h>
#include <libtransmission/quark.h>
+#include <libtransmission/variant.h>
class QByteArray;
class QNetworkAccessManager;
+typedef std::shared_ptr<tr_variant> TrVariantPtr;
+Q_DECLARE_METATYPE (TrVariantPtr);
+
extern "C"
{
struct evbuffer;
struct tr_session;
- struct tr_variant;
}
class RpcClient: public QObject
void executed (int64_t tag, const QString& result, tr_variant * args);
// private
- void responseReceived (const QByteArray& json);
+ void responseReceived (TrVariantPtr json);
private:
- void sendRequest (const QByteArray& json);
+ void sendRequest (TrVariantPtr json);
QNetworkAccessManager * networkAccessManager ();
- static void localSessionCallback (tr_session * s, evbuffer * json, void * vself);
+ static void localSessionCallback (tr_session * s, tr_variant * response, void * vself);
private slots:
void onFinished (QNetworkReply * reply);
- void parseResponse (const QByteArray& json);
+ void parseResponse (TrVariantPtr json);
private:
tr_session * mySession;