summaryrefslogtreecommitdiff
path: root/battleNetApi/src/hearthstoneApi.cpp
blob: 32c3164024882e6a8efc1c0bc804b388447c4b50 (plain)
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
#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 std::string &iUrl, const std::string &iTokenText) {
    CURL *curl = curl_easy_init();
    std::string aRequestOutput;
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, iUrl.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<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());
    }
    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;
}