Skip to content

Commit f7a2490

Browse files
committed
Extend error handling.
1 parent ae52c2c commit f7a2490

3 files changed

Lines changed: 92 additions & 51 deletions

File tree

libs/internal/src/network/curl_requester.cpp

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -103,35 +103,51 @@ void CurlRequester::PerformRequestStatic(net::any_io_executor ctx, TlsOptions co
103103
// Use a unique_ptr to manage the cleanup of our curl instance.
104104
std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl_guard(curl, curl_easy_cleanup);
105105

106+
// Helper macro to check curl_easy_setopt return values
107+
// Note: This macro captures ctx and cb by reference for error reporting
108+
#define CURL_SETOPT_CHECK(handle, option, parameter) \
109+
do { \
110+
CURLcode code = curl_easy_setopt(handle, option, parameter); \
111+
if (code != CURLE_OK) { \
112+
std::string error_message = kErrorCurlPrefix; \
113+
error_message += "curl_easy_setopt failed for " #option ": "; \
114+
error_message += curl_easy_strerror(code); \
115+
boost::asio::post(ctx, [cb = std::move(cb), error_message = std::move(error_message)]() { \
116+
cb(HttpResult(error_message)); \
117+
}); \
118+
return; \
119+
} \
120+
} while(0)
121+
106122
std::string response_body;
107123
HttpResult::HeadersType response_headers;
108124

109125
// Store URL to keep it alive for the duration of the request
110126
std::string url = request.Url();
111127

112128
// Set URL
113-
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
129+
CURL_SETOPT_CHECK(curl, CURLOPT_URL, url.c_str());
114130

115131
// Set HTTP method
116132
if (request.Method() == HttpMethod::kPost) {
117133
// Basically CURLOPT_POST is a flag that indicates this is a post.
118134
// Passing 1 enables this flag.
119135
// This will also set a content type, but the headers for the request
120136
// should override that with the correct value.
121-
curl_easy_setopt(curl, CURLOPT_POST, 1L);
137+
CURL_SETOPT_CHECK(curl, CURLOPT_POST, 1L);
122138
} else if (request.Method() == HttpMethod::kPut) {
123-
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, kHttpMethodPut);
139+
CURL_SETOPT_CHECK(curl, CURLOPT_CUSTOMREQUEST, kHttpMethodPut);
124140
} else if (request.Method() == HttpMethod::kReport) {
125-
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, kHttpMethodReport);
141+
CURL_SETOPT_CHECK(curl, CURLOPT_CUSTOMREQUEST, kHttpMethodReport);
126142
} else if (request.Method() == HttpMethod::kGet) {
127-
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
143+
CURL_SETOPT_CHECK(curl, CURLOPT_HTTPGET, 1L);
128144
}
129145

130146
// Set request body if present
131147
if (request.Body().has_value()) {
132148
const std::string& body = request.Body().value();
133-
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
134-
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size());
149+
CURL_SETOPT_CHECK(curl, CURLOPT_POSTFIELDS, body.c_str());
150+
CURL_SETOPT_CHECK(curl, CURLOPT_POSTFIELDSIZE, body.size());
135151
}
136152

137153
// Set headers
@@ -156,31 +172,31 @@ void CurlRequester::PerformRequestStatic(net::any_io_executor ctx, TlsOptions co
156172
headers = appendResult;
157173
}
158174
if (headers) {
159-
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
175+
CURL_SETOPT_CHECK(curl, CURLOPT_HTTPHEADER, headers);
160176
}
161177

162178
// Set timeouts with millisecond precision
163179
const long connect_timeout_ms = request.Properties().ConnectTimeout().count();
164180
const long response_timeout_ms = request.Properties().ResponseTimeout().count();
165181

166-
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, connect_timeout_ms > 0 ? connect_timeout_ms : 30000L);
167-
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, response_timeout_ms > 0 ? response_timeout_ms : 60000L);
182+
CURL_SETOPT_CHECK(curl, CURLOPT_CONNECTTIMEOUT_MS, connect_timeout_ms > 0 ? connect_timeout_ms : 30000L);
183+
CURL_SETOPT_CHECK(curl, CURLOPT_TIMEOUT_MS, response_timeout_ms > 0 ? response_timeout_ms : 60000L);
168184

