| 1 | #include "cpr/session.h" |
| 2 | |
| 3 | #include <algorithm> |
| 4 | #include <cstdlib> |
| 5 | #include <cstring> |
| 6 | #include <fstream> |
| 7 | #include <functional> |
| 8 | #include <iostream> |
| 9 | #include <stdexcept> |
| 10 | #include <string> |
| 11 | |
| 12 | #include <curl/curl.h> |
| 13 | |
| 14 | #include "cpr/async.h" |
| 15 | #include "cpr/cprtypes.h" |
| 16 | #include "cpr/interceptor.h" |
| 17 | #include "cpr/util.h" |
| 18 | |
| 19 | #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION |
| 20 | #include "cpr/ssl_ctx.h" |
| 21 | #endif |
| 22 | |
| 23 | |
| 24 | namespace cpr { |
| 25 | // Ignored here since libcurl reqires a long: |
| 26 | // NOLINTNEXTLINE(google-runtime-int) |
| 27 | constexpr long ON = 1L; |
| 28 | // Ignored here since libcurl reqires a long: |
| 29 | // NOLINTNEXTLINE(google-runtime-int) |
| 30 | constexpr long OFF = 0L; |
| 31 | |
| 32 | CURLcode Session::DoEasyPerform() { |
| 33 | if (isUsedInMultiPerform) { |
| 34 | std::cerr << "curl_easy_perform cannot be executed if the CURL handle is used in a MultiPerform." << std::endl; |
| 35 | return CURLcode::CURLE_FAILED_INIT; |
| 36 | } |
| 37 | return curl_easy_perform(curl: curl_->handle); |
| 38 | } |
| 39 | |
| 40 | void Session::() { |
| 41 | curl_slist* chunk = nullptr; |
| 42 | for (const std::pair<const std::string, std::string>& item : header_) { |
| 43 | std::string = item.first; |
| 44 | if (item.second.empty()) { |
| 45 | header_string += ";" ; |
| 46 | } else { |
| 47 | header_string += ": " + item.second; |
| 48 | } |
| 49 | |
| 50 | curl_slist* temp = curl_slist_append(list: chunk, data: header_string.c_str()); |
| 51 | if (temp) { |
| 52 | chunk = temp; |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | // Set the chunked transfer encoding in case it does not already exist: |
| 57 | if (chunkedTransferEncoding_ && header_.find(x: "Transfer-Encoding" ) == header_.end()) { |
| 58 | curl_slist* temp = curl_slist_append(list: chunk, data: "Transfer-Encoding:chunked" ); |
| 59 | if (temp) { |
| 60 | chunk = temp; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | // libcurl would prepare the header "Expect: 100-continue" by default when uploading files larger than 1 MB. |
| 65 | // Here we would like to disable this feature: |
| 66 | curl_slist* temp = curl_slist_append(list: chunk, data: "Expect:" ); |
| 67 | if (temp) { |
| 68 | chunk = temp; |
| 69 | } |
| 70 | |
| 71 | curl_easy_setopt(curl_->handle, CURLOPT_HTTPHEADER, chunk); |
| 72 | |
| 73 | curl_slist_free_all(list: curl_->chunk); |
| 74 | curl_->chunk = chunk; |
| 75 | } |
| 76 | |
| 77 | // Only supported with libcurl >= 7.61.0. |
| 78 | // As an alternative use SetHeader and add the token manually. |
| 79 | #if LIBCURL_VERSION_NUM >= 0x073D00 |
| 80 | void Session::SetBearer(const Bearer& token) { |
| 81 | // Ignore here since this has been defined by libcurl. |
| 82 | curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); |
| 83 | curl_easy_setopt(curl_->handle, CURLOPT_XOAUTH2_BEARER, token.GetToken()); |
| 84 | } |
| 85 | #endif |
| 86 | |
| 87 | Session::Session() : curl_(new CurlHolder()) { |
| 88 | // Set up some sensible defaults |
| 89 | curl_version_info_data* version_info = curl_version_info(CURLVERSION_NOW); |
| 90 | const std::string version = "curl/" + std::string{version_info->version}; |
| 91 | curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, version.c_str()); |
| 92 | SetRedirect(Redirect()); |
| 93 | curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 1L); |
| 94 | curl_easy_setopt(curl_->handle, CURLOPT_ERRORBUFFER, curl_->error.data()); |
| 95 | curl_easy_setopt(curl_->handle, CURLOPT_COOKIEFILE, "" ); |
| 96 | #ifdef CPR_CURL_NOSIGNAL |
| 97 | curl_easy_setopt(curl_->handle, CURLOPT_NOSIGNAL, 1L); |
| 98 | #endif |
| 99 | |
| 100 | #if LIBCURL_VERSION_NUM >= 0x071900 |
| 101 | curl_easy_setopt(curl_->handle, CURLOPT_TCP_KEEPALIVE, 1L); |
| 102 | #endif |
| 103 | } |
| 104 | |
| 105 | Response Session::makeDownloadRequest() { |
| 106 | if (!interceptors_.empty()) { |
| 107 | return intercept(); |
| 108 | } |
| 109 | |
| 110 | const CURLcode curl_error = DoEasyPerform(); |
| 111 | |
| 112 | return CompleteDownload(curl_error); |
| 113 | } |
| 114 | |
| 115 | void Session::prepareCommon() { |
| 116 | assert(curl_->handle); |
| 117 | |
| 118 | // Set Header: |
| 119 | SetHeaderInternal(); |
| 120 | |
| 121 | const std::string parametersContent = parameters_.GetContent(*curl_); |
| 122 | if (!parametersContent.empty()) { |
| 123 | const Url new_url{url_ + "?" + parametersContent}; |
| 124 | curl_easy_setopt(curl_->handle, CURLOPT_URL, new_url.c_str()); |
| 125 | } else { |
| 126 | curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); |
| 127 | } |
| 128 | |
| 129 | // Proxy: |
| 130 | const std::string protocol = url_.str().substr(pos: 0, n: url_.str().find(c: ':')); |
| 131 | if (proxies_.has(protocol)) { |
| 132 | curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str()); |
| 133 | if (proxyAuth_.has(protocol)) { |
| 134 | curl_easy_setopt(curl_->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); |
| 135 | curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERPWD, proxyAuth_[protocol]); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | #if LIBCURL_VERSION_NUM >= 0x072100 |
| 140 | if (acceptEncoding_.empty()) { |
| 141 | // Enable all supported built-in compressions |
| 142 | curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, "" ); |
| 143 | } else if (acceptEncoding_.disabled()) { |
| 144 | // Disable curl adding the 'Accept-Encoding' header |
| 145 | curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, nullptr); |
| 146 | } else { |
| 147 | curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, acceptEncoding_.getString().c_str()); |
| 148 | } |
| 149 | #endif |
| 150 | |
| 151 | #if LIBCURL_VERSION_NUM >= 0x077100 |
| 152 | #if SUPPORT_SSL_NO_REVOKE |
| 153 | // NOLINTNEXTLINE (google-runtime-int) |
| 154 | long bitmask{0}; |
| 155 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, &bitmask); |
| 156 | const bool noRevoke = bitmask & CURLSSLOPT_NO_REVOKE; |
| 157 | #endif |
| 158 | |
| 159 | // Fix loading certs from Windows cert store when using OpenSSL: |
| 160 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); |
| 161 | |
| 162 | // Ensure SSL no revoke is still set |
| 163 | #if SUPPORT_SSL_NO_REVOKE |
| 164 | if (noRevoke) { |
| 165 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); |
| 166 | } |
| 167 | #endif |
| 168 | #endif |
| 169 | |
| 170 | curl_->error[0] = '\0'; |
| 171 | |
| 172 | response_string_.clear(); |
| 173 | if (response_string_reserve_size_ > 0) { |
| 174 | response_string_.reserve(res_arg: response_string_reserve_size_); |
| 175 | } |
| 176 | header_string_.clear(); |
| 177 | if (!this->writecb_.callback) { |
| 178 | curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFunction); |
| 179 | curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &response_string_); |
| 180 | } |
| 181 | if (!this->headercb_.callback) { |
| 182 | curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction); |
| 183 | curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_); |
| 184 | } |
| 185 | |
| 186 | // Enable so we are able to retrive certificate information: |
| 187 | curl_easy_setopt(curl_->handle, CURLOPT_CERTINFO, 1L); |
| 188 | } |
| 189 | |
| 190 | void Session::prepareCommonDownload() { |
| 191 | assert(curl_->handle); |
| 192 | |
| 193 | // Set Header: |
| 194 | SetHeaderInternal(); |
| 195 | |
| 196 | const std::string parametersContent = parameters_.GetContent(*curl_); |
| 197 | if (!parametersContent.empty()) { |
| 198 | const Url new_url{url_ + "?" + parametersContent}; |
| 199 | curl_easy_setopt(curl_->handle, CURLOPT_URL, new_url.c_str()); |
| 200 | } else { |
| 201 | curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); |
| 202 | } |
| 203 | |
| 204 | const std::string protocol = url_.str().substr(pos: 0, n: url_.str().find(c: ':')); |
| 205 | if (proxies_.has(protocol)) { |
| 206 | curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str()); |
| 207 | if (proxyAuth_.has(protocol)) { |
| 208 | curl_easy_setopt(curl_->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); |
| 209 | curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERPWD, proxyAuth_[protocol]); |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | curl_->error[0] = '\0'; |
| 214 | |
| 215 | header_string_.clear(); |
| 216 | if (headercb_.callback) { |
| 217 | curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction); |
| 218 | curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &headercb_); |
| 219 | } else { |
| 220 | curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction); |
| 221 | curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | Response Session::makeRequest() { |
| 226 | if (!interceptors_.empty()) { |
| 227 | return intercept(); |
| 228 | } |
| 229 | |
| 230 | const CURLcode curl_error = DoEasyPerform(); |
| 231 | return Complete(curl_error); |
| 232 | } |
| 233 | |
| 234 | void Session::SetLimitRate(const LimitRate& limit_rate) { |
| 235 | curl_easy_setopt(curl_->handle, CURLOPT_MAX_RECV_SPEED_LARGE, limit_rate.downrate); |
| 236 | curl_easy_setopt(curl_->handle, CURLOPT_MAX_SEND_SPEED_LARGE, limit_rate.uprate); |
| 237 | } |
| 238 | |
| 239 | void Session::SetReadCallback(const ReadCallback& read) { |
| 240 | readcb_ = read; |
| 241 | curl_easy_setopt(curl_->handle, CURLOPT_INFILESIZE_LARGE, read.size); |
| 242 | curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, read.size); |
| 243 | curl_easy_setopt(curl_->handle, CURLOPT_READFUNCTION, cpr::util::readUserFunction); |
| 244 | curl_easy_setopt(curl_->handle, CURLOPT_READDATA, &readcb_); |
| 245 | chunkedTransferEncoding_ = read.size == -1; |
| 246 | } |
| 247 | |
| 248 | void Session::(const HeaderCallback& ) { |
| 249 | curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction); |
| 250 | headercb_ = header; |
| 251 | curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &headercb_); |
| 252 | } |
| 253 | |
| 254 | void Session::SetWriteCallback(const WriteCallback& write) { |
| 255 | curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeUserFunction); |
| 256 | writecb_ = write; |
| 257 | curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &writecb_); |
| 258 | } |
| 259 | |
| 260 | void Session::SetProgressCallback(const ProgressCallback& progress) { |
| 261 | progresscb_ = progress; |
| 262 | if (isCancellable) { |
| 263 | cancellationcb_.SetProgressCallback(progresscb_); |
| 264 | return; |
| 265 | } |
| 266 | #if LIBCURL_VERSION_NUM < 0x072000 |
| 267 | curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction<ProgressCallback>); |
| 268 | curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &progresscb_); |
| 269 | #else |
| 270 | curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction<ProgressCallback>); |
| 271 | curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &progresscb_); |
| 272 | #endif |
| 273 | curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L); |
| 274 | } |
| 275 | |
| 276 | void Session::SetDebugCallback(const DebugCallback& debug) { |
| 277 | curl_easy_setopt(curl_->handle, CURLOPT_DEBUGFUNCTION, cpr::util::debugUserFunction); |
| 278 | debugcb_ = debug; |
| 279 | curl_easy_setopt(curl_->handle, CURLOPT_DEBUGDATA, &debugcb_); |
| 280 | curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, 1L); |
| 281 | } |
| 282 | |
| 283 | void Session::SetUrl(const Url& url) { |
| 284 | url_ = url; |
| 285 | } |
| 286 | |
| 287 | void Session::SetResolve(const Resolve& resolve) { |
| 288 | SetResolves({resolve}); |
| 289 | } |
| 290 | |
| 291 | void Session::SetResolves(const std::vector<Resolve>& resolves) { |
| 292 | curl_slist_free_all(list: curl_->resolveCurlList); |
| 293 | curl_->resolveCurlList = nullptr; |
| 294 | for (const Resolve& resolve : resolves) { |
| 295 | for (const uint16_t port : resolve.ports) { |
| 296 | curl_->resolveCurlList = curl_slist_append(list: curl_->resolveCurlList, data: (resolve.host + ":" + std::to_string(val: port) + ":" + resolve.addr).c_str()); |
| 297 | } |
| 298 | } |
| 299 | curl_easy_setopt(curl_->handle, CURLOPT_RESOLVE, curl_->resolveCurlList); |
| 300 | } |
| 301 | |
| 302 | void Session::SetParameters(const Parameters& parameters) { |
| 303 | parameters_ = parameters; |
| 304 | } |
| 305 | |
| 306 | void Session::SetParameters(Parameters&& parameters) { |
| 307 | parameters_ = std::move(parameters); |
| 308 | } |
| 309 | |
| 310 | void Session::(const Header& ) { |
| 311 | header_ = header; |
| 312 | } |
| 313 | |
| 314 | void Session::(const Header& ) { |
| 315 | for (const std::pair<const std::string, std::string>& item : header) { |
| 316 | header_[item.first] = item.second; |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | void Session::SetTimeout(const Timeout& timeout) { |
| 321 | curl_easy_setopt(curl_->handle, CURLOPT_TIMEOUT_MS, timeout.Milliseconds()); |
| 322 | } |
| 323 | |
| 324 | void Session::SetConnectTimeout(const ConnectTimeout& timeout) { |
| 325 | curl_easy_setopt(curl_->handle, CURLOPT_CONNECTTIMEOUT_MS, timeout.Milliseconds()); |
| 326 | } |
| 327 | |
| 328 | void Session::SetAuth(const Authentication& auth) { |
| 329 | // Ignore here since this has been defined by libcurl. |
| 330 | switch (auth.GetAuthMode()) { |
| 331 | case AuthMode::BASIC: |
| 332 | curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); |
| 333 | curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); |
| 334 | break; |
| 335 | case AuthMode::DIGEST: |
| 336 | curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); |
| 337 | curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); |
| 338 | break; |
| 339 | case AuthMode::NTLM: |
| 340 | curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM); |
| 341 | curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); |
| 342 | break; |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | void Session::SetUserAgent(const UserAgent& ua) { |
| 347 | curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, ua.c_str()); |
| 348 | } |
| 349 | |
| 350 | void Session::SetPayload(const Payload& payload) { |
| 351 | hasBodyOrPayload_ = true; |
| 352 | const std::string content = payload.GetContent(*curl_); |
| 353 | curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(content.length())); |
| 354 | curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, content.c_str()); |
| 355 | } |
| 356 | |
| 357 | void Session::SetPayload(Payload&& payload) { |
| 358 | hasBodyOrPayload_ = true; |
| 359 | const std::string content = payload.GetContent(*curl_); |
| 360 | curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(content.length())); |
| 361 | curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, content.c_str()); |
| 362 | } |
| 363 | |
| 364 | void Session::SetProxies(const Proxies& proxies) { |
| 365 | proxies_ = proxies; |
| 366 | } |
| 367 | |
| 368 | void Session::SetProxies(Proxies&& proxies) { |
| 369 | proxies_ = std::move(proxies); |
| 370 | } |
| 371 | |
| 372 | void Session::SetProxyAuth(ProxyAuthentication&& proxy_auth) { |
| 373 | proxyAuth_ = std::move(proxy_auth); |
| 374 | } |
| 375 | |
| 376 | void Session::SetProxyAuth(const ProxyAuthentication& proxy_auth) { |
| 377 | proxyAuth_ = proxy_auth; |
| 378 | } |
| 379 | |
| 380 | void Session::SetMultipart(const Multipart& multipart) { |
| 381 | // Make sure, we have a empty multipart to start with: |
| 382 | if (curl_->multipart) { |
| 383 | curl_mime_free(mime: curl_->multipart); |
| 384 | } |
| 385 | curl_->multipart = curl_mime_init(easy: curl_->handle); |
| 386 | |
| 387 | // Add all multipart pieces: |
| 388 | for (const Part& part : multipart.parts) { |
| 389 | if (part.is_file) { |
| 390 | for (const File& file : part.files) { |
| 391 | curl_mimepart* mimePart = curl_mime_addpart(mime: curl_->multipart); |
| 392 | if (!part.content_type.empty()) { |
| 393 | curl_mime_type(part: mimePart, mimetype: part.content_type.c_str()); |
| 394 | } |
| 395 | |
| 396 | curl_mime_filedata(part: mimePart, filename: file.filepath.c_str()); |
| 397 | curl_mime_name(part: mimePart, name: part.name.c_str()); |
| 398 | |
| 399 | if (file.hasOverridenFilename()) { |
| 400 | curl_mime_filename(part: mimePart, filename: file.overriden_filename.c_str()); |
| 401 | } |
| 402 | } |
| 403 | } else { |
| 404 | curl_mimepart* mimePart = curl_mime_addpart(mime: curl_->multipart); |
| 405 | if (!part.content_type.empty()) { |
| 406 | curl_mime_type(part: mimePart, mimetype: part.content_type.c_str()); |
| 407 | } |
| 408 | if (part.is_buffer) { |
| 409 | // Do not use formdata, to prevent having to use reinterpreter_cast: |
| 410 | curl_mime_name(part: mimePart, name: part.name.c_str()); |
| 411 | curl_mime_data(part: mimePart, data: part.data, datasize: part.datalen); |
| 412 | curl_mime_filename(part: mimePart, filename: part.value.c_str()); |
| 413 | } else { |
| 414 | curl_mime_name(part: mimePart, name: part.name.c_str()); |
| 415 | curl_mime_data(part: mimePart, data: part.value.c_str(), CURL_ZERO_TERMINATED); |
| 416 | } |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | curl_easy_setopt(curl_->handle, CURLOPT_MIMEPOST, curl_->multipart); |
| 421 | hasBodyOrPayload_ = true; |
| 422 | } |
| 423 | |
| 424 | void Session::SetMultipart(Multipart&& multipart) { |
| 425 | SetMultipart(multipart); |
| 426 | } |
| 427 | |
| 428 | void Session::SetRedirect(const Redirect& redirect) { |
| 429 | curl_easy_setopt(curl_->handle, CURLOPT_FOLLOWLOCATION, redirect.follow ? 1L : 0L); |
| 430 | curl_easy_setopt(curl_->handle, CURLOPT_MAXREDIRS, redirect.maximum); |
| 431 | curl_easy_setopt(curl_->handle, CURLOPT_UNRESTRICTED_AUTH, redirect.cont_send_cred ? 1L : 0L); |
| 432 | |
| 433 | // NOLINTNEXTLINE (google-runtime-int) |
| 434 | long mask = 0; |
| 435 | if (any(flag: redirect.post_flags & PostRedirectFlags::POST_301)) { |
| 436 | mask |= CURL_REDIR_POST_301; |
| 437 | } |
| 438 | if (any(flag: redirect.post_flags & PostRedirectFlags::POST_302)) { |
| 439 | mask |= CURL_REDIR_POST_302; |
| 440 | } |
| 441 | if (any(flag: redirect.post_flags & PostRedirectFlags::POST_303)) { |
| 442 | mask |= CURL_REDIR_POST_303; |
| 443 | } |
| 444 | curl_easy_setopt(curl_->handle, CURLOPT_POSTREDIR, mask); |
| 445 | } |
| 446 | |
| 447 | void Session::SetCookies(const Cookies& cookies) { |
| 448 | curl_easy_setopt(curl_->handle, CURLOPT_COOKIELIST, "ALL" ); |
| 449 | curl_easy_setopt(curl_->handle, CURLOPT_COOKIE, cookies.GetEncoded(*curl_).c_str()); |
| 450 | } |
| 451 | |
| 452 | void Session::SetBody(const Body& body) { |
| 453 | hasBodyOrPayload_ = true; |
| 454 | curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(body.str().length())); |
| 455 | curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, body.c_str()); |
| 456 | } |
| 457 | |
| 458 | void Session::SetBody(Body&& body) { |
| 459 | hasBodyOrPayload_ = true; |
| 460 | curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t>(body.str().length())); |
| 461 | curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, body.c_str()); |
| 462 | } |
| 463 | |
| 464 | void Session::SetLowSpeed(const LowSpeed& low_speed) { |
| 465 | curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_LIMIT, low_speed.limit); |
| 466 | curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_TIME, low_speed.time); |
| 467 | } |
| 468 | |
| 469 | void Session::SetVerifySsl(const VerifySsl& verify) { |
| 470 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYPEER, verify ? ON : OFF); |
| 471 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYHOST, verify ? 2L : 0L); |
| 472 | } |
| 473 | |
| 474 | void Session::SetUnixSocket(const UnixSocket& unix_socket) { |
| 475 | curl_easy_setopt(curl_->handle, CURLOPT_UNIX_SOCKET_PATH, unix_socket.GetUnixSocketString()); |
| 476 | } |
| 477 | |
| 478 | void Session::SetSslOptions(const SslOptions& options) { |
| 479 | if (!options.cert_file.empty()) { |
| 480 | curl_easy_setopt(curl_->handle, CURLOPT_SSLCERT, options.cert_file.c_str()); |
| 481 | if (!options.cert_type.empty()) { |
| 482 | curl_easy_setopt(curl_->handle, CURLOPT_SSLCERTTYPE, options.cert_type.c_str()); |
| 483 | } |
| 484 | } |
| 485 | if (!options.key_file.empty()) { |
| 486 | curl_easy_setopt(curl_->handle, CURLOPT_SSLKEY, options.key_file.c_str()); |
| 487 | if (!options.key_type.empty()) { |
| 488 | curl_easy_setopt(curl_->handle, CURLOPT_SSLKEYTYPE, options.key_type.c_str()); |
| 489 | } |
| 490 | if (!options.key_pass.empty()) { |
| 491 | curl_easy_setopt(curl_->handle, CURLOPT_KEYPASSWD, options.key_pass.c_str()); |
| 492 | } |
| 493 | #if SUPPORT_CURLOPT_SSLKEY_BLOB |
| 494 | } else if (!options.key_blob.empty()) { |
| 495 | std::string key_blob(options.key_blob); |
| 496 | curl_blob blob{}; |
| 497 | // NOLINTNEXTLINE (readability-container-data-pointer) |
| 498 | blob.data = &key_blob[0]; |
| 499 | blob.len = key_blob.length(); |
| 500 | curl_easy_setopt(curl_->handle, CURLOPT_SSLKEY_BLOB, &blob); |
| 501 | if (!options.key_type.empty()) { |
| 502 | curl_easy_setopt(curl_->handle, CURLOPT_SSLKEYTYPE, options.key_type.c_str()); |
| 503 | } |
| 504 | if (!options.key_pass.empty()) { |
| 505 | curl_easy_setopt(curl_->handle, CURLOPT_KEYPASSWD, options.key_pass.c_str()); |
| 506 | } |
| 507 | #endif |
| 508 | } |
| 509 | if (!options.pinned_public_key.empty()) { |
| 510 | curl_easy_setopt(curl_->handle, CURLOPT_PINNEDPUBLICKEY, options.pinned_public_key.c_str()); |
| 511 | } |
| 512 | #if SUPPORT_ALPN |
| 513 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_ENABLE_ALPN, options.enable_alpn ? ON : OFF); |
| 514 | #endif |
| 515 | #if SUPPORT_NPN |
| 516 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_ENABLE_NPN, options.enable_npn ? ON : OFF); |
| 517 | #endif |
| 518 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYPEER, options.verify_peer ? ON : OFF); |
| 519 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYHOST, options.verify_host ? 2L : 0L); |
| 520 | #if LIBCURL_VERSION_NUM >= 0x072900 |
| 521 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYSTATUS, options.verify_status ? ON : OFF); |
| 522 | #endif |
| 523 | |
| 524 | int maxTlsVersion = options.ssl_version; |
| 525 | #if SUPPORT_MAX_TLS_VERSION |
| 526 | maxTlsVersion |= options.max_version; |
| 527 | #endif |
| 528 | |
| 529 | curl_easy_setopt(curl_->handle, CURLOPT_SSLVERSION, |
| 530 | // Ignore here since this has been defined by libcurl. |
| 531 | maxTlsVersion); |
| 532 | #if SUPPORT_SSL_NO_REVOKE |
| 533 | if (options.ssl_no_revoke) { |
| 534 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); |
| 535 | } |
| 536 | #endif |
| 537 | if (!options.ca_info.empty()) { |
| 538 | curl_easy_setopt(curl_->handle, CURLOPT_CAINFO, options.ca_info.c_str()); |
| 539 | } |
| 540 | if (!options.ca_path.empty()) { |
| 541 | curl_easy_setopt(curl_->handle, CURLOPT_CAPATH, options.ca_path.c_str()); |
| 542 | } |
| 543 | #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION |
| 544 | #ifdef OPENSSL_BACKEND_USED |
| 545 | if (!options.ca_buffer.empty()) { |
| 546 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function_load_ca_cert_from_buffer); |
| 547 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_CTX_DATA, options.ca_buffer.c_str()); |
| 548 | } |
| 549 | #endif |
| 550 | #endif |
| 551 | if (!options.crl_file.empty()) { |
| 552 | curl_easy_setopt(curl_->handle, CURLOPT_CRLFILE, options.crl_file.c_str()); |
| 553 | } |
| 554 | if (!options.ciphers.empty()) { |
| 555 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_CIPHER_LIST, options.ciphers.c_str()); |
| 556 | } |
| 557 | #if SUPPORT_TLSv13_CIPHERS |
| 558 | if (!options.tls13_ciphers.empty()) { |
| 559 | curl_easy_setopt(curl_->handle, CURLOPT_TLS13_CIPHERS, options.ciphers.c_str()); |
| 560 | } |
| 561 | #endif |
| 562 | #if SUPPORT_SESSIONID_CACHE |
| 563 | curl_easy_setopt(curl_->handle, CURLOPT_SSL_SESSIONID_CACHE, options.session_id_cache ? ON : OFF); |
| 564 | #endif |
| 565 | } |
| 566 | |
| 567 | void Session::SetVerbose(const Verbose& verbose) { |
| 568 | curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, verbose.verbose ? ON : OFF); |
| 569 | } |
| 570 | |
| 571 | void Session::SetInterface(const Interface& iface) { |
| 572 | if (iface.str().empty()) { |
| 573 | curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, nullptr); |
| 574 | } else { |
| 575 | curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, iface.c_str()); |
| 576 | } |
| 577 | } |
| 578 | |
| 579 | void Session::SetLocalPort(const LocalPort& local_port) { |
| 580 | curl_easy_setopt(curl_->handle, CURLOPT_LOCALPORT, local_port); |
| 581 | } |
| 582 | |
| 583 | void Session::SetLocalPortRange(const LocalPortRange& local_port_range) { |
| 584 | curl_easy_setopt(curl_->handle, CURLOPT_LOCALPORTRANGE, local_port_range); |
| 585 | } |
| 586 | |
| 587 | void Session::SetHttpVersion(const HttpVersion& version) { |
| 588 | switch (version.code) { |
| 589 | case HttpVersionCode::VERSION_NONE: |
| 590 | curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE); |
| 591 | break; |
| 592 | |
| 593 | case HttpVersionCode::VERSION_1_0: |
| 594 | curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); |
| 595 | break; |
| 596 | |
| 597 | case HttpVersionCode::VERSION_1_1: |
| 598 | curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); |
| 599 | break; |
| 600 | |
| 601 | #if LIBCURL_VERSION_NUM >= 0x072100 // 7.33.0 |
| 602 | case HttpVersionCode::VERSION_2_0: |
| 603 | curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); |
| 604 | break; |
| 605 | #endif |
| 606 | |
| 607 | #if LIBCURL_VERSION_NUM >= 0x072F00 // 7.47.0 |
| 608 | case HttpVersionCode::VERSION_2_0_TLS: |
| 609 | curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); |
| 610 | break; |
| 611 | #endif |
| 612 | |
| 613 | #if LIBCURL_VERSION_NUM >= 0x073100 // 7.49.0 |
| 614 | case HttpVersionCode::VERSION_2_0_PRIOR_KNOWLEDGE: |
| 615 | curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); |
| 616 | break; |
| 617 | #endif |
| 618 | |
| 619 | #if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0 |
| 620 | case HttpVersionCode::VERSION_3_0: |
| 621 | curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3); |
| 622 | break; |
| 623 | #endif |
| 624 | |
| 625 | default: // Should not happen |
| 626 | throw std::invalid_argument("Invalid/Unknown HTTP version type." ); |
| 627 | break; |
| 628 | } |
| 629 | } |
| 630 | |
| 631 | void Session::SetRange(const Range& range) { |
| 632 | const std::string range_str = range.str(); |
| 633 | curl_easy_setopt(curl_->handle, CURLOPT_RANGE, range_str.c_str()); |
| 634 | } |
| 635 | |
| 636 | void Session::SetMultiRange(const MultiRange& multi_range) { |
| 637 | const std::string multi_range_str = multi_range.str(); |
| 638 | curl_easy_setopt(curl_->handle, CURLOPT_RANGE, multi_range_str.c_str()); |
| 639 | } |
| 640 | |
| 641 | void Session::SetReserveSize(const ReserveSize& reserve_size) { |
| 642 | ResponseStringReserve(size: reserve_size.size); |
| 643 | } |
| 644 | |
| 645 | void Session::SetAcceptEncoding(const AcceptEncoding& accept_encoding) { |
| 646 | acceptEncoding_ = accept_encoding; |
| 647 | } |
| 648 | |
| 649 | void Session::SetAcceptEncoding(AcceptEncoding&& accept_encoding) { |
| 650 | acceptEncoding_ = std::move(accept_encoding); |
| 651 | } |
| 652 | |
| 653 | cpr_off_t Session::GetDownloadFileLength() { |
| 654 | cpr_off_t downloadFileLenth = -1; |
| 655 | curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); |
| 656 | |
| 657 | const std::string protocol = url_.str().substr(pos: 0, n: url_.str().find(c: ':')); |
| 658 | if (proxies_.has(protocol)) { |
| 659 | curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str()); |
| 660 | if (proxyAuth_.has(protocol)) { |
| 661 | curl_easy_setopt(curl_->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); |
| 662 | curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERPWD, proxyAuth_[protocol]); |
| 663 | } |
| 664 | } |
| 665 | |
| 666 | curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); |
| 667 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1); |
| 668 | if (DoEasyPerform() == CURLE_OK) { |
| 669 | // NOLINTNEXTLINE (google-runtime-int) |
| 670 | long status_code{}; |
| 671 | curl_easy_getinfo(curl_->handle, CURLINFO_RESPONSE_CODE, &status_code); |
| 672 | if (200 == status_code) { |
| 673 | curl_easy_getinfo(curl_->handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &downloadFileLenth); |
| 674 | } |
| 675 | } |
| 676 | return downloadFileLenth; |
| 677 | } |
| 678 | |
| 679 | void Session::ResponseStringReserve(size_t size) { |
| 680 | response_string_reserve_size_ = size; |
| 681 | } |
| 682 | |
| 683 | Response Session::Delete() { |
| 684 | PrepareDelete(); |
| 685 | return makeRequest(); |
| 686 | } |
| 687 | |
| 688 | Response Session::Download(const WriteCallback& write) { |
| 689 | PrepareDownload(write); |
| 690 | return makeDownloadRequest(); |
| 691 | } |
| 692 | |
| 693 | Response Session::Download(std::ofstream& file) { |
| 694 | PrepareDownload(file); |
| 695 | return makeDownloadRequest(); |
| 696 | } |
| 697 | |
| 698 | Response Session::Get() { |
| 699 | PrepareGet(); |
| 700 | return makeRequest(); |
| 701 | } |
| 702 | |
| 703 | Response Session::Head() { |
| 704 | PrepareHead(); |
| 705 | return makeRequest(); |
| 706 | } |
| 707 | |
| 708 | Response Session::Options() { |
| 709 | PrepareOptions(); |
| 710 | return makeRequest(); |
| 711 | } |
| 712 | |
| 713 | Response Session::Patch() { |
| 714 | PreparePatch(); |
| 715 | return makeRequest(); |
| 716 | } |
| 717 | |
| 718 | Response Session::Post() { |
| 719 | PreparePost(); |
| 720 | return makeRequest(); |
| 721 | } |
| 722 | |
| 723 | Response Session::Put() { |
| 724 | PreparePut(); |
| 725 | return makeRequest(); |
| 726 | } |
| 727 | |
| 728 | std::shared_ptr<Session> Session::GetSharedPtrFromThis() { |
| 729 | try { |
| 730 | return shared_from_this(); |
| 731 | } catch (std::bad_weak_ptr&) { |
| 732 | throw std::runtime_error("Failed to get a shared pointer from this. The reason is probably that the session object is not managed by a shared pointer, which is required to use this functionality." ); |
| 733 | } |
| 734 | } |
| 735 | |
| 736 | AsyncResponse Session::GetAsync() { |
| 737 | auto shared_this = shared_from_this(); |
| 738 | return async(fn: [shared_this]() { return shared_this->Get(); }); |
| 739 | } |
| 740 | |
| 741 | AsyncResponse Session::DeleteAsync() { |
| 742 | return async(fn: [shared_this = GetSharedPtrFromThis()]() { return shared_this->Delete(); }); |
| 743 | } |
| 744 | |
| 745 | AsyncResponse Session::DownloadAsync(const WriteCallback& write) { |
| 746 | return async(fn: [shared_this = GetSharedPtrFromThis(), write]() { return shared_this->Download(write); }); |
| 747 | } |
| 748 | |
| 749 | AsyncResponse Session::DownloadAsync(std::ofstream& file) { |
| 750 | return async(fn: [shared_this = GetSharedPtrFromThis(), &file]() { return shared_this->Download(file); }); |
| 751 | } |
| 752 | |
| 753 | AsyncResponse Session::HeadAsync() { |
| 754 | return async(fn: [shared_this = GetSharedPtrFromThis()]() { return shared_this->Head(); }); |
| 755 | } |
| 756 | |
| 757 | AsyncResponse Session::OptionsAsync() { |
| 758 | return async(fn: [shared_this = GetSharedPtrFromThis()]() { return shared_this->Options(); }); |
| 759 | } |
| 760 | |
| 761 | AsyncResponse Session::PatchAsync() { |
| 762 | return async(fn: [shared_this = GetSharedPtrFromThis()]() { return shared_this->Patch(); }); |
| 763 | } |
| 764 | |
| 765 | AsyncResponse Session::PostAsync() { |
| 766 | return async(fn: [shared_this = GetSharedPtrFromThis()]() { return shared_this->Post(); }); |
| 767 | } |
| 768 | |
| 769 | AsyncResponse Session::PutAsync() { |
| 770 | return async(fn: [shared_this = GetSharedPtrFromThis()]() { return shared_this->Put(); }); |
| 771 | } |
| 772 | |
| 773 | std::shared_ptr<CurlHolder> Session::GetCurlHolder() { |
| 774 | return curl_; |
| 775 | } |
| 776 | |
| 777 | std::string Session::GetFullRequestUrl() { |
| 778 | const std::string parametersContent = parameters_.GetContent(*curl_); |
| 779 | return url_.str() + (parametersContent.empty() ? "" : "?" ) + parametersContent; |
| 780 | } |
| 781 | |
| 782 | void Session::PrepareDelete() { |
| 783 | curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 0L); |
| 784 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); |
| 785 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "DELETE" ); |
| 786 | prepareCommon(); |
| 787 | } |
| 788 | |
| 789 | void Session::PrepareGet() { |
| 790 | // In case there is a body or payload for this request, we create a custom GET-Request since a |
| 791 | // GET-Request with body is based on the HTTP RFC **not** a leagal request. |
| 792 | if (hasBodyOrPayload_) { |
| 793 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); |
| 794 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "GET" ); |
| 795 | } else { |
| 796 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); |
| 797 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); |
| 798 | curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1L); |
| 799 | } |
| 800 | prepareCommon(); |
| 801 | } |
| 802 | |
| 803 | void Session::PrepareHead() { |
| 804 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1L); |
| 805 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); |
| 806 | prepareCommon(); |
| 807 | } |
| 808 | |
| 809 | void Session::PrepareOptions() { |
| 810 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); |
| 811 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "OPTIONS" ); |
| 812 | prepareCommon(); |
| 813 | } |
| 814 | |
| 815 | void Session::PreparePatch() { |
| 816 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); |
| 817 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "PATCH" ); |
| 818 | prepareCommon(); |
| 819 | } |
| 820 | |
| 821 | void Session::PreparePost() { |
| 822 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); |
| 823 | |
| 824 | // In case there is no body or payload set it to an empty post: |
| 825 | if (hasBodyOrPayload_) { |
| 826 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); |
| 827 | } else { |
| 828 | curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, readcb_.callback ? nullptr : "" ); |
| 829 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "POST" ); |
| 830 | } |
| 831 | prepareCommon(); |
| 832 | } |
| 833 | |
| 834 | void Session::PreparePut() { |
| 835 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); |
| 836 | if (!hasBodyOrPayload_ && readcb_.callback) { |
| 837 | /** |
| 838 | * Yes, this one has to be CURLOPT_POSTFIELDS even if we are performing a PUT request. |
| 839 | * In case we don't set this one, performing a POST-request with PUT won't work. |
| 840 | * It in theory this only enforces the usage of the readcallback for POST requests, but works here as well. |
| 841 | **/ |
| 842 | curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, nullptr); |
| 843 | } |
| 844 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "PUT" ); |
| 845 | curl_easy_setopt(curl_->handle, CURLOPT_RANGE, nullptr); |
| 846 | prepareCommon(); |
| 847 | } |
| 848 | |
| 849 | void Session::PrepareDownload(std::ofstream& file) { |
| 850 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); |
| 851 | curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); |
| 852 | curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFileFunction); |
| 853 | curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &file); |
| 854 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); |
| 855 | |
| 856 | prepareCommonDownload(); |
| 857 | } |
| 858 | |
| 859 | void Session::PrepareDownload(const WriteCallback& write) { |
| 860 | curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); |
| 861 | curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); |
| 862 | curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); |
| 863 | |
| 864 | SetWriteCallback(write); |
| 865 | |
| 866 | prepareCommonDownload(); |
| 867 | } |
| 868 | |
| 869 | Response Session::Complete(CURLcode curl_error) { |
| 870 | curl_slist* raw_cookies{nullptr}; |
| 871 | curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies); |
| 872 | Cookies cookies = util::parseCookies(raw_cookies); |
| 873 | curl_slist_free_all(list: raw_cookies); |
| 874 | |
| 875 | // Reset the has no body property: |
| 876 | hasBodyOrPayload_ = false; |
| 877 | |
| 878 | std::string errorMsg = curl_->error.data(); |
| 879 | return Response(curl_, std::move(response_string_), std::move(header_string_), std::move(cookies), Error(curl_error, std::move(errorMsg))); |
| 880 | } |
| 881 | |
| 882 | Response Session::CompleteDownload(CURLcode curl_error) { |
| 883 | if (!headercb_.callback) { |
| 884 | curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, nullptr); |
| 885 | curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, 0); |
| 886 | } |
| 887 | |
| 888 | curl_slist* raw_cookies{nullptr}; |
| 889 | curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies); |
| 890 | Cookies cookies = util::parseCookies(raw_cookies); |
| 891 | curl_slist_free_all(list: raw_cookies); |
| 892 | std::string errorMsg = curl_->error.data(); |
| 893 | |
| 894 | return Response(curl_, "" , std::move(header_string_), std::move(cookies), Error(curl_error, std::move(errorMsg))); |
| 895 | } |
| 896 | |
| 897 | void Session::AddInterceptor(const std::shared_ptr<Interceptor>& pinterceptor) { |
| 898 | interceptors_.push(x: pinterceptor); |
| 899 | } |
| 900 | |
| 901 | Response Session::proceed() { |
| 902 | prepareCommon(); |
| 903 | return makeRequest(); |
| 904 | } |
| 905 | |
| 906 | Response Session::intercept() { |
| 907 | // At least one interceptor exists -> Execute its intercept function |
| 908 | const std::shared_ptr<Interceptor> interceptor = interceptors_.front(); |
| 909 | interceptors_.pop(); |
| 910 | return interceptor->intercept(session&: *this); |
| 911 | } |
| 912 | |
| 913 | // clang-format off |
| 914 | void Session::SetOption(const Resolve& resolve) { SetResolve(resolve); } |
| 915 | void Session::SetOption(const std::vector<Resolve>& resolves) { SetResolves(resolves); } |
| 916 | void Session::SetOption(const ReadCallback& read) { SetReadCallback(read); } |
| 917 | void Session::(const HeaderCallback& ) { SetHeaderCallback(header); } |
| 918 | void Session::SetOption(const WriteCallback& write) { SetWriteCallback(write); } |
| 919 | void Session::SetOption(const ProgressCallback& progress) { SetProgressCallback(progress); } |
| 920 | void Session::SetOption(const DebugCallback& debug) { SetDebugCallback(debug); } |
| 921 | void Session::SetOption(const Url& url) { SetUrl(url); } |
| 922 | void Session::SetOption(const Parameters& parameters) { SetParameters(parameters); } |
| 923 | void Session::SetOption(Parameters&& parameters) { SetParameters(std::move(parameters)); } |
| 924 | void Session::SetOption(const Header& ) { SetHeader(header); } |
| 925 | void Session::SetOption(const Timeout& timeout) { SetTimeout(timeout); } |
| 926 | void Session::SetOption(const ConnectTimeout& timeout) { SetConnectTimeout(timeout); } |
| 927 | void Session::SetOption(const Authentication& auth) { SetAuth(auth); } |
| 928 | void Session::SetOption(const LimitRate& limit_rate) { SetLimitRate(limit_rate); } |
| 929 | // Only supported with libcurl >= 7.61.0. |
| 930 | // As an alternative use SetHeader and add the token manually. |
| 931 | #if LIBCURL_VERSION_NUM >= 0x073D00 |
| 932 | void Session::SetOption(const Bearer& auth) { SetBearer(auth); } |
| 933 | #endif |
| 934 | void Session::SetOption(const UserAgent& ua) { SetUserAgent(ua); } |
| 935 | void Session::SetOption(const Payload& payload) { SetPayload(payload); } |
| 936 | void Session::SetOption(Payload&& payload) { SetPayload(std::move(payload)); } |
| 937 | void Session::SetOption(const Proxies& proxies) { SetProxies(proxies); } |
| 938 | void Session::SetOption(Proxies&& proxies) { SetProxies(std::move(proxies)); } |
| 939 | void Session::SetOption(ProxyAuthentication&& proxy_auth) { SetProxyAuth(std::move(proxy_auth)); } |
| 940 | void Session::SetOption(const ProxyAuthentication& proxy_auth) { SetProxyAuth(proxy_auth); } |
| 941 | void Session::SetOption(const Multipart& multipart) { SetMultipart(multipart); } |
| 942 | void Session::SetOption(Multipart&& multipart) { SetMultipart(std::move(multipart)); } |
| 943 | void Session::SetOption(const Redirect& redirect) { SetRedirect(redirect); } |
| 944 | void Session::SetOption(const Cookies& cookies) { SetCookies(cookies); } |
| 945 | void Session::SetOption(const Body& body) { SetBody(body); } |
| 946 | void Session::SetOption(Body&& body) { SetBody(std::move(body)); } |
| 947 | void Session::SetOption(const LowSpeed& low_speed) { SetLowSpeed(low_speed); } |
| 948 | void Session::SetOption(const VerifySsl& verify) { SetVerifySsl(verify); } |
| 949 | void Session::SetOption(const Verbose& verbose) { SetVerbose(verbose); } |
| 950 | void Session::SetOption(const UnixSocket& unix_socket) { SetUnixSocket(unix_socket); } |
| 951 | void Session::SetOption(const SslOptions& options) { SetSslOptions(options); } |
| 952 | void Session::SetOption(const Interface& iface) { SetInterface(iface); } |
| 953 | void Session::SetOption(const LocalPort& local_port) { SetLocalPort(local_port); } |
| 954 | void Session::SetOption(const LocalPortRange& local_port_range) { SetLocalPortRange(local_port_range); } |
| 955 | void Session::SetOption(const HttpVersion& version) { SetHttpVersion(version); } |
| 956 | void Session::SetOption(const Range& range) { SetRange(range); } |
| 957 | void Session::SetOption(const MultiRange& multi_range) { SetMultiRange(multi_range); } |
| 958 | void Session::SetOption(const ReserveSize& reserve_size) { SetReserveSize(reserve_size.size); } |
| 959 | void Session::SetOption(const AcceptEncoding& accept_encoding) { SetAcceptEncoding(accept_encoding); } |
| 960 | void Session::SetOption(AcceptEncoding&& accept_encoding) { SetAcceptEncoding(accept_encoding); } |
| 961 | // clang-format on |
| 962 | |
| 963 | void Session::SetCancellationParam(std::shared_ptr<std::atomic_bool> param) { |
| 964 | cancellationcb_ = CancellationCallback{std::move(param)}; |
| 965 | isCancellable = true; |
| 966 | #if LIBCURL_VERSION_NUM < 0x072000 |
| 967 | curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction<CancellationCallback>); |
| 968 | curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &cancellationcb_); |
| 969 | #else |
| 970 | curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction<CancellationCallback>); |
| 971 | curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &cancellationcb_); |
| 972 | #endif |
| 973 | curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L); |
| 974 | } |
| 975 | } // namespace cpr |
| 976 | |