Skip to content

Commit

Permalink
Merge branch 'main' into 00782-test-nodecreatetransaction-with-solo
Browse files Browse the repository at this point in the history
  • Loading branch information
gsstoykov authored Oct 16, 2024
2 parents 8a359c7 + 35bc1a9 commit d7bad92
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 206 deletions.
1 change: 0 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ find_package(absl CONFIG REQUIRED)
find_package(upb CONFIG REQUIRED)
find_package(Threads REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
find_package(CURL CONFIG REQUIRED)
find_package(EXPAT CONFIG REQUIRED)

include(FetchContent)
Expand Down
2 changes: 0 additions & 2 deletions src/sdk/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,6 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
absl::time
absl::time_zone)

target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl)

target_link_libraries(${PROJECT_NAME} PRIVATE
upb::upb
upb::descriptor_upb_proto
Expand Down
60 changes: 0 additions & 60 deletions src/sdk/main/include/exceptions/CURLException.h

This file was deleted.

77 changes: 21 additions & 56 deletions src/sdk/main/include/impl/HttpClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,66 +20,31 @@
#ifndef HEDERA_SDK_CPP_IMPL_HTTP_CLIENT_H_
#define HEDERA_SDK_CPP_IMPL_HTTP_CLIENT_H_

#include <curl/curl.h>
#include <iostream>
#include <memory>
#include <httplib.h>
#include <string>
#include <string_view>

namespace Hedera::internal
namespace Hedera::internal::HttpClient
{
class HttpClient
{
public:
/**
* Constructor for HttpClient.
*
* This constructor initializes the HttpClient and performs global libcurl initialization
* using CURL_GLOBAL_DEFAULT.
*
* @throw std::runtime_error If libcurl initialization fails.
*/
HttpClient();

HttpClient(const HttpClient&) = delete; // no use case for httpClient copy
HttpClient& operator=(const HttpClient&) = delete; // no use case for httpClient copy
/**
* Fetches data from the specified URL using the provided RPC method.
* @param url The URL to fetch data from.
* @param rpcMethod The RPC method.
* @return The response data as a string.
*/
[[nodiscard]] std::string invokeRPC(std::string_view url, std::string_view rpcMethod);

/**
* Destructor for HttpClient.
*
* This destructor cleans up global libcurl resources using curl_global_cleanup.
* It should be called when an HttpClient instance is no longer needed to release libcurl resources.
*/
~HttpClient();
/**
* Fetches data from the specified URL using the provided RPC method.
* @param url The URL to fetch data from.
* @param rpcMethod The RPC method.
* @return The fetched data as a string.
*/
[[nodiscard]] std::string invokeRPC(const std::string& url, const std::string& rpcMethod);
/**
* This invokeREST function creates GET and POST requests.
* Can be further extended for supporting other HTTP
* methods or handle more advanced scenarios as needed.
* @param url The URL to fetch data from.
* @param httpMethod The HTTP method.
* @param requestBody The HTTP request body.
* @return The fetched data as a string.
*/
[[nodiscard]] std::string invokeREST(const std::string& url,
const std::string& httpMethod = "GET",
const std::string& requestBody = "");
/**
* The callback function used for writing fetched data.
* @param contents A pointer to the fetched data.
* @param size The size of each element.
* @param nmemb The number of elements.
* @param output A pointer to the output string.
* @return The total size of the fetched data.
*/
private:
static size_t writeCallback(char* contents, size_t size, size_t nmemb, std::string* output);
};
/**
* Create a GET or POST request. Can be further extended for supporting other HTTP methods or handle more advanced
* scenarios as needed.
* @param url The URL to which to submit the request.
* @param httpMethod The HTTP method.
* @param requestBody The HTTP request body.
* @return The response data as a string.
*/
[[nodiscard]] std::string invokeREST(std::string_view url,
std::string_view httpMethod = "GET",
std::string_view requestBody = "");

} // namespace Hedera::internal

Expand Down
4 changes: 1 addition & 3 deletions src/sdk/main/src/AccountId.cc
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,8 @@ AccountId& AccountId::populateAccountEvmAddress(const Client& client)
// build url for Mirror Node
std::string url = "https://" + mirrorNetworks.front() + "/api/v1/accounts/0.0." + std::to_string(mAccountNum.value());

internal::HttpClient httpClient;

// fetch account data for this account from Mirror Node
std::string response = httpClient.invokeREST(url, "GET");
std::string response = internal::HttpClient::invokeREST(url, "GET");
json responseData = json::parse(response);

if (responseData["account"].empty() || responseData["evm_address"].empty())
Expand Down
122 changes: 49 additions & 73 deletions src/sdk/main/src/impl/HttpClient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,101 +18,77 @@
*
*/

#include "exceptions/CURLException.h"

#include "impl/HttpClient.h"

#include <httplib.h>
#include <stdexcept>
#include <string>
#include <string_view>

namespace Hedera::internal
{
HttpClient::HttpClient()
namespace
{
if (curl_global_init(CURL_GLOBAL_DEFAULT) != 0)
{
throw CURLException("Failed to initialize libcurl!");
}
}

HttpClient::~HttpClient()
// The index in a URL to begin searching for the path after the end of the URL scheme ("http://" or "https://").
const int SCHEME_END_INDEX = 8;

//
// Perform an HTTP request.
//
// @param url The URL to which to send the request.
// @param method The HTTP method type of this request.
// @param body The body of the request.
// @return The response of the request.
//
[[nodiscard]] std::string performRequest(std::string_view url, std::string_view method, std::string_view body)
{
curl_global_cleanup();
}
// Create an HTTP client to communicate with the given URL.
httplib::Client client(std::string(url.substr(0, url.find('/', SCHEME_END_INDEX))));
const std::string path = url.substr(url.find('/', SCHEME_END_INDEX)).data();

// example mirrorNode query:
// httpClient.invokeREST("https://testnet.mirrornode.hedera.com/api/v1/accounts/" + newAccountId ,"GET", "");
// note: should time out before calling this function because the mirror node is not updated on time if accountID has
// been created exactly before the call. Works without timeout if the data in the mirror node is there from some seconds
// beforehand
std::string HttpClient::invokeREST(const std::string& url,
const std::string& httpMethod,
const std::string& requestBody)
{
std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl(curl_easy_init(), curl_easy_cleanup);
httplib::Result res;

if (!curl)
// Perform the request based on the HTTP method
if (method == "GET")
{
throw CURLException("CURL initialization failed!");
res = client.Get(path);
}

curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());

std::string response;

curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, &HttpClient::writeCallback);
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response);

curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST, httpMethod.c_str());

if (httpMethod == "POST")
else if (method == "POST")
{
res = client.Post(path, body.data(), body.size(), "application/json");
}
else
{
curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, requestBody.c_str());
throw std::invalid_argument(std::string("Unsupported HTTP method: ") + method.data());
}

CURLcode res = curl_easy_perform(curl.get());
if (res != CURLE_OK)
if (!res)
{
throw CURLException(std::string("Error getting curl result: ") + curl_easy_strerror(res));
throw std::runtime_error("HTTP error: " + httplib::to_string(res.error()));
}

return response;
return res->body;
}

} // namespace

// example infura query: rpcMethod = R"({"jsonrpc":"2.0","method":"eth_getTransactionByHash","params":[")" + hash +
// R"("],"id":1})"
std::string HttpClient::invokeRPC(const std::string& url, const std::string& rpcMethod)
std::string HttpClient::invokeRPC(std::string_view url, std::string_view rpcMethod)
{
std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl(curl_easy_init(), curl_easy_cleanup);
if (!curl)
{
throw CURLException("Failed to initialize libcurl");
}

curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
curl_easy_setopt(curl.get(), CURLOPT_POST, 1L);

std::shared_ptr<curl_slist> headers(curl_slist_append(nullptr, "Content-Type: application/json"),
curl_slist_free_all);
curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());

