Fix RPC error results

This commit is contained in:
badaix 2025-01-15 22:41:46 +01:00
parent 9e6009cad0
commit ea07cb715d
3 changed files with 48 additions and 28 deletions

View file

@ -1,6 +1,6 @@
/*** /***
This file is part of snapcast This file is part of snapcast
Copyright (C) 2014-2024 Johannes Pohl Copyright (C) 2014-2025 Johannes Pohl
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -34,6 +34,17 @@
static constexpr auto LOG_TAG = "ControlRequest"; static constexpr auto LOG_TAG = "ControlRequest";
/// throw InvalidParamsException if one of @p params is missing in @p request
static void checkParams(const jsonrpcpp::request_ptr& request, const std::vector<std::string>& params)
{
for (const auto& param : params)
{
if (!request->params().has(param))
throw jsonrpcpp::InvalidParamsException("Parameter '" + param + "' is missing", request->id());
}
}
Request::Request(const Server& server, const std::string& method) : server_(server), method_(method) Request::Request(const Server& server, const std::string& method) : server_(server), method_(method)
{ {
} }
@ -120,8 +131,7 @@ ClientRequest::ClientRequest(const Server& server, const std::string& method) :
ClientInfoPtr ClientRequest::getClient(const jsonrpcpp::request_ptr& request) ClientInfoPtr ClientRequest::getClient(const jsonrpcpp::request_ptr& request)
{ {
if (!request->params().has("id")) checkParams(request, {"id"});
throw jsonrpcpp::InvalidParamsException("Parameter 'id' is missing", request->id());
ClientInfoPtr clientInfo = Config::instance().getClientInfo(request->params().get<std::string>("id")); ClientInfoPtr clientInfo = Config::instance().getClientInfo(request->params().get<std::string>("id"));
if (clientInfo == nullptr) if (clientInfo == nullptr)
@ -180,6 +190,8 @@ void ClientSetVolumeRequest::execute(const jsonrpcpp::request_ptr& request, Auth
// Notification: {"jsonrpc":"2.0","method":"Client.OnVolumeChanged","params":{"id":"00:21:6a:7d:74:fc","volume":{"muted":false,"percent":74}}} // Notification: {"jsonrpc":"2.0","method":"Client.OnVolumeChanged","params":{"id":"00:21:6a:7d:74:fc","volume":{"muted":false,"percent":74}}}
// clang-format on // clang-format on
checkParams(request, {"volume"});
std::ignore = authinfo; std::ignore = authinfo;
auto client_info = getClient(request); auto client_info = getClient(request);
client_info->config.volume.fromJson(request->params().get("volume")); client_info->config.volume.fromJson(request->params().get("volume"));
@ -207,6 +219,8 @@ void ClientSetLatencyRequest::execute(const jsonrpcpp::request_ptr& request, Aut
// Notification: {"jsonrpc":"2.0","method":"Client.OnLatencyChanged","params":{"id":"00:21:6a:7d:74:fc#2","latency":10}} // Notification: {"jsonrpc":"2.0","method":"Client.OnLatencyChanged","params":{"id":"00:21:6a:7d:74:fc#2","latency":10}}
// clang-format on // clang-format on
checkParams(request, {"latency"});
std::ignore = authinfo; std::ignore = authinfo;
int latency = request->params().get("latency"); int latency = request->params().get("latency");
if (latency < -10000) if (latency < -10000)
@ -239,6 +253,8 @@ void ClientSetNameRequest::execute(const jsonrpcpp::request_ptr& request, AuthIn
// Notification: {"jsonrpc":"2.0","method":"Client.OnNameChanged","params":{"id":"00:21:6a:7d:74:fc#2","name":"Laptop"}} // Notification: {"jsonrpc":"2.0","method":"Client.OnNameChanged","params":{"id":"00:21:6a:7d:74:fc#2","name":"Laptop"}}
// clang-format on // clang-format on
checkParams(request, {"name"});
std::ignore = authinfo; std::ignore = authinfo;
auto client_info = getClient(request); auto client_info = getClient(request);
client_info->config.name = request->params().get<std::string>("name"); client_info->config.name = request->params().get<std::string>("name");
@ -264,8 +280,7 @@ GroupRequest::GroupRequest(const Server& server, const std::string& method) : Re
GroupPtr GroupRequest::getGroup(const jsonrpcpp::request_ptr& request) GroupPtr GroupRequest::getGroup(const jsonrpcpp::request_ptr& request)
{ {
if (!request->params().has("id")) checkParams(request, {"id"});
throw jsonrpcpp::InvalidParamsException("Parameter 'id' is missing", request->id());
GroupPtr group = Config::instance().getGroup(request->params().get<std::string>("id")); GroupPtr group = Config::instance().getGroup(request->params().get<std::string>("id"));
if (group == nullptr) if (group == nullptr)
@ -306,6 +321,8 @@ void GroupSetNameRequest::execute(const jsonrpcpp::request_ptr& request, AuthInf
// Notification: {"jsonrpc":"2.0","method":"Group.OnNameChanged","params":{"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1","MediaPlayer":"Laptop"}} // Notification: {"jsonrpc":"2.0","method":"Group.OnNameChanged","params":{"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1","MediaPlayer":"Laptop"}}
// clang-format on // clang-format on
checkParams(request, {"name"});
std::ignore = authinfo; std::ignore = authinfo;
Json result; Json result;
auto group = getGroup(request); auto group = getGroup(request);
@ -330,6 +347,8 @@ void GroupSetMuteRequest::execute(const jsonrpcpp::request_ptr& request, AuthInf
// Notification: {"jsonrpc":"2.0","method":"Group.OnMute","params":{"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1","mute":true}} // Notification: {"jsonrpc":"2.0","method":"Group.OnMute","params":{"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1","mute":true}}
// clang-format on // clang-format on
checkParams(request, {"mute"});
std::ignore = authinfo; std::ignore = authinfo;
bool muted = request->params().get<bool>("mute"); bool muted = request->params().get<bool>("mute");
auto group = getGroup(request); auto group = getGroup(request);
@ -372,6 +391,8 @@ void GroupSetStreamRequest::execute(const jsonrpcpp::request_ptr& request, AuthI
// Notification: {"jsonrpc":"2.0","method":"Group.OnStreamChanged","params":{"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1","stream_id":"stream 1"}} // Notification: {"jsonrpc":"2.0","method":"Group.OnStreamChanged","params":{"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1","stream_id":"stream 1"}}
// clang-format on // clang-format on
checkParams(request, {"stream_id"});
std::ignore = authinfo; std::ignore = authinfo;
auto streamId = request->params().get<std::string>("stream_id"); auto streamId = request->params().get<std::string>("stream_id");
PcmStreamPtr stream = getStreamManager().getStream(streamId); PcmStreamPtr stream = getStreamManager().getStream(streamId);
@ -415,6 +436,8 @@ void GroupSetClientsRequest::execute(const jsonrpcpp::request_ptr& request, Auth
// Notification: {"jsonrpc":"2.0","method":"Server.OnUpdate","params":{"server":{"groups":[{"clients":[{"config":{"instance":2,"latency":6,"name":"123 456","volume":{"muted":false,"percent":48}},"connected":true,"host":{"arch":"x86_64","ip":"127.0.0.1","mac":"00:21:6a:7d:74:fc","name":"T400","os":"Linux Mint 17.3 Rosa"},"id":"00:21:6a:7d:74:fc#2","lastSeen":{"sec":1488025901,"usec":864472},"snapclient":{"name":"Snapclient","protocolVersion":2,"version":"0.10.0"}},{"config":{"instance":1,"latency":0,"name":"","volume":{"muted":false,"percent":100}},"connected":true,"host":{"arch":"x86_64","ip":"127.0.0.1","mac":"00:21:6a:7d:74:fc","name":"T400","os":"Linux Mint 17.3 Rosa"},"id":"00:21:6a:7d:74:fc","lastSeen":{"sec":1488025905,"usec":45238},"snapclient":{"name":"Snapclient","protocolVersion":2,"version":"0.10.0"}}],"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1","muted":false,"name":"","stream_id":"stream 2"}],"server":{"host":{"arch":"x86_64","ip":"","mac":"","name":"T400","os":"Linux Mint 17.3 Rosa"},"snapserver":{"controlProtocolVersion":1,"name":"Snapserver","protocolVersion":1,"version":"0.10.0"}},"streams":[{"id":"stream 1","status":"idle","uri":{"fragment":"","host":"","path":"/tmp/snapfifo","query":{"chunk_ms":"20","codec":"flac","name":"stream 1","sampleformat":"48000:16:2"},"raw":"pipe:///tmp/snapfifo?name=stream 1","scheme":"pipe"}},{"id":"stream 2","status":"idle","uri":{"fragment":"","host":"","path":"/tmp/snapfifo","query":{"chunk_ms":"20","codec":"flac","name":"stream 2","sampleformat":"48000:16:2"},"raw":"pipe:///tmp/snapfifo?name=stream 2","scheme":"pipe"}}]}}} // Notification: {"jsonrpc":"2.0","method":"Server.OnUpdate","params":{"server":{"groups":[{"clients":[{"config":{"instance":2,"latency":6,"name":"123 456","volume":{"muted":false,"percent":48}},"connected":true,"host":{"arch":"x86_64","ip":"127.0.0.1","mac":"00:21:6a:7d:74:fc","name":"T400","os":"Linux Mint 17.3 Rosa"},"id":"00:21:6a:7d:74:fc#2","lastSeen":{"sec":1488025901,"usec":864472},"snapclient":{"name":"Snapclient","protocolVersion":2,"version":"0.10.0"}},{"config":{"instance":1,"latency":0,"name":"","volume":{"muted":false,"percent":100}},"connected":true,"host":{"arch":"x86_64","ip":"127.0.0.1","mac":"00:21:6a:7d:74:fc","name":"T400","os":"Linux Mint 17.3 Rosa"},"id":"00:21:6a:7d:74:fc","lastSeen":{"sec":1488025905,"usec":45238},"snapclient":{"name":"Snapclient","protocolVersion":2,"version":"0.10.0"}}],"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1","muted":false,"name":"","stream_id":"stream 2"}],"server":{"host":{"arch":"x86_64","ip":"","mac":"","name":"T400","os":"Linux Mint 17.3 Rosa"},"snapserver":{"controlProtocolVersion":1,"name":"Snapserver","protocolVersion":1,"version":"0.10.0"}},"streams":[{"id":"stream 1","status":"idle","uri":{"fragment":"","host":"","path":"/tmp/snapfifo","query":{"chunk_ms":"20","codec":"flac","name":"stream 1","sampleformat":"48000:16:2"},"raw":"pipe:///tmp/snapfifo?name=stream 1","scheme":"pipe"}},{"id":"stream 2","status":"idle","uri":{"fragment":"","host":"","path":"/tmp/snapfifo","query":{"chunk_ms":"20","codec":"flac","name":"stream 2","sampleformat":"48000:16:2"},"raw":"pipe:///tmp/snapfifo?name=stream 2","scheme":"pipe"}}]}}}
// clang-format on // clang-format on
checkParams(request, {"clients"});
std::ignore = authinfo; std::ignore = authinfo;
std::vector<std::string> clients = request->params().get("clients"); std::vector<std::string> clients = request->params().get("clients");
@ -496,8 +519,7 @@ PcmStreamPtr StreamRequest::getStream(const StreamManager& stream_manager, const
std::string StreamRequest::getStreamId(const jsonrpcpp::request_ptr& request) std::string StreamRequest::getStreamId(const jsonrpcpp::request_ptr& request)
{ {
if (!request->params().has("id")) checkParams(request, {"id"});
throw jsonrpcpp::InvalidParamsException("Parameter 'id' is missing", request->id());
return request->params().get<std::string>("id"); return request->params().get<std::string>("id");
} }
@ -517,6 +539,8 @@ void StreamControlRequest::execute(const jsonrpcpp::request_ptr& request, AuthIn
// Response: {"id":4,"jsonrpc":"2.0","result":{"id":"Spotify"}} // Response: {"id":4,"jsonrpc":"2.0","result":{"id":"Spotify"}}
// clang-format on // clang-format on
checkParams(request, {"id", "command"});
std::ignore = authinfo; std::ignore = authinfo;
LOG(INFO, LOG_TAG) << "Stream.Control id: " << request->params().get<std::string>("id") << ", command: " << request->params().get("command") LOG(INFO, LOG_TAG) << "Stream.Control id: " << request->params().get<std::string>("id") << ", command: " << request->params().get("command")
<< ", params: " << (request->params().has("params") ? request->params().get("params") : "") << "\n"; << ", params: " << (request->params().has("params") ? request->params().get("params") : "") << "\n";
@ -524,9 +548,6 @@ void StreamControlRequest::execute(const jsonrpcpp::request_ptr& request, AuthIn
// Find stream // Find stream
PcmStreamPtr stream = getStream(getStreamManager(), request); PcmStreamPtr stream = getStream(getStreamManager(), request);
if (!request->params().has("command"))
throw jsonrpcpp::InvalidParamsException("Parameter 'commmand' is missing", request->id());
auto command = request->params().get<std::string>("command"); auto command = request->params().get<std::string>("command");
auto handle_response = [request, on_response, command](const snapcast::ErrorCode& ec) auto handle_response = [request, on_response, command](const snapcast::ErrorCode& ec)
@ -595,6 +616,8 @@ StreamSetPropertyRequest::StreamSetPropertyRequest(const Server& server) : Strea
void StreamSetPropertyRequest::execute(const jsonrpcpp::request_ptr& request, AuthInfo& authinfo, const OnResponse& on_response) void StreamSetPropertyRequest::execute(const jsonrpcpp::request_ptr& request, AuthInfo& authinfo, const OnResponse& on_response)
{ {
checkParams(request, {"property", "value", "id"});
LOG(INFO, LOG_TAG) << "Stream.SetProperty id: " << request->params().get<std::string>("id") << ", property: " << request->params().get("property") LOG(INFO, LOG_TAG) << "Stream.SetProperty id: " << request->params().get<std::string>("id") << ", property: " << request->params().get("property")
<< ", value: " << request->params().get("value") << "\n"; << ", value: " << request->params().get("value") << "\n";
@ -603,12 +626,6 @@ void StreamSetPropertyRequest::execute(const jsonrpcpp::request_ptr& request, Au
std::string streamId = getStreamId(request); std::string streamId = getStreamId(request);
PcmStreamPtr stream = getStream(getStreamManager(), request); PcmStreamPtr stream = getStream(getStreamManager(), request);
if (!request->params().has("property"))
throw jsonrpcpp::InvalidParamsException("Parameter 'property' is missing", request->id());
if (!request->params().has("value"))
throw jsonrpcpp::InvalidParamsException("Parameter 'value' is missing", request->id());
auto name = request->params().get<std::string>("property"); auto name = request->params().get<std::string>("property");
auto value = request->params().get("value"); auto value = request->params().get("value");
LOG(INFO, LOG_TAG) << "Stream '" << streamId << "' set property: " << name << " = " << value << "\n"; LOG(INFO, LOG_TAG) << "Stream '" << streamId << "' set property: " << name << " = " << value << "\n";
@ -673,6 +690,8 @@ void StreamAddRequest::execute(const jsonrpcpp::request_ptr& request, AuthInfo&
// Response: {"id":4,"jsonrpc":"2.0","result":{"id":"Spotify"}} // Response: {"id":4,"jsonrpc":"2.0","result":{"id":"Spotify"}}
// clang-format on // clang-format on
checkParams(request, {"streamUri"});
std::ignore = authinfo; std::ignore = authinfo;
LOG(INFO, LOG_TAG) << "Stream.AddStream(" << request->params().get("streamUri") << ")\n"; LOG(INFO, LOG_TAG) << "Stream.AddStream(" << request->params().get("streamUri") << ")\n";
@ -702,6 +721,7 @@ void StreamRemoveRequest::execute(const jsonrpcpp::request_ptr& request, AuthInf
// Request: {"id":4,"jsonrpc":"2.0","method":"Stream.RemoveStream","params":{"id":"Spotify"}} // Request: {"id":4,"jsonrpc":"2.0","method":"Stream.RemoveStream","params":{"id":"Spotify"}}
// Response: {"id":4,"jsonrpc":"2.0","result":{"id":"Spotify"}} // Response: {"id":4,"jsonrpc":"2.0","result":{"id":"Spotify"}}
// clang-format on // clang-format on
checkParams(request, {"id"});
std::ignore = authinfo; std::ignore = authinfo;
LOG(INFO, LOG_TAG) << "Stream.RemoveStream(" << request->params().get("id") << ")\n"; LOG(INFO, LOG_TAG) << "Stream.RemoveStream(" << request->params().get("id") << ")\n";
@ -780,6 +800,8 @@ void ServerDeleteClientRequest::execute(const jsonrpcpp::request_ptr& request, A
// Notification: {"jsonrpc":"2.0","method":"Server.OnUpdate","params":{"server":{"groups":[{"clients":[{"config":{"instance":2,"latency":6,"name":"123 456","volume":{"muted":false,"percent":48}},"connected":true,"host":{"arch":"x86_64","ip":"127.0.0.1","mac":"00:21:6a:7d:74:fc","name":"T400","os":"Linux Mint 17.3 Rosa"},"id":"00:21:6a:7d:74:fc#2","lastSeen":{"sec":1488025751,"usec":654777},"snapclient":{"name":"Snapclient","protocolVersion":2,"version":"0.10.0"}}],"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1","muted":false,"name":"","stream_id":"stream 2"}],"server":{"host":{"arch":"x86_64","ip":"","mac":"","name":"T400","os":"Linux Mint 17.3 Rosa"},"snapserver":{"controlProtocolVersion":1,"name":"Snapserver","protocolVersion":1,"version":"0.10.0"}},"streams":[{"id":"stream 1","status":"idle","uri":{"fragment":"","host":"","path":"/tmp/snapfifo","query":{"chunk_ms":"20","codec":"flac","name":"stream 1","sampleformat":"48000:16:2"},"raw":"pipe:///tmp/snapfifo?name=stream 1","scheme":"pipe"}},{"id":"stream 2","status":"idle","uri":{"fragment":"","host":"","path":"/tmp/snapfifo","query":{"chunk_ms":"20","codec":"flac","name":"stream 2","sampleformat":"48000:16:2"},"raw":"pipe:///tmp/snapfifo?name=stream 2","scheme":"pipe"}}]}}} // Notification: {"jsonrpc":"2.0","method":"Server.OnUpdate","params":{"server":{"groups":[{"clients":[{"config":{"instance":2,"latency":6,"name":"123 456","volume":{"muted":false,"percent":48}},"connected":true,"host":{"arch":"x86_64","ip":"127.0.0.1","mac":"00:21:6a:7d:74:fc","name":"T400","os":"Linux Mint 17.3 Rosa"},"id":"00:21:6a:7d:74:fc#2","lastSeen":{"sec":1488025751,"usec":654777},"snapclient":{"name":"Snapclient","protocolVersion":2,"version":"0.10.0"}}],"id":"4dcc4e3b-c699-a04b-7f0c-8260d23c43e1","muted":false,"name":"","stream_id":"stream 2"}],"server":{"host":{"arch":"x86_64","ip":"","mac":"","name":"T400","os":"Linux Mint 17.3 Rosa"},"snapserver":{"controlProtocolVersion":1,"name":"Snapserver","protocolVersion":1,"version":"0.10.0"}},"streams":[{"id":"stream 1","status":"idle","uri":{"fragment":"","host":"","path":"/tmp/snapfifo","query":{"chunk_ms":"20","codec":"flac","name":"stream 1","sampleformat":"48000:16:2"},"raw":"pipe:///tmp/snapfifo?name=stream 1","scheme":"pipe"}},{"id":"stream 2","status":"idle","uri":{"fragment":"","host":"","path":"/tmp/snapfifo","query":{"chunk_ms":"20","codec":"flac","name":"stream 2","sampleformat":"48000:16:2"},"raw":"pipe:///tmp/snapfifo?name=stream 2","scheme":"pipe"}}]}}}
// clang-format on // clang-format on
checkParams(request, {"id"});
std::ignore = authinfo; std::ignore = authinfo;
ClientInfoPtr clientInfo = Config::instance().getClientInfo(request->params().get<std::string>("id")); ClientInfoPtr clientInfo = Config::instance().getClientInfo(request->params().get<std::string>("id"));
if (clientInfo == nullptr) if (clientInfo == nullptr)
@ -810,10 +832,7 @@ void ServerAuthenticateRequest::execute(const jsonrpcpp::request_ptr& request, A
// Response: {"id":8,"jsonrpc":"2.0","result":"ok"} // Response: {"id":8,"jsonrpc":"2.0","result":"ok"}
// clang-format on // clang-format on
if (!request->params().has("scheme")) checkParams(request, {"scheme", "param"});
throw jsonrpcpp::InvalidParamsException("Parameter 'scheme' is missing", request->id());
if (!request->params().has("param"))
throw jsonrpcpp::InvalidParamsException("Parameter 'param' is missing", request->id());
auto scheme = request->params().get<std::string>("scheme"); auto scheme = request->params().get<std::string>("scheme");
auto param = request->params().get<std::string>("param"); auto param = request->params().get<std::string>("param");
@ -841,10 +860,8 @@ void ServerGetTokenRequest::execute(const jsonrpcpp::request_ptr& request, AuthI
// Request: {"id":8,"jsonrpc":"2.0","method":"Server.GetToken","params":{"username":"Badaix","password":"secret"}} // Request: {"id":8,"jsonrpc":"2.0","method":"Server.GetToken","params":{"username":"Badaix","password":"secret"}}
// Response: {"id":8,"jsonrpc":"2.0","result":{"token":"<token>"}} // Response: {"id":8,"jsonrpc":"2.0","result":{"token":"<token>"}}
// clang-format on // clang-format on
if (!request->params().has("username"))
throw jsonrpcpp::InvalidParamsException("Parameter 'username' is missing", request->id()); checkParams(request, {"username", "password"});
if (!request->params().has("password"))
throw jsonrpcpp::InvalidParamsException("Parameter 'password' is missing", request->id());
auto username = request->params().get<std::string>("username"); auto username = request->params().get<std::string>("username");
auto password = request->params().get<std::string>("password"); auto password = request->params().get<std::string>("password");

View file

@ -3,11 +3,11 @@
_( )/ ___) / \ ( ( \( _ \( _ \ / __)( ) ( ) _( )/ ___) / \ ( ( \( _ \( _ \ / __)( ) ( )
/ \) \\___ \( O )/ / ) / ) __/( (__(_ _)(_ _) / \) \\___ \( O )/ / ) / ) __/( (__(_ _)(_ _)
\____/(____/ \__/ \_)__)(__\_)(__) \___)(_) (_) \____/(____/ \__/ \_)__)(__\_)(__) \___)(_) (_)
version 1.4.0 version 1.4.1
https://github.com/badaix/jsonrpcpp https://github.com/badaix/jsonrpcpp
This file is part of jsonrpc++ This file is part of jsonrpc++
Copyright (C) 2017-2024 Johannes Pohl Copyright (C) 2017-2025 Johannes Pohl
This software may be modified and distributed under the terms This software may be modified and distributed under the terms
of the MIT license. See the LICENSE file for details. of the MIT license. See the LICENSE file for details.
@ -765,7 +765,9 @@ inline bool Parameter::has(const std::string& key) const
inline Json Parameter::get(const std::string& key) const inline Json Parameter::get(const std::string& key) const
{ {
return param_map.at(key); if (has(key))
return param_map.at(key);
throw RpcException("Parameter '" + key + "' not found");
} }
inline bool Parameter::has(size_t idx) const inline bool Parameter::has(size_t idx) const

View file

@ -157,7 +157,8 @@ void Server::processRequest(const jsonrpcpp::request_ptr& request, AuthInfo& aut
else else
{ {
LOG(ERROR, LOG_TAG) << "Method not found: " << request->method() << "\n"; LOG(ERROR, LOG_TAG) << "Method not found: " << request->method() << "\n";
throw jsonrpcpp::MethodNotFoundException(request->id()); auto response = std::make_shared<jsonrpcpp::MethodNotFoundException>(request->id());
on_response(std::move(response), nullptr);
} }
} }