169185
// Set TLS options
170186
using VerifyMode = config::shared::built::TlsOptions::VerifyMode;
171187
if (tls_options.PeerVerifyMode() == VerifyMode::kVerifyNone) {
172-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
173-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
188+
CURL_SETOPT_CHECK(curl, CURLOPT_SSL_VERIFYPEER, 0L);
189+
CURL_SETOPT_CHECK(curl, CURLOPT_SSL_VERIFYHOST, 0L);
174190
} else {
175-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
191+
CURL_SETOPT_CHECK(curl, CURLOPT_SSL_VERIFYPEER, 1L);
176192
// 1 or 2 seem to basically be the same, but the documentation says to
177193
// use 2, and that it would default to 2.
178194
// https://curl.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html
179-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
195+
CURL_SETOPT_CHECK(curl, CURLOPT_SSL_VERIFYHOST, 2L);
180196

181197
// Set custom CA file if provided
182198
if (tls_options.CustomCAFile().has_value()) {
183-
curl_easy_setopt(curl, CURLOPT_CAINFO, tls_options.CustomCAFile()->c_str());
199+
CURL_SETOPT_CHECK(curl, CURLOPT_CAINFO, tls_options.CustomCAFile()->c_str());
184200
}
185201
}
186202

@@ -189,19 +205,19 @@ void CurlRequester::PerformRequestStatic(net::any_io_executor ctx, TlsOptions co
189205
// Empty string explicitly disables proxy (overrides environment variables).
190206
auto const& proxy_url = request.Properties().Proxy().Url();
191207
if (proxy_url.has_value()) {
192-
curl_easy_setopt(curl, CURLOPT_PROXY, proxy_url->c_str());
208+
CURL_SETOPT_CHECK(curl, CURLOPT_PROXY, proxy_url->c_str());
193209
}
194210
// If proxy URL is std::nullopt, CURL will use environment variables (default behavior)
195211

196212
// Set callbacks
197-
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
198-
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body);
199-
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
200-
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response_headers);
213+
CURL_SETOPT_CHECK(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
214+
CURL_SETOPT_CHECK(curl, CURLOPT_WRITEDATA, &response_body);
215+
CURL_SETOPT_CHECK(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
216+
CURL_SETOPT_CHECK(curl, CURLOPT_HEADERDATA, &response_headers);
201217

202218
// Follow redirects
203-
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
204-
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 20L);
219+
CURL_SETOPT_CHECK(curl, CURLOPT_FOLLOWLOCATION, 1L);
220+
CURL_SETOPT_CHECK(curl, CURLOPT_MAXREDIRS, 20L);
205221

206222
// Perform the request
207223
CURLcode res = curl_easy_perform(curl);

libs/server-sent-events/src/curl_client.cpp

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -161,30 +161,42 @@ std::string CurlClient::build_url() const {
161161
return url;
162162
}
163163

164-
struct curl_slist* CurlClient::setup_curl_options(CURL* curl) {
164+
bool CurlClient::setup_curl_options(CURL* curl, struct curl_slist** out_headers) {
165+
// Helper macro to check curl_easy_setopt return values
166+
// Returns false on error to signal setup failure
167+
#define CURL_SETOPT_CHECK(handle, option, parameter) \
168+
do { \
169+
CURLcode code = curl_easy_setopt(handle, option, parameter); \
170+
if (code != CURLE_OK) { \
171+
log_message("curl_easy_setopt failed for " #option ": " + \
172+
std::string(curl_easy_strerror(code))); \
173+
return false; \
174+
} \
175+
} while(0)
176+
165177
// Set URL
166-
curl_easy_setopt(curl, CURLOPT_URL, build_url().c_str());
178+
CURL_SETOPT_CHECK(curl, CURLOPT_URL, build_url().c_str());
167179

168180
// Set HTTP method
169181
switch (req_.method()) {
170182
case http::verb::get:
171-
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
183+
CURL_SETOPT_CHECK(curl, CURLOPT_HTTPGET, 1L);
172184
break;
173185
case http::verb::post:
174-
curl_easy_setopt(curl, CURLOPT_POST, 1L);
186+
CURL_SETOPT_CHECK(curl, CURLOPT_POST, 1L);
175187
break;
176188
case http::verb::report:
177-
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "REPORT");
189+
CURL_SETOPT_CHECK(curl, CURLOPT_CUSTOMREQUEST, "REPORT");
178190
break;
179191
default:
180-
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
192+
CURL_SETOPT_CHECK(curl, CURLOPT_HTTPGET, 1L);
181193
break;
182194
}
183195

184196
// Set request body if present
185197
if (!req_.body().empty()) {
186-
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req_.body().c_str());
187-
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req_.body().size());
198+
CURL_SETOPT_CHECK(curl, CURLOPT_POSTFIELDS, req_.body().c_str());
199+
CURL_SETOPT_CHECK(curl, CURLOPT_POSTFIELDSIZE, req_.body().size());
188200
}
189201

190202
// Set headers
@@ -202,57 +214,60 @@ struct curl_slist* CurlClient::setup_curl_options(CURL* curl) {
202214
}
203215

204216
if (headers) {
205-
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
217+
CURL_SETOPT_CHECK(curl, CURLOPT_HTTPHEADER, headers);
206218
}
207219