std::string json_body = rpcMethod;
curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, json_body.c_str());

std::string response;
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response);

CURLcode res = curl_easy_perform(curl.get());
if (res != CURLE_OK)
{
throw CURLException(std::string("Error getting curl result! ") + curl_easy_strerror(res));
}

return response;
return performRequest(url, "POST", rpcMethod);
}

size_t HttpClient::writeCallback(char* contents, size_t size, size_t nmemb, std::string* output)
// example mirrorNode query:
// httpClient.invokeREST("https://testnet.mirrornode.hedera.com/api/v1/accounts/" + newAccountId ,"GET", "");
// note: should time out before calling this function because the mirror node is not updated on time if accountID has
// been created exactly before the call. Works without timeout if the data in the mirror node is there from some seconds
// beforehand
std::string HttpClient::invokeREST(std::string_view url, std::string_view httpMethod, std::string_view requestBody)
{
size_t totalSize = size * nmemb;
output->append(contents, totalSize);
return totalSize;
};
return performRequest(url, httpMethod, requestBody);
}

//-----

} // namespace Hedera::internal
6 changes: 2 additions & 4 deletions src/sdk/main/src/impl/MirrorNodeGateway.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@
*/

#include "impl/MirrorNodeGateway.h"
#include "exceptions/CURLException.h"
#include "exceptions/IllegalStateException.h"
#include "impl/HttpClient.h"

#include <chrono>
#include <iostream>
#include <string>
#include <thread>

Expand All @@ -39,7 +38,6 @@ json MirrorNodeQuery(std::string_view mirrorNodeUrl, const std::vector<std::stri
{
bool isLocalNetwork = true;
const std::string url = buildUrlForNetwork(mirrorNodeUrl, queryType, params, isLocalNetwork);
HttpClient httpClient;

// this is needed because of Mirror Node update delay time
// agreed to be handled by the user not a local network
Expand All @@ -48,7 +46,7 @@ json MirrorNodeQuery(std::string_view mirrorNodeUrl, const std::vector<std::stri
std::this_thread::sleep_for(std::chrono::seconds(3));
}

response = httpClient.invokeREST(url);
response = HttpClient::invokeREST(url);
}
catch (const std::exception& e)
{
Expand Down
1 change: 0 additions & 1 deletion src/sdk/tests/integration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ add_executable(${TEST_PROJECT_NAME}
TransactionRecordQueryIntegrationTests.cc
TransferTransactionIntegrationTests.cc)

target_link_libraries(${TEST_PROJECT_NAME} PRIVATE CURL::libcurl)
target_link_libraries(${TEST_PROJECT_NAME} PRIVATE gtest_main gtest ${PROJECT_NAME})
target_link_libraries(${TEST_PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json)
gtest_discover_tests(${TEST_PROJECT_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE})
Expand Down
3 changes: 1 addition & 2 deletions src/sdk/tests/integration/HttpClientIntegrationTests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,8 @@ TEST_F(HttpClientIntegrationTests, GETAccountFromLocalMirrorNode)
mUrl += accountIdStr;

// When
internal::HttpClient httpClient;
std::string response;
ASSERT_NO_THROW(response = httpClient.invokeREST(mUrl, "GET"));
ASSERT_NO_THROW(response = internal::HttpClient::invokeREST(mUrl, "GET"));

// Then
json responseData = json::parse(response);
Expand Down
4 changes: 0 additions & 4 deletions vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@
{
"name": "nlohmann-json"
},
{
"name": "curl",
"version>=": "7.84.0"
},
{
"name": "expat"
},
Expand Down

0 comments on commit d7bad92

Please sign in to comment.