Skip to content

Commit 7468857

Browse files
committed
[accless] E: Thread-Local HTTP Clients
1 parent 1d5d885 commit 7468857

File tree

5 files changed

+148
-91
lines changed

5 files changed

+148
-91
lines changed

accless/libs/attestation/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ set(AZ_GUEST_ATTESTATION_INCLUDE_DIRS
1919
add_library(${CMAKE_PROJECT_TARGET}
2020
attestation.cpp
2121
ec_keypair.cpp
22+
http_client.cpp
2223
mock.cpp
2324
mock_sgx.cpp
2425
mock_snp.cpp

accless/libs/attestation/attestation.cpp

Lines changed: 4 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -10,61 +10,11 @@
1010
#include <vector>
1111

1212
namespace accless::attestation {
13-
// Must match the signature libcurl expects
14-
static size_t curlWriteCallback(char *ptr, size_t size, size_t nmemb,
15-
void *userdata) {
16-
auto *out = static_cast<std::string *>(userdata);
17-
if (!out) {
18-
return 0; // tells libcurl this is an error
19-
}
20-
21-
const size_t total = size * nmemb;
22-
out->append(ptr, total);
23-
return total;
24-
}
25-
2613
// Helper for GET requests
2714
static std::string http_get(const std::string &url,
2815
const std::string &certPath) {
29-
CURL *curl = curl_easy_init();
30-
if (curl == nullptr) {
31-
throw std::runtime_error("accless(att): failed to init curl");
32-
}
33-
34-
char errbuf[CURL_ERROR_SIZE];
35-
errbuf[0] = 0;
36-
37-
std::string response;
38-
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
39-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
40-
curl_easy_setopt(curl, CURLOPT_CAINFO, certPath.c_str());
41-
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
42-
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
43-
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
44-
45-
CURLcode res = curl_easy_perform(curl);
46-
long status = 0;
47-
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
48-
curl_easy_cleanup(curl);
49-
50-
if (res != CURLE_OK) {
51-
size_t len = strlen(errbuf);
52-
fprintf(stderr, "accless(att): curl error: ");
53-
if (len) {
54-
fprintf(stderr, "%s%s", errbuf,
55-
((errbuf[len - 1] != '\n') ? "\n" : ""));
56-
} else {
57-
fprintf(stderr, "%s\n", curl_easy_strerror(res));
58-
}
59-
throw std::runtime_error("accless(att): curl GET error");
60-
}
61-
if (status != 200) {
62-
throw std::runtime_error(
63-
"accless(att): GET request failed with status " +
64-
std::to_string(status));
65-
}
66-
67-
return response;
16+
auto &client = http::getHttpClient(certPath);
17+
return client.get(url);
6818
}
6919

7020
std::pair<std::string, std::string>
@@ -86,45 +36,9 @@ std::string getJwtFromReport(const std::string &asUrl,
8636
const std::string &certPath,
8737
const std::string &endpoint,
8838
const std::string &reportJson) {
89-
std::string jwt;
90-
91-
CURL *curl = curl_easy_init();
92-
if (!curl) {
93-
std::cerr << "accless: failed to initialize CURL" << std::endl;
94-
throw std::runtime_error("curl error");
95-
}
96-
9739
std::string url = asUrl + endpoint;
98-
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
99-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
100-
curl_easy_setopt(curl, CURLOPT_CAINFO, certPath.c_str());
101-
curl_easy_setopt(curl, CURLOPT_POST, 1L);
102-
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, reportJson.c_str());
103-
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
104-
static_cast<long>(reportJson.size()));
105-
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
106-
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &jwt);
107-
108-
// TODO: set error-buffer in C++ format
109-
110-
struct curl_slist *headers = nullptr;
111-
headers = curl_slist_append(headers, "Content-Type: application/json");
112-
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
113-
114-
// Perform the request
115-
CURLcode res = curl_easy_perform(curl);
116-
if (res != CURLE_OK) {
117-
std::cerr << "accless: CURL error: " << curl_easy_strerror(res)
118-
<< std::endl;
119-
curl_easy_cleanup(curl);
120-
curl_slist_free_all(headers);
121-
throw std::runtime_error("curl error");
122-
}
123-
124-
curl_easy_cleanup(curl);
125-
curl_slist_free_all(headers);
126-
127-
return jwt;
40+
auto &client = http::getHttpClient(certPath);
41+
return client.postJson(url, reportJson);
12842
}
12943

13044
std::string decryptJwt(const std::vector<uint8_t> &encrypted,

accless/libs/attestation/attestation.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#include <array>
77
#include <cstdint>
8+
#include <curl/curl.h>
9+
#include <memory>
810
#include <optional>
911
#include <string>
1012
#include <vector>
@@ -42,6 +44,29 @@ std::string buildRequestBody(const std::string &quoteB64,
4244
const std::string &nodeId);
4345
} // namespace utils
4446