208220
// Set timeouts with millisecond precision
209221
if (connect_timeout_) {
210-
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, connect_timeout_->count());
222+
CURL_SETOPT_CHECK(curl, CURLOPT_CONNECTTIMEOUT_MS, connect_timeout_->count());
211223
}
212224

213225
// For read timeout, use progress callback
214226
if (read_timeout_) {
215227
effective_read_timeout_ = read_timeout_;
216228
last_progress_time_ = std::chrono::steady_clock::now();
217229
last_download_amount_ = 0;
218-
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback);
219-
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this);
220-
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
230+
CURL_SETOPT_CHECK(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback);
231+
CURL_SETOPT_CHECK(curl, CURLOPT_XFERINFODATA, this);
232+
CURL_SETOPT_CHECK(curl, CURLOPT_NOPROGRESS, 0L);
221233
}
222234

223235
// Set TLS options
224236
if (skip_verify_peer_) {
225-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
226-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
237+
CURL_SETOPT_CHECK(curl, CURLOPT_SSL_VERIFYPEER, 0L);
238+
CURL_SETOPT_CHECK(curl, CURLOPT_SSL_VERIFYHOST, 0L);
227239
} else {
228-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
229-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
240+
CURL_SETOPT_CHECK(curl, CURLOPT_SSL_VERIFYPEER, 1L);
241+
CURL_SETOPT_CHECK(curl, CURLOPT_SSL_VERIFYHOST, 2L);
230242

231243
if (custom_ca_file_) {
232-
curl_easy_setopt(curl, CURLOPT_CAINFO, custom_ca_file_->c_str());
244+
CURL_SETOPT_CHECK(curl, CURLOPT_CAINFO, custom_ca_file_->c_str());
233245
}
234246
}
235247

236248
// Set proxy if configured
237249
// When proxy_url_ is set, it takes precedence over environment variables.
238250
// Empty string explicitly disables proxy (overrides environment variables).
239251
if (proxy_url_) {
240-
curl_easy_setopt(curl, CURLOPT_PROXY, proxy_url_->c_str());
252+
CURL_SETOPT_CHECK(curl, CURLOPT_PROXY, proxy_url_->c_str());
241253
}
242254
// If proxy_url_ is std::nullopt, CURL will use environment variables (default behavior)
243255

244256
// Set callbacks
245-
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
246-
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
247-
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
248-
curl_easy_setopt(curl, CURLOPT_HEADERDATA, this);
249-
curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, OpenSocketCallback);
250-
curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, this);
257+
CURL_SETOPT_CHECK(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
258+
CURL_SETOPT_CHECK(curl, CURLOPT_WRITEDATA, this);
259+
CURL_SETOPT_CHECK(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
260+
CURL_SETOPT_CHECK(curl, CURLOPT_HEADERDATA, this);
261+
CURL_SETOPT_CHECK(curl, CURLOPT_OPENSOCKETFUNCTION, OpenSocketCallback);
262+
CURL_SETOPT_CHECK(curl, CURLOPT_OPENSOCKETDATA, this);
251263

252264
// Don't follow redirects automatically - we'll handle them ourselves
253-
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L);
265+
CURL_SETOPT_CHECK(curl, CURLOPT_FOLLOWLOCATION, 0L);
266+
267+
#undef CURL_SETOPT_CHECK
254268

255-
return headers;
269+
*out_headers = headers;
270+
return true;
256271
}
257272

258273
int CurlClient::ProgressCallback(void* clientp, curl_off_t dltotal, curl_off_t dlnow,
@@ -530,7 +545,17 @@ void CurlClient::perform_request() {
530545
return;
531546
}
532547

533-
struct curl_slist* headers = setup_curl_options(curl);
548+
struct curl_slist* headers = nullptr;
549+
if (!setup_curl_options(curl, &headers)) {
550+
// setup_curl_options returned false, indicating an error (it already logged the error)
551+
curl_easy_cleanup(curl);
552+
if (!shutting_down_) {
553+
boost::asio::post(backoff_timer_.get_executor(), [self = shared_from_this()]() {
554+
self->async_backoff("failed to set CURL options");
555+
});
556+
}
557+
return;
558+
}
534559

535560
// Perform the request
536561
CURLcode res = curl_easy_perform(curl);

libs/server-sent-events/src/curl_client.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class CurlClient : public Client,
6363
void report_error(Error error);
6464

6565
std::string build_url() const;
66-
struct curl_slist* setup_curl_options(CURL* curl);
66+
bool setup_curl_options(CURL* curl, struct curl_slist** headers);
6767
bool handle_redirect(long response_code, CURL* curl);
6868

6969
static int ProgressCallback(void* clientp, curl_off_t dltotal, curl_off_t dlnow,

0 commit comments

Comments
 (0)