mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 05:32:22 +02:00
cli: Remove libevent usage
This also removes the now-unused raii_evhttp_{request,connection}
helpers from support/events.h.
This commit is contained in:
@@ -10,11 +10,13 @@
|
||||
#include <common/args.h>
|
||||
#include <common/license_info.h>
|
||||
#include <common/system.h>
|
||||
#include <common/url.h>
|
||||
#include <compat/compat.h>
|
||||
#include <compat/stdin.h>
|
||||
#include <interfaces/init.h>
|
||||
#include <interfaces/ipc.h>
|
||||
#include <interfaces/rpc.h>
|
||||
#include <netbase.h>
|
||||
#include <policy/feerate.h>
|
||||
#include <rpc/client.h>
|
||||
#include <rpc/mining.h>
|
||||
@@ -24,6 +26,7 @@
|
||||
#include <univalue.h>
|
||||
#include <util/chaintype.h>
|
||||
#include <util/exception.h>
|
||||
#include <util/sock.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
#include <util/time.h>
|
||||
@@ -34,20 +37,19 @@
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <event2/buffer.h>
|
||||
#include <event2/keyvalq_struct.h>
|
||||
#include <support/events.h>
|
||||
|
||||
using util::Join;
|
||||
using util::ToString;
|
||||
|
||||
@@ -185,15 +187,6 @@ std::optional<std::string> RpcWalletName(const ArgsManager& args)
|
||||
return args.GetArg("-rpcwallet");
|
||||
}
|
||||
|
||||
/** libevent event log callback */
|
||||
static void libevent_log_cb(int severity, const char *msg)
|
||||
{
|
||||
// Ignore everything other than errors
|
||||
if (severity >= EVENT_LOG_ERR) {
|
||||
throw std::runtime_error(strprintf("libevent error: %s", msg));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Exception thrown on connection error. This error is used to determine
|
||||
// when to wait if -rpcwait is given.
|
||||
@@ -261,68 +254,12 @@ static int AppInitRPC(int argc, char* argv[])
|
||||
return CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
|
||||
/** Reply structure for request_done to fill in */
|
||||
struct HTTPReply
|
||||
struct HTTPResponse
|
||||
{
|
||||
HTTPReply() = default;
|
||||
|
||||
int status{0};
|
||||
int error{-1};
|
||||
std::string body;
|
||||
};
|
||||
|
||||
static std::string http_errorstring(int code)
|
||||
{
|
||||
switch(code) {
|
||||
case EVREQ_HTTP_TIMEOUT:
|
||||
return "timeout reached";
|
||||
case EVREQ_HTTP_EOF:
|
||||
return "EOF reached";
|
||||
case EVREQ_HTTP_INVALID_HEADER:
|
||||
return "error while reading header, or invalid header";
|
||||
case EVREQ_HTTP_BUFFER_ERROR:
|
||||
return "error encountered while reading or writing";
|
||||
case EVREQ_HTTP_REQUEST_CANCEL:
|
||||
return "request was canceled";
|
||||
case EVREQ_HTTP_DATA_TOO_LONG:
|
||||
return "response body is larger than allowed";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void http_request_done(struct evhttp_request *req, void *ctx)
|
||||
{
|
||||
HTTPReply *reply = static_cast<HTTPReply*>(ctx);
|
||||
|
||||
if (req == nullptr) {
|
||||
/* If req is nullptr, it means an error occurred while connecting: the
|
||||
* error code will have been passed to http_error_cb.
|
||||
*/
|
||||
reply->status = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
reply->status = evhttp_request_get_response_code(req);
|
||||
|
||||
struct evbuffer *buf = evhttp_request_get_input_buffer(req);
|
||||
if (buf)
|
||||
{
|
||||
size_t size = evbuffer_get_length(buf);
|
||||
const char *data = (const char*)evbuffer_pullup(buf, size);
|
||||
if (data)
|
||||
reply->body = std::string(data, size);
|
||||
evbuffer_drain(buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void http_error_cb(enum evhttp_request_error err, void *ctx)
|
||||
{
|
||||
HTTPReply *reply = static_cast<HTTPReply*>(ctx);
|
||||
reply->error = err;
|
||||
}
|
||||
|
||||
static int8_t NetworkStringToId(const std::string& str)
|
||||
{
|
||||
for (size_t i = 0; i < NETWORKS.size(); ++i) {
|
||||
@@ -893,6 +830,328 @@ static std::optional<UniValue> CallIPC(BaseRequestHandler* rh, const std::string
|
||||
return rh->ProcessReply(reply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple synchronous HTTP client using Sock class.
|
||||
*/
|
||||
class HTTPClient
|
||||
{
|
||||
public:
|
||||
static HTTPClient Connect(const std::string& host, uint16_t port, std::chrono::seconds timeout);
|
||||
|
||||
HTTPResponse Post(const std::string& endpoint,
|
||||
std::span<const std::pair<std::string, std::string>> headers,
|
||||
const std::string& body);
|
||||
|
||||
private:
|
||||
// Signal that the peer closed the connection cleanly. Used in the read-until-close fallback.
|
||||
struct RecvEOF : CConnectionFailed { using CConnectionFailed::CConnectionFailed; };
|
||||
|
||||
std::unique_ptr<Sock> m_socket;
|
||||
std::string m_host;
|
||||
std::chrono::seconds m_timeout;
|
||||
|
||||
HTTPClient(std::unique_ptr<Sock>&& socket, const std::string& host, std::chrono::seconds timeout)
|
||||
: m_socket(std::move(socket)), m_host(host), m_timeout(timeout) {}
|
||||
bool SendRequest(std::string_view request);
|
||||
HTTPResponse ReadResponse();
|
||||
std::optional<std::string> Recv(std::chrono::time_point<std::chrono::steady_clock> deadline);
|
||||
};
|
||||
|
||||
HTTPClient HTTPClient::Connect(const std::string& host, uint16_t port, std::chrono::seconds timeout)
|
||||
{
|
||||
std::vector<CService> services = Lookup(host, port, /*fAllowLookup=*/true, /*nMaxSolutions=*/256);
|
||||
if (services.empty()) {
|
||||
throw CConnectionFailed(strprintf("Could not resolve host: %s", host));
|
||||
}
|
||||
|
||||
const auto deadline{std::chrono::steady_clock::now() + timeout};
|
||||
for (const CService& service : services) {
|
||||
const auto time_left{std::chrono::duration_cast<std::chrono::milliseconds>(deadline - std::chrono::steady_clock::now())};
|
||||
if (time_left.count() <= 0) break;
|
||||
|
||||
auto sock = ConnectDirectly(service, /*manual_connection=*/true, time_left);
|
||||
if (sock) return HTTPClient{std::move(sock), host, timeout};
|
||||
}
|
||||
|
||||
throw CConnectionFailed{"Could not connect to the server"};
|
||||
}
|
||||
|
||||
HTTPResponse HTTPClient::Post(const std::string& endpoint,
|
||||
std::span<const std::pair<std::string, std::string>> headers,
|
||||
const std::string& body)
|
||||
{
|
||||
try {
|
||||
// Build HTTP request
|
||||
std::string request = strprintf("POST %s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Length: %d\r\n",
|
||||
endpoint, m_host, body.size());
|
||||
|
||||
for (const auto& [name, value] : headers) {
|
||||
request += strprintf("%s: %s\r\n", name, value);
|
||||
}
|
||||
request += "\r\n";
|
||||
request += body;
|
||||
|
||||
if (!SendRequest(request)) {
|
||||
throw CConnectionFailed("Failed to send HTTP request");
|
||||
}
|
||||
|
||||
return ReadResponse();
|
||||
} catch (const HTTPError& e) {
|
||||
throw CConnectionFailed(strprintf("HTTP error: %s", e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
bool HTTPClient::SendRequest(std::string_view request)
|
||||
{
|
||||
const auto deadline{std::chrono::steady_clock::now() + m_timeout};
|
||||
|
||||
while (!request.empty()) {
|
||||
Sock::Event event{0};
|
||||
auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
deadline - std::chrono::steady_clock::now());
|
||||
if (time_left.count() <= 0 || !m_socket->Wait(time_left, Sock::SEND, &event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(event & Sock::SEND)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ssize_t sent = m_socket->Send(request.data(), request.size(), MSG_NOSIGNAL);
|
||||
if (sent < 0) {
|
||||
int err = WSAGetLastError();
|
||||
if (!IOErrorIsPermanent(err)) {
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
request.remove_prefix(sent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
HTTPResponse HTTPClient::ReadResponse()
|
||||
{
|
||||
HTTPResponse response;
|
||||
std::string buffer;
|
||||
const auto deadline{std::chrono::steady_clock::now() + m_timeout};
|
||||
|
||||
// Read data until we have complete headers
|
||||
size_t headers_end = 0;
|
||||
|
||||
while (headers_end == 0) {
|
||||
if (auto result{Recv(deadline)}) {
|
||||
buffer.append(*result);
|
||||
} else {
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for header terminator
|
||||
size_t pos = buffer.find("\r\n\r\n");
|
||||
if (pos != std::string::npos) {
|
||||
headers_end = pos + 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse http status
|
||||
util::LineReader reader(std::string_view{buffer.data(), headers_end}, headers_end);
|
||||
auto status_line = reader.ReadLine();
|
||||
if (!status_line) {
|
||||
throw HTTPError{"Failed to read status line"};
|
||||
}
|
||||
|
||||
const std::string& status_str = *status_line;
|
||||
// Minimum status line is "HTTP/X.Y NNN" (e.g. "HTTP/1.1 200"), 12 characters.
|
||||
if (status_str.size() < 12 || !status_str.starts_with("HTTP/")) {
|
||||
throw HTTPError{"Invalid status line"};
|
||||
}
|
||||
|
||||
size_t space1 = status_str.find(' ');
|
||||
if (space1 == std::string::npos || space1 + 4 > status_str.size()) {
|
||||
throw HTTPError{"Invalid status line format"};
|
||||
}
|
||||
|
||||
std::string status_code_str = status_str.substr(space1 + 1, 3);
|
||||
auto status_code = ToIntegral<int>(status_code_str);
|
||||
if (!status_code) {
|
||||
throw HTTPError{"Invalid status code"};
|
||||
}
|
||||
response.status = *status_code;
|
||||
|
||||
HTTPResponseHeaders headers;
|
||||
headers.Read(reader);
|
||||
|
||||
// Determine body length
|
||||
size_t content_length = 0;
|
||||
bool chunked = false;
|
||||
|
||||
// RFC 9112 §6.3 says responses with both Transfer-Encoding and Content-Length
|
||||
// must be rejected. We are more lenient: Transfer-Encoding takes precedence
|
||||
// and Content-Length is ignored.
|
||||
auto transfer_encoding = headers.FindFirst("transfer-encoding");
|
||||
if (transfer_encoding && ToLower(*transfer_encoding).find("chunked") != std::string::npos) {
|
||||
chunked = true;
|
||||
} else {
|
||||
auto content_length_header = headers.FindFirst("content-length");
|
||||
if (content_length_header) {
|
||||
auto maybe_len = ToIntegral<size_t>(*content_length_header);
|
||||
if (!maybe_len) {
|
||||
throw HTTPError{"Invalid Content-Length"};
|
||||
}
|
||||
content_length = *maybe_len;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove headers data from buffer, so only initial body data remains
|
||||
buffer.erase(0, headers_end);
|
||||
|
||||
// Read remaining body
|
||||
if (chunked) {
|
||||
// Handle chunked transfer encoding
|
||||
std::string body;
|
||||
|
||||
while (true) {
|
||||
// Try to parse a chunk from current buffer
|
||||
std::string_view chunk_data{buffer};
|
||||
size_t line_end = chunk_data.find("\r\n");
|
||||
|
||||
if (line_end != std::string::npos) {
|
||||
// Parse chunk size
|
||||
std::string_view size_str = chunk_data.substr(0, line_end);
|
||||
// Ignore chunk extensions
|
||||
size_t semi = size_str.find(';');
|
||||
if (semi != std::string::npos) {
|
||||
size_str = size_str.substr(0, semi);
|
||||
}
|
||||
|
||||
const auto chunk_size{ToIntegral<uint64_t>(util::TrimStringView(size_str), /*base=*/16)};
|
||||
if (!chunk_size) {
|
||||
throw HTTPError{"Invalid chunk size"};
|
||||
}
|
||||
|
||||
if (*chunk_size == 0) {
|
||||
// Allow (but ignore) Chunked Trailer section, by
|
||||
// reading CRLF-terminated lines until we read an empty line,
|
||||
// which indicates the end of this response.
|
||||
// See https://httpwg.org/specs/rfc9112.html#rfc.section.7.1.2
|
||||
buffer.erase(0, line_end + 2);
|
||||
while (true) {
|
||||
size_t crlf_pos = buffer.find("\r\n");
|
||||
if (crlf_pos == std::string::npos) {
|
||||
// Need more data
|
||||
if (auto result{Recv(deadline)}) {
|
||||
buffer.append(*result);
|
||||
} else {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
buffer.erase(0, crlf_pos + 2);
|
||||
if (crlf_pos == 0) break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if we have the full chunk
|
||||
size_t chunk_start = line_end + 2;
|
||||
if (*chunk_size > std::numeric_limits<size_t>::max() - chunk_start - 2) {
|
||||
throw HTTPError{"Chunk size too large"};
|
||||
}
|
||||
size_t chunk_end = chunk_start + *chunk_size + 2; // +2 for trailing CRLF
|
||||
|
||||
if (buffer.size() >= chunk_end) {
|
||||
// Extract chunk data
|
||||
body.append(buffer, chunk_start, *chunk_size);
|
||||
|
||||
// Remove processed data
|
||||
buffer.erase(0, chunk_end);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Need more data
|
||||
while (true) {
|
||||
if (auto result{Recv(deadline)}) {
|
||||
buffer.append(*result);
|
||||
break;
|
||||
} else {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.body = std::move(body);
|
||||
} else if (content_length > 0) {
|
||||
// Fixed content length
|
||||
while (buffer.size() < content_length) {
|
||||
if (auto result{Recv(deadline)}) {
|
||||
buffer.append(*result);
|
||||
} else {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
// Possibly shrink buffer in case we got a larger response than
|
||||
// originally specified.
|
||||
buffer.resize(content_length);
|
||||
response.body = std::move(buffer);
|
||||
} else {
|
||||
// No Content-Length and not chunked: read until the peer closes the
|
||||
// connection (RFC 9112 §6.3, HTTP/1.0 fallback).
|
||||
try {
|
||||
while (true) {
|
||||
if (auto result{Recv(deadline)}) {
|
||||
buffer.append(*result);
|
||||
} else {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
} catch (const RecvEOF&) {}
|
||||
response.body = std::move(buffer);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::optional<std::string> HTTPClient::Recv(const std::chrono::time_point<std::chrono::steady_clock> deadline)
|
||||
{
|
||||
auto wait_for_readable{[this](std::chrono::milliseconds timeout) -> bool {
|
||||
Sock::Event event{0};
|
||||
if (!m_socket->Wait(timeout, Sock::RECV, &event)) {
|
||||
return false;
|
||||
}
|
||||
return (event & Sock::RECV) != 0;
|
||||
}};
|
||||
|
||||
auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
deadline - std::chrono::steady_clock::now());
|
||||
if (time_left.count() <= 0 || !wait_for_readable(time_left)) {
|
||||
throw CConnectionFailed{"timeout"};
|
||||
}
|
||||
|
||||
char recv_buf[4096];
|
||||
ssize_t nrecv = m_socket->Recv(recv_buf, sizeof(recv_buf), /*flags=*/0);
|
||||
|
||||
if (nrecv < 0) {
|
||||
int err = WSAGetLastError();
|
||||
if (!IOErrorIsPermanent(err)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
throw CConnectionFailed{strprintf("Read error: %s", NetworkErrorString(err))};
|
||||
}
|
||||
|
||||
if (nrecv == 0) {
|
||||
throw RecvEOF{"EOF"};
|
||||
}
|
||||
|
||||
return std::string{recv_buf, static_cast<size_t>(nrecv)};
|
||||
}
|
||||
|
||||
static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::string& endpoint, const std::string& username)
|
||||
{
|
||||
std::string host;
|
||||
@@ -937,34 +1196,16 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain event base
|
||||
raii_event_base base = obtain_event_base();
|
||||
|
||||
// Synchronously look up hostname
|
||||
raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
|
||||
|
||||
// Set connection timeout
|
||||
{
|
||||
const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
|
||||
if (timeout > 0) {
|
||||
evhttp_connection_set_timeout(evcon.get(), timeout);
|
||||
} else {
|
||||
// Indefinite request timeouts are not possible in libevent-http, so we
|
||||
// set the timeout to a very long time period instead.
|
||||
|
||||
constexpr int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar
|
||||
evhttp_connection_set_timeout(evcon.get(), 5 * YEAR_IN_SECONDS);
|
||||
}
|
||||
const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
|
||||
std::chrono::seconds timeout_duration;
|
||||
if (timeout > 0) {
|
||||
timeout_duration = std::chrono::seconds(timeout);
|
||||
} else {
|
||||
// Use 5 year timeout for "indefinite"
|
||||
timeout_duration = std::chrono::years(5);
|
||||
}
|
||||
|
||||
HTTPReply response;
|
||||
raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
|
||||
if (req == nullptr) {
|
||||
throw std::runtime_error("create http request failed");
|
||||
}
|
||||
|
||||
evhttp_request_set_error_cb(req.get(), http_error_cb);
|
||||
|
||||
// Get credentials
|
||||
std::string rpc_credentials;
|
||||
std::optional<AuthCookieResult> auth_cookie_result;
|
||||
@@ -975,36 +1216,25 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
|
||||
rpc_credentials = username + ":" + gArgs.GetArg("-rpcpassword", "");
|
||||
}
|
||||
|
||||
struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
|
||||
assert(output_headers);
|
||||
evhttp_add_header(output_headers, "Host", host.c_str());
|
||||
evhttp_add_header(output_headers, "Connection", "close");
|
||||
evhttp_add_header(output_headers, "Content-Type", "application/json");
|
||||
evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(rpc_credentials)).c_str());
|
||||
|
||||
// Attach request data
|
||||
const std::pair<std::string, std::string> headers[]{
|
||||
{"Content-Type", "application/json"},
|
||||
{"Authorization", "Basic " + EncodeBase64(rpc_credentials)},
|
||||
};
|
||||
std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n";
|
||||
struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
|
||||
assert(output_buffer);
|
||||
evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
|
||||
|
||||
int r = evhttp_make_request(evcon.get(), req.release(), EVHTTP_REQ_POST, endpoint.c_str());
|
||||
if (r != 0) {
|
||||
throw CConnectionFailed("send http request failed");
|
||||
}
|
||||
|
||||
event_base_dispatch(base.get());
|
||||
|
||||
if (response.status == 0) {
|
||||
std::string responseErrorMessage;
|
||||
if (response.error != -1) {
|
||||
responseErrorMessage = strprintf(" (error code %d - \"%s\")", response.error, http_errorstring(response.error));
|
||||
}
|
||||
throw CConnectionFailed(strprintf("Could not connect to the server %s:%d%s\n\n"
|
||||
HTTPResponse response;
|
||||
try {
|
||||
HTTPClient client{HTTPClient::Connect(host, port, timeout_duration)};
|
||||
response = client.Post(endpoint, headers, strRequest);
|
||||
} catch (const CConnectionFailed& e) {
|
||||
const std::string formatted_error{*e.what() ? strprintf(" (%s)", e.what()) : ""};
|
||||
throw CConnectionFailed(strprintf("Error while attempting to communicate with server %s:%d%s\n\n"
|
||||
"Make sure the bitcoind server is running and that you are connecting to the correct RPC port.\n"
|
||||
"Use \"bitcoin-cli -help\" for more info.",
|
||||
host, port, responseErrorMessage));
|
||||
} else if (response.status == HTTP_UNAUTHORIZED) {
|
||||
host, port, formatted_error));
|
||||
}
|
||||
|
||||
if (response.status == HTTP_UNAUTHORIZED) {
|
||||
std::string error{"Authorization failed: "};
|
||||
if (auth_cookie_result.has_value()) {
|
||||
switch (*auth_cookie_result) {
|
||||
@@ -1061,13 +1291,7 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
|
||||
// check if we should use a special wallet endpoint
|
||||
std::string endpoint = "/";
|
||||
if (rpcwallet) {
|
||||
char* encodedURI = evhttp_uriencode(rpcwallet->data(), rpcwallet->size(), false);
|
||||
if (encodedURI) {
|
||||
endpoint = "/wallet/" + std::string(encodedURI);
|
||||
free(encodedURI);
|
||||
} else {
|
||||
throw CConnectionFailed("uri-encode failed");
|
||||
}
|
||||
endpoint = "/wallet/" + UrlEncode(*rpcwallet);
|
||||
}
|
||||
|
||||
std::string username{gArgs.GetArg("-rpcuser", "")};
|
||||
@@ -1088,8 +1312,10 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
|
||||
} catch (const CConnectionFailed& e) {
|
||||
if (fWait && (timeout <= 0 || std::chrono::steady_clock::now() < deadline)) {
|
||||
UninterruptibleSleep(1s);
|
||||
} else {
|
||||
} else if (fWait) {
|
||||
throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what()));
|
||||
} else {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
} while (fWait);
|
||||
@@ -1448,7 +1674,6 @@ MAIN_FUNCTION
|
||||
tfm::format(std::cerr, "Error: Initializing networking failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
event_set_log_callback(&libevent_log_cb);
|
||||
|
||||
try {
|
||||
int ret = AppInitRPC(argc, argv);
|
||||
|
||||
@@ -24,8 +24,6 @@ typedef std::unique_ptr<struct type, type##_deleter> raii_##type
|
||||
MAKE_RAII(event_base);
|
||||
MAKE_RAII(event);
|
||||
MAKE_RAII(evhttp);
|
||||
MAKE_RAII(evhttp_request);
|
||||
MAKE_RAII(evhttp_connection);
|
||||
|
||||
inline raii_event_base obtain_event_base() {
|
||||
auto result = raii_event_base(event_base_new());
|
||||
@@ -42,15 +40,4 @@ inline raii_evhttp obtain_evhttp(struct event_base* base) {
|
||||
return raii_evhttp(evhttp_new(base));
|
||||
}
|
||||
|
||||
inline raii_evhttp_request obtain_evhttp_request(void(*cb)(struct evhttp_request *, void *), void *arg) {
|
||||
return raii_evhttp_request(evhttp_request_new(cb, arg));
|
||||
}
|
||||
|
||||
inline raii_evhttp_connection obtain_evhttp_connection_base(struct event_base* base, std::string host, uint16_t port) {
|
||||
auto result = raii_evhttp_connection(evhttp_connection_base_new(base, nullptr, host.c_str(), port));
|
||||
if (!result.get())
|
||||
throw std::runtime_error("create connection failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // BITCOIN_SUPPORT_EVENTS_H
|
||||
|
||||
@@ -177,9 +177,9 @@ class TestBitcoinCli(BitcoinTestFramework):
|
||||
conf_rpcport = "rpcport=" + str(node_rpc_port)
|
||||
self.nodes[0].replace_in_config([(conf_rpcport, "#" + conf_rpcport)])
|
||||
# prefer rpcport over rpcconnect
|
||||
assert_raises_process_error(1, "Could not connect to the server 127.0.0.1:1", self.nodes[0].cli(f"-rpcconnect=127.0.0.1:{node_rpc_port}", "-rpcport=1").echo)
|
||||
assert_raises_process_error(1, "Error while attempting to communicate with server 127.0.0.1:1 (Could not connect to the server)", self.nodes[0].cli(f"-rpcconnect=127.0.0.1:{node_rpc_port}", "-rpcport=1").echo)
|
||||
if have_ipv6:
|
||||
assert_raises_process_error(1, "Could not connect to the server ::1:1", self.nodes[0].cli(f"-rpcconnect=[::1]:{node_rpc_port}", "-rpcport=1").echo)
|
||||
assert_raises_process_error(1, "Error while attempting to communicate with server ::1:1 (Could not connect to the server)", self.nodes[0].cli(f"-rpcconnect=[::1]:{node_rpc_port}", "-rpcport=1").echo)
|
||||
|
||||
assert_equal(BLOCKS, self.nodes[0].cli("-rpcconnect=127.0.0.1:18999", f'-rpcport={node_rpc_port}').getblockcount())
|
||||
if have_ipv6:
|
||||
|
||||
@@ -41,8 +41,8 @@ class TestBitcoinIpcCli(BitcoinTestFramework):
|
||||
self.log.info("Skipping a few checks because temporary directory path is too long")
|
||||
|
||||
http_auth_error = "error: Authorization failed: Incorrect rpcuser or rpcpassword were specified."
|
||||
http_connect_error = f"error: timeout on transient error: Could not connect to the server 127.0.0.1:{rpc_port(node.index)}\n\nMake sure the bitcoind server is running and that you are connecting to the correct RPC port.\nUse \"bitcoin-cli -help\" for more info.\n"
|
||||
ipc_connect_error = "error: timeout on transient error: Connection refused\n\nProbably bitcoin-node is not running or not listening on a unix socket. Can be started with:\n\n bitcoin-node -chain=regtest -ipcbind=unix\n"
|
||||
http_connect_error = f"error: Error while attempting to communicate with server 127.0.0.1:{rpc_port(node.index)} (Could not connect to the server)\n\nMake sure the bitcoind server is running and that you are connecting to the correct RPC port.\nUse \"bitcoin-cli -help\" for more info.\n"
|
||||
ipc_connect_error = "error: Connection refused\n\nProbably bitcoin-node is not running or not listening on a unix socket. Can be started with:\n\n bitcoin-node -chain=regtest -ipcbind=unix\n"
|
||||
ipc_http_conflict = "error: -rpcconnect and -ipcconnect options cannot both be enabled\n"
|
||||
|
||||
for started in (True, False):
|
||||
|
||||
Reference in New Issue
Block a user