2021-08-22 10:20:59 +00:00
|
|
|
/*
|
|
|
|
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
2023-01-04 12:07:55 +00:00
|
|
|
* Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
|
2021-08-22 10:20:59 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* the Free Software Foundation, version 3.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "uv_util.h"
|
|
|
|
#include "json_rpc_request.h"
|
2022-06-04 11:16:05 +00:00
|
|
|
#include <curl/curl.h>
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2023-08-16 11:00:11 +00:00
|
|
|
LOG_CATEGORY(JSONRPCRequest)
|
2021-08-22 10:20:59 +00:00
|
|
|
|
|
|
|
namespace p2pool {
|
2022-06-04 11:16:05 +00:00
|
|
|
namespace JSONRPCRequest {
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
struct CurlContext
|
|
|
|
{
|
2022-08-31 14:37:33 +00:00
|
|
|
CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop);
|
2022-06-04 11:16:05 +00:00
|
|
|
~CurlContext();
|
|
|
|
|
|
|
|
static int socket_func(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp)
|
|
|
|
{
|
|
|
|
CurlContext* ctx = reinterpret_cast<CurlContext*>(socketp ? socketp : userp);
|
|
|
|
return ctx->on_socket(easy, s, action);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int timer_func(CURLM* multi, long timeout_ms, void* ctx)
|
|
|
|
{
|
|
|
|
return reinterpret_cast<CurlContext*>(ctx)->on_timer(multi, timeout_ms);
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t write_func(const void* buffer, size_t size, size_t count, void* ctx)
|
|
|
|
{
|
|
|
|
return reinterpret_cast<CurlContext*>(ctx)->on_write(buffer, size, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
int on_socket(CURL* easy, curl_socket_t s, int action);
|
|
|
|
int on_timer(CURLM* multi, long timeout_ms);
|
|
|
|
|
|
|
|
static void on_timeout(uv_handle_t* req);
|
|
|
|
|
|
|
|
size_t on_write(const void* buffer, size_t size, size_t count);
|
|
|
|
|
|
|
|
static void curl_perform(uv_poll_t* req, int status, int events);
|
|
|
|
void check_multi_info();
|
|
|
|
|
|
|
|
static void on_close(uv_handle_t* h);
|
|
|
|
|
2022-08-24 13:17:23 +00:00
|
|
|
void shutdown();
|
2022-07-05 06:36:16 +00:00
|
|
|
|
2022-08-23 12:13:09 +00:00
|
|
|
std::vector<std::pair<curl_socket_t, uv_poll_t*>> m_pollHandles;
|
2022-06-04 11:16:05 +00:00
|
|
|
|
|
|
|
CallbackBase* m_callback;
|
|
|
|
CallbackBase* m_closeCallback;
|
|
|
|
|
|
|
|
uv_loop_t* m_loop;
|
|
|
|
uv_timer_t m_timer;
|
|
|
|
uv_async_t m_async;
|
|
|
|
CURLM* m_multiHandle;
|
|
|
|
CURL* m_handle;
|
|
|
|
|
|
|
|
std::string m_url;
|
|
|
|
std::string m_req;
|
|
|
|
|
|
|
|
std::vector<char> m_response;
|
|
|
|
std::string m_error;
|
2022-07-05 12:54:04 +00:00
|
|
|
|
|
|
|
curl_slist* m_headers;
|
2023-06-19 15:54:22 +00:00
|
|
|
|
|
|
|
uint64_t m_startTime;
|
|
|
|
uint64_t m_connectedTime;
|
2022-06-04 11:16:05 +00:00
|
|
|
};
|
|
|
|
|
2022-08-31 14:37:33 +00:00
|
|
|
CurlContext::CurlContext(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
|
2022-08-23 12:13:09 +00:00
|
|
|
: m_callback(cb)
|
2021-09-10 14:18:16 +00:00
|
|
|
, m_closeCallback(close_cb)
|
2022-06-04 11:16:05 +00:00
|
|
|
, m_loop(loop)
|
|
|
|
, m_timer{}
|
|
|
|
, m_async{}
|
|
|
|
, m_multiHandle(nullptr)
|
|
|
|
, m_handle(nullptr)
|
|
|
|
, m_req(req)
|
2022-07-05 12:54:04 +00:00
|
|
|
, m_headers(nullptr)
|
2023-06-19 15:54:22 +00:00
|
|
|
, m_startTime(0)
|
|
|
|
, m_connectedTime(0)
|
2021-08-22 10:20:59 +00:00
|
|
|
{
|
2022-08-23 12:13:09 +00:00
|
|
|
m_pollHandles.reserve(2);
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
{
|
|
|
|
char buf[log::Stream::BUF_SIZE + 1];
|
|
|
|
buf[0] = '\0';
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
log::Stream s(buf);
|
|
|
|
s << "http://" << address << ':' << port;
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
if (!m_req.empty() && (m_req.front() == '/')) {
|
|
|
|
s << m_req.c_str() << '\0';
|
|
|
|
m_req.clear();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
s << "/json_rpc\0";
|
2021-09-08 18:25:39 +00:00
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
m_url = buf;
|
|
|
|
}
|
2022-02-17 19:03:47 +00:00
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
int err = uv_timer_init(m_loop, &m_timer);
|
|
|
|
if (err) {
|
|
|
|
LOGERR(1, "uv_timer_init failed, error " << uv_err_name(err));
|
|
|
|
throw std::runtime_error("uv_timer_init failed");
|
2022-02-17 19:03:47 +00:00
|
|
|
}
|
2022-06-04 11:16:05 +00:00
|
|
|
m_timer.data = this;
|
2022-02-17 19:03:47 +00:00
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
err = uv_async_init(m_loop, &m_async, reinterpret_cast<uv_async_cb>(on_timeout));
|
|
|
|
if (err) {
|
|
|
|
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
|
|
|
|
throw std::runtime_error("uv_async_init failed");
|
|
|
|
}
|
|
|
|
m_async.data = this;
|
|
|
|
|
|
|
|
m_multiHandle = curl_multi_init();
|
|
|
|
if (!m_multiHandle) {
|
2022-06-06 15:08:36 +00:00
|
|
|
static constexpr char msg[] = "curl_multi_init() failed";
|
2022-06-04 11:16:05 +00:00
|
|
|
LOGERR(1, msg);
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
|
|
|
|
throw std::runtime_error(msg);
|
2022-02-17 19:03:47 +00:00
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-06-06 15:08:36 +00:00
|
|
|
#define curl_multi_setopt_checked(...) \
|
|
|
|
do { \
|
|
|
|
const CURLMcode r = curl_multi_setopt(__VA_ARGS__); \
|
|
|
|
if (r != CURLM_OK) { \
|
|
|
|
static constexpr char msg[] = "curl_multi_setopt(" #__VA_ARGS__ ") failed"; \
|
|
|
|
LOGERR(1, msg << ": " << curl_multi_strerror(r)); \
|
|
|
|
throw std::runtime_error(msg); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_SOCKETFUNCTION, socket_func);
|
|
|
|
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_SOCKETDATA, this);
|
2022-06-04 11:16:05 +00:00
|
|
|
|
2022-06-06 15:08:36 +00:00
|
|
|
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_TIMERFUNCTION, timer_func);
|
|
|
|
curl_multi_setopt_checked(m_multiHandle, CURLMOPT_TIMERDATA, this);
|
2022-06-04 11:16:05 +00:00
|
|
|
|
|
|
|
m_handle = curl_easy_init();
|
|
|
|
if (!m_handle) {
|
2022-06-06 15:08:36 +00:00
|
|
|
static constexpr char msg[] = "curl_easy_init() failed";
|
2022-06-04 11:16:05 +00:00
|
|
|
LOGERR(1, msg);
|
|
|
|
curl_multi_cleanup(m_multiHandle);
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
|
|
|
|
throw std::runtime_error(msg);
|
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-06-06 15:08:36 +00:00
|
|
|
#define curl_easy_setopt_checked(...) \
|
|
|
|
do { \
|
|
|
|
const CURLcode r = curl_easy_setopt(__VA_ARGS__); \
|
|
|
|
if (r != CURLE_OK) { \
|
|
|
|
static constexpr char msg[] = "curl_easy_setopt(" #__VA_ARGS__ ") failed"; \
|
|
|
|
LOGERR(1, msg << ": " << curl_easy_strerror(r)); \
|
|
|
|
throw std::runtime_error(msg); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2023-10-11 06:17:35 +00:00
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_BUFFERSIZE, 102400L);
|
|
|
|
|
2022-06-06 15:08:36 +00:00
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEFUNCTION, write_func);
|
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_WRITEDATA, this);
|
|
|
|
|
2023-05-17 21:36:58 +00:00
|
|
|
#ifdef DEV_TEST_SYNC
|
|
|
|
const int timeout = 10;
|
|
|
|
#else
|
2022-08-31 14:37:33 +00:00
|
|
|
const int timeout = proxy.empty() ? 1 : 5;
|
2023-05-17 21:36:58 +00:00
|
|
|
#endif
|
2022-08-31 14:37:33 +00:00
|
|
|
|
2022-06-06 15:08:36 +00:00
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_URL, m_url.c_str());
|
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_POSTFIELDS, m_req.c_str());
|
2022-08-31 14:37:33 +00:00
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_CONNECTTIMEOUT, timeout);
|
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_TIMEOUT, timeout * 10);
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-07-05 12:54:04 +00:00
|
|
|
m_headers = curl_slist_append(m_headers, "Content-Type: application/json");
|
|
|
|
if (m_headers) {
|
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPHEADER, m_headers);
|
|
|
|
}
|
|
|
|
|
2022-08-31 14:37:33 +00:00
|
|
|
if (!auth.empty()) {
|
2022-06-06 15:08:36 +00:00
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_ONLY);
|
2022-08-31 14:37:33 +00:00
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_USERPWD, auth.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!proxy.empty()) {
|
|
|
|
if (is_localhost(address)) {
|
|
|
|
LOGINFO(5, "not using proxy to connect to localhost address " << log::Gray() << address);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
|
|
|
|
curl_easy_setopt_checked(m_handle, CURLOPT_PROXY, proxy.c_str());
|
|
|
|
}
|
2022-06-04 11:16:05 +00:00
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
CURLMcode curl_err = curl_multi_add_handle(m_multiHandle, m_handle);
|
|
|
|
if (curl_err != CURLM_OK) {
|
|
|
|
LOGERR(1, "curl_multi_add_handle failed, error " << curl_multi_strerror(curl_err));
|
|
|
|
curl_easy_cleanup(m_handle);
|
|
|
|
curl_multi_cleanup(m_multiHandle);
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
|
|
|
|
throw std::runtime_error("curl_multi_add_handle failed");
|
2021-09-08 18:25:39 +00:00
|
|
|
}
|
2023-06-19 15:54:22 +00:00
|
|
|
|
|
|
|
m_startTime = microseconds_since_epoch();
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
CurlContext::~CurlContext()
|
2021-08-22 10:20:59 +00:00
|
|
|
{
|
2023-06-19 15:54:22 +00:00
|
|
|
double tcp_ping = 0.0;
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
if (m_error.empty() && !m_response.empty()) {
|
2023-07-05 13:29:54 +00:00
|
|
|
if (m_connectedTime) {
|
|
|
|
tcp_ping = static_cast<double>(m_connectedTime - m_startTime) / 1000.0;
|
|
|
|
}
|
2023-06-19 15:54:22 +00:00
|
|
|
(*m_callback)(m_response.data(), m_response.size(), tcp_ping);
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
2022-06-04 11:16:05 +00:00
|
|
|
delete m_callback;
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-08-23 12:13:09 +00:00
|
|
|
if (m_response.empty()) {
|
|
|
|
if (m_error.empty()) {
|
2022-08-24 13:17:23 +00:00
|
|
|
m_error = "empty response";
|
2022-08-23 12:13:09 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_error += " (empty response)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-19 15:54:22 +00:00
|
|
|
(*m_closeCallback)(m_error.c_str(), m_error.length(), tcp_ping);
|
2022-06-04 11:16:05 +00:00
|
|
|
delete m_closeCallback;
|
2022-07-05 12:54:04 +00:00
|
|
|
|
|
|
|
curl_slist_free_all(m_headers);
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
int CurlContext::on_socket(CURL* /*easy*/, curl_socket_t s, int action)
|
2021-08-22 10:20:59 +00:00
|
|
|
{
|
2022-08-23 12:13:09 +00:00
|
|
|
auto it = std::find_if(m_pollHandles.begin(), m_pollHandles.end(), [s](const auto& value) { return value.first == s; });
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
switch (action) {
|
|
|
|
case CURL_POLL_IN:
|
|
|
|
case CURL_POLL_OUT:
|
|
|
|
case CURL_POLL_INOUT:
|
2022-08-23 12:13:09 +00:00
|
|
|
{
|
|
|
|
uv_poll_t* h = nullptr;
|
|
|
|
|
|
|
|
if (it != m_pollHandles.end()) {
|
|
|
|
h = it->second;
|
2022-06-04 11:16:05 +00:00
|
|
|
}
|
2022-08-23 12:13:09 +00:00
|
|
|
else {
|
|
|
|
h = new uv_poll_t{};
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-08-23 12:13:09 +00:00
|
|
|
// cppcheck-suppress nullPointer
|
|
|
|
h->data = this;
|
2022-06-04 11:16:05 +00:00
|
|
|
|
2022-08-23 12:13:09 +00:00
|
|
|
const int result = uv_poll_init_socket(m_loop, h, s);
|
|
|
|
if (result < 0) {
|
|
|
|
LOGERR(1, "uv_poll_init_socket failed: " << uv_err_name(result));
|
|
|
|
delete h;
|
|
|
|
h = nullptr;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_pollHandles.emplace_back(s, h);
|
|
|
|
}
|
2022-06-04 11:16:05 +00:00
|
|
|
}
|
|
|
|
|
2022-08-23 12:13:09 +00:00
|
|
|
if (h) {
|
|
|
|
const CURLMcode err = curl_multi_assign(m_multiHandle, s, this);
|
|
|
|
if (err != CURLM_OK) {
|
|
|
|
LOGERR(1, "curl_multi_assign(action = " << action << ") failed: " << curl_multi_strerror(err));
|
|
|
|
}
|
|
|
|
|
|
|
|
int events = 0;
|
|
|
|
if (action != CURL_POLL_IN) events |= UV_WRITABLE;
|
|
|
|
if (action != CURL_POLL_OUT) events |= UV_READABLE;
|
|
|
|
|
|
|
|
const int result = uv_poll_start(h, events, curl_perform);
|
|
|
|
if (result < 0) {
|
|
|
|
LOGERR(1, "uv_poll_start failed with error " << uv_err_name(result));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOGERR(1, "failed to start polling on socket " << static_cast<int>(s));
|
2022-08-06 08:03:10 +00:00
|
|
|
}
|
2022-06-04 11:16:05 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CURL_POLL_REMOVE:
|
|
|
|
default:
|
2022-08-23 12:13:09 +00:00
|
|
|
{
|
|
|
|
if (it != m_pollHandles.end()) {
|
|
|
|
uv_poll_t* h = it->second;
|
|
|
|
m_pollHandles.erase(it);
|
|
|
|
|
|
|
|
uv_poll_stop(h);
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(h), [](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
|
|
|
|
}
|
|
|
|
|
|
|
|
const CURLMcode err = curl_multi_assign(m_multiHandle, s, nullptr);
|
|
|
|
if (err != CURLM_OK) {
|
|
|
|
LOGERR(1, "curl_multi_assign(action = " << action << ") failed: " << curl_multi_strerror(err));
|
|
|
|
}
|
|
|
|
}
|
2022-06-04 11:16:05 +00:00
|
|
|
break;
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
return 0;
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
int CurlContext::on_timer(CURLM* /*multi*/, long timeout_ms)
|
2021-08-22 10:20:59 +00:00
|
|
|
{
|
2022-06-04 11:16:05 +00:00
|
|
|
if (timeout_ms < 0) {
|
|
|
|
uv_timer_stop(&m_timer);
|
|
|
|
return 0;
|
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2023-01-13 17:25:04 +00:00
|
|
|
if ((timeout_ms == 0) && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_async))) {
|
2022-06-04 11:16:05 +00:00
|
|
|
// 0 ms timeout, but we can't just call on_timeout() here - we have to kick the UV loop
|
2022-08-24 13:17:23 +00:00
|
|
|
const int result = uv_async_send(&m_async);
|
|
|
|
if (result < 0) {
|
|
|
|
LOGERR(1, "uv_async_send failed with error " << uv_err_name(result));
|
|
|
|
|
|
|
|
// if async call didn't work, try to use the timer with 1 ms timeout
|
|
|
|
timeout_ms = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const int result = uv_timer_start(&m_timer, reinterpret_cast<uv_timer_cb>(on_timeout), timeout_ms, 0);
|
|
|
|
if (result < 0) {
|
|
|
|
LOGERR(1, "uv_timer_start failed with error " << uv_err_name(result));
|
|
|
|
return -1;
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
return 0;
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
void CurlContext::on_timeout(uv_handle_t* req)
|
2021-08-22 10:20:59 +00:00
|
|
|
{
|
2022-06-04 11:16:05 +00:00
|
|
|
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-07-05 06:36:16 +00:00
|
|
|
int running_handles = 0;
|
2022-08-24 13:17:23 +00:00
|
|
|
const CURLMcode err = curl_multi_socket_action(ctx->m_multiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles);
|
2022-08-23 12:13:09 +00:00
|
|
|
if (err != CURLM_OK) {
|
|
|
|
LOGERR(1, "curl_multi_socket_action failed, error " << curl_multi_strerror(err));
|
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
ctx->check_multi_info();
|
2022-07-05 06:36:16 +00:00
|
|
|
|
|
|
|
if (running_handles == 0) {
|
2022-08-24 13:17:23 +00:00
|
|
|
ctx->shutdown();
|
2022-07-05 06:36:16 +00:00
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
size_t CurlContext::on_write(const void* buffer, size_t size, size_t count)
|
2021-08-22 10:20:59 +00:00
|
|
|
{
|
2023-07-05 13:29:54 +00:00
|
|
|
if (!m_connectedTime) {
|
|
|
|
m_connectedTime = microseconds_since_epoch();
|
|
|
|
}
|
|
|
|
|
2022-08-24 13:17:23 +00:00
|
|
|
const size_t realsize = size * count;
|
2022-06-04 11:16:05 +00:00
|
|
|
const char* p = reinterpret_cast<const char*>(buffer);
|
2022-08-24 13:17:23 +00:00
|
|
|
m_response.insert(m_response.end(), p, p + realsize);
|
|
|
|
return realsize;
|
2022-06-04 11:16:05 +00:00
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
void CurlContext::curl_perform(uv_poll_t* req, int status, int events)
|
|
|
|
{
|
2023-06-19 15:54:22 +00:00
|
|
|
CurlContext* ctx = reinterpret_cast<CurlContext*>(req->data);
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
int flags = 0;
|
|
|
|
if (status < 0) {
|
|
|
|
flags |= CURL_CSELECT_ERR;
|
|
|
|
LOGERR(1, "uv_poll_start returned error " << uv_err_name(status));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (events & UV_READABLE) flags |= CURL_CSELECT_IN;
|
2023-06-19 15:54:22 +00:00
|
|
|
if (events & UV_WRITABLE) {
|
|
|
|
flags |= CURL_CSELECT_OUT;
|
|
|
|
if (!ctx->m_connectedTime) {
|
|
|
|
ctx->m_connectedTime = microseconds_since_epoch();
|
|
|
|
}
|
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-08-23 12:13:09 +00:00
|
|
|
int running_handles = 0;
|
|
|
|
auto it = std::find_if(ctx->m_pollHandles.begin(), ctx->m_pollHandles.end(), [req](const auto& value) { return value.second == req; });
|
|
|
|
if (it != ctx->m_pollHandles.end()) {
|
2022-08-24 13:17:23 +00:00
|
|
|
const CURLMcode err = curl_multi_socket_action(ctx->m_multiHandle, it->first, flags, &running_handles);
|
|
|
|
if (err != CURLM_OK) {
|
|
|
|
LOGERR(1, "curl_multi_socket_action failed, error " << curl_multi_strerror(err));
|
|
|
|
}
|
2022-08-23 12:13:09 +00:00
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
ctx->check_multi_info();
|
2022-08-23 12:13:09 +00:00
|
|
|
|
|
|
|
if (running_handles == 0) {
|
2022-08-24 13:17:23 +00:00
|
|
|
ctx->shutdown();
|
2022-08-23 12:13:09 +00:00
|
|
|
}
|
2022-06-04 11:16:05 +00:00
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
void CurlContext::check_multi_info()
|
|
|
|
{
|
|
|
|
int pending;
|
|
|
|
while (CURLMsg* message = curl_multi_info_read(m_multiHandle, &pending)) {
|
|
|
|
if (message->msg == CURLMSG_DONE) {
|
2022-06-08 16:58:32 +00:00
|
|
|
if (message->data.result != CURLE_OK) {
|
|
|
|
m_error = curl_easy_strerror(message->data.result);
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
2022-06-08 16:58:32 +00:00
|
|
|
else {
|
|
|
|
long http_code = 0;
|
|
|
|
curl_easy_getinfo(message->easy_handle, CURLINFO_RESPONSE_CODE, &http_code);
|
|
|
|
|
|
|
|
if (http_code != 200) {
|
|
|
|
char buf[32] = {};
|
|
|
|
log::Stream s(buf);
|
|
|
|
s << "HTTP error " << static_cast<int>(http_code) << '\0';
|
|
|
|
m_error = buf;
|
|
|
|
}
|
|
|
|
else if (m_response.empty()) {
|
|
|
|
m_error = "empty response";
|
|
|
|
}
|
2022-06-04 11:16:05 +00:00
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
curl_multi_remove_handle(m_multiHandle, m_handle);
|
|
|
|
curl_easy_cleanup(m_handle);
|
|
|
|
curl_multi_cleanup(m_multiHandle);
|
|
|
|
return;
|
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
void CurlContext::on_close(uv_handle_t* h)
|
2021-08-22 10:20:59 +00:00
|
|
|
{
|
2022-06-04 11:16:05 +00:00
|
|
|
CurlContext* ctx = reinterpret_cast<CurlContext*>(h->data);
|
|
|
|
h->data = nullptr;
|
2021-08-22 10:20:59 +00:00
|
|
|
|
2022-08-23 12:13:09 +00:00
|
|
|
if (ctx->m_timer.data || ctx->m_async.data) {
|
2022-06-04 11:16:05 +00:00
|
|
|
return;
|
2021-09-10 14:18:16 +00:00
|
|
|
}
|
2022-06-04 11:16:05 +00:00
|
|
|
|
|
|
|
delete ctx;
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-08-24 13:17:23 +00:00
|
|
|
void CurlContext::shutdown()
|
2022-07-05 06:36:16 +00:00
|
|
|
{
|
2022-08-23 12:13:09 +00:00
|
|
|
for (const auto& p : m_pollHandles) {
|
|
|
|
uv_poll_stop(p.second);
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(p.second), [](uv_handle_t* h) { delete reinterpret_cast<uv_poll_t*>(h); });
|
2022-07-05 06:36:16 +00:00
|
|
|
}
|
2022-08-23 12:13:09 +00:00
|
|
|
m_pollHandles.clear();
|
2022-07-05 06:36:16 +00:00
|
|
|
|
|
|
|
if (m_async.data && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_async))) {
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_async), on_close);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_timer.data && !uv_is_closing(reinterpret_cast<uv_handle_t*>(&m_timer))) {
|
|
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), on_close);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-31 14:37:33 +00:00
|
|
|
void Call(const std::string& address, int port, const std::string& req, const std::string& auth, const std::string& proxy, CallbackBase* cb, CallbackBase* close_cb, uv_loop_t* loop)
|
2021-08-22 10:20:59 +00:00
|
|
|
{
|
2022-06-07 17:40:13 +00:00
|
|
|
if (!loop) {
|
|
|
|
loop = uv_default_loop();
|
|
|
|
}
|
|
|
|
|
2022-07-05 12:29:41 +00:00
|
|
|
const bool result = CallOnLoop(loop,
|
2022-06-04 11:16:05 +00:00
|
|
|
[=]()
|
|
|
|
{
|
|
|
|
try {
|
2022-08-31 14:37:33 +00:00
|
|
|
new CurlContext(address, port, req, auth, proxy, cb, close_cb, loop);
|
2022-06-04 11:16:05 +00:00
|
|
|
}
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
const char* msg = e.what();
|
2023-06-19 15:54:22 +00:00
|
|
|
(*close_cb)(msg, strlen(msg), 0.0);
|
2022-06-04 11:16:05 +00:00
|
|
|
}
|
|
|
|
});
|
2022-07-05 12:29:41 +00:00
|
|
|
|
|
|
|
if (!result) {
|
|
|
|
LOGERR(1, "JSON RPC \"" << req << "\" failed");
|
|
|
|
}
|
2021-08-22 10:20:59 +00:00
|
|
|
}
|
|
|
|
|
2022-06-04 11:16:05 +00:00
|
|
|
} // namespace JSONRPCRequest
|
2021-08-22 10:20:59 +00:00
|
|
|
} // namespace p2pool
|