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;
}