• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jbaldwin / liblifthttp / 9f8abf248aadabbcf27c7c11c45d16e857432729-PR-164

29 Apr 2025 01:43PM UTC coverage: 88.2%. First build
9f8abf248aadabbcf27c7c11c45d16e857432729-PR-164

Pull #164

github

web-flow
Merge 418233221 into b9996bd10
Pull Request #164: fixed sending JSON put requests by using CURLOPT_CUSTOMREQUEST

0 of 1 new or added line in 1 file covered. (0.0%)

1166 of 1322 relevant lines covered (88.2%)

10199.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.77
/src/executor.cpp
1
#include "lift/executor.hpp"
2
#include "lift/client.hpp"
3
#include "lift/init.hpp"
4

5
namespace lift
6
{
7
auto curl_write_header(char* buffer, size_t size, size_t nitems, void* user_ptr) -> size_t;
8

9
auto curl_write_data(void* buffer, size_t size, size_t nitems, void* user_ptr) -> size_t;
10

11
auto curl_xfer_info(
12
    void*      clientp,
13
    curl_off_t download_total_bytes,
14
    curl_off_t download_now_bytes,
15
    curl_off_t upload_total_bytes,
16
    curl_off_t upload_now_bytes) -> int;
17

18
auto curl_debug_info_callback(CURL* handle, curl_infotype type, char* data, size_t size, void* userptr) -> int;
19

20
executor::executor(request* request, share* share) : m_request_sync(request), m_request(m_request_sync), m_response()
25✔
21
{
22
    if (share != nullptr)
25✔
23
    {
24
        m_curl_share_handle = share->m_curl_share_ptr;
10✔
25
    }
26
}
25✔
27

28
executor::executor(client* c) : m_client(c)
7,689✔
29
{
30
}
7,689✔
31

32
executor::~executor()
7,714✔
33
{
34
    reset();
7,695✔
35
    curl_easy_cleanup(m_curl_handle);
7,711✔
36
}
7,693✔
37

38
auto executor::start_async(request_ptr req_ptr, share* share) -> void
20,206✔
39
{
40
    m_request_async = std::move(req_ptr);
20,206✔
41
    m_request       = m_request_async.get();
20,206✔
42
    if (share != nullptr)
20,207✔
43
    {
44
        m_curl_share_handle = share->m_curl_share_ptr;
19,998✔
45
    }
46
}
20,207✔
47

48
auto executor::perform() -> response
25✔
49
{
50
    global_init();
25✔
51

52
    prepare();
25✔
53

54
    auto curl_error_code     = curl_easy_perform(m_curl_handle);
25✔
55
    m_response.m_lift_status = convert(curl_error_code);
25✔
56
    copy_curl_to_response();
25✔
57

58
    global_cleanup();
25✔
59

60
    return std::move(m_response);
25✔
61
}
62

63
auto executor::prepare() -> void
20,230✔
64
{
65
    curl_easy_setopt(m_curl_handle, CURLOPT_PRIVATE, this);
20,230✔
66
    curl_easy_setopt(m_curl_handle, CURLOPT_HEADERFUNCTION, curl_write_header);
20,238✔
67
    curl_easy_setopt(m_curl_handle, CURLOPT_HEADERDATA, this);
20,235✔
68
    curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, curl_write_data);
20,233✔
69
    curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, this);
20,234✔
70
    curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1L);
20,237✔
71

72
    curl_easy_setopt(m_curl_handle, CURLOPT_URL, m_request->url().c_str());
20,239✔
73

74
    switch (m_request->method())
