std::string ImdsClient::InvokeHttpRequest()

in client-library/src/Attestation/AttestationClient/lib/ImdsClient.cpp [189:290]


std::string ImdsClient::InvokeHttpRequest(
	const std::string& url,  
	const ImdsClient::HttpVerb& http_verb,
	const std::string& request_body) {
	std::string http_response;
	if (url.empty()) {
		CLIENT_LOG_ERROR("The URL can not be empty");
		return http_response;
	}

	CURL* curl = curl_easy_init();
	if (curl == nullptr) {
		CLIENT_LOG_ERROR("Failed to initialize curl for http request.");
		return http_response;
	}

	// Set the the HTTPHEADER object to send Metadata in the response.
	struct curl_slist* headers = NULL;
	headers = curl_slist_append(headers, "Metadata:true");
	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 == ImdsClient::HttpVerb::POST) {
		if (request_body.empty()) {
			CLIENT_LOG_ERROR("Request body missing for POST request");
			return http_response;
		}

		// Set Http request to be a POST request as expected by the THIM endpoint.
		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("HTTP response found empty");
				break;
			}

			CLIENT_LOG_INFO("HTTP response retrieved: %s", http_response.c_str());
			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 we receive any of these responses from IMDS, we can retry
			//after an exponential backoff time
			// Sleep for the backoff period and try again
			if (retries == MAX_RETRIES) {
				CLIENT_LOG_ERROR("HTTP request failed. Maximum retries exceeded\n");

				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());
			break;
		}
	}

	if (res != CURLE_OK) {
		CLIENT_LOG_ERROR("curl_easy_perform() failed:%s", curl_easy_strerror(res));
	}

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