| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 3 | * 2013-2024 by the Falcon Player Developers. | ||
| 4 | * | ||
| 5 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 6 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 7 | * file for descriptions of what files are covered by each license. | ||
| 8 | * | ||
| 9 | * This source file is covered under the LGPL v2.1 as described in the | ||
| 10 | * included LICENSE.LGPL file. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include "fpp-pch.h" | ||
| 14 | |||
| 15 | #include <algorithm> | ||
| 16 | #include <atomic> | ||
| 17 | #include <chrono> | ||
| 18 | #include <cstring> | ||
| 19 | #include <list> | ||
| 20 | #include <memory> | ||
| 21 | #include <mutex> | ||
| 22 | #include <string> | ||
| 23 | #include <thread> | ||
| 24 | #include <vector> | ||
| 25 | |||
| 26 | #include "CurlManager.h" | ||
| 27 | #include "CurlSocket.h" | ||
| 28 | #include "fppversion.h" | ||
| 29 | #include "log.h" | ||
| 30 | |||
| 31 | CurlManager CurlManager::INSTANCE; | ||
| 32 | |||
| 33 | 2346 | CurlManager::CurlManager() { | |
| 34 | 2346 | } | |
| 35 | |||
| 36 | 458 | CurlManager::~CurlManager() { | |
| 37 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 458 times.
|
458 | if (curlMulti) { |
| 38 | ✗ | curl_multi_cleanup(curlMulti); | |
| 39 | } | ||
| 40 | // No handle should still reference these shares by the time the singleton is | ||
| 41 | // torn down at process exit; free whatever cookie sessions were left behind. | ||
| 42 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 458 times.
|
458 | for (auto& p : cookieSessions) { |
| 43 | ✗ | if (p.second->share) { | |
| 44 | ✗ | curl_share_cleanup(p.second->share); | |
| 45 | } | ||
| 46 | ✗ | delete p.second; | |
| 47 | } | ||
| 48 | 458 | cookieSessions.clear(); | |
| 49 | 458 | } | |
| 50 | |||
| 51 | // curl calls these around every access to shared data (only COOKIE is shared | ||
| 52 | // here). The userdata is the session's own std::mutex; a single mutex is | ||
| 53 | // sufficient because only one data class is shared, so curl never nests locks. | ||
| 54 | 68 | static void curlShareLockCB(CURL* handle, curl_lock_data data, curl_lock_access access, void* userptr) { | |
| 55 | 68 | static_cast<std::mutex*>(userptr)->lock(); | |
| 56 | 68 | } | |
| 57 | 68 | static void curlShareUnlockCB(CURL* handle, curl_lock_data data, void* userptr) { | |
| 58 | 68 | static_cast<std::mutex*>(userptr)->unlock(); | |
| 59 | 68 | } | |
| 60 | |||
| 61 | 12 | CURLSH* CurlManager::getCookieShare(const std::string& token) { | |
| 62 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | std::unique_lock<std::mutex> l(lock); |
| 63 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | CookieSession*& cs = cookieSessions[token]; |
| 64 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 7 times.
|
12 | if (cs == nullptr) { |
| 65 |
2/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
|
5 | cs = new CookieSession(); |
| 66 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | cs->share = curl_share_init(); |
| 67 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | curl_share_setopt(cs->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); |
| 68 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | curl_share_setopt(cs->share, CURLSHOPT_LOCKFUNC, curlShareLockCB); |
| 69 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | curl_share_setopt(cs->share, CURLSHOPT_UNLOCKFUNC, curlShareUnlockCB); |
| 70 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | curl_share_setopt(cs->share, CURLSHOPT_USERDATA, &cs->mutex); |
| 71 | } | ||
| 72 | 12 | return cs->share; | |
| 73 | 12 | } | |
| 74 | |||
| 75 | 15 | void CurlManager::releaseCookieSession(const std::string& token) { | |
| 76 | 15 | CookieSession* cs = nullptr; | |
| 77 | { | ||
| 78 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | std::unique_lock<std::mutex> l(lock); |
| 79 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | auto it = cookieSessions.find(token); |
| 80 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
|
15 | if (it == cookieSessions.end()) { |
| 81 | 10 | return; | |
| 82 | } | ||
| 83 | 5 | cs = it->second; | |
| 84 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | cookieSessions.erase(it); |
| 85 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10 times.
|
15 | } |
| 86 | // Caller contract: no easy handle still has CURLOPT_SHARE pointing at this | ||
| 87 | // share (the owner's requests have completed or been cancelled). If curl | ||
| 88 | // still sees it in use we must NOT free it - the share holds &cs->mutex, so | ||
| 89 | // deleting cs would leave curl calling into a freed mutex. Leak + shout. | ||
| 90 | 5 | CURLSHcode sc = curl_share_cleanup(cs->share); | |
| 91 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (sc == CURLSHE_IN_USE) { |
| 92 | ✗ | LogErr(VB_GENERAL, "CurlManager cookie session '%s' still in use at release; leaking share to avoid a UAF\n", token.c_str()); | |
| 93 | ✗ | return; | |
| 94 | } | ||
| 95 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | delete cs; |
| 96 | } | ||
| 97 | |||
| 98 | 1342 | void CurlManager::addCURL(const std::string& furl, CURL* curl, std::function<void(CURL*)>&& callback, bool autoCleanCurl, | |
| 99 | const std::string& owner) { | ||
| 100 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | std::unique_lock<std::mutex> l(lock); |
| 101 |
2/2✓ Branch 0 taken 136 times.
✓ Branch 1 taken 1206 times.
|
1342 | if (curlMulti == nullptr) { |
| 102 |
1/2✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
|
136 | curlMulti = curl_multi_init(); |
| 103 |
1/2✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
|
136 | curl_multi_setopt(curlMulti, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); |
| 104 | } | ||
| 105 | |||
| 106 |
2/4✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1342 times.
|
1342 | CurlInfo* i = new CurlInfo; |
| 107 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | i->host = getHost(furl); |
| 108 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | i->url = furl; |
| 109 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | i->callback = callback; |
| 110 | 1342 | i->curl = curl; | |
| 111 | 1342 | i->cleanCurl = autoCleanCurl; | |
| 112 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | i->owner = owner; |
| 113 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_multi_add_handle(curlMulti, curl); |
| 114 | 1342 | numCurls++; | |
| 115 |
2/2✓ Branch 0 taken 7379 times.
✓ Branch 1 taken 324 times.
|
7703 | for (int x = 0; x < curls.size(); x++) { |
| 116 |
2/2✓ Branch 0 taken 1018 times.
✓ Branch 1 taken 6361 times.
|
7379 | if (curls[x] == nullptr) { |
| 117 | 1018 | curls[x] = i; | |
| 118 | 1018 | return; | |
| 119 | } | ||
| 120 | } | ||
| 121 |
1/2✓ Branch 0 taken 324 times.
✗ Branch 1 not taken.
|
324 | curls.push_back(i); |
| 122 |
2/2✓ Branch 0 taken 324 times.
✓ Branch 1 taken 1018 times.
|
1342 | } |
| 123 | |||
| 124 | 3405 | static size_t urlWriteData(void* buffer, size_t size, size_t nmemb, void* userp) { | |
| 125 | 3405 | std::vector<uint8_t>* str = (std::vector<uint8_t>*)userp; | |
| 126 | 3405 | size_t pos = str->size(); | |
| 127 | 3405 | str->resize(str->size() + size * nmemb); | |
| 128 | 3405 | memcpy(&str->data()[pos], buffer, size * nmemb); | |
| 129 | 3405 | return size * nmemb; | |
| 130 | } | ||
| 131 | |||
| 132 | // The requests FPP makes itself (add*(), do*()) and its discovery probes hold | ||
| 133 | // the whole response in memory, and discovery reaches devices that are merely | ||
| 134 | // on the LAN: one that streamed an endless body grew fppd until the OOM killer | ||
| 135 | // stepped in (the timeout alone allows ~100MB at LAN speed). Returning short | ||
| 136 | // aborts the transfer with CURLE_WRITE_ERROR -- but the HTTP status already | ||
| 137 | // received still reads 200, so the request paths below report that result, | ||
| 138 | // like any other failed transfer, as a failure themselves (failedTransfer()). | ||
| 139 | // A plugin's own handle from createCurl() keeps no | ||
| 140 | // limit: it reads the status and the body itself, and a cut-off body with a | ||
| 141 | // 200 is what its proxy then served (limitResponseSize() opts a handle in). | ||
| 142 | static constexpr size_t MAX_CURL_RESPONSE = 16 * 1024 * 1024; | ||
| 143 | |||
| 144 | 3407 | static size_t urlWriteDataLimited(void* buffer, size_t size, size_t nmemb, void* userp) { | |
| 145 | 3407 | std::vector<uint8_t>* str = (std::vector<uint8_t>*)userp; | |
| 146 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3405 times.
|
3407 | if (size * nmemb > MAX_CURL_RESPONSE - std::min(str->size(), MAX_CURL_RESPONSE)) { |
| 147 | 2 | return 0; | |
| 148 | } | ||
| 149 | 3405 | return urlWriteData(buffer, size, nmemb, userp); | |
| 150 | } | ||
| 151 | |||
| 152 | 1342 | void CurlManager::limitResponseSize(CURL* c) { | |
| 153 | 1342 | curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, urlWriteDataLimited); | |
| 154 | 1342 | } | |
| 155 | |||
| 156 | // The result of the transfer whose callback is running on this thread, set by | ||
| 157 | // doProcessCurls() around it: the callbacks take only the handle, and the | ||
| 158 | // result isn't one of its infos. | ||
| 159 | static thread_local CURLcode t_completedResult = CURLE_OK; | ||
| 160 | |||
| 161 | // Why a transfer failed, or "" if it completed. CURLINFO_RESPONSE_CODE is | ||
| 162 | // the last status received even when the transfer then failed: a body cut | ||
| 163 | // off by the size cap, a timeout or a reset partway through, or a server | ||
| 164 | // that closed before its Content-Length all read as a 200 with part of the | ||
| 165 | // body. So the requests below go by the transfer's result, not the status. | ||
| 166 | 969 | static std::string failedTransfer(CURLcode result, const CurlManager::CurlPrivateData* data) { | |
| 167 |
2/2✓ Branch 0 taken 922 times.
✓ Branch 1 taken 47 times.
|
969 | if (result == CURLE_OK) { |
| 168 |
1/2✓ Branch 0 taken 922 times.
✗ Branch 1 not taken.
|
1844 | return ""; |
| 169 | } | ||
| 170 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 45 times.
|
47 | if (result == CURLE_WRITE_ERROR) { |
| 171 | // only urlWriteDataLimited() returns short on these handles | ||
| 172 |
3/6✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | return "the response was larger than " + std::to_string(MAX_CURL_RESPONSE / (1024 * 1024)) + " MB"; |
| 173 | } | ||
| 174 |
2/4✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
|
45 | if (data && data->errorResp[0]) { |
| 175 |
1/2✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
|
90 | return data->errorResp; |
| 176 | } | ||
| 177 | ✗ | return curl_easy_strerror(result); | |
| 178 | } | ||
| 179 | ✗ | static size_t urlReadData(void* ptr, size_t size, size_t nmemb, void* userp) { | |
| 180 | ✗ | size_t buffer_size = size * nmemb; | |
| 181 | ✗ | CurlManager::CurlPrivateData* dt = (CurlManager::CurlPrivateData*)userp; | |
| 182 | ✗ | size_t numb = dt->req->size() - dt->curPos; | |
| 183 | ✗ | if (numb > buffer_size) { | |
| 184 | ✗ | numb = buffer_size; | |
| 185 | } | ||
| 186 | ✗ | if (numb > 0) { | |
| 187 | ✗ | memcpy(ptr, &(*dt->req)[dt->curPos], numb); | |
| 188 | } | ||
| 189 | ✗ | dt->curPos += numb; | |
| 190 | ✗ | return numb; | |
| 191 | } | ||
| 192 | 1342 | CURL* CurlManager::createCurl(const std::string& fullUrl, CurlPrivateData** cpd, bool upload, CURLSH* share) { | |
| 193 |
6/12✓ Branch 0 taken 136 times.
✓ Branch 1 taken 1206 times.
✓ Branch 2 taken 136 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 136 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 136 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 136 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
1614 | static std::string USERAGENT = std::string("FPP/") + getFPPVersionTriplet(); |
| 194 | |||
| 195 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | const std::string host = getHost(fullUrl); |
| 196 | // Under the lock: requests are created from API and command threads as | ||
| 197 | // well as the main loop, and getHostData() inserts into the map. Copy the | ||
| 198 | // credentials out (curl copies string options anyway). | ||
| 199 | 1342 | std::string username; | |
| 200 | 1342 | std::string password; | |
| 201 | { | ||
| 202 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | std::unique_lock<std::mutex> l(lock); |
| 203 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | HostData* hd = getHostData(host); |
| 204 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | username = hd->username; |
| 205 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | password = hd->password; |
| 206 | 1342 | } | |
| 207 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | CURL* c = curl_easy_init(); |
| 208 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | SetCurlCloseOnExec(c); |
| 209 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_URL, fullUrl.c_str()); |
| 210 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1330 times.
|
1342 | if (share) { |
| 211 | // Join the cookie session: arm the cookie engine (empty COOKIEFILE) and | ||
| 212 | // point at the shared jar so cookies persist across handles in the group. | ||
| 213 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | curl_easy_setopt(c, CURLOPT_COOKIEFILE, ""); |
| 214 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | curl_easy_setopt(c, CURLOPT_SHARE, share); |
| 215 | } | ||
| 216 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_USERAGENT, USERAGENT.c_str()); |
| 217 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT_MS, 2000L); |
| 218 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_TIMEOUT_MS, 10000L); |
| 219 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_ACCEPT_ENCODING, ""); |
| 220 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1); |
| 221 | //curl_easy_setopt(c, CURLOPT_VERBOSE, 2L); | ||
| 222 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_UPKEEP_INTERVAL_MS, 5000L); |
| 223 | |||
| 224 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, urlWriteData); |
| 225 |
2/4✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1342 times.
|
1342 | CurlPrivateData* data = new CurlPrivateData(); |
| 226 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | data->host = host; |
| 227 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_ERRORBUFFER, data->errorResp); |
| 228 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_WRITEDATA, &data->resp); |
| 229 |
1/2✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
|
1342 | curl_easy_setopt(c, CURLOPT_PRIVATE, data); |
| 230 |
2/4✓ Branch 0 taken 1342 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1342 times.
|
1342 | if (username != "") { |
| 231 | ✗ | curl_easy_setopt(c, CURLOPT_USERNAME, username.c_str()); | |
| 232 | ✗ | curl_easy_setopt(c, CURLOPT_PASSWORD, password.c_str()); | |
| 233 | ✗ | curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NEGOTIATE); | |
| 234 | } | ||
| 235 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1342 times.
|
1342 | if (upload) { |
| 236 | ✗ | data->req = new std::vector<uint8_t>(); | |
| 237 | ✗ | curl_easy_setopt(c, CURLOPT_READDATA, data); | |
| 238 | ✗ | curl_easy_setopt(c, CURLOPT_READFUNCTION, urlReadData); | |
| 239 | } | ||
| 240 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1342 times.
|
1342 | if (cpd) { |
| 241 | ✗ | *cpd = data; | |
| 242 | } | ||
| 243 | 1342 | return c; | |
| 244 | 1342 | } | |
| 245 | |||
| 246 | 387 | void CurlManager::add(const std::string& furl, const std::string& method, const std::string& data, | |
| 247 | const std::list<std::string>& extraHeaders, | ||
| 248 | std::function<void(int rc, const std::string& resp)>&& callback, | ||
| 249 | const std::string& owner, const std::string& cookieSession) { | ||
| 250 |
3/4✓ Branch 0 taken 375 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
387 | CURLSH* share = cookieSession.empty() ? nullptr : getCookieShare(cookieSession); |
| 251 |
1/2✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
|
387 | CURL* curl = createCurl(furl, nullptr, false, share); |
| 252 |
1/2✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
|
387 | limitResponseSize(curl); |
| 253 | |||
| 254 |
3/4✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 345 times.
|
387 | if (method == "POST") { |
| 255 |
1/2✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
|
42 | curl_easy_setopt(curl, CURLOPT_POST, 1); |
| 256 |
2/4✓ Branch 0 taken 345 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 345 times.
|
345 | } else if (method == "PUT") { |
| 257 | ✗ | curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); | |
| 258 |
2/4✓ Branch 0 taken 345 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 345 times.
|
345 | } else if (method == "PATCH") { |
| 259 | ✗ | curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); | |
| 260 |
2/4✓ Branch 0 taken 345 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 345 times.
|
345 | } else if (method == "DELETE") { |
| 261 | ✗ | curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); | |
| 262 | } | ||
| 263 | |||
| 264 |
7/10✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 345 times.
✓ Branch 3 taken 42 times.
✓ Branch 4 taken 345 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 345 times.
✓ Branch 8 taken 42 times.
✓ Branch 9 taken 345 times.
|
387 | if ((method == "POST") || (method == "PUT")) { |
| 265 |
1/2✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
|
42 | curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, data.c_str()); |
| 266 | } | ||
| 267 | |||
| 268 | 387 | struct curl_slist* headers = NULL; | |
| 269 |
2/2✓ Branch 0 taken 78 times.
✓ Branch 1 taken 387 times.
|
465 | for (auto& h : extraHeaders) { |
| 270 |
1/2✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
|
78 | headers = curl_slist_append(headers, h.c_str()); |
| 271 | } | ||
| 272 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 347 times.
|
387 | if (headers) { |
| 273 |
1/2✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
|
40 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); |
| 274 | } | ||
| 275 | |||
| 276 | // The header list is owned by the callback rather than freed inside it, so | ||
| 277 | // it is released whether the request completes or is cancelled (which | ||
| 278 | // destroys the callback without running it). Either way that happens after | ||
| 279 | // curl_easy_cleanup(), which is also the right order - the old code freed | ||
| 280 | // the list while the easy handle still referenced it. | ||
| 281 | 374 | std::shared_ptr<curl_slist> headerList(headers, [](curl_slist* h) { | |
| 282 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 334 times.
|
374 | if (h) { |
| 283 | 40 | curl_slist_free_all(h); | |
| 284 | } | ||
| 285 |
1/2✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
|
387 | }); |
| 286 | |||
| 287 |
3/8✓ Branch 0 taken 387 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 387 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 387 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
387 | addCURL(furl, curl, [callback, headerList](CURL* c) { |
| 288 | 368 | CurlPrivateData* data = nullptr; | |
| 289 |
1/2✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
|
368 | curl_easy_getinfo(c, CURLINFO_PRIVATE, &data); |
| 290 |
1/2✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
|
368 | std::string failure = failedTransfer(t_completedResult, data); |
| 291 |
2/2✓ Branch 0 taken 47 times.
✓ Branch 1 taken 321 times.
|
368 | if (!failure.empty()) { |
| 292 |
1/2✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
|
47 | callback(0, failure); |
| 293 | 47 | return; | |
| 294 | } | ||
| 295 | 321 | long rc = 0; | |
| 296 |
1/2✓ Branch 0 taken 321 times.
✗ Branch 1 not taken.
|
321 | curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &rc); |
| 297 | |||
| 298 | 321 | char* urlp = nullptr; | |
| 299 |
1/2✓ Branch 0 taken 321 times.
✗ Branch 1 not taken.
|
321 | curl_easy_getinfo(c, CURLINFO_EFFECTIVE_URL, &urlp); |
| 300 |
1/2✓ Branch 0 taken 321 times.
✗ Branch 1 not taken.
|
321 | std::string resp(reinterpret_cast<char*>(data->resp.data()), data->resp.size()); |
| 301 |
1/2✓ Branch 0 taken 321 times.
✗ Branch 1 not taken.
|
321 | callback(rc, resp); |
| 302 |
2/2✓ Branch 0 taken 321 times.
✓ Branch 1 taken 47 times.
|
368 | }, true, owner); |
| 303 | 387 | } | |
| 304 | 320 | void CurlManager::addGet(const std::string& url, std::function<void(int rc, const std::string& resp)>&& callback, | |
| 305 | const std::string& owner, const std::string& cookieSession) { | ||
| 306 |
5/10✓ Branch 0 taken 320 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 320 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 320 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 320 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 320 times.
✗ Branch 9 not taken.
|
1908 | add(url, "GET", "", {}, [callback](int rc, const std::string& resp) { callback(rc, resp); }, owner, cookieSession); |
| 307 | 320 | } | |
| 308 | |||
| 309 | 1 | void CurlManager::addPost(const std::string& url, const std::string& data, const std::string& contentType, | |
| 310 | std::function<void(int rc, const std::string& resp)>&& callback, const std::string& owner) { | ||
| 311 |
8/16✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
|
7 | add(url, "POST", data, { "Content-Type: " + contentType }, [callback](int rc, const std::string& resp) { callback(rc, resp); }, owner); |
| 312 |
1/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
2 | } |
| 313 | ✗ | void CurlManager::addPut(const std::string& url, const std::string& data, const std::string& contentType, | |
| 314 | std::function<void(int rc, const std::string& resp)>&& callback, const std::string& owner) { | ||
| 315 | ✗ | add(url, "PUT", data, { "Content-Type: " + contentType }, [callback](int rc, const std::string& resp) { callback(rc, resp); }, owner); | |
| 316 | ✗ | } | |
| 317 | |||
| 318 | 863 | int CurlManager::cancelRequests(const std::string& owner) { | |
| 319 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 863 times.
|
863 | if (owner.empty()) { |
| 320 | ✗ | return 0; // "" is FPP's own traffic; cancelling all of it is never wanted | |
| 321 | } | ||
| 322 | // The callbacks are moved out and destroyed after the lock is dropped: they | ||
| 323 | // belong to a plugin, and a captured object's destructor that touched | ||
| 324 | // CurlManager again would deadlock on this non-recursive mutex. | ||
| 325 | 863 | std::vector<std::function<void(CURL*)>> doomed; | |
| 326 | { | ||
| 327 |
1/2✓ Branch 0 taken 863 times.
✗ Branch 1 not taken.
|
863 | std::unique_lock<std::mutex> l(lock); |
| 328 |
2/2✓ Branch 0 taken 204 times.
✓ Branch 1 taken 863 times.
|
1067 | for (size_t x = 0; x < curls.size(); x++) { |
| 329 | 204 | CurlInfo* ci = curls[x]; | |
| 330 |
6/6✓ Branch 0 taken 34 times.
✓ Branch 1 taken 170 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 198 times.
✓ Branch 5 taken 6 times.
|
204 | if (!ci || ci->owner != owner) { |
| 331 | 198 | continue; | |
| 332 | } | ||
| 333 | 6 | curls[x] = nullptr; | |
| 334 | 6 | --numCurls; | |
| 335 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | curl_multi_remove_handle(curlMulti, ci->curl); |
| 336 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (ci->cleanCurl) { |
| 337 | 6 | CurlPrivateData* data = nullptr; | |
| 338 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | curl_easy_getinfo(ci->curl, CURLINFO_PRIVATE, &data); |
| 339 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (data) { |
| 340 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | delete data; |
| 341 | } | ||
| 342 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | curl_easy_cleanup(ci->curl); |
| 343 | } | ||
| 344 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | doomed.push_back(std::move(ci->callback)); |
| 345 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | delete ci; |
| 346 | } | ||
| 347 | 863 | } | |
| 348 |
1/2✓ Branch 0 taken 863 times.
✗ Branch 1 not taken.
|
863 | LogDebug(VB_GENERAL, "CurlManager cancelled %d request(s) for %s\n", (int)doomed.size(), owner.c_str()); |
| 349 | 863 | return (int)doomed.size(); | |
| 350 | 863 | } | |
| 351 | |||
| 352 | // Runs one synchronous request to completion. The main loop pumps curl | ||
| 353 | // too, and when it completes this request it drops numCurls before calling | ||
| 354 | // the callback: processCurls() here then returned false with `done` still | ||
| 355 | // unset, this frame returned, and the callback wrote `done` into it. The | ||
| 356 | // flag is shared instead, and the wait is for this request only (doPut | ||
| 357 | // waited for every request in flight). | ||
| 358 | // Returns the transfer's result. | ||
| 359 | 601 | static CURLcode runToCompletion(CurlManager& mgr, const std::string& furl, CURL* curl) { | |
| 360 | struct State { | ||
| 361 | std::atomic<bool> done{ false }; | ||
| 362 | CURLcode result = CURLE_OK; // written before done, read after | ||
| 363 | }; | ||
| 364 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | auto state = std::make_shared<State>(); |
| 365 |
3/6✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 601 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 601 times.
✗ Branch 5 not taken.
|
1202 | mgr.addCURL( |
| 366 | 1803 | furl, curl, [state](CURL* c) { state->result = t_completedResult; state->done = true; }, false); | |
| 367 |
2/2✓ Branch 0 taken 35915 times.
✓ Branch 1 taken 601 times.
|
36516 | while (!state->done) { |
| 368 |
3/4✓ Branch 0 taken 35915 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 557 times.
✓ Branch 3 taken 35358 times.
|
35915 | if (!mgr.processCurls()) { |
| 369 | // another thread holds it between completion and the callback | ||
| 370 |
1/2✓ Branch 0 taken 557 times.
✗ Branch 1 not taken.
|
557 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| 371 | } else { | ||
| 372 | 35358 | std::this_thread::yield(); | |
| 373 | } | ||
| 374 | } | ||
| 375 | 1202 | return state->result; | |
| 376 | 601 | } | |
| 377 | |||
| 378 | 601 | std::string CurlManager::doGet(const std::string& furl, int& rc, const std::string& cookieSession) { | |
| 379 |
1/4✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
601 | CURLSH* share = cookieSession.empty() ? nullptr : getCookieShare(cookieSession); |
| 380 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | CURL* curl = createCurl(furl, nullptr, false, share); |
| 381 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | limitResponseSize(curl); |
| 382 | |||
| 383 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | CURLcode result = runToCompletion(*this, furl, curl); |
| 384 | |||
| 385 | 601 | CurlPrivateData* data = nullptr; | |
| 386 | 601 | long rc2 = 0; | |
| 387 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | curl_easy_getinfo(curl, CURLINFO_PRIVATE, &data); |
| 388 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc2); |
| 389 | // On a failed transfer, rc 0 and the reason, as for one that never connected. | ||
| 390 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | std::string resp = failedTransfer(result, data); |
| 391 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | rc = resp.empty() ? rc2 : 0; |
| 392 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | if (resp.empty()) { |
| 393 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | resp.assign(reinterpret_cast<char*>(data->resp.data()), data->resp.size()); |
| 394 | } | ||
| 395 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | delete data; |
| 396 |
1/2✓ Branch 0 taken 601 times.
✗ Branch 1 not taken.
|
601 | curl_easy_cleanup(curl); |
| 397 | |||
| 398 | 601 | return resp; | |
| 399 | ✗ | } | |
| 400 | |||
| 401 | struct ReadDataInfo { | ||
| 402 | const std::vector<uint8_t>* data; | ||
| 403 | size_t curPos = 0; | ||
| 404 | }; | ||
| 405 | ✗ | static size_t read_callback(void* ptr, size_t size, size_t nmemb, void* userp) { | |
| 406 | ✗ | size_t buffer_size = size * nmemb; | |
| 407 | ✗ | struct ReadDataInfo* dt = (struct ReadDataInfo*)userp; | |
| 408 | ✗ | int numb = dt->data->size() - dt->curPos; | |
| 409 | ✗ | if (numb > buffer_size) { | |
| 410 | ✗ | numb = buffer_size; | |
| 411 | } | ||
| 412 | ✗ | memcpy(ptr, &(*dt->data)[dt->curPos], numb); | |
| 413 | ✗ | dt->curPos += numb; | |
| 414 | ✗ | return numb; | |
| 415 | } | ||
| 416 | ✗ | std::string CurlManager::doPost(const std::string& furl, const std::string& contentType, const std::vector<uint8_t>& data, int& rc) { | |
| 417 | ✗ | CURL* curl = createCurl(furl); | |
| 418 | ✗ | limitResponseSize(curl); | |
| 419 | |||
| 420 | ✗ | struct curl_slist* head = nullptr; | |
| 421 | ✗ | std::string ct = "Content-Type: " + contentType; | |
| 422 | ✗ | head = curl_slist_append(head, ct.c_str()); | |
| 423 | ✗ | std::string cl = "Content-Length: " + std::to_string(data.size()); | |
| 424 | ✗ | head = curl_slist_append(head, cl.c_str()); | |
| 425 | |||
| 426 | ✗ | curl_easy_setopt(curl, CURLOPT_POST, 1L); | |
| 427 | ✗ | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, head); | |
| 428 | |||
| 429 | ✗ | ReadDataInfo dta; | |
| 430 | ✗ | dta.data = &data; | |
| 431 | ✗ | dta.curPos = 0; | |
| 432 | |||
| 433 | ✗ | curl_easy_setopt(curl, CURLOPT_READDATA, &dta); | |
| 434 | ✗ | curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); | |
| 435 | ✗ | curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)data.size()); | |
| 436 | |||
| 437 | ✗ | CURLcode result = runToCompletion(*this, furl, curl); | |
| 438 | |||
| 439 | ✗ | CurlPrivateData* cdata = nullptr; | |
| 440 | ✗ | long rc2 = 0; | |
| 441 | ✗ | curl_easy_getinfo(curl, CURLINFO_PRIVATE, &cdata); | |
| 442 | ✗ | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc2); | |
| 443 | // On a failed transfer, rc 0 and the reason, as for one that never connected. | ||
| 444 | ✗ | std::string resp = failedTransfer(result, cdata); | |
| 445 | ✗ | rc = resp.empty() ? rc2 : 0; | |
| 446 | ✗ | if (resp.empty()) { | |
| 447 | ✗ | resp.assign(reinterpret_cast<char*>(cdata->resp.data()), cdata->resp.size()); | |
| 448 | } | ||
| 449 | ✗ | delete cdata; | |
| 450 | ✗ | curl_slist_free_all(head); | |
| 451 | ✗ | curl_easy_cleanup(curl); | |
| 452 | ✗ | return resp; | |
| 453 | ✗ | } | |
| 454 | ✗ | std::string CurlManager::doPut(const std::string& furl, const std::string& contentType, const std::vector<uint8_t>& data, int& rc) { | |
| 455 | ✗ | CURL* curl = createCurl(furl); | |
| 456 | ✗ | limitResponseSize(curl); | |
| 457 | |||
| 458 | ✗ | struct curl_slist* head = nullptr; | |
| 459 | ✗ | std::string ct = "Content-Type: " + contentType; | |
| 460 | ✗ | head = curl_slist_append(head, ct.c_str()); | |
| 461 | ✗ | std::string cl = "Content-Length: " + std::to_string(data.size()); | |
| 462 | ✗ | head = curl_slist_append(head, cl.c_str()); | |
| 463 | |||
| 464 | ✗ | curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); | |
| 465 | //curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); | ||
| 466 | ✗ | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, head); | |
| 467 | |||
| 468 | ✗ | ReadDataInfo dta; | |
| 469 | ✗ | dta.data = &data; | |
| 470 | ✗ | dta.curPos = 0; | |
| 471 | |||
| 472 | ✗ | curl_easy_setopt(curl, CURLOPT_READDATA, &dta); | |
| 473 | ✗ | curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); | |
| 474 | ✗ | curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)data.size()); | |
| 475 | |||
| 476 | ✗ | CURLcode result = runToCompletion(*this, furl, curl); | |
| 477 | |||
| 478 | ✗ | CurlPrivateData* cdata = nullptr; | |
| 479 | ✗ | long rc2 = 0; | |
| 480 | ✗ | curl_easy_getinfo(curl, CURLINFO_PRIVATE, &cdata); | |
| 481 | ✗ | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc2); | |
| 482 | // On a failed transfer, rc 0 and the reason, as for one that never connected. | ||
| 483 | ✗ | std::string resp = failedTransfer(result, cdata); | |
| 484 | ✗ | rc = resp.empty() ? rc2 : 0; | |
| 485 | ✗ | if (resp.empty()) { | |
| 486 | ✗ | resp.assign(reinterpret_cast<char*>(cdata->resp.data()), cdata->resp.size()); | |
| 487 | } | ||
| 488 | ✗ | delete cdata; | |
| 489 | ✗ | curl_slist_free_all(head); | |
| 490 | ✗ | curl_easy_cleanup(curl); | |
| 491 | ✗ | return resp; | |
| 492 | ✗ | } | |
| 493 | |||
| 494 | 45307 | bool CurlManager::doProcessCurls() { | |
| 495 | CURLMcode mc; | ||
| 496 | 45307 | int stillRunning = 0; | |
| 497 |
1/2✓ Branch 0 taken 45307 times.
✗ Branch 1 not taken.
|
45307 | std::unique_lock<std::mutex> l(lock); |
| 498 |
1/2✓ Branch 0 taken 45307 times.
✗ Branch 1 not taken.
|
45307 | mc = curl_multi_perform(curlMulti, &stillRunning); |
| 499 |
2/2✓ Branch 0 taken 853 times.
✓ Branch 1 taken 44454 times.
|
45307 | if (stillRunning != numCurls) { |
| 500 | 853 | CURLMsg* m = nullptr; | |
| 501 | do { | ||
| 502 | 2113 | int msgq = 0; | |
| 503 |
1/2✓ Branch 0 taken 2113 times.
✗ Branch 1 not taken.
|
2113 | m = curl_multi_info_read(curlMulti, &msgq); |
| 504 |
3/4✓ Branch 0 taken 1260 times.
✓ Branch 1 taken 853 times.
✓ Branch 2 taken 1260 times.
✗ Branch 3 not taken.
|
2113 | if (m && (m->msg == CURLMSG_DONE)) { |
| 505 | 1260 | CURL* e = m->easy_handle; | |
| 506 | // m is invalid once the handle is removed | ||
| 507 | 1260 | CURLcode result = m->data.result; | |
| 508 |
1/2✓ Branch 0 taken 1260 times.
✗ Branch 1 not taken.
|
1260 | curl_multi_remove_handle(curlMulti, e); |
| 509 | 1260 | CurlInfo* ci = nullptr; | |
| 510 |
1/2✓ Branch 0 taken 6456 times.
✗ Branch 1 not taken.
|
6456 | for (int x = 0; x < curls.size(); x++) { |
| 511 |
6/6✓ Branch 0 taken 5348 times.
✓ Branch 1 taken 1108 times.
✓ Branch 2 taken 1260 times.
✓ Branch 3 taken 4088 times.
✓ Branch 4 taken 1260 times.
✓ Branch 5 taken 5196 times.
|
6456 | if (curls[x] && curls[x]->curl == e) { |
| 512 | 1260 | ci = curls[x]; | |
| 513 | 1260 | curls[x] = nullptr; | |
| 514 | 1260 | --numCurls; | |
| 515 | 1260 | break; | |
| 516 | } | ||
| 517 | } | ||
| 518 |
1/2✓ Branch 0 taken 1260 times.
✗ Branch 1 not taken.
|
1260 | if (ci) { |
| 519 |
1/2✓ Branch 0 taken 1260 times.
✗ Branch 1 not taken.
|
1260 | l.unlock(); |
| 520 | 1260 | t_completedResult = result; | |
| 521 | // The callbacks parse what a remote or a probed device sent | ||
| 522 | // back, on the main loop with nothing above to catch: one | ||
| 523 | // jsoncpp throw on a field of the wrong type ({"Platform":{}} | ||
| 524 | // from a MultiSync remote, {"majorVersion":"9"} from a | ||
| 525 | // discovery probe) ended fppd. Contain it here, for every | ||
| 526 | // callback, and carry on with the cleanup. | ||
| 527 | try { | ||
| 528 |
1/2✓ Branch 0 taken 1260 times.
✗ Branch 1 not taken.
|
1260 | ci->callback(e); |
| 529 | ✗ | } catch (const std::exception& ex) { | |
| 530 | ✗ | char* url = nullptr; | |
| 531 | ✗ | curl_easy_getinfo(e, CURLINFO_EFFECTIVE_URL, &url); | |
| 532 | ✗ | LogWarn(VB_GENERAL, "Ignoring the reply from %s: handling it failed: %s\n", | |
| 533 | url ? url : "(unknown)", ex.what()); | ||
| 534 | ✗ | } catch (...) { | |
| 535 | ✗ | char* url = nullptr; | |
| 536 | ✗ | curl_easy_getinfo(e, CURLINFO_EFFECTIVE_URL, &url); | |
| 537 | ✗ | LogWarn(VB_GENERAL, "Ignoring the reply from %s: handling it failed\n", url ? url : "(unknown)"); | |
| 538 | ✗ | } | |
| 539 | 1260 | t_completedResult = CURLE_OK; | |
| 540 |
1/2✓ Branch 0 taken 1260 times.
✗ Branch 1 not taken.
|
1260 | l.lock(); |
| 541 |
2/2✓ Branch 0 taken 659 times.
✓ Branch 1 taken 601 times.
|
1260 | if (ci->cleanCurl) { |
| 542 | 659 | CurlPrivateData* data = nullptr; | |
| 543 |
1/2✓ Branch 0 taken 659 times.
✗ Branch 1 not taken.
|
659 | curl_easy_getinfo(e, CURLINFO_PRIVATE, &data); |
| 544 |
1/2✓ Branch 0 taken 659 times.
✗ Branch 1 not taken.
|
659 | if (data) { |
| 545 |
1/2✓ Branch 0 taken 659 times.
✗ Branch 1 not taken.
|
659 | delete data; |
| 546 | } | ||
| 547 |
1/2✓ Branch 0 taken 659 times.
✗ Branch 1 not taken.
|
659 | curl_easy_cleanup(e); |
| 548 | } | ||
| 549 |
1/2✓ Branch 0 taken 1260 times.
✗ Branch 1 not taken.
|
1260 | delete ci; |
| 550 | } | ||
| 551 | } | ||
| 552 |
2/2✓ Branch 0 taken 1260 times.
✓ Branch 1 taken 853 times.
|
2113 | } while (m); |
| 553 | } | ||
| 554 | 90614 | return numCurls > 0; | |
| 555 | 45307 | } | |
| 556 | |||
| 557 | 1342 | CurlManager::HostData* CurlManager::getHostData(const std::string& host) { | |
| 558 | 1342 | HostData* h = hostData[host]; | |
| 559 |
2/2✓ Branch 0 taken 754 times.
✓ Branch 1 taken 588 times.
|
1342 | if (h == nullptr) { |
| 560 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 754 times.
|
754 | h = new HostData(); |
| 561 | 754 | hostData[host] = h; | |
| 562 | } | ||
| 563 | 1342 | return h; | |
| 564 | } | ||
| 565 | |||
| 566 | ✗ | void CurlManager::setHostUsernamePassword(const std::string& host, const std::string& username, const std::string password) { | |
| 567 | ✗ | std::unique_lock<std::mutex> l(lock); | |
| 568 | ✗ | HostData* h = getHostData(host); | |
| 569 | ✗ | h->username = username; | |
| 570 | ✗ | h->password = password; | |
| 571 | ✗ | } | |
| 572 | |||
| 573 | 2684 | std::string CurlManager::getHost(const std::string& url) { | |
| 574 | 2684 | int idx = url.find("://"); | |
| 575 | 2684 | std::string host = url.substr(idx + 3); | |
| 576 | 2684 | idx = host.find("/"); | |
| 577 |
1/2✓ Branch 0 taken 2684 times.
✗ Branch 1 not taken.
|
2684 | host = host.substr(0, idx); |
| 578 | 2684 | return host; | |
| 579 | ✗ | } | |
| 580 |