20,234✔
75
    {
76
        default:
20,232✔
77
            /* INTENTIONAL FALLTHROUGH */
78
        case http::method::unknown: // default to GET on unknown/bad value.
79
            /* INTENTIONAL FALLTHROUGH */
80
        case http::method::get:
81
            curl_easy_setopt(m_curl_handle, CURLOPT_HTTPGET, 1L);
20,232✔
82
            break;
20,225✔
83
        case http::method::head:
1✔
84
            curl_easy_setopt(m_curl_handle, CURLOPT_NOBODY, 1L);
1✔
85
            break;
1✔
86
        case http::method::post:
4✔
87
            curl_easy_setopt(m_curl_handle, CURLOPT_POST, 1L);
4✔
88
            break;
4✔
89
        case http::method::put:
×
NEW
90
            curl_easy_setopt(m_curl_handle, CURLOPT_CUSTOMREQUEST, "PUT");
×
91
            break;
×
92
        case http::method::delete_t:
×
93
            curl_easy_setopt(m_curl_handle, CURLOPT_CUSTOMREQUEST, "DELETE");
×
94
            break;
×
95
        case http::method::connect:
×
96
            curl_easy_setopt(m_curl_handle, CURLOPT_CONNECT_ONLY, 1L);
×
97
            break;
×
98
        case http::method::options:
×
99
            curl_easy_setopt(m_curl_handle, CURLOPT_CUSTOMREQUEST, "OPTIONS");
×
100
            break;
×
101
        case http::method::patch:
×
102
            curl_easy_setopt(m_curl_handle, CURLOPT_CUSTOMREQUEST, "PATCH");
×
103
            break;
×
104
    }
105

106
    switch (m_request->version())
