feat: Qt client now formats RPC requests in the RPC server's preferred style (#7963)

* feat: Qt client now formats RPC requests in the RPC server's preferred style

* add macro to build TrRpcVersionSemver

Co-authored-by: Yat Ho <lagoho7@gmail.com>
This commit is contained in:
Charles Kerr
2025-12-20 11:05:35 -06:00
committed by GitHub
parent 615bb4f5e7
commit ed827dd1a1
3 changed files with 53 additions and 8 deletions

View File

@@ -11,7 +11,13 @@
struct tr_session;
struct tr_variant;
auto inline constexpr TrRpcVersionSemver = std::string_view{ "6.0.0" };
#define RPC_VERSION_VARS(major, minor, patch) \
auto inline constexpr TrRpcVersionSemver = std::string_view{ #major "." #minor "." #patch }; \
auto inline constexpr TrRpcVersionSemverMajor = major;
RPC_VERSION_VARS(6, 0, 0)
#undef RPC_VERSION_VARS
namespace JsonRpc
{

View File

@@ -16,6 +16,7 @@
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QVersionNumber>
#include <libtransmission/api-compat.h>
#include <libtransmission/rpcimpl.h>
@@ -70,6 +71,7 @@ void RpcClient::stop()
session_id_.clear();
url_.clear();
request_.reset();
network_style_ = DefaultNetworkStyle;
if (nam_ != nullptr)
{
@@ -93,7 +95,6 @@ void RpcClient::start(QUrl const& url)
RpcResponseFuture RpcClient::exec(tr_quark const method, tr_variant* args)
{
auto [req, id] = buildRequest(method, args);
req = api_compat::convert_outgoing_data(req);
auto promise = QFutureInterface<RpcResponse>{};
promise.setExpectedResultCount(1);
@@ -107,6 +108,7 @@ RpcResponseFuture RpcClient::exec(tr_quark const method, tr_variant* args)
}
else if (!url_.isEmpty())
{
req = api_compat::convert(req, network_style_);
auto const json = tr_variant_serde::json().compact().to_string(req);
auto const body = QByteArray::fromStdString(json);
sendNetworkRequest(body, promise);
@@ -165,20 +167,20 @@ void RpcClient::sendLocalRequest(tr_variant const& req, QFutureInterface<RpcResp
tr_rpc_request_exec(
session_,
req,
[this](tr_session* /*sesson*/, tr_variant&& response)
[this](tr_session* /*sesson*/, tr_variant&& response_in)
{
auto converted = std::make_shared<tr_variant>(api_compat::convert_incoming_data(response));
auto response = std::make_shared<tr_variant>(std::move(response_in));
if (verbose_)
{
auto serde = tr_variant_serde::json();
serde.compact();
fmt::print("{:s}:{:d} got raw response:\n{:s}\n", __FILE__, __LINE__, serde.to_string(response));
fmt::print("{:s}:{:d} converted response:\n{:s}\n", __FILE__, __LINE__, serde.to_string(*converted));
fmt::print("{:s}:{:d} got response:\n{:s}\n", __FILE__, __LINE__, serde.to_string(*response));
}
// 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.
QMetaObject::invokeMethod(this, "localRequestFinished", Qt::QueuedConnection, Q_ARG(TrVariantPtr, converted));
QMetaObject::invokeMethod(this, "localRequestFinished", Qt::QueuedConnection, Q_ARG(TrVariantPtr, response));
});
}
@@ -217,6 +219,38 @@ void RpcClient::networkRequestFinished(QNetworkReply* reply)
{
// we got a 409 telling us our session id has expired.
// update it and resubmit the request.
auto version_str = QString::fromUtf8("unknown");
if (reply->hasRawHeader(TR_RPC_RPC_VERSION_HEADER))
{
network_style_ = api_compat::Style::Tr5;
version_str = QString::fromUtf8(reply->rawHeader(TR_RPC_RPC_VERSION_HEADER));
if (QVersionNumber::fromString(version_str).majorVersion() > TrRpcVersionSemverMajor)
{
fmt::print(
stderr,
"Server '{:s}' RPC version is {:s}, which may be incompatible with our version {:s}.\n",
url_.toDisplayString().toStdString(),
version_str.toStdString(),
TrRpcVersionSemver);
}
}
else
{
network_style_ = api_compat::Style::Tr4;
}
if (verbose_)
{
fmt::print(
"Server '{:s}' RPC version is {:s}. Using style {:d}\n",
url_.toDisplayString().toStdString(),
version_str.toStdString(),
static_cast<int>(network_style_));
}
session_id_ = QString::fromUtf8(reply->rawHeader(TR_RPC_SESSION_ID_HEADER));
request_.reset();
@@ -247,7 +281,7 @@ void RpcClient::networkRequestFinished(QNetworkReply* reply)
if (auto var = tr_variant_serde::json().parse(json))
{
var = api_compat::convert_incoming_data(*var);
var = api_compat::convert(*var, api_compat::Style::Tr5);
if (verbose_)
{

View File

@@ -21,6 +21,7 @@
#include <libtransmission/transmission.h>
#include <libtransmission/api-compat.h>
#include <libtransmission/quark.h>
#include <libtransmission/variant.h>
@@ -92,8 +93,12 @@ private:
[[nodiscard]] int64_t parseResponseId(tr_variant& response) const;
[[nodiscard]] RpcResponse parseResponseData(tr_variant& response) const;
// TODO: change this default in 5.0.0-beta.1
static auto constexpr DefaultNetworkStyle = libtransmission::api_compat::Style::Tr4;
std::optional<QNetworkRequest> request_;
libtransmission::api_compat::Style network_style_ = DefaultNetworkStyle;
tr_session* session_ = {};
QString session_id_;
QUrl url_;