| 713 | }; |
| 714 | |
| 715 | static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {}) |
| 716 | { |
| 717 | std::string host; |
| 718 | // In preference order, we choose the following for the port: |
| 719 | // 1. -rpcport |
| 720 | // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6) |
| 721 | // 3. default port for chain |
| 722 | uint16_t port{BaseParams().RPCPort()}; |
| 723 | SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host); |
| 724 | port = static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", port)); |
| 725 | |
| 726 | // Obtain event base |
| 727 | raii_event_base base = obtain_event_base(); |
| 728 | |
| 729 | // Synchronously look up hostname |
| 730 | raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port); |
| 731 | |
| 732 | // Set connection timeout |
| 733 | { |
| 734 | const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT); |
| 735 | if (timeout > 0) { |
| 736 | evhttp_connection_set_timeout(evcon.get(), timeout); |
| 737 | } else { |
| 738 | // Indefinite request timeouts are not possible in libevent-http, so we |
| 739 | // set the timeout to a very long time period instead. |
| 740 | |
| 741 | constexpr int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar |
| 742 | evhttp_connection_set_timeout(evcon.get(), 5 * YEAR_IN_SECONDS); |
| 743 | } |
| 744 | } |
| 745 | |
| 746 | HTTPReply response; |
| 747 | raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response); |
| 748 | if (req == nullptr) |
| 749 | throw std::runtime_error("create http request failed"); |
| 750 | #if LIBEVENT_VERSION_NUMBER >= 0x02010300 |
| 751 | evhttp_request_set_error_cb(req.get(), http_error_cb); |
| 752 | #endif |
| 753 | |
| 754 | // Get credentials |
| 755 | std::string strRPCUserColonPass; |
| 756 | bool failedToGetAuthCookie = false; |
| 757 | if (gArgs.GetArg("-rpcpassword", "") == "") { |
| 758 | // Try fall back to cookie-based authentication if no password is provided |
| 759 | if (!GetAuthCookie(&strRPCUserColonPass)) { |
| 760 | failedToGetAuthCookie = true; |
| 761 | } |
| 762 | } else { |
| 763 | strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); |
| 764 | } |
| 765 | |
| 766 | struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get()); |
| 767 | assert(output_headers); |
| 768 | evhttp_add_header(output_headers, "Host", host.c_str()); |
| 769 | evhttp_add_header(output_headers, "Connection", "close"); |
| 770 | evhttp_add_header(output_headers, "Content-Type", "application/json"); |
| 771 | evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); |
| 772 | |