diff --git a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp index 1db7c889..ce4081c2 100644 --- a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp +++ b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp @@ -3,10 +3,6 @@ #include #include -#include -#include -#include - #include #include @@ -14,317 +10,6 @@ #include #include -#include - -#include -#include - -namespace graphene { namespace peerplays_sidechain { - -struct http_request { - - std::string method; // ex: "POST" - std::string path; // ex: "/" - std::string headers; - std::string body; - std::string content_type; // ex: "application/json" - - http_request() { - } - - http_request(const std::string &method_, const std::string &path_, const std::string &headers_, const std::string &body_, const std::string &content_type_) : - method(method_), - path(path_), - headers(headers_), - body(body_), - content_type(content_type_) { - } - - http_request(const std::string &method_, const std::string &path_, const std::string &headers_, const std::string &body_ = std::string()) : - method(method_), - path(path_), - headers(headers_), - body(body_), - content_type("application/json") { - } - - void clear() { - method.clear(); - path.clear(); - headers.clear(); - body.clear(); - content_type.clear(); - } -}; - -struct http_response { - - uint16_t status_code; - std::string body; - - void clear() { - status_code = 0; - body = decltype(body)(); - } -}; - -class https_call { -public: - https_call(const std::string &host, uint16_t port = 0) : - m_host(host), - m_port(port) { - } - - const std::string &host() const { - return m_host; - } - - uint16_t port() const { - return m_port; - } - - uint32_t response_size_limit_bytes() const { - return 1024 * 1024; - } - - bool exec(const http_request &request, http_response *response); - -private: - std::string m_host; - uint16_t m_port; -}; - -namespace detail { - -static const char cr = 0x0D; -static const char lf = 0x0A; -static const char *crlf = "\x0D\x0A"; -static const char *crlfcrlf = "\x0D\x0A\x0D\x0A"; - -using namespace boost::asio; - -class https_call_impl { -public: - https_call_impl(const https_call &call, const http_request &request, http_response &response) : - m_call(call), - m_request(request), - m_response(response), - m_service(), - m_context(ssl::context::tlsv12_client), - m_socket(m_service, m_context), - m_endpoint(), - m_response_buf(call.response_size_limit_bytes()), - m_content_length(0) { - m_context.set_default_verify_paths(); - } - - void exec() { - resolve(); - connect(); - send_request(); - process_response(); - } - -private: - const https_call &m_call; - const http_request &m_request; - http_response &m_response; - - io_service m_service; - ssl::context m_context; - ssl::stream m_socket; - ip::tcp::endpoint m_endpoint; - streambuf m_response_buf; - uint32_t m_content_length; - - void resolve() { - - // resolve TCP endpoint for host name - - ip::tcp::resolver resolver(m_service); - auto query = ip::tcp::resolver::query(m_call.host(), "https"); - auto iter = resolver.resolve(query); - m_endpoint = *iter; - - if (m_call.port() != 0) // if port was specified - m_endpoint.port(m_call.port()); // force set port - } - - void connect() { - - // TCP connect - - m_socket.lowest_layer().connect(m_endpoint); - - // SSL connect - - m_socket.set_verify_mode(ssl::verify_peer); - m_socket.handshake(ssl::stream_base::client); - } - - void send_request() { - - streambuf request_buf; - std::ostream stream(&request_buf); - - // start string: HTTP/1.0 - - stream << m_request.method << " " << m_request.path << " HTTP/1.0" << crlf; - - // host - - stream << "Host: " << m_call.host(); - - if (m_call.port() != 0) { - //ASSERT(m_Endpoint.port() == m_Call.port()); - stream << ":" << m_call.port(); - } - - stream << crlf; - - // content - - if (!m_request.body.empty()) { - stream << "Content-Type: " << m_request.content_type << crlf; - stream << "Content-Length: " << m_request.body.size() << crlf; - } - - // additional headers - - const auto &h = m_request.headers; - - if (!h.empty()) { - if (h.size() < 2) { - FC_THROW("invalid headers data"); - } - stream << h; - // ensure headers finished correctly - if ((h.substr(h.size() - 2) != crlf)) - stream << crlf; - } - - // other - - stream << "Accept: *\x2F*" << crlf; - stream << "Connection: close" << crlf; - - // end - - stream << crlf; - - // content - - if (!m_request.body.empty()) - stream << m_request.body; - - // send - - write(m_socket, request_buf); - } - - void process_headers() { - - std::istream stream(&m_response_buf); - - std::string http_version; - stream >> http_version; - stream >> m_response.status_code; - - if (!stream || http_version.substr(0, 5) != "HTTP/") { - FC_THROW("invalid response data"); - } - - // read/skip headers - - for (;;) { - std::string header; - if (!std::getline(stream, header, lf) || (header.size() == 1 && header[0] == cr)) - break; - if (m_content_length) // if content length is already known - continue; // continue skipping headers - auto pos = header.find(':'); - if (pos == std::string::npos) - continue; - auto name = header.substr(0, pos); - boost::algorithm::trim(name); - boost::algorithm::to_lower(name); - if (name != "content-length") - continue; - auto value = header.substr(pos + 1); - boost::algorithm::trim(value); - m_content_length = std::stol(value); - } - } - - void process_response() { - - auto &socket = m_socket; - auto &buf = m_response_buf; - auto &content_length = m_content_length; - auto &body = m_response.body; - - read_until(socket, buf, crlfcrlf); - - process_headers(); - - // check content length - - if (content_length < 2) { // minimum content is "{}" - FC_THROW("invalid response body (too short)"); - } - - if (content_length > m_call.response_size_limit_bytes()) { - FC_THROW("response body size limit exceeded"); - } - - // read body - - auto avail = buf.size(); // size of body data already stored in the buffer - - if (avail > content_length) { - FC_THROW("invalid response body (content length mismatch)"); - } - - body.resize(content_length); - - if (avail) { - // copy already existing data - if (avail != buf.sgetn(&body[0], avail)) { - FC_THROW("stream read failed"); - } - } - - auto rest = content_length - avail; // size of remaining part of response body - - boost::system::error_code error_code; - - read(socket, buffer(&body[avail], rest), error_code); // read remaining part - - socket.shutdown(error_code); - } -}; - -} // namespace detail - -bool https_call::exec(const http_request &request, http_response *response) { - - // ASSERT(response); - auto &resp = *response; - - detail::https_call_impl impl(*this, request, resp); - - try { - resp.clear(); - impl.exec(); - } catch (...) { - resp.clear(); - return false; - } - - return true; -} - -}} // namespace graphene::peerplays_sidechain namespace graphene { namespace peerplays_sidechain { @@ -416,60 +101,75 @@ std::string rpc_client::send_post_request(std::string method, std::string params return ""; } +//fc::http::reply rpc_client::send_post_request(std::string body, bool show_log) { +// fc::http::connection conn; +// conn.connect_to(fc::ip::endpoint(fc::ip::address(ip), port)); +// +// std::string url = "http://" + ip + ":" + std::to_string(port); +// +// //if (wallet.length() > 0) { +// // url = url + "/wallet/" + wallet; +// //} +// +// fc::http::reply reply = conn.request("POST", url, body, fc::http::headers{authorization}); +// +// if (show_log) { +// ilog("### Request URL: ${url}", ("url", url)); +// ilog("### Request: ${body}", ("body", body)); +// std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); +// ilog("### Response: ${ss}", ("ss", ss.str())); +// } +// +// return reply; +//} + +static size_t write_callback(char *ptr, size_t size, size_t nmemb, rpc_reply *reply) { + size_t retval = 0; + if (reply != nullptr) { + reply->body.append(ptr, size * nmemb); + retval = size * nmemb; + } + return retval; +} + rpc_reply rpc_client::send_post_request(std::string body, bool show_log) { - rpc_reply reply; - auto start = ip.substr(0, 6); - boost::algorithm::to_lower(start); - - if (start == "https:") { - - auto host = ip.substr(8); // skip "https://" - - https_call call(host, port); - http_request request("POST", "/", authorization.key + ":" + authorization.val, body); - http_response response; - - if (call.exec(request, &response)) { - reply.status = response.status_code; - reply.body.resize(response.body.size()); - memcpy(&reply.body[0], &response.body[0], response.body.size()); - } + struct curl_slist *headers = nullptr; + headers = curl_slist_append(headers, "Accept: application/json"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + headers = curl_slist_append(headers, "charset: utf-8"); + CURL *curl = curl_easy_init(); + if (ip.find("https://", 0) != 0) { + curl_easy_setopt(curl, CURLOPT_URL, ip.c_str()); + curl_easy_setopt(curl, CURLOPT_PORT, port); } else { - - std::string host; - - if (start == "http:/") - host = ip.substr(7); // skip "http://" - else - host = ip; - - std::string url = "http://" + host + ":" + std::to_string(port); - fc::ip::address addr; - - try { - addr = fc::ip::address(host); - } catch (...) { - return reply; - } - - try { - - fc::http::connection conn; - conn.connect_to(fc::ip::endpoint(addr, port)); - - //if (wallet.length() > 0) { - // url = url + "/wallet/" + wallet; - //} - - auto r = conn.request("POST", url, body, fc::http::headers{authorization}); - reply.status = r.status; - reply.body.assign(r.body.begin(), r.body.end()); - - } catch (...) { - } + std::string full_address = ip + ":" + std::to_string(port); + curl_easy_setopt(curl, CURLOPT_URL, full_address.c_str()); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false); } + if (!user.empty()) { + curl_easy_setopt(curl, CURLOPT_USERNAME, user.c_str()); + curl_easy_setopt(curl, CURLOPT_PASSWORD, password.c_str()); + } + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); + + //curl_easy_setopt(curl, CURLOPT_VERBOSE, true); + + rpc_reply reply; + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &reply); + + curl_easy_perform(curl); + + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &reply.status); + + curl_easy_cleanup(curl); + curl_slist_free_all(headers); if (show_log) { std::string url = ip + ":" + std::to_string(port);