#include "hearthstoneApi.hpp" #include #include #include #include #include #include #include namespace bnet { std::string HearthstoneApi::authorizationHeader(const std::string &iTokenText) { return std::format("Authorization: Bearer {}", iTokenText); } size_t HearthstoneApi::curlCallbackStringOutput(void *iContent, size_t iSize, size_t iNmemb, std::string *iS) { const size_t kNewLength = iSize * iNmemb; try { iS->append(static_cast(iContent), kNewLength); } catch (std::bad_alloc &e) { // handle memory problem return 0; } return kNewLength; } json HearthstoneApi::makeApiRequest(const ApiUrl &iApiUrl, const std::string &iTokenText) { CURL *curl = curl_easy_init(); std::string aRequestOutput; if (curl) { curl_easy_setopt(curl, CURLOPT_URL, iApiUrl.text().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, &aRequestOutput); struct curl_slist *chunk = nullptr; chunk = curl_slist_append(chunk, authorizationHeader(iTokenText).c_str()); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); curl_easy_perform(curl); curl_easy_cleanup(curl); } json aOutputJson = json::parse(aRequestOutput); return aOutputJson; } std::vector HearthstoneApi::makeApiRequest(const std::vector &iApiUrls, const std::string &iTokenText) { std::vector aApiUrls(iApiUrls.size()); std::ranges::transform(iApiUrls, aApiUrls.begin(), [](const ApiUrl &iApiUrl) { return iApiUrl.text(); }); return makeApiRequest(aApiUrls, iTokenText); } std::vector HearthstoneApi::makeApiRequest(const std::vector &iUrls, const std::string &iTokenText) { std::vector aResponses(iUrls.size()); CURLM *curlm = curl_multi_init(); if (curlm) { for (std::vector::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, authorizationHeader(iTokenText).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(aMessage->msg)); } } curl_multi_cleanup(curlm); } std::vector aJsonOutputs; for (std::string aResponse : aResponses) { aJsonOutputs.push_back(json::parse(aResponse)); } return aJsonOutputs; } json HearthstoneApi::downloadCards(ApiUrl iApiUrl, const Token &iToken) { if (!iToken.text().has_value() || iToken.text().value().empty()) { throw std::invalid_argument("Token text does not exist or is empty"); } if (iApiUrl.hasPage()) { return makeApiRequest(iApiUrl, *iToken.text()); } iApiUrl.setPage("1"); std::vector aRequests; json aFirstPageJson = makeApiRequest(iApiUrl, *iToken.text()); int kNumberOfPages{aFirstPageJson["pageCount"]}; for (int i{1}; i < kNumberOfPages; ++i) { iApiUrl.setPage(std::to_string(i + 1)); aRequests.push_back(iApiUrl.text()); } json::reference &aOutputJson = aFirstPageJson["cards"]; std::vector kApiResponses = makeApiRequest(aRequests, *iToken.text()); for (json &aPage : kApiResponses) { for (json::reference &aCard : aPage["cards"]) { aOutputJson.push_back(std::move(aCard)); } } return aOutputJson; } }; // namespace bnet