Skip to content

Commit 92e1f7e

Browse files
author
MatthewColvin
committed
Tweak CurlHttpclient to match style
1 parent c16e7ac commit 92e1f7e

2 files changed

Lines changed: 62 additions & 76 deletions

File tree

Platformio/lib/SimulatorHalImpl/wifiHandlerSim/CurlHttpClient.cpp

Lines changed: 47 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,55 @@
44
#include <sstream>
55

66
// Callback for curl to write response data
7-
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, std::string *userp) {
8-
userp->append((char *)contents, size * nmemb);
9-
return size * nmemb;
7+
static size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp) {
8+
size_t realsize = size * nmemb;
9+
if (userp) {
10+
std::string *str = static_cast<std::string *>(userp);
11+
str->append(contents, realsize);
12+
}
13+
return realsize;
1014
}
1115

1216
// Callback for curl to write response headers
13-
static size_t HeaderCallback(char *buffer, size_t size, size_t nmemb, std::map<std::string, std::string> *userp) {
17+
static size_t HeaderCallback(char *buffer, size_t size, size_t nmemb, void *userp) {
1418
size_t realsize = size * nmemb;
1519
std::string header(buffer, realsize);
1620

1721
// Parse header line (format: "Header-Name: value\r\n")
1822
size_t colon_pos = header.find(':');
19-
if (colon_pos != std::string::npos) {
23+
if (colon_pos != std::string::npos && userp) {
2024
std::string key = header.substr(0, colon_pos);
21-
std::string value = header.substr(colon_pos + 2);
25+
// allow for ": " after key but be defensive
26+
size_t value_start = colon_pos + 1;
27+
if (value_start < header.size() && header[value_start] == ' ') {
28+
++value_start;
29+
}
30+
std::string value = header.substr(value_start);
2231

2332
// Trim trailing whitespace/CRLF
2433
while (!value.empty() && (value.back() == '\r' || value.back() == '\n' || value.back() == ' ')) {
2534
value.pop_back();
2635
}
2736

28-
userp->emplace(key, value);
37+
auto *mapPtr = static_cast<std::map<std::string, std::string> *>(userp);
38+
mapPtr->emplace(key, value);
2939
}
3040

3141
return realsize;
3242
}
3343

3444
CurlHttpClient::CurlHttpClient(size_t num_threads)
35-
: shutdown_requested_(false), initialized_(false) {
45+
: mShutdownRequested(false), mInitialized(false) {
3646

3747
// Initialize curl globally
3848
curl_global_init(CURL_GLOBAL_DEFAULT);
3949

4050
// Start worker threads
4151
for (size_t i = 0; i < num_threads; ++i) {
42-
worker_threads_.emplace_back(&CurlHttpClient::workerThread, this);
52+
mWorkerThreads.emplace_back(&CurlHttpClient::workerThread, this);
4353
}
4454

45-
initialized_ = true;
55+
mInitialized = true;
4656
}
4757

4858
CurlHttpClient::~CurlHttpClient() {
@@ -52,17 +62,12 @@ CurlHttpClient::~CurlHttpClient() {
5262
std::shared_ptr<HttpFuture> CurlHttpClient::executeAsync(const HttpRequest &request) {
5363
auto promise = std::make_shared<std::promise<HttpResponse>>();
5464
auto future = promise->get_future();
55-
5665
{
57-
std::lock_guard<std::mutex> lock(queue_mutex_);
58-
request_queue_.push(request);
59-
promise_queue_.push(std::move(*promise));
66+
std::lock_guard<std::mutex> lock(mQueueMutex);
67+
mRequestQueue.emplace(request, promise);
6068
}
61-
62-
queue_cv_.notify_one();
63-
64-
auto http_future = std::make_shared<HttpFuture>(std::move(future));
65-
return http_future;
69+
mQueueCv.notify_one();
70+
return std::make_shared<HttpFuture>(std::move(future));
6671
}
6772

6873
std::shared_ptr<HttpFuture> CurlHttpClient::getAsync(const std::string &url, int timeout_ms) {
@@ -98,33 +103,33 @@ std::shared_ptr<HttpFuture> CurlHttpClient::deleteAsync(
98103
}
99104

100105
void CurlHttpClient::setDefaultHeader(const std::string &key, const std::string &value) {
101-
std::lock_guard<std::mutex> lock(headers_mutex_);
106+
std::lock_guard<std::mutex> lock(mHeadersMutex);
102107
default_headers_[key] = value;
103108
}
104109

105110
void CurlHttpClient::clearDefaultHeaders() {
106-
std::lock_guard<std::mutex> lock(headers_mutex_);
111+
std::lock_guard<std::mutex> lock(mHeadersMutex);
107112
default_headers_.clear();
108113
}
109114

110115
bool CurlHttpClient::isReady() const {
111-
return initialized_ && !shutdown_requested_;
116+
return mInitialized && !mShutdownRequested;
112117
}
113118

114119
bool CurlHttpClient::initialize() {
115120
// Already initialized in constructor
116-
return initialized_;
121+
return mInitialized;
117122
}
118123

119124
void CurlHttpClient::shutdown() {
120125
{
121-
std::lock_guard<std::mutex> lock(queue_mutex_);
122-
shutdown_requested_ = true;
126+
std::lock_guard<std::mutex> lock(mQueueMutex);
127+
mShutdownRequested = true;
123128
}
124-
queue_cv_.notify_all();
129+
mQueueCv.notify_all();
125130

126131
// Wait for all worker threads to finish
127-
for (auto &thread : worker_threads_) {
132+
for (auto &thread : mWorkerThreads) {
128133
if (thread.joinable()) {
129134
thread.join();
130135
}
@@ -135,39 +140,22 @@ void CurlHttpClient::shutdown() {
135140

136141
void CurlHttpClient::workerThread() {
137142
while (true) {
138-
std::unique_lock<std::mutex> lock(queue_mutex_);
139-
143+
std::unique_lock<std::mutex> lock(mQueueMutex);
140144
// Wait for a request or shutdown signal
141-
queue_cv_.wait(lock, [this]() {
142-
return !request_queue_.empty() || shutdown_requested_;
145+
mQueueCv.wait(lock, [this]() {
146+
return !mRequestQueue.empty() || mShutdownRequested;
143147
});
144-
145-
if (shutdown_requested_ && request_queue_.empty()) {
148+
if (mShutdownRequested && mRequestQueue.empty()) {
146149
break;
147150
}
148-
149-
if (request_queue_.empty()) {
150-
continue;
151-
}
152-
153-
// Get the next request and promise pair
154-
HttpRequest request = std::move(request_queue_.front());
155-
request_queue_.pop();
156-
std::promise<HttpResponse> promise = std::move(promise_queue_.front());
157-
promise_queue_.pop();
151+
// Get the request to process
152+
auto [request, promise] = mRequestQueue.front();
153+
mRequestQueue.pop();
158154
lock.unlock();
159155

160-
// Execute the request
161-
try {
162-
HttpResponse response = executeSyncRequest(request);
163-
promise.set_value(response);
164-
} catch (const std::exception &e) {
165-
HttpResponse error_response(
166-
-1,
167-
"",
168-
std::string("Error: ") + e.what());
169-
error_response.success = false;
170-
promise.set_value(error_response);
156+
HttpResponse response = executeSyncRequest(request);
157+
if (auto sPromise = promise.lock(); sPromise) {
158+
sPromise->set_value(response);
171159
}
172160
}
173161
}
@@ -188,8 +176,12 @@ HttpResponse CurlHttpClient::executeSyncRequest(const HttpRequest &request) {
188176
// Set URL
189177
curl_easy_setopt(curl, CURLOPT_URL, request.url.c_str());
190178

191-
// Set timeout
179+
// Set timeouts (connection and total transfer)
180+
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, (long)request.timeout_ms);
192181
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, (long)request.timeout_ms);
182+
// In multithreaded programs, libcurl should not install or use signals.
183+
// Prevent use of signals (like SIGALRM) which can interfere with threads.
184+
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
193185

194186
// Set HTTP method
195187
switch (request.method) {

Platformio/lib/SimulatorHalImpl/wifiHandlerSim/CurlHttpClient.hpp

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,15 @@
1010
/**
1111
* @brief Curl-based HTTP client for desktop simulator
1212
*
13-
* This implementation uses libcurl to provide HTTP functionality
14-
* on desktop platforms. Requests are executed asynchronously using
15-
* a thread pool pattern with custom HttpFuture for fluent callbacks.
13+
* Uses a thread pool and queue to handle asynchronous HTTP requests
14+
* via libcurl. Implements HttpClientInterface with custom HttpFuture
15+
* for fluent callback handling.
1616
*/
1717
class CurlHttpClient : public HttpClientInterface {
1818
public:
19-
/**
20-
* @brief Constructor
21-
*
22-
* @param num_threads Number of worker threads for async requests (default: 2)
23-
*/
24-
explicit CurlHttpClient(size_t num_threads = 2);
19+
using QueueRequestItemType = std::pair<HttpRequest, std::weak_ptr<std::promise<HttpResponse>>>;
2520

26-
/**
27-
* @brief Destructor - ensures proper cleanup
28-
*/
21+
explicit CurlHttpClient(size_t aNumWorkerThreads = 2);
2922
~CurlHttpClient() override;
3023

3124
// Prevent copy operations
@@ -80,18 +73,19 @@ class CurlHttpClient : public HttpClientInterface {
8073
*/
8174
static std::string methodToString(HttpRequest::Method method);
8275

83-
// Thread pool management
84-
std::vector<std::thread> worker_threads_;
85-
std::queue<HttpRequest> request_queue_;
86-
std::queue<std::promise<HttpResponse>> promise_queue_;
87-
mutable std::mutex queue_mutex_;
88-
std::condition_variable queue_cv_;
89-
bool shutdown_requested_ = false;
76+
// Thread pool for executing requests
77+
std::vector<std::thread> mWorkerThreads;
78+
79+
// Queue for thread-safe non-blocking request handling
80+
mutable std::mutex mQueueMutex;
81+
std::condition_variable mQueueCv;
82+
std::queue<QueueRequestItemType> mRequestQueue;
9083

9184
// Default headers
9285
std::map<std::string, std::string> default_headers_;
93-
mutable std::mutex headers_mutex_;
86+
mutable std::mutex mHeadersMutex;
9487

9588
// State
96-
bool initialized_ = false;
89+
bool mInitialized = false;
90+
bool mShutdownRequested = false;
9791
};

0 commit comments

Comments
 (0)