13#include <curl/curlver.h>
15#include <curl/header.h>
16#include <curl/options.h>
17#include <curl/urlapi.h>
29namespace proto_fuzzer {
33constexpr unsigned int kAllHeaderOrigins = CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX | CURLH_PSEUDO;
34constexpr std::size_t kMaxResultHeaders = 16;
38constexpr CURLUPart kUrlParts[] = {
39 CURLUPART_URL, CURLUPART_SCHEME, CURLUPART_USER, CURLUPART_PASSWORD, CURLUPART_OPTIONS, CURLUPART_HOST,
40 CURLUPART_PORT, CURLUPART_PATH, CURLUPART_QUERY, CURLUPART_FRAGMENT, CURLUPART_ZONEID,
46void ProbeUrlPart(CURLU* url, CURLUPart part,
unsigned int flags) {
47 char* result =
nullptr;
48 if (curl_url_get(url, part, &result, flags) == CURLUE_OK) {
54enum class InfoResultType {
69struct InfoDescriptor {
71 InfoResultType result_type;
77constexpr InfoDescriptor kInfoDescriptors[] = {
78 {CURLINFO_EFFECTIVE_URL, InfoResultType::kString},
79 {CURLINFO_RESPONSE_CODE, InfoResultType::kLong},
80 {CURLINFO_TOTAL_TIME, InfoResultType::kDouble},
81 {CURLINFO_SIZE_DOWNLOAD_T, InfoResultType::kOffset},
82 {CURLINFO_ACTIVESOCKET, InfoResultType::kSocket},
83 {CURLINFO_CERTINFO, InfoResultType::kCertificateInfo},
84 {CURLINFO_TLS_SSL_PTR, InfoResultType::kTlsSessionInfo},
85 {CURLINFO_SSL_ENGINES, InfoResultType::kOwnedSlist},
86 {CURLINFO_NONE, InfoResultType::kUnknown},
87 {CURLINFO_CONTENT_TYPE, InfoResultType::kString},
88 {CURLINFO_PRIVATE, InfoResultType::kString},
89 {CURLINFO_FTP_ENTRY_PATH, InfoResultType::kString},
90 {CURLINFO_REDIRECT_URL, InfoResultType::kString},
91 {CURLINFO_PRIMARY_IP, InfoResultType::kString},
92 {CURLINFO_RTSP_SESSION_ID, InfoResultType::kString},
93 {CURLINFO_LOCAL_IP, InfoResultType::kString},
94 {CURLINFO_SCHEME, InfoResultType::kString},
95 {CURLINFO_EFFECTIVE_METHOD, InfoResultType::kString},
96 {CURLINFO_REFERER, InfoResultType::kString},
97 {CURLINFO_CAINFO, InfoResultType::kString},
98 {CURLINFO_CAPATH, InfoResultType::kString},
99 {CURLINFO_HEADER_SIZE, InfoResultType::kLong},
100 {CURLINFO_REQUEST_SIZE, InfoResultType::kLong},
101 {CURLINFO_SSL_VERIFYRESULT, InfoResultType::kLong},
102 {CURLINFO_FILETIME, InfoResultType::kLong},
103 {CURLINFO_REDIRECT_COUNT, InfoResultType::kLong},
104 {CURLINFO_HTTP_CONNECTCODE, InfoResultType::kLong},
105 {CURLINFO_HTTPAUTH_AVAIL, InfoResultType::kLong},
106 {CURLINFO_PROXYAUTH_AVAIL, InfoResultType::kLong},
107 {CURLINFO_OS_ERRNO, InfoResultType::kLong},
108 {CURLINFO_NUM_CONNECTS, InfoResultType::kLong},
109 {CURLINFO_CONDITION_UNMET, InfoResultType::kLong},
110 {CURLINFO_RTSP_CLIENT_CSEQ, InfoResultType::kLong},
111 {CURLINFO_RTSP_SERVER_CSEQ, InfoResultType::kLong},
112 {CURLINFO_RTSP_CSEQ_RECV, InfoResultType::kLong},
113 {CURLINFO_PRIMARY_PORT, InfoResultType::kLong},
114 {CURLINFO_LOCAL_PORT, InfoResultType::kLong},
115 {CURLINFO_HTTP_VERSION, InfoResultType::kLong},
116 {CURLINFO_PROXY_SSL_VERIFYRESULT, InfoResultType::kLong},
117 {CURLINFO_PROXY_ERROR, InfoResultType::kLong},
118 {CURLINFO_NAMELOOKUP_TIME, InfoResultType::kDouble},
119 {CURLINFO_CONNECT_TIME, InfoResultType::kDouble},
120 {CURLINFO_PRETRANSFER_TIME, InfoResultType::kDouble},
121 {CURLINFO_STARTTRANSFER_TIME, InfoResultType::kDouble},
122 {CURLINFO_REDIRECT_TIME, InfoResultType::kDouble},
123 {CURLINFO_APPCONNECT_TIME, InfoResultType::kDouble},
124 {CURLINFO_SIZE_UPLOAD_T, InfoResultType::kOffset},
125 {CURLINFO_SPEED_DOWNLOAD_T, InfoResultType::kOffset},
126 {CURLINFO_SPEED_UPLOAD_T, InfoResultType::kOffset},
127 {CURLINFO_FILETIME_T, InfoResultType::kOffset},
128 {CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, InfoResultType::kOffset},
129 {CURLINFO_CONTENT_LENGTH_UPLOAD_T, InfoResultType::kOffset},
130 {CURLINFO_TOTAL_TIME_T, InfoResultType::kOffset},
131 {CURLINFO_NAMELOOKUP_TIME_T, InfoResultType::kOffset},
132 {CURLINFO_CONNECT_TIME_T, InfoResultType::kOffset},
133 {CURLINFO_PRETRANSFER_TIME_T, InfoResultType::kOffset},
134 {CURLINFO_STARTTRANSFER_TIME_T, InfoResultType::kOffset},
135 {CURLINFO_REDIRECT_TIME_T, InfoResultType::kOffset},
136 {CURLINFO_APPCONNECT_TIME_T, InfoResultType::kOffset},
137 {CURLINFO_RETRY_AFTER, InfoResultType::kOffset},
138#if LIBCURL_VERSION_NUM >= 0x080200
139 {CURLINFO_XFER_ID, InfoResultType::kOffset},
140 {CURLINFO_CONN_ID, InfoResultType::kOffset},
142#if LIBCURL_VERSION_NUM >= 0x080600
143 {CURLINFO_QUEUE_TIME_T, InfoResultType::kOffset},
145#if LIBCURL_VERSION_NUM >= 0x080700
146 {CURLINFO_USED_PROXY, InfoResultType::kLong},
148#if LIBCURL_VERSION_NUM >= 0x080a00
149 {CURLINFO_POSTTRANSFER_TIME_T, InfoResultType::kOffset},
151#if LIBCURL_VERSION_NUM >= 0x080b00
152 {CURLINFO_EARLYDATA_SENT_T, InfoResultType::kOffset},
154#if LIBCURL_VERSION_NUM >= 0x080c00
155 {CURLINFO_HTTPAUTH_USED, InfoResultType::kLong},
156 {CURLINFO_PROXYAUTH_USED, InfoResultType::kLong},
158#if LIBCURL_VERSION_NUM >= 0x081400
159 {CURLINFO_SIZE_DELIVERED, InfoResultType::kOffset},
161 {CURLINFO_COOKIELIST, InfoResultType::kOwnedSlist},
164constexpr std::size_t kInfoDescriptorCount =
sizeof(kInfoDescriptors) /
sizeof(kInfoDescriptors[0]);
165static_assert(kInfoDescriptorCount <= 96,
"the three API result seeds cover selector indexes 0 through 95");
171constexpr curl_lock_data kShareData[] = {
172 CURL_LOCK_DATA_COOKIE, CURL_LOCK_DATA_DNS, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_DATA_CONNECT, CURL_LOCK_DATA_PSL,
173 CURL_LOCK_DATA_HSTS, CURL_LOCK_DATA_NONE, CURL_LOCK_DATA_SHARE, CURL_LOCK_DATA_LAST,
175constexpr std::size_t kShareDataCount =
sizeof(kShareData) /
sizeof(kShareData[0]);
186enum class ReentrantProbeKind {
205struct ReentrantProbe {
206 ReentrantProbeKind kind;
220constexpr ReentrantProbe kReentrantProbes[] = {
221 {ReentrantProbeKind::kEasySetopt,
false}, {ReentrantProbeKind::kEasyGetinfo,
false},
222 {ReentrantProbeKind::kEasyHeader,
false}, {ReentrantProbeKind::kEasyDuphandle,
false},
223 {ReentrantProbeKind::kEasyReset,
false}, {ReentrantProbeKind::kEasyPerform,
false},
224 {ReentrantProbeKind::kEasyUpkeep,
false}, {ReentrantProbeKind::kEasySend,
false},
225 {ReentrantProbeKind::kEasyRecv,
false}, {ReentrantProbeKind::kEasyPause,
false},
226 {ReentrantProbeKind::kMultiPerform,
true}, {ReentrantProbeKind::kMultiSetopt,
true},
227 {ReentrantProbeKind::kMultiAddHandle,
true}, {ReentrantProbeKind::kMultiWait,
true},
229constexpr std::size_t kReentrantProbeCount =
sizeof(kReentrantProbes) /
sizeof(kReentrantProbes[0]);
233bool RecursiveRejected(CURLcode code) {
return code == CURLE_RECURSIVE_API_CALL; }
236bool RecursiveRejected(CURLMcode code) {
return code == CURLM_RECURSIVE_API_CALL; }
241bool ProbePublicApiOnce(CURL* easy, CURLM* multi, ReentrantProbeKind kind) {
243 case ReentrantProbeKind::kEasySetopt:
244 return RecursiveRejected(curl_easy_setopt(easy, CURLOPT_REFERER,
"reentrant.test"));
245 case ReentrantProbeKind::kEasyGetinfo: {
246 long response_code = 0;
247 return RecursiveRejected(curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &response_code));
249 case ReentrantProbeKind::kEasyHeader: {
252 struct curl_header* header =
nullptr;
253 (void)curl_easy_header(easy,
"Content-Type", 0, kAllHeaderOrigins, -1, &header);
256 case ReentrantProbeKind::kEasyDuphandle: {
259 CURL* duplicate = curl_easy_duphandle(easy);
260 if (duplicate !=
nullptr) {
261 curl_easy_reset(duplicate);
262 curl_easy_cleanup(duplicate);
266 case ReentrantProbeKind::kEasyReset:
269 curl_easy_reset(easy);
271 case ReentrantProbeKind::kEasyPerform:
272 return RecursiveRejected(curl_easy_perform(easy));
273 case ReentrantProbeKind::kEasyUpkeep:
274 return RecursiveRejected(curl_easy_upkeep(easy));
275 case ReentrantProbeKind::kEasySend: {
277 std::size_t sent = 0;
278 return RecursiveRejected(curl_easy_send(easy, &
byte, 1, &sent));
280 case ReentrantProbeKind::kEasyRecv: {
282 std::size_t received = 0;
283 return RecursiveRejected(curl_easy_recv(easy, &
byte, 1, &received));
285 case ReentrantProbeKind::kEasyPause:
286 return RecursiveRejected(curl_easy_pause(easy, CURLPAUSE_CONT));
287 case ReentrantProbeKind::kMultiPerform: {
289 return RecursiveRejected(curl_multi_perform(multi, &running));
291 case ReentrantProbeKind::kMultiSetopt:
292 return RecursiveRejected(curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, 0L));
293 case ReentrantProbeKind::kMultiAddHandle:
294 return RecursiveRejected(curl_multi_add_handle(multi, easy));
295 case ReentrantProbeKind::kMultiWait: {
297 return RecursiveRejected(curl_multi_wait(multi,
nullptr, 0, 0, &numfds));
304void ProbeInfoDescriptor(CURL* easy,
const InfoDescriptor& descriptor) {
305 switch (descriptor.result_type) {
306 case InfoResultType::kString: {
307 char* result =
nullptr;
308 (void)curl_easy_getinfo(easy, descriptor.info, &result);
311 case InfoResultType::kLong: {
313 (void)curl_easy_getinfo(easy, descriptor.info, &result);
316 case InfoResultType::kDouble: {
318 (void)curl_easy_getinfo(easy, descriptor.info, &result);
321 case InfoResultType::kOffset: {
322 curl_off_t result = 0;
323 (void)curl_easy_getinfo(easy, descriptor.info, &result);
326 case InfoResultType::kSocket: {
327 curl_socket_t result = CURL_SOCKET_BAD;
328 (void)curl_easy_getinfo(easy, descriptor.info, &result);
331 case InfoResultType::kCertificateInfo: {
332 struct curl_certinfo* result =
nullptr;
333 (void)curl_easy_getinfo(easy, descriptor.info, &result);
336 case InfoResultType::kTlsSessionInfo: {
337 struct curl_tlssessioninfo* result =
nullptr;
338 (void)curl_easy_getinfo(easy, descriptor.info, &result);
341 case InfoResultType::kOwnedSlist: {
342 struct curl_slist* result =
nullptr;
343 (void)curl_easy_getinfo(easy, descriptor.info, &result);
344 curl_slist_free_all(result);
347 case InfoResultType::kUnknown: {
348 void* result =
nullptr;
349 (void)curl_easy_getinfo(easy, descriptor.info, &result);
358void ProbeKnownErrorStringsOnce() {
359 static const bool probed = [] {
360 for (
int code = 0; code <= static_cast<int>(CURL_LAST); ++code) {
361 (void)curl_easy_strerror(
static_cast<CURLcode
>(code));
363 (void)curl_multi_strerror(CURLM_CALL_MULTI_PERFORM);
364 for (
int code = 0; code <= static_cast<int>(CURLM_LAST); ++code) {
365 (void)curl_multi_strerror(
static_cast<CURLMcode
>(code));
367 for (
int code = 0; code <= static_cast<int>(CURLSHE_LAST); ++code) {
368 (void)curl_share_strerror(
static_cast<CURLSHcode
>(code));
370 for (
int code = 0; code <= static_cast<int>(CURLUE_LAST); ++code) {
371 (void)curl_url_strerror(
static_cast<CURLUcode
>(code));
382void ProbeEasyOptionMetadataOnce() {
383 static const bool probed = [] {
384 const struct curl_easyoption* option =
nullptr;
385 std::size_t count = 0;
386 while (count++ < 512 && (option = curl_easy_option_next(option)) !=
nullptr) {
387 (void)curl_easy_option_by_name(option->name);
388 (void)curl_easy_option_by_id(option->id);
390 (void)curl_easy_option_by_name(
"");
391 (void)curl_easy_option_by_name(
"NOT_A_CURL_OPTION");
392 (void)curl_easy_option_by_id(CURLOPT_LASTENTRY);
413 active_multi_(nullptr),
414 guessed_scheme_url_(false),
415 version_info_probed_(false),
416 https_proxy_feature_present_(false) {
417 ProbeKnownErrorStringsOnce();
418 ProbeEasyOptionMetadataOnce();
419 if (plan_.probe_version_info()) {
420 const curl_version_info_data* info = curl_version_info(CURLVERSION_NOW);
421 version_info_probed_ = info !=
nullptr;
422 if (info !=
nullptr) {
423 https_proxy_feature_present_ = (info->features & CURL_VERSION_HTTPS_PROXY) != 0;
426 if (info->feature_names !=
nullptr) {
427 for (std::size_t index = 0; index < 64 && info->feature_names[index] !=
nullptr; ++index) {
428 (void)std::string_view(info->feature_names[index]);
433 ProbeUrlAndEscaping(url);
434 response_callback_state_.owner =
this;
435 const bool wants_response_callback = plan_.pause_response_once() || plan_.reentrant_probe_selectors_size() > 0;
436 if (wants_response_callback) {
437 response_callback_state_.pause_once = plan_.pause_response_once();
441 (void)curl_easy_setopt(easy_, CURLOPT_WRITEDATA, &response_callback_state_);
442 (void)curl_easy_setopt(easy_, CURLOPT_WRITEFUNCTION, &ApiLifecycle::ResponseWrite);
444 if (plan_.attach_share()) {
457std::size_t ApiLifecycle::ResponseWrite(
char* , std::size_t size, std::size_t nmemb,
void* user_data) {
458 auto* state =
static_cast<ResponseCallbackState*
>(user_data);
459 if (state ==
nullptr || (size != 0 && nmemb > std::numeric_limits<std::size_t>::max() / size)) {
462 const std::size_t bytes = size * nmemb;
463 if (bytes != 0 && state->pause_once && !state->pause_returned) {
464 state->pause_returned =
true;
465 return CURL_WRITEFUNC_PAUSE;
467 if (bytes > std::numeric_limits<std::size_t>::max() - state->bytes_received) {
470 state->bytes_received += bytes;
471 if (bytes != 0 && state->owner !=
nullptr && !state->probes_done) {
472 state->probes_done =
true;
473 state->owner->RunReentrantProbes();
496 return response_callback_state_.recursive_rejections;
503void ApiLifecycle::RunReentrantProbes() {
505 static_cast<std::size_t
>(plan_.reentrant_probe_selectors_size()));
506 for (std::size_t index = 0; index < count; ++index) {
507 const auto selector =
static_cast<std::size_t
>(plan_.reentrant_probe_selectors(
static_cast<int>(index)));
508 const ReentrantProbe& probe = kReentrantProbes[selector % kReentrantProbeCount];
509 if (probe.needs_multi && active_multi_ ==
nullptr) {
512 ++response_callback_state_.probes_run;
513 if (ProbePublicApiOnce(easy_, active_multi_, probe.kind)) {
514 ++response_callback_state_.recursive_rejections;
522void ApiLifecycle::ShareLock(CURL* , curl_lock_data , curl_lock_access ,
void* user_data) {
523 auto* state =
static_cast<ShareCallbackState*
>(user_data);
529void ApiLifecycle::ShareUnlock(CURL* , curl_lock_data ,
void* user_data) {
530 auto* state =
static_cast<ShareCallbackState*
>(user_data);
538void ApiLifecycle::ConfigureShare() {
539 share_ = curl_share_init();
540 if (share_ ==
nullptr) {
547 (void)curl_share_setopt(share_, CURLSHOPT_USERDATA, &share_callback_state_);
548 (void)curl_share_setopt(share_, CURLSHOPT_LOCKFUNC, &ApiLifecycle::ShareLock);
549 (void)curl_share_setopt(share_, CURLSHOPT_UNLOCKFUNC, &ApiLifecycle::ShareUnlock);
551 const std::size_t selector_count = std::min<std::size_t>(scenario_limits::kMaxApiShareDataSelectors,
552 static_cast<std::size_t
>(plan_.share_data_selectors_size()));
553 for (std::size_t index = 0; index < selector_count; ++index) {
554 const std::uint32_t selector = plan_.share_data_selectors(
static_cast<int>(index));
555 const curl_lock_data data = kShareData[selector % kShareDataCount];
556 if (curl_share_setopt(share_, CURLSHOPT_SHARE, data) == CURLSHE_OK) {
562 (void)curl_share_setopt(share_, CURLSHOPT_UNSHARE, data);
563 (void)curl_share_setopt(share_, CURLSHOPT_SHARE, data);
567 if (curl_easy_setopt(easy_, CURLOPT_SHARE, share_) == CURLE_OK) {
570 (void)curl_share_setopt(share_, CURLSHOPT_USERDATA, &share_callback_state_);
571 (void)curl_share_cleanup(share_);
580void ApiLifecycle::CleanupShare() {
581 if (share_ ==
nullptr) {
584 if (curl_share_cleanup(share_) == CURLSHE_OK) {
594void ApiLifecycle::ProbeUrlAndEscaping(std::string_view url) {
595 const std::size_t bounded_size = std::min(url.size(), scenario_limits::kMaxApiStringBytes);
596 const std::string input(url.substr(0, bounded_size));
598 CURLU* url_handle = curl_url();
599 if (url_handle !=
nullptr) {
600 if (plan_.guess_scheme_url()) {
601 constexpr char kGuessInput[] =
"ftp.example.test/path";
602 if (curl_url_set(url_handle, CURLUPART_URL, kGuessInput, CURLU_GUESS_SCHEME) == CURLUE_OK) {
603 char* scheme =
nullptr;
604 if (curl_url_get(url_handle, CURLUPART_SCHEME, &scheme, 0) == CURLUE_OK) {
605 guessed_scheme_url_ = scheme !=
nullptr && std::string_view(scheme) ==
"ftp";
609 curl_url_cleanup(url_handle);
610 url_handle = curl_url();
613 if (url_handle !=
nullptr) {
614 const unsigned int parse_flags = CURLU_ALLOW_SPACE | CURLU_NON_SUPPORT_SCHEME;
615 if (curl_url_set(url_handle, CURLUPART_URL, input.c_str(), parse_flags) != CURLUE_OK) {
616 (void)curl_url_set(url_handle, CURLUPART_SCHEME,
"http", 0);
617 (void)curl_url_set(url_handle, CURLUPART_HOST,
"api.test", 0);
618 (void)curl_url_set(url_handle, CURLUPART_PATH, input.c_str(), CURLU_URLENCODE);
624 for (
const CURLUPart part : kUrlParts) {
625 ProbeUrlPart(url_handle, part, 0);
626 ProbeUrlPart(url_handle, part, CURLU_URLDECODE);
628 ProbeUrlPart(url_handle, CURLUPART_URL, CURLU_DEFAULT_PORT);
629 ProbeUrlPart(url_handle, CURLUPART_URL, CURLU_NO_DEFAULT_PORT);
630 ProbeUrlPart(url_handle, CURLUPART_URL, CURLU_URLENCODE);
631#if LIBCURL_VERSION_NUM >= 0x075800
632 ProbeUrlPart(url_handle, CURLUPART_HOST, CURLU_PUNYCODE);
634#if LIBCURL_VERSION_NUM >= 0x080300
635 ProbeUrlPart(url_handle, CURLUPART_HOST, CURLU_PUNY2IDN);
637#if LIBCURL_VERSION_NUM >= 0x080800
638 ProbeUrlPart(url_handle, CURLUPART_QUERY, CURLU_GET_EMPTY);
639 ProbeUrlPart(url_handle, CURLUPART_FRAGMENT, CURLU_GET_EMPTY);
641#if LIBCURL_VERSION_NUM >= 0x080900
642 ProbeUrlPart(url_handle, CURLUPART_URL, CURLU_NO_GUESS_SCHEME);
648 CURLU* duplicate = curl_url_dup(url_handle);
649 if (duplicate !=
nullptr) {
650 (void)curl_url_set(duplicate, CURLUPART_QUERY, input.c_str(), CURLU_APPENDQUERY | CURLU_URLENCODE);
651 ProbeUrlPart(duplicate, CURLUPART_URL, 0);
652 curl_url_cleanup(duplicate);
654 curl_url_cleanup(url_handle);
660 const int input_length =
static_cast<int>(input.size());
661 char* escaped = curl_easy_escape(easy_, input.data(), input_length);
662 if (escaped !=
nullptr) {
663 int decoded_length = 0;
664 char* decoded = curl_easy_unescape(easy_, escaped, 0, &decoded_length);
668 int decoded_length = 0;
669 char* decoded = curl_easy_unescape(easy_, input.data(), input_length, &decoded_length);
691 std::array<bool, kInfoDescriptorCount> seen{};
693 static_cast<std::size_t
>(plan_.easy_info_selectors_size()));
694 for (std::size_t index = 0; index < selector_count; ++index) {
695 const std::uint32_t selector = plan_.easy_info_selectors(
static_cast<int>(index));
696 const std::size_t descriptor_index = selector % kInfoDescriptorCount;
697 if (!seen[descriptor_index]) {
698 ProbeInfoDescriptor(easy_, kInfoDescriptors[descriptor_index]);
699 seen[descriptor_index] =
true;
703 struct curl_header* header =
nullptr;
704 (void)curl_easy_header(easy_,
"Content-Type", 0, kAllHeaderOrigins, -1, &header);
706 for (std::size_t index = 0; index < kMaxResultHeaders; ++index) {
707 header = curl_easy_nextheader(easy_, kAllHeaderOrigins, -1, header);
708 if (header ==
nullptr) {
717 (void)curl_easy_upkeep(easy_);
722 (void)curl_easy_pause(easy_, CURLPAUSE_ALL);
723 (void)curl_easy_pause(easy_, CURLPAUSE_CONT);
732 if (!plan_.duplicate_easy()) {
736 if (duplicate !=
nullptr) {
737 curl_easy_reset(duplicate.get());
Safe ownership and typed probes for the dedicated public-API lane.
~ApiLifecycle()
RunScenario keeps this owner alive through easy cleanup, which releases even an incomplete connection...
std::size_t reentrant_recursive_rejections() const
void ProbeEasyDuplication()
Duplicate, reset, and destroy a scratch easy handle while every pointer- valued option copied from th...
bool version_info_probed() const
ApiLifecycle(CURL *easy, const curl::fuzzer::proto::ApiPlan &plan, std::string_view url)
Configure the plan's share handle and cover public error-string tables.
std::size_t reentrant_probes_run() const
bool response_pause_returned() const
void SetActiveMulti(CURLM *multi)
Hand the driving multi handle to the reentrancy probes so the multi-entry probes can target the live ...
void ProbeTransferResults(bool probe_upkeep)
Run correctly typed CURLINFO and response-header probes selected by the plan.
bool guessed_scheme_url() const
bool https_proxy_feature_present() const
std::size_t response_bytes_received() const
Common ownership types for independently cleaned-up curl handles.
std::unique_ptr< CURL, CurlEasyDeleter > CurlEasyPtr
Unique ownership of a CURL easy handle.
@ kString
string_value passed to a string or byte-data option.
constexpr std::size_t kMaxApiReentrantSelectors
Each reentrancy selector fires one public API call from inside a callback.
constexpr std::size_t kMaxApiInfoSelectors
One API iteration can cheaply inspect several result families, but an unbounded list would repeatedly...