summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAotrix <[email protected]>2026-01-31 00:37:35 +0100
committerAotrix <[email protected]>2026-01-31 00:37:35 +0100
commitae0aeff7ac5e3bda80547babdba9a26533d428d9 (patch)
tree2b4d79fd8653ff6bcd81d76c1021a646e8b83c5e
parent40d7391fcbc057823d423ddd0f0c6d532d7cf4eb (diff)
added parallelization for cards retrieval
-rw-r--r--battleNetApi/include/hearthstoneApi.hpp1
-rw-r--r--battleNetApi/src/hearthstoneApi.cpp61
-rw-r--r--battleNetApi/tests/tests.cpp43
3 files changed, 102 insertions, 3 deletions
diff --git a/battleNetApi/include/hearthstoneApi.hpp b/battleNetApi/include/hearthstoneApi.hpp
index 72f5141..56ac53c 100644
--- a/battleNetApi/include/hearthstoneApi.hpp
+++ b/battleNetApi/include/hearthstoneApi.hpp
@@ -10,6 +10,7 @@ class HearthstoneApi {
HearthstoneApi(const Token &iToken);
HearthstoneApi(Token &&iToken);
json makeApiRequest(const std::string &iUrl) const;
+ std::vector<json> makeApiRequest(const std::vector<std::string> &iUrls) const;
private:
std::string authorizationHeader();
diff --git a/battleNetApi/src/hearthstoneApi.cpp b/battleNetApi/src/hearthstoneApi.cpp
index 945425f..1d2d870 100644
--- a/battleNetApi/src/hearthstoneApi.cpp
+++ b/battleNetApi/src/hearthstoneApi.cpp
@@ -1,6 +1,9 @@
#include "hearthstoneApi.hpp"
#include <curl/curl.h>
+#include <curl/easy.h>
+#include <curl/multi.h>
#include <format>
+#include <iostream>
HearthstoneApi::HearthstoneApi(Token &&iToken) : mToken(iToken), mAuthorizationHeader(authorizationHeader()) {}
HearthstoneApi::HearthstoneApi(const Token &iToken) : mToken(iToken), mAuthorizationHeader(authorizationHeader()) {}
@@ -36,3 +39,61 @@ json HearthstoneApi::makeApiRequest(const std::string &iUrl) const {
json aOutputJson = json::parse(aRequestOutput);
return aOutputJson;
}
+
+std::vector<json> HearthstoneApi::makeApiRequest(const std::vector<std::string> &iUrls) const {
+ std::vector<std::string> aResponses(iUrls.size());
+ CURLM *curlm = curl_multi_init();
+ if (curlm) {
+ for (std::vector<std::string>::size_type i{0}; i < aResponses.size(); ++i) {
+ CURL *curl = curl_easy_init();
+ curl_easy_setopt(curl, CURLOPT_URL, iUrls[i].c_str());
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlCallbackStringOutput);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &(aResponses[i]));
+ struct curl_slist *chunk = nullptr;
+ chunk = curl_slist_append(chunk, mAuthorizationHeader.c_str());
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
+ curl_multi_add_handle(curlm, curl);
+ }
+ const int kMaxWaitMs{5000};
+ int aStillRunning{0};
+ curl_multi_perform(curlm, &aStillRunning);
+ do {
+ int numfds = 0;
+ int res = curl_multi_wait(curlm, NULL, 0, kMaxWaitMs, &numfds);
+ if (res != CURLM_OK) {
+ std::cerr << std::format("error: curl_multi_wait() returned {}\n", res);
+ }
+ curl_multi_perform(curlm, &aStillRunning);
+ } while (aStillRunning);
+
+ // clean-up
+ CURLMsg *aMessage{nullptr};
+ CURL *aHandle{nullptr};
+ int aMessagesLeft{0};
+ CURLcode aReturnCode;
+ while ((aMessage = curl_multi_info_read(curlm, &aMessagesLeft))) {
+ if (aMessage->msg == CURLMSG_DONE) {
+ aHandle = aMessage->easy_handle;
+ aReturnCode = aMessage->data.result;
+ if (aReturnCode != CURLE_OK) {
+ fprintf(stderr, "CURL error code: %d\n", aMessage->data.result);
+ continue;
+ }
+ curl_multi_remove_handle(curlm, aHandle);
+ curl_easy_cleanup(aHandle);
+ aHandle = nullptr;
+ } else {
+ std::cerr << std::format("error: after curl_multi_info_read(), CURLMsg={}\n",
+ static_cast<int>(aMessage->msg));
+ }
+ }
+ curl_multi_cleanup(curlm);
+ }
+ std::vector<json> aJsonOutputs;
+ for (std::string aResponse : aResponses) {
+ aJsonOutputs.push_back(json::parse(aResponse));
+ }
+ return aJsonOutputs;
+}
diff --git a/battleNetApi/tests/tests.cpp b/battleNetApi/tests/tests.cpp
index 062608e..39951bf 100644
--- a/battleNetApi/tests/tests.cpp
+++ b/battleNetApi/tests/tests.cpp
@@ -1,5 +1,12 @@
#include "apiUrl.hpp"
#include "hearthstoneApi.hpp"
+#include <catch2/benchmark/catch_benchmark.hpp>
+#include <catch2/benchmark/catch_chronometer.hpp>
+#include <catch2/benchmark/detail/catch_analyse.hpp>
+#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
+#include <catch2/benchmark/detail/catch_estimate_clock.hpp>
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_config.hpp>
#include <catch2/catch_test_macros.hpp>
#include <cstdint>
#include <iostream>
@@ -46,7 +53,7 @@ TEST_CASE("Retrieve cards", "[retrieveCardsOnce]") {
REQUIRE(kApiResponse["cards"].size() == 16);
}
-TEST_CASE("Retrieve cards", "[retrieveCardsFourTimes]") {
+TEST_CASE("Retrieve cards 10 times without parallelization", "[retrieveCardsNoParallel]") {
const std::string kCredentialsFile{"tests/resources/credentials.json"};
Token aToken(kCredentialsFile);
HearthstoneApi aHearthstoneApi(aToken);
@@ -60,9 +67,10 @@ TEST_CASE("Retrieve cards", "[retrieveCardsFourTimes]") {
json kApiResponse = aHearthstoneApi.makeApiRequest(aApiUrl());
// Retrieve number of pages
+ const int kNumberOfRetrievals{10};
const int kNumberOfPages{kApiResponse["pageCount"]};
- REQUIRE(kNumberOfPages >= 4);
- for (int i{1}; i < 4; ++i) {
+ REQUIRE(kNumberOfPages >= kNumberOfRetrievals);
+ for (int i{1}; i < kNumberOfRetrievals; ++i) {
REQUIRE(kApiResponse["cards"].size() == 16);
aApiUrl.setPage(std::to_string(i + 1));
kApiResponse = aHearthstoneApi.makeApiRequest(aApiUrl());
@@ -70,3 +78,32 @@ TEST_CASE("Retrieve cards", "[retrieveCardsFourTimes]") {
REQUIRE(kApiResponse["cards"].size() >= 1);
REQUIRE(kApiResponse["cards"].size() <= 16);
}
+
+TEST_CASE("Retrieve cards 30 times with parallelization", "[retrieveCardsParallel]") {
+ const std::string kCredentialsFile{"tests/resources/credentials.json"};
+ Token aToken(kCredentialsFile);
+ HearthstoneApi aHearthstoneApi(aToken);
+ const std::string kContentType{"cards"};
+ const std::string kRegion{"eu"};
+ ApiUrl aApiUrl(kContentType, kRegion);
+ aApiUrl.setLocale("fr_FR");
+ aApiUrl.setGameMode("constructed");
+ aApiUrl.setPageSize("16");
+ aApiUrl.setPage("1");
+ std::vector<std::string> aRequests;
+ json kFirstPageJson = aHearthstoneApi.makeApiRequest(aApiUrl());
+
+ int kNumberOfPages{kFirstPageJson["pageCount"]};
+ const int kNumberOfRetrievals{30};
+ REQUIRE(kNumberOfPages >= kNumberOfRetrievals);
+ for (int i{1}; i < kNumberOfRetrievals; ++i) {
+ aApiUrl.setPage(std::to_string(i + 1));
+ aRequests.push_back(aApiUrl());
+ }
+ std::vector<json> kApiResponses = aHearthstoneApi.makeApiRequest(aRequests);
+ kApiResponses.emplace(kApiResponses.begin(), kFirstPageJson);
+ REQUIRE(kApiResponses.size() == kNumberOfRetrievals);
+ for (const json &kApiResponse : kApiResponses) {
+ REQUIRE(kApiResponse["cards"].size() == 16);
+ }
+}