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
3444CurlHttpClient::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
4858CurlHttpClient::~CurlHttpClient () {
@@ -52,17 +62,12 @@ CurlHttpClient::~CurlHttpClient() {
5262std::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
6873std::shared_ptr<HttpFuture> CurlHttpClient::getAsync (const std::string &url, int timeout_ms) {
@@ -98,33 +103,33 @@ std::shared_ptr<HttpFuture> CurlHttpClient::deleteAsync(
98103}
99104
100105void 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
105110void 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
110115bool CurlHttpClient::isReady () const {
111- return initialized_ && !shutdown_requested_ ;
116+ return mInitialized && !mShutdownRequested ;
112117}
113118
114119bool CurlHttpClient::initialize () {
115120 // Already initialized in constructor
116- return initialized_ ;
121+ return mInitialized ;
117122}
118123
119124void 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
136141void 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 ) {
0 commit comments