inline CurlWrapper::Result CurlWrapper::get()

in lib/CurlWrapper.h [97:181]


inline CurlWrapper::Result CurlWrapper::get(const std::string& url, const std::string& header,
                                            const Options& options, const TlsContext* tlsContext) const {
    assert(handle_);
    curl_easy_setopt(handle_, CURLOPT_URL, url.c_str());

    if (!options.postFields.empty()) {
        curl_easy_setopt(handle_, CURLOPT_CUSTOMREQUEST, "POST");
        curl_easy_setopt(handle_, CURLOPT_POSTFIELDS, options.postFields.c_str());
    }

    // Write response
    curl_easy_setopt(
        handle_, CURLOPT_WRITEFUNCTION,
        +[](char* buffer, size_t size, size_t nitems, void* outstream) -> size_t {
            static_cast<std::string*>(outstream)->append(buffer, size * nitems);
            return size * nitems;
        });
    std::string response;
    curl_easy_setopt(handle_, CURLOPT_WRITEDATA, &response);

    // New connection is made for each call
    curl_easy_setopt(handle_, CURLOPT_FRESH_CONNECT, 1L);
    curl_easy_setopt(handle_, CURLOPT_FORBID_REUSE, 1L);

    // Skipping signal handling - results in timeouts not honored during the DNS lookup
    // Without this config, Curl_resolv_timeout might crash in multi-threads environment
    curl_easy_setopt(handle_, CURLOPT_NOSIGNAL, 1L);

    curl_easy_setopt(handle_, CURLOPT_TIMEOUT, options.timeoutInSeconds);
    if (!options.userAgent.empty()) {
        curl_easy_setopt(handle_, CURLOPT_USERAGENT, options.userAgent.c_str());
    }
    curl_easy_setopt(handle_, CURLOPT_FAILONERROR, 1L);

    // Redirects
    curl_easy_setopt(handle_, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(handle_, CURLOPT_MAXREDIRS, options.maxLookupRedirects);

    char errorBuffer[CURL_ERROR_SIZE] = "";
    curl_easy_setopt(handle_, CURLOPT_ERRORBUFFER, errorBuffer);

    curl_slist* headers = nullptr;
    CurlListGuard headersGuard{headers};
    if (!header.empty()) {
        headers = curl_slist_append(headers, header.c_str());
        curl_easy_setopt(handle_, CURLOPT_HTTPHEADER, headers);
    }

    if (tlsContext) {
        CURLcode code;
        code = curl_easy_setopt(handle_, CURLOPT_SSLENGINE, nullptr);
        if (code != CURLE_OK) {
            return {code, "", -1, "",
                    "Unable to load SSL engine for url " + url + ": " + curl_easy_strerror(code)};
        }
        code = curl_easy_setopt(handle_, CURLOPT_SSLENGINE_DEFAULT, 1L);
        if (code != CURLE_OK) {
            return {code, "", -1, "",
                    "Unable to load SSL engine as default for url " + url + ": " + curl_easy_strerror(code)};
        }
        curl_easy_setopt(handle_, CURLOPT_SSL_VERIFYHOST, tlsContext->validateHostname ? 1L : 0L);
        curl_easy_setopt(handle_, CURLOPT_SSL_VERIFYPEER, tlsContext->allowInsecure ? 0L : 1L);
        if (!tlsContext->trustCertsFilePath.empty()) {
            curl_easy_setopt(handle_, CURLOPT_CAINFO, tlsContext->trustCertsFilePath.c_str());
        }
        if (!tlsContext->certPath.empty() && !tlsContext->keyPath.empty()) {
            curl_easy_setopt(handle_, CURLOPT_SSLCERT, tlsContext->certPath.c_str());
            curl_easy_setopt(handle_, CURLOPT_SSLKEY, tlsContext->keyPath.c_str());
        }
    }

    auto res = curl_easy_perform(handle_);
    long responseCode;
    curl_easy_getinfo(handle_, CURLINFO_RESPONSE_CODE, &responseCode);
    Result result{res, response, responseCode, "", "", std::string(errorBuffer)};
    if (responseCode == 307 || responseCode == 302 || responseCode == 301) {
        char* url;
        curl_easy_getinfo(handle_, CURLINFO_REDIRECT_URL, &url);
        // `url` is null when the host of the redirect URL cannot be resolved
        if (url) {
            result.redirectUrl = url;
        }
    }
    return result;
}