AttestationResult SendRequest()

in client-library/src/Attestation/AttestationClient/lib/AttestationLibUtils.cpp [281:411]


AttestationResult SendRequest(const std::string& url,
                              const std::string& payload,
                              std::string& http_response) {
    AttestationResult result(AttestationResult::ErrorCode::SUCCESS);

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

    // Create a header object to add the authentication token and content-type to the header.
    struct curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json");

    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

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

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

#ifdef PLATFORM_UNIX
    char* cainfo = NULL;
    curl_easy_getinfo(curl, CURLINFO_CAINFO, &cainfo);
    struct stat buffer;
    if(cainfo && stat(cainfo, &buffer) == 0) {
        CLIENT_LOG_INFO("Using default ca info path: %s", cainfo);
        curl_easy_setopt(curl, CURLOPT_CAINFO, cainfo);
    } else {
        CLIENT_LOG_INFO("Using ca-bundle certs");
        curl_easy_setopt(curl, CURLOPT_CAINFO, "curl-ca-bundle.crt");
    }
#else 
    curl_easy_setopt(curl, CURLOPT_CAINFO, "curl-ca-bundle.crt");
    curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
#endif
    // 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);

    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(response_code == HTTP_STATUS_OK) {
            http_response = response;
            break;
        } else if(response_code == HTTP_STATUS_ATTESTATION_FAILURE) {
                std::string error_msg = response;

                CLIENT_LOG_ERROR("Attestation failed with error code:%ld description:%s",
                                 response_code,
                                 error_msg.c_str());

                result.code_ = AttestationResult::ErrorCode::ERROR_ATTESTATION_FAILED;
                result.description_ = error_msg;
                break;
        } else if (response_code == HTTP_STATUS_TOO_MANY_REQUESTS 
                    || response_code == HTTP_STATUS_REQUEST_TIMEOUT
                    || response_code >= HTTP_STATUS_SERVER_ERROR) {
            std::string error_msg = response;

            CLIENT_LOG_ERROR("Http Request failed with error:%ld description:%s",
                              response_code,
                              error_msg.c_str());
            CLIENT_LOG_INFO("Retrying");

            //Retry sending the request since this is a server failure.
            if(retries == MAX_RETRIES) {
                CLIENT_LOG_ERROR("Http Request failed with error:%ld description:%s",
                                 response_code,
                                 error_msg.c_str());
                CLIENT_LOG_ERROR("Maxinum retries exceeded.");

                result.code_ = AttestationResult::ErrorCode::ERROR_HTTP_REQUEST_EXCEEDED_RETRIES;
                result.description_ = error_msg;
                break;
            }

            curl_off_t retry_after_seconds = 0;
            curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &retry_after_seconds);
            if (retry_after_seconds) {
                CLIENT_LOG_INFO("Http Request throttled by MAA, retry-after: %ld", retry_after_seconds);
            }

            long long exponential_back_off_seconds = static_cast<long long>(BACK_OFF_TIME_SECONDS * pow(2.0, static_cast<double>(retries++)));
            long long sleep_time_milliseconds = To_MilliSeconds((exponential_back_off_seconds > retry_after_seconds ? 
                                                    exponential_back_off_seconds : 
                                                    retry_after_seconds)) + static_cast<long long>(generateRandomJitter());

            CLIENT_LOG_INFO("Http Request wait time: %ld", sleep_time_milliseconds);
            // Sleep for the backoff period and try again.
            std::this_thread::sleep_for(
                std::chrono::milliseconds(
                    sleep_time_milliseconds
                ));
            response = std::string();
            continue;
        } else {
            std::string error_msg = response;

            CLIENT_LOG_ERROR("Http Request failed with error:%ld description:%s",
                             response_code,
                             error_msg.c_str());

            result.code_ = AttestationResult::ErrorCode::ERROR_HTTP_REQUEST_FAILED;
            result.description_ = error_msg;
            break;
        }
    }
    if(res != CURLE_OK) {
        CLIENT_LOG_ERROR("Failed sending curl request with error:%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;
}