39 :
public std::enable_shared_from_this<QuoteEndorsementsClient>
47 static constexpr size_t server_connection_timeout_s = 3;
48 static constexpr size_t server_response_timeout_s = 3;
53 std::vector<uint8_t> endorsements_pem;
58 std::list<Server> servers;
59 size_t server_retries_count = 0;
60 size_t total_retries_count = 0;
62 struct QuoteEndorsementsClientMsg
64 QuoteEndorsementsClientMsg(
65 std::shared_ptr<QuoteEndorsementsClient> self_,
Server server_) :
66 self(std::move(self_)),
67 server(std::move(server_))
70 std::shared_ptr<QuoteEndorsementsClient> self;
74 void handle_success_response_unsafe(std::vector<uint8_t>&& data)
76 auto& server = servers.front();
81 auto endpoint = server.front();
83 if (endpoint.response_is_der)
86 endorsements_pem.insert(endorsements_pem.end(), raw.begin(), raw.end());
88 else if (endpoint.response_is_thim_json)
90 auto j = nlohmann::json::parse(data);
91 auto vcekCert = j.at(
"vcekCert").get<std::string>();
92 auto certificateChain = j.at(
"certificateChain").get<std::string>();
93 endorsements_pem.insert(
94 endorsements_pem.end(), vcekCert.begin(), vcekCert.end());
95 endorsements_pem.insert(
96 endorsements_pem.end(),
97 certificateChain.begin(),
98 certificateChain.end());
102 endorsements_pem.insert(
103 endorsements_pem.end(), data.begin(), data.end());
111 LOG_INFO_FMT(
"Complete endorsement chain successfully retrieved");
113 "{}", std::string(endorsements_pem.begin(), endorsements_pem.end()));
114 done_cb(std::move(endorsements_pem));
122 std::string get_formatted_query(
123 const std::map<std::string, std::string> params)
const
125 std::string formatted_query;
127 for (
const auto& it : params)
130 fmt::format(
"{}{}={}", (first ?
'?' :
'&'), it.first, it.second);
133 return formatted_query;
138 std::lock_guard<ccf::pal::Mutex> guard(this->lock);
144 std::shared_ptr<QuoteEndorsementsClient> self;
145 std::unique_ptr<curl::CurlRequest> request;
146 CURLcode curl_response;
150 std::shared_ptr<QuoteEndorsementsClient> self_,
151 std::unique_ptr<curl::CurlRequest>&& request_,
152 CURLcode curl_response_,
154 self(std::move(self_)),
155 request(std::move(request_)),
156 curl_response(curl_response_),
157 status_code(status_code_)
160 void do_task_implementation()
override
162 std::lock_guard<ccf::pal::Mutex> guard(self->lock);
164 auto* response_body = request->get_response_body();
165 const auto& response_headers = request->get_response_headers();
167 if (curl_response == CURLE_OK && status_code == HTTP_STATUS_OK)
170 "Successfully retrieved endorsements for attestation report: "
172 response_body->buffer.size());
174 self->handle_success_response_unsafe(
175 std::move(response_body->buffer));
180 "Error fetching endorsements for attestation report: {} ({}) {}",
181 curl_easy_strerror(curl_response),
186 self->server_retries_count >=
187 max_retries_count(self->servers.front()))
189 self->servers.pop_front();
191 if (self->servers.empty())
193 auto servers_tried = std::accumulate(
194 self->config.servers.begin(),
195 self->config.servers.end(),
197 [](
const std::string& a,
const Server& b) {
198 return a + (!a.empty() ?
", " :
"") + b.front().host;
201 "Giving up retrying fetching attestation endorsements from [{}] "
204 self->total_retries_count);
206 "Timed out fetching attestation endorsements from all "
207 "configured servers");
210 self->server_retries_count = 0;
211 self->fetch_unsafe();
215 ++self->server_retries_count;
216 ++self->total_retries_count;
218 const auto& endpoint = self->servers.front().front();
220 constexpr size_t default_retry_after_s = 3;
221 size_t retry_after_s = default_retry_after_s;
223 curl_response == CURLE_OK &&
224 status_code == HTTP_STATUS_TOO_MANY_REQUESTS)
226 auto h = response_headers.find(http::headers::RETRY_AFTER);
227 if (h != response_headers.end())
229 const auto& retry_after_value = h->second;
232 retry_after_value.data(),
233 retry_after_value.data() + retry_after_value.size(),
238 "{} endorsements endpoint had too many requests. Retrying "
246 "{} endorsements endpoint failed to respond. Retrying "
252 const std::chrono::seconds retry_after(retry_after_s);
256 [self = this->self]() { self->fetch(); }),
261 [[nodiscard]]
const std::string& get_name()
const override
263 static const std::string name =
264 "QuoteEndorsementsClient::HandleResponseTask";
271 const auto& server = servers.front();
272 const auto& endpoint = server.front();
277 curl_handle.
set_opt(CURLOPT_HTTPGET, 1L);
279 curl_handle.
set_opt(CURLOPT_CONNECTTIMEOUT, server_connection_timeout_s);
281 curl_handle.
set_opt(CURLOPT_TIMEOUT, server_response_timeout_s);
283 auto url = fmt::format(
285 endpoint.tls ?
"https" :
"http",
289 get_formatted_query(endpoint.params));
297 curl_handle.
set_opt(CURLOPT_SSL_VERIFYHOST, 0L);
298 curl_handle.
set_opt(CURLOPT_SSL_VERIFYPEER, 0L);
299 curl_handle.
set_opt(CURLOPT_SSL_VERIFYSTATUS, 0L);
303 for (
auto const& [k, v] : endpoint.headers)
305 headers.append(k, v);
307 headers.append(http::headers::HOST, endpoint.host);
309 auto response_callback = ([self = shared_from_this()](
310 std::unique_ptr<curl::CurlRequest>&& request,
311 CURLcode curl_response,
313 std::shared_ptr<HandleResponseTask> response_task =
314 std::make_shared<HandleResponseTask>(
315 self, std::move(request), curl_response, status_code);
319 auto request = std::make_unique<curl::CurlRequest>(
320 std::move(curl_handle),
325 std::make_unique<ccf::curl::ResponseBody>(
326 endpoint.max_client_response_size),
327 std::move(response_callback));
330 "Fetching endorsements for attestation report at {}",
340 config(
std::move(config_)),
341 done_cb(
std::move(cb)) {};
345 std::lock_guard<ccf::pal::Mutex> guard(this->lock);
346 servers = std::list<Server>(config.
servers);
347 server_retries_count = 0;