33 :
public std::enable_shared_from_this<QuoteEndorsementsClient>
41 static constexpr size_t server_connection_timeout_s = 3;
43 std::shared_ptr<RPCSessions> rpcsessions;
48 std::vector<uint8_t> endorsements_pem;
55 size_t last_submitted_request_id = 0;
56 bool has_completed =
false;
57 size_t server_retries_count = 0;
59 struct QuoteEndorsementsClientMsg
61 QuoteEndorsementsClientMsg(
62 const std::shared_ptr<QuoteEndorsementsClient>& self_,
68 std::shared_ptr<QuoteEndorsementsClient> self;
72 struct QuoteEndorsementsClientTimeoutMsg
74 QuoteEndorsementsClientTimeoutMsg(
75 const std::shared_ptr<QuoteEndorsementsClient>& self_,
80 request_id(request_id_)
83 std::shared_ptr<QuoteEndorsementsClient> self;
88 void handle_success_response(
89 std::vector<uint8_t>&& data,
const EndpointInfo& response_endpoint)
93 auto& server = config.
servers.front();
98 auto endpoint = server.front();
99 if (has_completed || response_endpoint != endpoint)
107 endorsements_pem.insert(endorsements_pem.end(), raw.begin(), raw.end());
111 auto j = nlohmann::json::parse(data);
112 auto vcekCert = j.at(
"vcekCert").get<std::string>();
113 auto certificateChain = j.at(
"certificateChain").get<std::string>();
114 endorsements_pem.insert(
115 endorsements_pem.end(), vcekCert.begin(), vcekCert.end());
116 endorsements_pem.insert(
117 endorsements_pem.end(),
118 certificateChain.begin(),
119 certificateChain.end());
123 endorsements_pem.insert(
124 endorsements_pem.end(), data.begin(), data.end());
130 LOG_INFO_FMT(
"Complete endorsement chain successfully retrieved");
132 "{}", std::string(endorsements_pem.begin(), endorsements_pem.end()));
133 has_completed =
true;
134 done_cb(std::move(endorsements_pem));
142 std::string get_formatted_query(
143 const std::map<std::string, std::string> params)
const
145 std::string formatted_query;
147 for (
const auto& it : params)
150 fmt::format(
"{}{}={}", (first ?
'?' :
'&'), it.first, it.second);
153 return formatted_query;
156 void fetch(
const Server& server)
158 auto request_id = ++last_submitted_request_id;
159 auto endpoint = server.front();
164 curl_handle.
set_opt(CURLOPT_HTTPGET, 1L);
166 auto url = fmt::format(
168 endpoint.tls ?
"https" :
"http",
172 get_formatted_query(endpoint.params));
180 curl_handle.
set_opt(CURLOPT_SSL_VERIFYHOST, 0L);
181 curl_handle.
set_opt(CURLOPT_SSL_VERIFYPEER, 0L);
182 curl_handle.
set_opt(CURLOPT_SSL_VERIFYSTATUS, 0L);
186 for (
auto const& [k, v] : endpoint.headers)
188 headers.append(k, v);
190 headers.append(http::headers::HOST, endpoint.host);
192 auto response_callback = ([
this, server, endpoint](
194 CURLcode curl_response,
196 std::lock_guard<ccf::pal::Mutex> guard(this->lock);
197 auto* response_body = request.get_response_body();
198 auto& response_headers = request.get_response_headers();
200 if (curl_response == CURLE_OK && status_code == HTTP_STATUS_OK)
203 "Successfully retrieved endorsements for attestation report: "
205 response_body->buffer.size());
207 handle_success_response(std::move(response_body->buffer), endpoint);
212 "Error fetching endorsements for attestation report: {} ({}) {}",
213 curl_easy_strerror(curl_response),
217 curl_response == CURLE_OK &&
218 status_code == HTTP_STATUS_TOO_MANY_REQUESTS)
220 constexpr size_t default_retry_after_s = 3;
221 size_t retry_after_s = default_retry_after_s;
222 auto h = response_headers.data.find(http::headers::RETRY_AFTER);
223 if (h != response_headers.data.end())
225 const auto& retry_after_value = h->second;
228 retry_after_value.data(),
229 retry_after_value.data() + retry_after_value.size(),
234 std::make_unique<::threading::Tmsg<QuoteEndorsementsClientMsg>>(
235 [](std::unique_ptr<::threading::Tmsg<QuoteEndorsementsClientMsg>>
236 msg) { msg->data.self->fetch(msg->data.server); },
241 "{} endorsements endpoint had too many requests. Retrying "
247 std::move(msg), std::chrono::milliseconds(retry_after_s * 1000));
252 auto request = std::make_unique<curl::CurlRequest>(
253 std::move(curl_handle),
258 std::make_unique<ccf::curl::ResponseBody>(
259 endpoint.max_client_response_size),
260 std::move(response_callback));
263 auto msg = std::make_unique<
265 [](std::unique_ptr<::threading::Tmsg<QuoteEndorsementsClientTimeoutMsg>>
267 std::lock_guard<ccf::pal::Mutex> guard(msg->data.self->lock);
268 if (msg->data.self->has_completed)
272 if (msg->data.request_id >= msg->data.self->last_submitted_request_id)
274 auto& servers = msg->data.self->config.servers;
279 throw std::logic_error(
280 "No server specified to fetch endorsements");
283 msg->data.self->server_retries_count++;
285 msg->data.self->server_retries_count >=
286 max_retries_count(servers.front()))
288 if (servers.size() > 1)
295 auto& server = servers.front();
297 "Giving up retrying fetching attestation endorsements from "
298 "{} after {} attempts",
300 server.front().max_retries_count);
302 "Timed out fetching attestation endorsements from all "
303 "configured servers");
308 msg->data.self->fetch(servers.front());
317 std::chrono::milliseconds(server_connection_timeout_s * 1000));
320 "Fetching endorsements for attestation report at {}",
329 const std::shared_ptr<RPCSessions>& rpcsessions_,
332 rpcsessions(rpcsessions_),
338 std::lock_guard<ccf::pal::Mutex> guard(this->lock);
339 auto const& server = config.
servers.front();
342 throw std::logic_error(
"No server specified to fetch endorsements");