13
13
#include < unordered_map>
14
14
#include < unordered_set>
15
15
16
+ #include " clock.h"
16
17
#include " dict_reader.h"
17
18
#include " dict_writer.h"
18
19
#include " http_client.h"
@@ -95,6 +96,10 @@ CURLcode CurlLibrary::easy_setopt_writefunction(CURL *handle,
95
96
return curl_easy_setopt (handle, CURLOPT_WRITEFUNCTION, on_write);
96
97
}
97
98
99
+ CURLcode CurlLibrary::easy_setopt_timeout_ms (CURL *handle, long timeout_ms) {
100
+ return curl_easy_setopt (handle, CURLOPT_TIMEOUT_MS, timeout_ms);
101
+ }
102
+
98
103
const char *CurlLibrary::easy_strerror (CURLcode error) {
99
104
return curl_easy_strerror (error);
100
105
}
@@ -162,6 +167,7 @@ class CurlImpl {
162
167
std::mutex mutex_;
163
168
CurlLibrary &curl_;
164
169
const std::shared_ptr<Logger> logger_;
170
+ Clock clock_;
165
171
CURLM *multi_handle_;
166
172
std::unordered_set<CURL *> request_handles_;
167
173
std::list<CURL *> new_handles_;
@@ -179,6 +185,7 @@ class CurlImpl {
179
185
char error_buffer[CURL_ERROR_SIZE] = " " ;
180
186
std::unordered_map<std::string, std::string> response_headers_lower;
181
187
std::string response_body;
188
+ std::chrono::steady_clock::time_point deadline;
182
189
183
190
~Request ();
184
191
};
@@ -221,13 +228,14 @@ class CurlImpl {
221
228
static StringView trim (StringView);
222
229
223
230
public:
224
- explicit CurlImpl (const std::shared_ptr<Logger> &, CurlLibrary &,
225
- const Curl::ThreadGenerator &);
231
+ explicit CurlImpl (const std::shared_ptr<Logger> &, const Clock &,
232
+ CurlLibrary &, const Curl::ThreadGenerator &);
226
233
~CurlImpl ();
227
234
228
235
Expected<void > post (const URL &url, HeadersSetter set_headers,
229
236
std::string body, ResponseHandler on_response,
230
- ErrorHandler on_error);
237
+ ErrorHandler on_error,
238
+ std::chrono::steady_clock::time_point deadline);
231
239
232
240
void drain (std::chrono::steady_clock::time_point deadline);
233
241
};
@@ -242,22 +250,25 @@ void throw_on_error(CURLcode result) {
242
250
243
251
} // namespace
244
252
245
- Curl::Curl (const std::shared_ptr<Logger> &logger) : Curl(logger, libcurl) {}
253
+ Curl::Curl (const std::shared_ptr<Logger> &logger, const Clock &clock)
254
+ : Curl(logger, clock, libcurl) {}
246
255
247
- Curl::Curl (const std::shared_ptr<Logger> &logger, CurlLibrary &curl)
248
- : Curl(logger, curl,
256
+ Curl::Curl (const std::shared_ptr<Logger> &logger, const Clock &clock,
257
+ CurlLibrary &curl)
258
+ : Curl(logger, clock, curl,
249
259
[](auto &&func) { return std::thread (std::move (func)); }) {}
250
260
251
- Curl::Curl (const std::shared_ptr<Logger> &logger, CurlLibrary &curl ,
252
- const Curl::ThreadGenerator &make_thread)
253
- : impl_(new CurlImpl{logger, curl, make_thread}) {}
261
+ Curl::Curl (const std::shared_ptr<Logger> &logger, const Clock &clock ,
262
+ CurlLibrary &curl, const Curl::ThreadGenerator &make_thread)
263
+ : impl_(new CurlImpl{logger, clock, curl, make_thread}) {}
254
264
255
265
Curl::~Curl () { delete impl_; }
256
266
257
267
Expected<void > Curl::post (const URL &url, HeadersSetter set_headers,
258
268
std::string body, ResponseHandler on_response,
259
- ErrorHandler on_error) {
260
- return impl_->post (url, set_headers, body, on_response, on_error);
269
+ ErrorHandler on_error,
270
+ std::chrono::steady_clock::time_point deadline) {
271
+ return impl_->post (url, set_headers, body, on_response, on_error, deadline);
261
272
}
262
273
263
274
void Curl::drain (std::chrono::steady_clock::time_point deadline) {
@@ -268,10 +279,11 @@ nlohmann::json Curl::config_json() const {
268
279
return nlohmann::json::object ({{" type" , " datadog::tracing::Curl" }});
269
280
}
270
281
271
- CurlImpl::CurlImpl (const std::shared_ptr<Logger> &logger, CurlLibrary &curl ,
272
- const Curl::ThreadGenerator &make_thread)
282
+ CurlImpl::CurlImpl (const std::shared_ptr<Logger> &logger, const Clock &clock ,
283
+ CurlLibrary &curl, const Curl::ThreadGenerator &make_thread)
273
284
: curl_(curl),
274
285
logger_ (logger),
286
+ clock_(clock),
275
287
shutting_down_(false ),
276
288
num_active_handles_(0 ) {
277
289
curl_.global_init (CURL_GLOBAL_ALL);
@@ -311,24 +323,35 @@ CurlImpl::~CurlImpl() {
311
323
}
312
324
log_on_error (curl_.multi_wakeup (multi_handle_));
313
325
event_loop_.join ();
326
+
327
+ log_on_error (curl_.multi_cleanup (multi_handle_));
328
+ curl_.global_cleanup ();
314
329
}
315
330
316
- Expected<void > CurlImpl::post (const HTTPClient::URL &url,
317
- HeadersSetter set_headers, std::string body,
318
- ResponseHandler on_response,
319
- ErrorHandler on_error ) try {
331
+ Expected<void > CurlImpl::post (
332
+ const HTTPClient::URL &url, HeadersSetter set_headers, std::string body,
333
+ ResponseHandler on_response, ErrorHandler on_error ,
334
+ std::chrono::steady_clock::time_point deadline ) try {
320
335
if (multi_handle_ == nullptr ) {
321
336
return Error{Error::CURL_HTTP_CLIENT_NOT_RUNNING,
322
337
" Unable to send request via libcurl because the HTTP client "
323
338
" failed to start." };
324
339
}
325
340
341
+ HeaderWriter writer{curl_};
342
+ set_headers (writer);
343
+ auto cleanup_list = [&](auto list) { curl_.slist_free_all (list); };
344
+ std::unique_ptr<curl_slist, decltype (cleanup_list)> headers{
345
+ writer.release (), std::move (cleanup_list)};
346
+
326
347
auto request = std::make_unique<Request>();
327
348
328
349
request->curl = &curl_;
350
+ request->request_headers = headers.get ();
329
351
request->request_body = std::move (body);
330
352
request->on_response = std::move (on_response);
331
353
request->on_error = std::move (on_error);
354
+ request->deadline = std::move (deadline);
332
355
333
356
auto cleanup_handle = [&](auto handle) { curl_.easy_cleanup (handle); };
334
357
std::unique_ptr<CURL, decltype (cleanup_handle)> handle{
@@ -339,6 +362,8 @@ Expected<void> CurlImpl::post(const HTTPClient::URL &url,
339
362
" unable to initialize a curl handle for request sending" };
340
363
}
341
364
365
+ throw_on_error (
366
+ curl_.easy_setopt_httpheader (handle.get (), request->request_headers ));
342
367
throw_on_error (curl_.easy_setopt_private (handle.get (), request.get ()));
343
368
throw_on_error (
344
369
curl_.easy_setopt_errorbuffer (handle.get (), request->error_buffer ));
@@ -365,25 +390,17 @@ Expected<void> CurlImpl::post(const HTTPClient::URL &url,
365
390
handle.get (), (url.scheme + " ://" + url.authority + url.path ).c_str ()));
366
391
}
367
392
368
- HeaderWriter writer{curl_};
369
- set_headers (writer);
370
- auto cleanup_list = [&](auto list) { curl_.slist_free_all (list); };
371
- std::unique_ptr<curl_slist, decltype (cleanup_list)> headers{
372
- writer.release (), std::move (cleanup_list)};
373
- request->request_headers = headers.get ();
374
- throw_on_error (
375
- curl_.easy_setopt_httpheader (handle.get (), request->request_headers ));
376
-
377
393
std::list<CURL *> node;
378
394
node.push_back (handle.get ());
379
395
{
380
396
std::lock_guard<std::mutex> lock (mutex_);
381
397
new_handles_.splice (new_handles_.end (), node);
382
398
383
- headers.release ();
384
- handle.release ();
385
- request.release ();
399
+ ( void ) headers.release ();
400
+ ( void ) handle.release ();
401
+ ( void ) request.release ();
386
402
}
403
+
387
404
log_on_error (curl_.multi_wakeup (multi_handle_));
388
405
389
406
return nullopt;
@@ -464,6 +481,7 @@ CURLMcode CurlImpl::log_on_error(CURLMcode result) {
464
481
void CurlImpl::run () {
465
482
int num_messages_remaining;
466
483
CURLMsg *message;
484
+ const int max_wait_milliseconds = 10000 ;
467
485
std::unique_lock<std::mutex> lock (mutex_);
468
486
469
487
for (;;) {
@@ -478,16 +496,46 @@ void CurlImpl::run() {
478
496
&num_messages_remaining))) {
479
497
handle_message (*message, lock);
480
498
}
481
-
482
- const int max_wait_milliseconds = 10 * 1000 ;
483
499
lock.unlock ();
484
500
log_on_error (curl_.multi_poll (multi_handle_, nullptr , 0 ,
485
501
max_wait_milliseconds, nullptr ));
486
502
lock.lock ();
487
503
488
504
// New requests might have been added while we were sleeping.
489
505
for (; !new_handles_.empty (); new_handles_.pop_front ()) {
490
- CURL *const handle = new_handles_.front ();
506
+ CURL *handle = new_handles_.front ();
507
+ char *user_data;
508
+ if (log_on_error (curl_.easy_getinfo_private (handle, &user_data)) !=
509
+ CURLE_OK) {
510
+ curl_.easy_cleanup (handle);
511
+ continue ;
512
+ }
513
+
514
+ auto *request = reinterpret_cast <Request *>(user_data);
515
+ const auto timeout = request->deadline - clock_ ().tick ;
516
+ if (timeout <= std::chrono::steady_clock::time_point::duration::zero ()) {
517
+ std::string message;
518
+ message +=
519
+ " Request deadline exceeded before request was even added to "
520
+ " libcurl "
521
+ " event loop. Deadline was " ;
522
+ message += std::to_string (
523
+ -std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
524
+ .count ());
525
+ message += " nanoseconds ago." ;
526
+ request->on_error (
527
+ Error{Error::CURL_DEADLINE_EXCEEDED_BEFORE_REQUEST_START,
528
+ std::move (message)});
529
+
530
+ curl_.easy_cleanup (handle);
531
+ delete request;
532
+
533
+ continue ;
534
+ }
535
+
536
+ log_on_error (curl_.easy_setopt_timeout_ms (
537
+ handle, std::chrono::duration_cast<std::chrono::milliseconds>(timeout)
538
+ .count ()));
491
539
log_on_error (curl_.multi_add_handle (multi_handle_, handle));
492
540
request_handles_.insert (handle);
493
541
}
@@ -510,8 +558,6 @@ void CurlImpl::run() {
510
558
}
511
559
512
560
request_handles_.clear ();
513
- log_on_error (curl_.multi_cleanup (multi_handle_));
514
- curl_.global_cleanup ();
515
561
}
516
562
517
563
void CurlImpl::handle_message (const CURLMsg &message,
0 commit comments