47+
// Helper methods around a thread-local, re-usable HTTP client.
48+
namespace http {
49+
class HttpClient {
50+
public:
51+
explicit HttpClient(const std::string &certPath);
52+
~HttpClient();
53+
54+
std::string get(const std::string &url);
55+
std::string postJson(const std::string &url, const std::string &body);
56+
57+
private:
58+
CURL *curl_{nullptr};
59+
std::string certPath_;
60+
std::string response_;
61+
char errbuf_[CURL_ERROR_SIZE];
62+
63+
void prepareRequest();
64+
void perform();
65+
};
66+
67+
HttpClient &getHttpClient(const std::string &certPath);
68+
} // namespace http
69+
4570
// Mock helpers used in integration tests.
4671
namespace mock {
4772
std::string getMockSgxAttestationJwt(const std::string &asUrl,
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#include "attestation.h"
2+
3+
#include <cstring>
4+
#include <curl/curl.h>
5+
6+
namespace accless::attestation::http {
7+
// Must match the signature libcurl expects
8+
static size_t curlWriteCallback(char *ptr, size_t size, size_t nmemb,
9+
void *userdata) {
10+
auto *out = static_cast<std::string *>(userdata);
11+
if (!out) {
12+
return 0; // tells libcurl this is an error
13+
}
14+
15+
const size_t total = size * nmemb;
16+
out->append(ptr, total);
17+
return total;
18+
}
19+
20+
HttpClient::HttpClient(const std::string &certPath) : certPath_(certPath) {
21+
curl_ = curl_easy_init();
22+
if (!curl_) {
23+
throw std::runtime_error("accless(att): failed to init curl");
24+
}
25+
26+
// Set options that don’t change between requests
27+
memset(errbuf_, 0, sizeof(errbuf_));
28+
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 1L);
29+
curl_easy_setopt(curl_, CURLOPT_CAINFO, certPath_.c_str());
30+
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, curlWriteCallback);
31+
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response_);
32+
curl_easy_setopt(curl_, CURLOPT_ERRORBUFFER, errbuf_);
33+
}
34+
35+
HttpClient::~HttpClient() {
36+
if (curl_) {
37+
curl_easy_cleanup(curl_);
38+
}
39+
}
40+
41+
std::string HttpClient::get(const std::string &url) {
42+
prepareRequest();
43+
curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L);
44+
curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
45+
46+
perform();
47+
48+
return response_;
49+
}
50+
51+
std::string HttpClient::postJson(const std::string &url,
52+
const std::string &body) {
53+
prepareRequest();
54+
55+
curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
56+
curl_easy_setopt(curl_, CURLOPT_POST, 1L);
57+
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, body.c_str());
58+
curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE,
59+
static_cast<long>(body.size()));
60+
61+
struct curl_slist *headers = nullptr;
62+
headers = curl_slist_append(headers, "Content-Type: application/json");
63+
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
64+
65+
perform();
66+
67+
curl_slist_free_all(headers);
68+
return response_;
69+
}
70+
71+
void HttpClient::prepareRequest() {
72+
response_.clear();
73+
memset(errbuf_, 0, sizeof(errbuf_));
74+
75+
// Make sure we’re not leaking POST state into a GET or vice versa
76+
curl_easy_setopt(curl_, CURLOPT_HTTPGET, 0L);
77+
curl_easy_setopt(curl_, CURLOPT_POST, 0L);
78+
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, nullptr);
79+
curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, 0L);
80+
81+
// WRITEDATA always points to our response_ string
82+
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response_);
83+
}
84+
85+
void HttpClient::perform() {
86+
CURLcode res = curl_easy_perform(curl_);
87+
long status = 0;
88+
curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &status);
89+
90+
if (res != CURLE_OK) {
91+
const size_t len = std::strlen(errbuf_);
92+
std::string msg = "accless(att): curl error: ";
93+
if (len) {
94+
msg += errbuf_;
95+
} else {
96+
msg += curl_easy_strerror(res);
97+
}
98+
throw std::runtime_error(msg);
99+
}
100+
101+
if (status != 200) {
102+
throw std::runtime_error(
103+
"accless(att): HTTP request failed with status " +
104+
std::to_string(status));
105+
}
106+
}
107+
108+
thread_local std::unique_ptr<HttpClient> tlsClient;
109+
110+
HttpClient &getHttpClient(const std::string &certPath) {
111+
if (!tlsClient) {
112+
tlsClient = std::make_unique<HttpClient>(certPath);
113+
}
114+
115+
return *tlsClient;
116+
}
117+
} // namespace accless::attestation::http

applications/functions/escrow-xput/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ void doBenchmark(const std::vector<int> &numRequests, int numWarmupRepeats,
258258
std::ofstream csvFile(resultsFile, std::ios::out);
259259
csvFile << "NumRequests,TimeElapsed\n";
260260

261-
int maxParallelism = 100;
261+
int maxParallelism = 10;
262262
try {
263263
for (const auto &i : numRequests) {
264264
for (int j = 0; j < numWarmupRepeats; j++) {

0 commit comments

Comments
 (0)