attest::AttestationResult HttpClient::InvokeHttpImdsRequest()

in client-library/src/Attestation/AttestationClient/lib/HttpClient.cpp [32:142]


attest::AttestationResult HttpClient::InvokeHttpImdsRequest(std::string& http_response,
    const std::string& url,
    const HttpClient::HttpVerb& http_verb,
    const std::string& request_body,
    const std::string& content_type) {
    AttestationResult result(AttestationResult::ErrorCode::SUCCESS);

    CURL* curl = curl_easy_init();
    if (curl == nullptr) {
        CLIENT_LOG_ERROR("Failed to initialize curl for http request.");
        result.code_ = AttestationResult::ErrorCode::ERROR_CURL_INITIALIZATION;
        result.description_ = std::string("Failed to initialize curl for http request.");
        return result;
    }

    // Set the the HTTPHEADER object to send Metadata in the response.
    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Metadata:true");
    if (!content_type.empty()) {
        headers = curl_slist_append(headers, content_type.c_str());
    }
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    // Send a pointer to a std::string to hold the response from the end
    // point along with the handler function.
    std::string response;
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteResponseCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

    // Set the url of the end point that we are trying to talk to.
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

    if (http_verb == HttpClient::HttpVerb::POST) {
        if (request_body.empty()) {
            CLIENT_LOG_ERROR("Request body missing for POST request");
            result.code_ = AttestationResult::ErrorCode::ERROR_EMPTY_REQUEST_BODY;
            result.description_ = std::string("Request body missing for POST request");
            return result;
        }

        // Set Http verb as POST
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");

        // set the payload that will be sent to the endpoint.
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request_body.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request_body.size());
    }

    // Adding timeout for 300 sec
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300L);

    CURLcode res = CURLE_OK;
    uint8_t retries = 0;
    while ((res = curl_easy_perform(curl)) == CURLE_OK) {
        long response_code = HTTP_STATUS_OK;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);

        if (HTTP_STATUS_OK == response_code) {
            http_response = response;
            if (http_response.size() == 0) {
                CLIENT_LOG_ERROR("Empty response received");
                result.code_ = AttestationResult::ErrorCode::ERROR_EMPTY_RESPONSE;
                result.description_ = std::string("Empty response received");
            }

            break;
        }
        else if (response_code == HTTP_STATUS_RESOURCE_NOT_FOUND ||
            response_code == HTTP_STATUS_TOO_MANY_REQUESTS ||
            response_code >= HTTP_STATUS_INTERNAL_SERVER_ERROR) {
            if (retries == MAX_RETRIES) {
                CLIENT_LOG_ERROR("Http Request failed with error:%ld description:%s",
                    response_code,
                    response.c_str());
                result.code_ = AttestationResult::ErrorCode::ERROR_HTTP_REQUEST_EXCEEDED_RETRIES;
                result.description_ = response;
                break;
            }
            CLIENT_LOG_ERROR("HTTP request failed with response code:%ld description:%s",
                response_code,
                response.c_str());
            CLIENT_LOG_INFO("Retrying HTTP request:%d", retries);

            // Retry with backoff 30 -> 60 -> 120 seconds
            std::this_thread::sleep_for(
                std::chrono::seconds(
                    static_cast<long long>(30 * pow(2.0, static_cast<double>(retries++)))
                ));
            response = std::string();
            continue;
        }
        else {
            CLIENT_LOG_ERROR("HTTP request failed with response code:%ld description:%s",
                response_code,
                response.c_str());
            result.code_ = AttestationResult::ErrorCode::ERROR_HTTP_REQUEST_FAILED;
            result.description_ = response;
            break;
        }
    }

    if (res != CURLE_OK) {
        CLIENT_LOG_ERROR("curl_easy_perform() failed:%s", curl_easy_strerror(res));
        result.code_ = AttestationResult::ErrorCode::ERROR_SENDING_CURL_REQUEST_FAILED;
        result.description_ = std::string("Failed sending curl request with error:") + std::string(curl_easy_strerror(res));
    }

    curl_easy_cleanup(curl);
    curl_slist_free_all(headers);
    return result;
}