20,230✔
107
    {
108
        default:
20,226✔
109
            /* INTENTIONAL FALLTHROUGH */
110
        case http::version::unknown: // default to USE_BEST on unknown/bad value.
111
            /* INTENTIONAL FALLTHROUGH */
112
        case http::version::use_best:
113
            curl_easy_setopt(m_curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);
20,226✔
114
            break;
20,235✔
115
        case http::version::v1_0:
×
116
            curl_easy_setopt(m_curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
×
117
            break;
×
118
        case http::version::v1_1:
4✔
119
            curl_easy_setopt(m_curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
4✔
120
            break;
4✔
121
        case http::version::v2_0:
×
122
            curl_easy_setopt(m_curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
×
123
            break;
×
124
        case http::version::v2_0_tls:
×
125
            curl_easy_setopt(m_curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
×
126
            break;
×
127
        case http::version::v2_0_only:
×
128
            curl_easy_setopt(m_curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
×
129
            break;
×
130
    }
131

132
    // Synchronous requests get their timeout value set directly on the curl easy handle.
133
    // Asynchronous requests will handle timeouts on the event loop due to Connection Time.
134
    if (m_request_sync != nullptr)
20,239✔
135
    {
136
        if (m_request->connect_timeout().has_value())
25✔
137
        {
138
            curl_easy_setopt(
×
139
                m_curl_handle,
140
                CURLOPT_CONNECTTIMEOUT_MS,
141
                static_cast<long>(m_request->connect_timeout().value().count()));
142
        }
143

144
        if (m_request->timeout().has_value())
25✔
145
        {
146
            curl_easy_setopt(
10✔
147
                m_curl_handle, CURLOPT_TIMEOUT_MS, static_cast<long>(m_request->timeout().value().count()));
148
        }
149
    }
150

151
    // Connection timeout is handled when injecting into the CURLM* event loop for asynchronous requests.
152

153
    if (m_request->follow_redirects())
20,239✔
154
    {
155
        curl_easy_setopt(m_curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
20,238✔
156
        curl_easy_setopt(m_curl_handle, CURLOPT_MAXREDIRS, static_cast<long>(m_request->max_redirects()));
20,236✔
157
    }
158
    else
159
    {
160
        curl_easy_setopt(m_curl_handle, CURLOPT_FOLLOWLOCATION, 0L);
×
161
    }
162

163
    // https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html
164
    curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYPEER, (m_request->verify_ssl_peer()) ? 1L : 0L);
20,231✔
165
    // Note that 1L is valid, but curl docs say its basically deprecated.
166
    // https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html
167
    curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYHOST, (m_request->verify_ssl_host()) ? 2L : 0L);
20,234✔
168
    // https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYSTATUS.html
169
    curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYSTATUS, (m_request->verify_ssl_status()) ? 1L : 0L);
20,229✔
170

171
    // https://curl.haxx.se/libcurl/c/CURLOPT_SSLCERT.html
172
    if (const auto& cert = m_request->ssl_cert(); cert.has_value())
20,235✔
173
    {
174
        curl_easy_setopt(m_curl_handle, CURLOPT_SSLCERT, cert.value().c_str());
×
175
    }
176
    // https://curl.haxx.se/libcurl/c/CURLOPT_SSLCERTTYPE.html
177
    if (const auto& cert_type = m_request->ssl_cert_type(); cert_type.has_value())
20,233✔
178
    {
179
        curl_easy_setopt(m_curl_handle, CURLOPT_SSLCERTTYPE, to_string(cert_type.value()).data());
×
180
    }
181
    // https://curl.haxx.se/libcurl/c/CURLOPT_SSLKEY.html
182
    if (const auto& key = m_request->ssl_key(); key.has_value())
20,230✔
183
    {
184
        curl_easy_setopt(m_curl_handle, CURLOPT_SSLKEY, key.value().c_str());
×
185
    }
186
    // https://curl.haxx.se/libcurl/c/CURLOPT_KEYPASSWD.html
187
    if (const auto& password = m_request->key_password(); password.has_value())
20,233✔
188
    {
189
        curl_easy_setopt(m_curl_handle, CURLOPT_KEYPASSWD, password.value().data());
×
190
    }
191

192
    // Set proxy information for the requst if provided.
193
    // https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html
194
    if (m_request->proxy().has_value())
20,230✔
195
    {
196
        auto& proxy_data = m_request->proxy().value();
3✔
197

198
        curl_easy_setopt(m_curl_handle, CURLOPT_PROXY, proxy_data.m_host.data());
3✔
199

200
        switch (proxy_data.m_type)
3✔
201
        {
202
            case proxy_type::https:
×
203
                curl_easy_setopt(m_curl_handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
×
204
                break;
×
205
            case proxy_type::http:
3✔
206
                /* intentional fallthrough */
207
            default:
208
                curl_easy_setopt(m_curl_handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
3✔
209
                break;
3✔
210
        }
211

212
        curl_easy_setopt(m_curl_handle, CURLOPT_PROXYPORT, proxy_data.m_port);
3✔
213

214
        if (proxy_data.m_username.has_value())
3✔
215
        {
216
            curl_easy_setopt(m_curl_handle, CURLOPT_PROXYUSERNAME, proxy_data.m_username.value().data());
2✔
217
        }
218
        if (proxy_data.m_password.has_value())
3✔
219
        {
220
            curl_easy_setopt(m_curl_handle, CURLOPT_PROXYPASSWORD, proxy_data.m_password.value().data());
2✔
221
        }
222

223
        if (proxy_data.m_auth_types.has_value())
3✔
224
        {
225
            int64_t auth_types{0};
2✔
226
            for (const auto& auth_type : proxy_data.m_auth_types.value())
4✔
227
            {
228
                switch (auth_type)
2✔
229
                {
230
                    default:
1✔
231
                        // CURLAUTH_BASIC is the default per docs https://curl.se/libcurl/c/CURLOPT_PROXYAUTH.html.
232
                        /* INTENTIONAL FALLTHROUGH */
233
                    case http_auth_type::basic:
234
                        auth_types |= CURLAUTH_BASIC;
1✔
235
                        break;
1✔
236
                    case http_auth_type::any:
×
237
                        auth_types |= CURLAUTH_ANY;
×
238
                        break;
×
239
                    case http_auth_type::any_safe:
1✔
240
                        auth_types |= CURLAUTH_ANYSAFE;
1✔
241
                        break;
1✔
242
                }
243
            }
244

245
            if (auth_types != 0)
2✔
246
            {
247
                curl_easy_setopt(m_curl_handle, CURLOPT_PROXYAUTH, auth_types);
2✔
248
            }
249
        }
250
    }
251

252
    const auto& encodings = m_request->accept_encodings();
20,230✔
253
    if (encodings.has_value())
20,229✔
254
    {
255
        if (!encodings.value().empty())
×
256
        {
257
            std::size_t length{0};
×
258
            for (const auto& e : encodings.value())
×
259
            {
260
                length += e.length() + 2; // for ", "
×
261
            }
262

263
            std::string joined{};
×
264
            joined.reserve(length);
×
265

266
            bool first{true};
×
267
            for (auto& e : encodings.value())
×
268
            {
269
                if (first)
×
270
                {
271
                    first = false;
×
272
                }
273
                else
274
                {
275
                    joined.append(", ");
×
276
                }
277
                joined.append(e);
×
278
            }
279

280
            // strings are copied into libcurl except for POSTFIELDS.
281
            curl_easy_setopt(m_curl_handle, CURLOPT_ACCEPT_ENCODING, joined.c_str());
×
282
        }
283
        else
284
        {
285
            // From the CURL docs (https://curl.haxx.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html):
286
            // 'To aid applications not having to bother about what specific algorithms this particular
287
            // libcurl build supports, libcurl allows a zero-length string to be set ("") to ask for an
288
            // Accept-Encoding: header to be used that contains all built-in supported encodings.'
289
            curl_easy_setopt(m_curl_handle, CURLOPT_ACCEPT_ENCODING, "");
×
290
        }
291
    }
292

293
    // Headers
294
    if (m_curl_request_headers != nullptr)
20,230✔
295
    {
296
        curl_slist_free_all(m_curl_request_headers);
×
297
        m_curl_request_headers = nullptr;
×
298
    }
299

300
    for (auto& header : m_request->m_request_headers)
20,237✔
301
    {
302
        m_curl_request_headers = curl_slist_append(m_curl_request_headers, header.data().data());
7✔
303
    }
304

305
    if (m_curl_request_headers != nullptr)
20,229✔
306
    {
307
        curl_easy_setopt(m_curl_handle, CURLOPT_HTTPHEADER, m_curl_request_headers);
3✔
308
    }
309
    else
310
    {
311
        curl_easy_setopt(m_curl_handle, CURLOPT_HTTPHEADER, nullptr);
20,226✔
312
    }
313

314
    // DNS resolve hosts
315
    if (!m_request->m_resolve_hosts.empty() || (m_client != nullptr && !m_client->m_resolve_hosts.empty()))
20,233✔
316
    {
317
        if (m_curl_resolve_hosts != nullptr)
3✔
318
        {
319
            curl_slist_free_all(m_curl_resolve_hosts);
×
320
            m_curl_resolve_hosts = nullptr;
×
321
        }
322

323
        for (const auto& resolve_host : m_request->m_resolve_hosts)
4✔
324
        {
325
            m_curl_resolve_hosts =
1✔
326
                curl_slist_append(m_curl_resolve_hosts, resolve_host.curl_formatted_resolve_host().data());
1✔
327
        }
328

329
        if (m_client != nullptr)
3✔
330
        {
331
            for (const auto& resolve_host : m_client->m_resolve_hosts)
6✔
332
            {
333
                m_curl_resolve_hosts =
4✔
334
                    curl_slist_append(m_curl_resolve_hosts, resolve_host.curl_formatted_resolve_host().data());
4✔
335
            }
336
        }
337

338
        curl_easy_setopt(m_curl_handle, CURLOPT_RESOLVE, m_curl_resolve_hosts);
3✔
339
    }
340

341
    // POST or MIME data
342
    if (m_request->m_request_data_set)
20,232✔
343
    {
344
        curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDSIZE, static_cast<long>(m_request->data().size()));
4✔
345
        curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDS, m_request->data().data());
4✔
346
    }
347
    else if (m_request->m_mime_fields_set)
20,228✔
348
    {
349
        m_mime_handle = curl_mime_init(m_curl_handle);
×
350

351
        for (const auto& mime_field : m_request->mime_fields())
×
352
        {
353
            auto* field = curl_mime_addpart(m_mime_handle);
×
354

355
            if (std::holds_alternative<std::string>(mime_field.value()))
×
356
            {
357
                curl_mime_name(field, mime_field.name().data());
×
358
                const auto& value = std::get<std::string>(mime_field.value());
×
359
                curl_mime_data(field, value.data(), value.length());
×
360
            }
361
            else
362
            {
363
                curl_mime_filename(field, mime_field.name().data());
×
364
                curl_mime_filedata(field, std::get<std::filesystem::path>(mime_field.value()).c_str());
×
365
            }
366
        }
367

368
        curl_easy_setopt(m_curl_handle, CURLOPT_MIMEPOST, m_mime_handle);
×
369
    }
370

371
    if (m_request->m_on_transfer_progress_handler != nullptr)
20,232✔
372
    {
373
        curl_easy_setopt(m_curl_handle, CURLOPT_XFERINFOFUNCTION, curl_xfer_info);
2✔
374
        curl_easy_setopt(m_curl_handle, CURLOPT_XFERINFODATA, this);
2✔
375
        curl_easy_setopt(m_curl_handle, CURLOPT_NOPROGRESS, 0L);
2✔
376
    }
377
    else
378
    {
379
        curl_easy_setopt(m_curl_handle, CURLOPT_NOPROGRESS, 1L);
20,223✔
380
    }
381

382
    if (const auto& timeout = m_request->happy_eyeballs_timeout(); timeout.has_value())
20,236✔
383
    {
384
        curl_easy_setopt(m_curl_handle, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, static_cast<long>(timeout.value().count()));
1✔
385
    }
386

387
    // Note that this will lock the mutexes in the share callbacks.
388
    if (m_curl_share_handle != nullptr)
20,222✔
389
    {
390
        curl_easy_setopt(m_curl_handle, CURLOPT_SHARE, m_curl_share_handle);
19,995✔
391
    }
392

393
    // Set debug info if the user added a debug info functor callback
394
    // https://curl.se/libcurl/c/CURLOPT_DEBUGFUNCTION.html
395
    if (m_request->m_debug_info_handler != nullptr)
20,239✔
396
    {
397
        curl_easy_setopt(m_curl_handle, CURLOPT_VERBOSE, 1L);
3✔
398
        curl_easy_setopt(m_curl_handle, CURLOPT_DEBUGFUNCTION, curl_debug_info_callback);
3✔
399
        curl_easy_setopt(m_curl_handle, CURLOPT_DEBUGDATA, this);
3✔
400
    }
401
}
20,239✔
402

403
auto executor::copy_curl_to_response() -> void
20,236✔
404
{
405
    long http_response_code = 0;
20,236✔
406
    curl_easy_getinfo(m_curl_handle, CURLINFO_RESPONSE_CODE, &http_response_code);
20,236✔
407
    m_response.m_status_code = http::to_enum(static_cast<uint16_t>(http_response_code));
20,236✔
408

409
    long http_version = 0;
20,236✔
410
    curl_easy_getinfo(m_curl_handle, CURLINFO_HTTP_VERSION, &http_version);
20,236✔
411
    m_response.m_version = static_cast<http::version>(http_version);
20,233✔
412

413
    double total_time = 0;
20,233✔
414
    curl_easy_getinfo(m_curl_handle, CURLINFO_TOTAL_TIME, &total_time);
20,233✔
415
    // std::duration defaults to seconds, so don't need to duration_cast total time to seconds.
416
    m_response.m_total_time = static_cast<uint32_t>(
20,228✔
417
        std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::duration<double>{total_time}).count());
20,229✔
418

419
    long connect_count = 0;
20,228✔
420
    curl_easy_getinfo(m_curl_handle, CURLINFO_NUM_CONNECTS, &connect_count);
20,228✔
421
    m_response.m_num_connects = (connect_count >= std::numeric_limits<uint8_t>::max())
20,234✔
422
                                    ? std::numeric_limits<uint8_t>::max()
20,237✔
423
                                    : static_cast<uint8_t>(connect_count);
424

425
    long redirect_count = 0;
20,236✔
426
    curl_easy_getinfo(m_curl_handle, CURLINFO_REDIRECT_COUNT, &redirect_count);
20,236✔
427
    m_response.m_num_redirects = (redirect_count >= std::numeric_limits<uint8_t>::max())
20,237✔
428
                                     ? std::numeric_limits<uint8_t>::max()
20,237✔
429
                                     : static_cast<uint8_t>(redirect_count);
430
}
20,237✔
431

432
auto executor::set_timesup_response(std::chrono::milliseconds total_time) -> void
1✔
433
{
434
    m_response.m_status_code   = lift::http::status_code::http_504_gateway_timeout;
1✔
435
    m_response.m_total_time    = static_cast<uint32_t>(total_time.count());
1✔
436
    m_response.m_num_connects  = 0;
1✔
437
    m_response.m_num_redirects = 0;
1✔
438
}
1✔
439

440
auto executor::reset() -> void
27,904✔
441
{
442
    if (m_mime_handle != nullptr)
27,904✔
443
    {
444
        curl_mime_free(m_mime_handle);
×
445
        m_mime_handle = nullptr;
×
446
    }
447

448
    if (m_curl_request_headers != nullptr)
27,904✔
449
    {
450
        curl_slist_free_all(m_curl_request_headers);
3✔
451
        m_curl_request_headers = nullptr;
3✔
452
    }
453

454
    if (m_curl_resolve_hosts != nullptr)
27,904✔
455
    {
456
        curl_slist_free_all(m_curl_resolve_hosts);
3✔
457
        m_curl_resolve_hosts = nullptr;
3✔
458
    }
459

460
    // Regardless of sync/async all three pointers get reset to nullptr.
461
    m_request_sync  = nullptr;
27,904✔
462
    m_request_async = nullptr;
27,904✔
463
    m_request       = nullptr;
27,908✔
464

465
    m_timeout_iterator.reset();
27,908✔
466
    m_on_complete_handler_processed = false;
27,909✔
467
    m_response                      = response{};
27,909✔
468

469
    curl_easy_setopt(m_curl_handle, CURLOPT_SHARE, nullptr);
27,893✔
470
    m_curl_share_handle = nullptr;
27,897✔
471

472
    curl_easy_reset(m_curl_handle);
27,897✔
473
}
27,923✔
474

475
auto executor::convert(CURLcode curl_code) -> lift_status
20,239✔
476
{
477
#pragma GCC diagnostic push
478
#pragma GCC diagnostic ignored "-Wswitch-enum"
479
    switch (curl_code)
20,239✔
480
    {
481
        case CURLcode::CURLE_OK:
15,824✔
482
            return lift_status::success;
15,824✔
483
        case CURLcode::CURLE_GOT_NOTHING:
×
484
            return lift_status::response_empty;
×
485
        case CURLcode::CURLE_OPERATION_TIMEDOUT:
4,410✔
486
            return lift_status::timeout;
4,410✔
487
        case CURLcode::CURLE_COULDNT_CONNECT:
×
488
            return lift_status::connect_error;
×
489
        case CURLcode::CURLE_COULDNT_RESOLVE_HOST:
×
490
            return lift_status::connect_dns_error;
×
491
        case CURLcode::CURLE_SSL_CONNECT_ERROR:
×
492
            return lift_status::connect_ssl_error;
×
493
        case CURLcode::CURLE_WRITE_ERROR:
×
494
            return lift_status::download_error;
×
495
        case CURLcode::CURLE_SEND_ERROR:
×
496
            return lift_status::error_failed_to_start;
×
497
        default:
5✔
498
            return lift_status::error;
5✔
499
    }
500
#pragma GCC diagnostic pop
501
}
502

503
auto curl_write_header(char* buffer, size_t size, size_t nitems, void* user_ptr) -> size_t
158,518✔
504
{
505
    auto*        executor_ptr = static_cast<executor*>(user_ptr);
158,518✔
506
    auto&        response     = executor_ptr->m_response;
158,518✔
507
    const size_t data_length  = size * nitems;
158,518✔
508

509
    std::string_view data_view{buffer, data_length};
158,518✔
510

511
    if (data_view.empty())
158,409✔
512
    {
513
        return data_length;
×
514
    }
515

516
    // Ignore empty header lines from curl.
517
    if (data_length == 2 && data_view == "\r\n")
158,441✔
518
    {
519
        return data_length;
15,848✔
520
    }
521
    // Ignore the HTTP/ 'header' line from curl.
522
    constexpr size_t HTTPSLASH_LEN = 5;
142,626✔
523
    if (data_length >= 4 && data_view.substr(0, HTTPSLASH_LEN) == "HTTP/")
142,626✔
524
    {
525
        return data_length;
15,831✔
526
    }
527

528
    // Drop the trailing \r\n from the header.
529
    if (data_length >= 2)
126,726✔
530
    {
531
        size_t rm_size = (data_view[data_length - 1] == '\n' && data_view[data_length - 2] == '\r') ? 2 : 0;
126,706✔
532
        data_view.remove_suffix(rm_size);
126,763✔
533
    }
534

535
    response.m_headers.emplace_back(std::string{data_view.data(), data_view.length()});
126,783✔
536

537
    return data_length; // return original size for curl to continue processing
126,689✔
538
}
539

540
auto curl_write_data(void* buffer, size_t size, size_t nitems, void* user_ptr) -> size_t
15,802✔
541
{
542
    auto*  executor_ptr = static_cast<executor*>(user_ptr);
15,802✔
543
    auto&  response     = executor_ptr->m_response;
15,802✔
544
    size_t data_length  = size * nitems;
15,802✔
545

546
    std::string_view from{static_cast<const char*>(buffer), data_length};
15,802✔
547

548
    std::copy(from.begin(), from.end(), std::back_inserter(response.m_data));
15,776✔
549

550
    return data_length;
15,824✔
551
}
552

553
auto curl_xfer_info(
29✔
554
    void*      clientp,
555
    curl_off_t download_total_bytes,
556
    curl_off_t download_now_bytes,
557
    curl_off_t upload_total_bytes,
558
    curl_off_t upload_now_bytes) -> int
559
{
560
    const auto* executor_ptr = static_cast<const executor*>(clientp);
29✔
561

562
    if (executor_ptr != nullptr && executor_ptr->m_request->m_on_transfer_progress_handler != nullptr)
29✔
563
    {
564
        if (executor_ptr->m_request->m_on_transfer_progress_handler(
29✔
565
                *executor_ptr->m_request,
29✔
566
                download_total_bytes,
567
                download_now_bytes,
568
                upload_total_bytes,
569
                upload_now_bytes))
570
        {
571
            return 0;
28✔
572
        }
573
        else
574
        {
575
            return 1;
1✔
576
        }
577
    }
578
    else
579
    {
580
        return 0; // continue the request.
×
581
    }
582
}
583

584
auto curl_debug_info_callback(CURL* /*handle*/, curl_infotype type, char* data, size_t size, void* userptr) -> int
51✔
585
{
586
    const auto* executor_ptr = static_cast<const executor*>(userptr);
51✔
587

588
    if (executor_ptr != nullptr && executor_ptr->m_request->m_debug_info_handler != nullptr)
51✔
589
    {
590
        executor_ptr->m_request->m_debug_info_handler(
102✔
591
            *executor_ptr->m_request, static_cast<debug_info_type>(type), std::string_view{data, size});
51✔
592
    }
593

594
    // "this function must return 0" according to libcurl docs.
595
    return 0;
51✔
596
}
597

598
} // namespace lift
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc