1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
#include "hearthstoneApi.hpp"
#include <apiUrl.hpp>
#include <curl/curl.h>
#include <curl/easy.h>
#include <curl/multi.h>
#include <format>
#include <iostream>
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<char *>(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<json> HearthstoneApi::makeApiRequest(const std::vector<ApiUrl> &iApiUrls, const std::string &iTokenText) {
std::vector<std::string> aApiUrls(iApiUrls.size());
std::ranges::transform(iApiUrls, aApiUrls.begin(), [](const ApiUrl &iApiUrl) { return iApiUrl.text(); });
return makeApiRequest(aApiUrls, iTokenText);
}
std::vector<json> HearthstoneApi::makeApiRequest(const std::vector<std::string> &iUrls, const std::string &iTokenText) {
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, 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<int>(aMessage->msg));
}
}
curl_multi_cleanup(curlm);
}
std::vector<json> aJsonOutputs;
for (std::string aResponse : aResponses) {
aJsonOutputs.push_back(json::parse(aResponse));
}
return aJsonOutputs;
}
json HearthstoneApi::downloadCards(ApiUrl &iApiUrl, Token &iToken) {
iApiUrl.setPage("1");
std::vector<std::string> 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<json> kApiResponses = makeApiRequest(aRequests, iToken.text());
for (json &aPage : kApiResponses) {
for (json::reference &aCard : aPage["cards"]) {
aOutputJson.push_back(std::move(aCard));
}
}
return aOutputJson;
}
|