Skip to content

deps: update libcurl to 8.19.0#435

Open
santigimeno wants to merge 1 commit intonode-v24.x-nsolid-v6.xfrom
santi/upgrade_curl
Open

deps: update libcurl to 8.19.0#435
santigimeno wants to merge 1 commit intonode-v24.x-nsolid-v6.xfrom
santi/upgrade_curl

Conversation

@santigimeno
Copy link
Member

@santigimeno santigimeno commented Mar 12, 2026

Summary by CodeRabbit

  • New Features

    • Upgraded libcurl to version 8.19.0 with improved protocol handling and error management.
  • Bug Fixes

    • Enhanced socket error handling and connection management.
    • Improved timeout calculation and HTTP/2 stream handling.
    • Fixed memory safety and const-correctness issues across protocol handlers.
  • Refactor

    • Restructured protocol implementation from handler-based to scheme-based architecture.
    • Reorganized verbose logging controls and debugging infrastructure.
    • Updated parameter naming conventions for consistency across public APIs.

@santigimeno santigimeno requested a review from RafaelGSS March 12, 2026 22:23
@santigimeno santigimeno self-assigned this Mar 12, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

Walkthrough

Updates curl library from version 8.18.0 to 8.19.0 with extensive architectural refactoring. Major changes include: transitioning from handler-centric to scheme-centric protocol organization, consolidating memory helpers and includes under curlx/ namespace, standardizing parameter names across public APIs, adjusting verbose output gating, and enhancing timeout and socket handling mechanisms.

Changes

Cohort / File(s) Summary
Version & Configuration Updates
include/curl/curlver.h, COPYING, curl_config.h.in, curl_config-cmake.h.in
Version bumped to 8.19.0; added CURL_CA_NATIVE, CURL_BORINGSSL_VERSION, CURL_PATCHSTAMP; removed CURL_DISABLE_TYPECHECK, HAVE_LONGLONG, HAVE_STRDUP, and related legacy macros.
Clang-Tidy & Build Configuration
.clang-tidy.yml, Makefile.am, Makefile.in, Makefile.inc, CMakeLists.txt, curl.gyp
Added clang-tidy configuration; restructured build variables with new CLANG_TIDY and TIDYFLAGS; reorganized source file lists (added basename.c, ws.c, vssh.c; removed strdup.c from main lib).
Verbose Output & Debug Gating
arpa_telnet.h, asyn-ares.c, cf-h2-proxy.c, cfilters.c, curl_gssapi.c, curl_sasl.c, curl_trc.c
Switched verbose output guards from CURL_DISABLE_VERBOSE_STRINGS to CURLVERBOSE; consolidated verbose-controlled feature definitions under new macro.
Protocol Handler to Scheme Migration
dict.c, dict.h, file.c, file.h, ftp.h, gopher.c, gopher.h, http.c, http.h, imap.c, imap.h, ldap.c, curl_ldap.h, curl_rtmp.c, curl_rtmp.h, mqtt.c, mqtt.h
Replaced public Curl_handler_* declarations with Curl_scheme_* and Curl_protocol_* structures across all protocol implementations; updated protocol references throughout codebase.
Core Framework API Changes
connect.c, connect.h, cshutdn.c, cshutdn.h, cfilters.c, cfilters.h, asyn-ares.c, asyn-thrdd.c, asyn.h
Removed duringconnect parameter from Curl_timeleft_ms/Curl_timeleft_now_ms; extended Curl_shutdown_start; changed parameter names for consistency (entry→dns, closeit→ctrl); introduced Curl_sigpipe_ctx for context management.
Public Header API Reorganization
include/curl/curl.h, include/curl/easy.h, include/curl/multi.h, include/curl/urlapi.h, include/curl/typecheck-gcc.h
Added parameter names to function signatures; updated CURLOPT/CURLAUTH macros for safer precedence; renamed error handling parameters (e.g., curl_easy_strerror now names parameter error).
Memory Helpers & String Functions
curlx/strdup.c, curlx/strdup.h, curlx/basename.c, curlx/basename.h, curlx/curlx.h
Renamed Curl_memdup→curlx_memdup, Curl_memdup0→curlx_memdup0; removed Curl_saferealloc; added curlx_basename helper; consolidated curlx includes under unified namespace.
Build System Headers
curlx/*.c, curlx/*.h, config-*.h, config-mac.h, config-os400.h, config-riscos.h, config-win32.h
Updated all curlx module includes from relative to curlx/-qualified paths; removed legacy config macros (HAVE_LONGLONG, HAVE_STRDUP, SEND_QUAL_ARG2); simplified platform-specific configurations.
Socket & Connection Handling
cf-socket.c, cf-socket.h, cf-ip-happy.c, cf-h1-proxy.c
Added Curl_sock_nosigpipe; replaced socktype comparisons with cf_socktype helper; switched from Curl_memdup to curlx_memdup; updated timeout calls; changed protocol checks from handler to scheme.
DNS & Address Resolution
curl_addrinfo.c, curl_addrinfo.h, hostip.c, hostip.h, hostip4.c, hostip6.c, asyn-ares.c
Updated parameter names (dnsentry→entry, address→dotted, paddr); changed Curl_dnscache_mk_entry to accept pointer-to-pointer; adjusted debug macro guards from CURLDEBUG to CURL_MEMDEBUG.
HTTP Protocol Layer
http.c, http.h, http1.c, http1.h, http2.c, http2.h, http_*.c
Replaced handler references with scheme; changed auth availability pointers from unsigned long* to uint32_t*; added http_should_fail helper; updated verbose gating; renamed parameters (header→name in push headers).
Configuration & Feature Flags
curl_setup.h, curl_setup_once.h, functypes.h, easy_lock.h, libcurl.rc
Massive curl_setup.h expansion with bool type, CURL_UNCONST, curl_bit, CURLVERBOSE, VERBOSE/NOVERBOSE macros; removed curl_setup_once.h entirely (consolidated into curl_setup.h); updated Windows mutex initialization.
Cryptographic & Utility Functions
curl_md5.c, md4.c, mprintf.c, curl_ntlm_core.c, curl_ntlm_core.h, hmac.c, curl_hmac.h, curl_sha256.h, curl_md5.h
Added const-correctness to crypto function parameters; renamed output parameters for consistency (outbuffer→output, buf→data); replaced u32plus with uint32_t; unified parameter naming.
High-Level Data Handling
easy.c, mime.c, mime.h, formdata.c, formdata.h, cookie.c, cookie.h, altsvc.c, altsvc.h
Added sigpipe context handling in easy_cleanup; updated mime parameter names (ptr→data); introduced conditional MIME/FORM API checks; renamed cookie parameters for clarity (c→ci, header→httpheader).
DNS & HSTS
doh.c, doh.h, hsts.c, hsts.h
Added DOH_DNS_BAD_ID and DOH_DNS_NAME_TOO_LONG error codes; renamed parameters (dns→dnsp, sts→header); updated Curl_dnscache_mk_entry calls; added file parameter to Curl_hsts_save.
Minor API & Macro Updates
bufq.h, dynhds.h, http1.h, cw-*.c, cw-pause.c, escape.c, hash.c, hash.h, headers.c, headers.h, if2ip.c, llist.h, and others
Macro constant formatting (parentheses removed from single-value defines); parameter names added to function prototypes; const-correctness improvements; verbose output wrapper adjustments.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Suggested reviewers

  • RafaelGSS

Poem

🐰 Whisker-twitching with delight,
Schemes and protocols shine bright,
Handlers transformed with care,
Names made consistent everywhere,
Curl hops forward to version eight-nineteen! 🎉

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch santi/upgrade_curl

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
deps/curl/lib/cfilters.c (1)

601-612: ⚠️ Potential issue | 🟡 Minor

Guard conn->scheme before using non-network fallback.

Curl_conn_is_connected() dereferences conn->scheme->flags without a null check. If this is called on a partially initialized connection, it can crash.

💡 Suggested fix
 bool Curl_conn_is_connected(struct connectdata *conn, int sockindex)
 {
   struct Curl_cfilter *cf;
 
-  if(!CONN_SOCK_IDX_VALID(sockindex))
+  if(!conn || !CONN_SOCK_IDX_VALID(sockindex))
     return FALSE;
   cf = conn->cfilter[sockindex];
   if(cf)
     return (bool)cf->connected;
-  else if(conn->scheme->flags & PROTOPT_NONETWORK)
+  else if(conn->scheme && (conn->scheme->flags & PROTOPT_NONETWORK))
     return TRUE;
   return FALSE;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/cfilters.c` around lines 601 - 612, Curl_conn_is_connected
dereferences conn->scheme->flags without checking conn->scheme; update the
function Curl_conn_is_connected to guard access to conn->scheme before using
PROTOPT_NONETWORK (e.g. only evaluate (conn->scheme->flags & PROTOPT_NONETWORK)
when conn->scheme is non-NULL) and keep existing behavior for conn->cfilter/
cf->connected; if conn->scheme is NULL, treat it as not having PROTOPT_NONETWORK
(return FALSE).
deps/curl/lib/doh.c (1)

1254-1278: ⚠️ Potential issue | 🟠 Major

Propagate DNS-entry creation/cache failures instead of returning success.

If Curl_dnscache_mk_entry() returns NULL, this block still reaches Line 1283 with result == CURLE_OK and reports a completed lookup with no entry. A Curl_dnscache_add() failure also leaves data->state.async.dns live on the error path.

Suggested fix
       /* we got a response, create a dns entry. */
       dns = Curl_dnscache_mk_entry(data, &ai, dohp->host, 0,
                                    dohp->port, FALSE);
-      if(dns) {
+      if(!dns) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto error;
+      }
+      {
         /* Now add and HTTPSRR information if we have */
 `#ifdef` USE_HTTPSRR
         if(de.numhttps_rrs > 0 && result == CURLE_OK) {
           struct Curl_https_rrinfo *hrr = NULL;
           result = doh_resp_decode_httpsrr(data, de.https_rrs->val,
@@
 `#endif` /* USE_HTTPSRR */
         /* and add the entry to the cache */
         data->state.async.dns = dns;
         result = Curl_dnscache_add(data, dns);
+        if(result) {
+          Curl_resolv_unlink(data, &data->state.async.dns);
+          goto error;
+        }
         *dnsp = data->state.async.dns;
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/doh.c` around lines 1254 - 1278, If Curl_dnscache_mk_entry()
returns NULL or Curl_dnscache_add() fails you must not report success; check the
return of Curl_dnscache_mk_entry (dns) and set an appropriate error (e.g. result
= CURLE_OUT_OF_MEMORY or CURLE_COULDNT_RESOLVE_HOST) and goto error when NULL,
and after calling Curl_dnscache_add detect non-OK result, clear/unlink
data->state.async.dns (use Curl_resolv_unlink or free the dns entry) so it isn't
left live, and then propagate the error (goto error); update handling around the
symbols Curl_dnscache_mk_entry, dns, data->state.async.dns and Curl_dnscache_add
to implement these checks and cleanup.
🧹 Nitpick comments (1)
deps/curl/lib/Makefile.in (1)

1034-1034: Align variable usage across Makefile.am and regenerate Makefile.in.

The declaration CLANG_TIDY = @CLANG_TIDY@ exists in Makefile.in (line 1034), but the recipe at line 5933 uses @CLANG_TIDY@ directly instead of $(CLANG_TIDY). More importantly, the source file Makefile.am lacks this variable declaration entirely and also uses @CLANG_TIDY@ in its recipe (line 178).

To enable straightforward make CLANG_TIDY=... tidy overrides, add the variable declaration to Makefile.am and update the recipe to use $(CLANG_TIDY), then regenerate Makefile.in from it rather than editing the generated file directly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/Makefile.in` at line 1034, Add a declarative variable for
CLANG_TIDY in Makefile.am (e.g., CLANG_TIDY = `@CLANG_TIDY`@) and update any
recipes in Makefile.am that currently use the literal `@CLANG_TIDY`@ (the tidy
recipe referenced) to use $(CLANG_TIDY) instead so overrides like `make
CLANG_TIDY=... tidy` work; then regenerate Makefile.in from the updated
Makefile.am (do not edit Makefile.in directly) so the generated CLANG_TIDY =
`@CLANG_TIDY`@ line in Makefile.in and all recipe usages are consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@deps/curl/lib/altsvc.c`:
- Around line 312-323: The function Curl_altsvc_ctrl currently rejects ctrl == 0
which prevents resetting data->asi->flags; change the input validation in
Curl_altsvc_ctrl so zero is accepted as a valid "reset/disable" value and is
assigned to data->asi->flags, and instead validate only against unknown bits
(e.g., compute a ALTSVC_CTRL_VALID mask and return CURLE_BAD_FUNCTION_ARGUMENT
if (ctrl & ~ALTSVC_CTRL_VALID) != 0); ensure data->asi is still allocated as
before and that data->asi->flags = ctrl handles the zero case.

In `@deps/curl/lib/asyn-ares.c`:
- Around line 329-335: The OOM path after Curl_dnscache_mk_entry currently jumps
to the early "out" label and skips async_ares_cleanup, leaking ares->hinfo and
nested allocations; change the control flow so that when Curl_dnscache_mk_entry
returns NULL you set result = CURLE_OUT_OF_MEMORY and jump to the common cleanup
that runs async_ares_cleanup (or call async_ares_cleanup before jumping out)
instead of jumping directly to out. Locate the block around
Curl_dnscache_mk_entry and replace the direct goto out with a goto or call that
ensures async_ares_cleanup(data) runs (referencing Curl_dnscache_mk_entry,
async_ares_cleanup, and ares->hinfo).

In `@deps/curl/lib/cf-ip-happy.c`:
- Around line 490-493: The final trace loop uses a local index i (set by
VERBOSE(i = 0)) but never increments it, so every trace prints "baller 0";
inside the loop that iterates for(a = bs->running; a; a = a->next) in
cf-ip-happy.c, increment i each iteration (e.g., i++ at the end of the loop
body) before or after calling CURL_TRC_CF so the trace message shows the correct
attempt index for each entry.

In `@deps/curl/lib/cf-socket.c`:
- Around line 315-317: The code mutates addr->socktype to add SOCK_CLOEXEC
before invoking the user fopen socket callback, which exposes a flag-augmented
value to that callback; change this so the public fopen socket callback
(data->set.fopensocket / the fopensocket invocation at the call site) sees the
original plain socket type (e.g., SOCK_STREAM/SOCK_DGRAM). Either apply
SOCK_CLOEXEC only after checking if data->set.fopensocket is set (like the
SOCK_NONBLOCK pattern) or pass a local copy of addr->socktype to the callback
and set the CLOEXEC flag on the actual addr->socktype only when creating the
socket; ensure references to addr->socktype and the fopensocket callback name
are used to locate and adjust the logic.
- Around line 357-374: Replace the direct sclose(*sockfd) calls in the socket
setup failure paths (inside the Curl_sock_nosigpipe and fcntl error blocks) with
the callback-aware close helper socket_close(data, data->conn, TRUE, *sockfd) so
any registered fclosesocket callback gets invoked; after invoking socket_close
set *sockfd = CURL_SOCKET_BAD and return CURLE_COULDNT_CONNECT as before. Ensure
you update both failure branches where sclose is used (the block around
Curl_sock_nosigpipe and the block around fcntl(F_SETFD, FD_CLOEXEC)) and keep
the existing failf messages and error returns unchanged.

In `@deps/curl/lib/curl_threads.h`:
- Line 44: The macro Curl_mutex_init currently calls
InitializeCriticalSectionEx(m, 0, 1) with an invalid Flags value; change the
third argument to a valid flag (either 0 or
CRITICAL_SECTION_NO_DEBUG_INFO/0x01000000) so initialization uses a supported
flag. Update the Curl_mutex_init definition to pass the proper flag constant
(and include or ensure CRITICAL_SECTION_NO_DEBUG_INFO is available) instead of
the literal 1 to avoid silent failures.

In `@deps/curl/lib/curl_trc.c`:
- Around line 204-225: The Curl_trc_feat_* symbols (Curl_trc_feat_multi,
Curl_trc_feat_read, Curl_trc_feat_write, Curl_trc_feat_dns, Curl_trc_feat_timer)
are only defined under `#ifdef` CURLVERBOSE but are referenced by code paths that
can be compiled when CURL_DISABLE_VERBOSE_STRINGS is defined (and CURLVERBOSE is
not), causing undefined symbols; fix by aligning the guards so consumers and
providers match: either move the definitions in curl_trc.c out of the
CURLVERBOSE guard and into a `#ifndef` CURL_DISABLE_VERBOSE_STRINGS block (so the
symbols exist whenever verbose strings are enabled), or change the header guard
at the consumer block to require CURLVERBOSE as well—update the preprocessor
condition around the Curl_trc_feat_* definitions (and any corresponding
declarations) so both definition and use are controlled by the same macro to
eliminate the linker error.

In `@deps/curl/lib/curlx/basename.c`:
- Around line 54-71: The curlx_basename fallback (function curlx_basename) must
first implement POSIX basename semantics: if path is NULL or empty return ".",
strip trailing slashes (while preserving root) before finding the last
separator, and if the remaining string consists only of slashes return "/"
(root); otherwise return the final component after the last '/' or '\\'. Modify
curlx_basename to check for NULL/empty, remove trailing separators with care to
detect root-only inputs, then locate the last separator via strrchr and return
the correct component per POSIX.

In `@deps/curl/lib/curlx/snprintf.h`:
- Around line 30-37: The Windows wrapper curlx_win32_snprintf currently has a
void return type while SNPRINTF is aliased to it under WITHOUT_LIBCURL, causing
a signature mismatch with snprintf (which returns int); change the declaration
of curlx_win32_snprintf to return int (matching snprintf) and update its
implementation to return the number of characters written (or a compatible
snprintf-style value), then keep the macro SNPRINTF mapped to that function so
all branches share the same int return contract.

In `@deps/curl/lib/http_aws_sigv4.c`:
- Around line 150-197: The split_to_dyn_array function currently treats reaching
MAX_QUERY_COMPONENTS as overflow because it checks for equality after
incrementing num_splits; change both overflow checks (the one inside the for
loop after storing a segment and the one after the final segment handling) to
trigger only when num_splits exceeds MAX_QUERY_COMPONENTS (i.e., use
if(++num_splits > MAX_QUERY_COMPONENTS) result = CURLE_TOO_LARGE) so exactly
MAX_QUERY_COMPONENTS entries are allowed; update references where num_splits is
incremented to use the > check accordingly.

In `@deps/curl/lib/http.c`:
- Around line 1435-1441: In Curl_compareheader the loop only runs once because
len is initialized and compared to the same curlx_strlen(&val) value; compute
the total length once (e.g. size_t total = curlx_strlen(&val)), set len = total
and iterate while len >= clen, advancing p and decrementing len so you test
every possible start position where remaining length >= clen; update uses of
curlx_strlen(&val) in that block to use the cached total and keep the comparison
using clen, referencing variables val, clen, p, len and the Curl_compareheader
function.
- Around line 516-527: The 401 handling in http_should_fail() treats any missing
data->state.aptr.user as terminal and will trigger CURLOPT_FAILONERROR before
Bearer auth can be selected; update the 401 check to allow continuation when a
Bearer credential is present by also checking the bearer string (e.g.
data->state.aptr.bearer or the STRING_BEARER storage used for CURLAUTH_BEARER)
so change the condition in the if((httpcode == 401) && !data->state.aptr.user)
branch to also verify there is no bearer token (e.g. &&
!data->state.aptr.bearer) so Curl_http_auth_act() can still pick
CURLAUTH_BEARER.

In `@deps/curl/lib/http2.c`:
- Around line 263-266: The problem is that H2_STREAM_CTX() looks up stream state
by data->mid, but h2_duphandle() calls http2_data_setup() on a duplicated push
handle before Curl_multi_add_perform() assigns newhandle->mid, which creates an
orphaned key-0 h2_stream_ctx; to fix, stop creating/looking up a stream context
keyed by mid while mid==0: either (A) make H2_STREAM_CTX() return NULL when
(data)->mid is zero and ensure http2_data_setup() is not invoked in
h2_duphandle() until after Curl_multi_add_perform() has assigned newhandle->mid,
or (B) defer the call to http2_data_setup() from h2_duphandle() to the
post-Curl_multi_add_perform() path where newhandle->mid is non-zero (references:
H2_STREAM_CTX, h2_duphandle(), http2_data_setup(), Curl_multi_add_perform(),
data->mid, newhandle->mid).

In `@deps/curl/lib/http2.h`:
- Around line 39-42: Add a direct include of nghttp2's public header so the
nghttp2_frame type is declared where Curl_nghttp2_fr_print is declared: modify
deps/curl/lib/http2.h to `#include` <nghttp2/nghttp2.h> (preferably guarded by the
existing macro that enables nghttp2, e.g. surround the include and the
Curl_nghttp2_fr_print prototype with the same USE_NGHTTP2/CURL_DISABLE_HTTP2
conditional) so callers of http2.h (like cf-https-connect.c, curl_trc.c,
multi.c) get the nghttp2_frame typedef when CURLVERBOSE is enabled.

In `@deps/curl/lib/imap.c`:
- Around line 1179-1214: The matcher currently only inspects the message-set and
thus treats any FETCH with a range/comma as a "listing", which causes BODY[]
responses to be swallowed; update is_custom_fetch_listing() (and its helper
is_custom_fetch_listing_match()) to not just recognize the message-set but also
verify the fetch data-item token that follows and only return true for
listing-style queries (e.g. when the data-item is FLAGS/ (FLAGS ...)), otherwise
return FALSE so UID/FETCH ... BODY[] responses are handled normally by
imap_state_listsearch_resp().
- Around line 219-236: The dynbuf initialized by curlx_dyn_init(&line, 2000)
must be freed on all error paths in imap_atom(): when any curlx_dyn_addn() fails
you should call curlx_dyn_free(&line) before returning NULL (or refactor to a
single cleanup label and jump there on error), ensuring curlx_dyn_free(&line) is
called for every early return and only return curlx_dyn_ptr(&line) on success.
- Around line 1697-1709: The imap_easy_reset function currently frees string
fields but doesn't clear the UIDVALIDITY tracking, so subsequent requests on the
same easy handle inherit the previous request's UIDVALIDITY constraint; update
imap_easy_reset to reset the UIDVALIDITY state by clearing imap->uidvalidity_set
(and reset imap->uidvalidity as well) so the next transfer starts without the
previous UIDVALIDITY constraint, referencing the imap_easy_reset function and
the imap->uidvalidity_set/imap->uidvalidity fields.

In `@deps/curl/lib/mprintf.c`:
- Around line 558-564: The va_arg usage for %ll boundaries is incorrect: in the
switch cases MTYPE_LONGLONGU and MTYPE_LONGLONG replace va_arg(arglist,
uint64_t)/va_arg(arglist, int64_t) with va_arg(arglist, unsigned long
long)/va_arg(arglist, long long) and then cast the returned long long/unsigned
long long to the union fields iptr->val.numu and iptr->val.nums respectively;
additionally, where FLAGS_LONGLONG causes a pointer to be stored/retrieved (the
code that currently casts to int64_t *), change that cast to long long * (or
unsigned long long * as appropriate) before assigning/copying so the varargs
boundary uses long long types per the %ll specifiers.

---

Outside diff comments:
In `@deps/curl/lib/cfilters.c`:
- Around line 601-612: Curl_conn_is_connected dereferences conn->scheme->flags
without checking conn->scheme; update the function Curl_conn_is_connected to
guard access to conn->scheme before using PROTOPT_NONETWORK (e.g. only evaluate
(conn->scheme->flags & PROTOPT_NONETWORK) when conn->scheme is non-NULL) and
keep existing behavior for conn->cfilter/ cf->connected; if conn->scheme is
NULL, treat it as not having PROTOPT_NONETWORK (return FALSE).

In `@deps/curl/lib/doh.c`:
- Around line 1254-1278: If Curl_dnscache_mk_entry() returns NULL or
Curl_dnscache_add() fails you must not report success; check the return of
Curl_dnscache_mk_entry (dns) and set an appropriate error (e.g. result =
CURLE_OUT_OF_MEMORY or CURLE_COULDNT_RESOLVE_HOST) and goto error when NULL, and
after calling Curl_dnscache_add detect non-OK result, clear/unlink
data->state.async.dns (use Curl_resolv_unlink or free the dns entry) so it isn't
left live, and then propagate the error (goto error); update handling around the
symbols Curl_dnscache_mk_entry, dns, data->state.async.dns and Curl_dnscache_add
to implement these checks and cleanup.

---

Nitpick comments:
In `@deps/curl/lib/Makefile.in`:
- Line 1034: Add a declarative variable for CLANG_TIDY in Makefile.am (e.g.,
CLANG_TIDY = `@CLANG_TIDY`@) and update any recipes in Makefile.am that currently
use the literal `@CLANG_TIDY`@ (the tidy recipe referenced) to use $(CLANG_TIDY)
instead so overrides like `make CLANG_TIDY=... tidy` work; then regenerate
Makefile.in from the updated Makefile.am (do not edit Makefile.in directly) so
the generated CLANG_TIDY = `@CLANG_TIDY`@ line in Makefile.in and all recipe
usages are consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c6c3bb3d-bc7e-47c6-95c2-adb3ff1c16fb

📥 Commits

Reviewing files that changed from the base of the PR and between 369ce34 and b1ed02d.

📒 Files selected for processing (300)
  • deps/curl/.clang-tidy.yml
  • deps/curl/COPYING
  • deps/curl/curl.gyp
  • deps/curl/include/Makefile.in
  • deps/curl/include/curl/Makefile.am
  • deps/curl/include/curl/Makefile.in
  • deps/curl/include/curl/curl.h
  • deps/curl/include/curl/curlver.h
  • deps/curl/include/curl/easy.h
  • deps/curl/include/curl/header.h
  • deps/curl/include/curl/multi.h
  • deps/curl/include/curl/system.h
  • deps/curl/include/curl/typecheck-gcc.h
  • deps/curl/include/curl/urlapi.h
  • deps/curl/lib/CMakeLists.txt
  • deps/curl/lib/Makefile.am
  • deps/curl/lib/Makefile.in
  • deps/curl/lib/Makefile.inc
  • deps/curl/lib/altsvc.c
  • deps/curl/lib/altsvc.h
  • deps/curl/lib/amigaos.c
  • deps/curl/lib/arpa_telnet.h
  • deps/curl/lib/asyn-ares.c
  • deps/curl/lib/asyn-base.c
  • deps/curl/lib/asyn-thrdd.c
  • deps/curl/lib/asyn.h
  • deps/curl/lib/bufq.h
  • deps/curl/lib/bufref.c
  • deps/curl/lib/cf-h1-proxy.c
  • deps/curl/lib/cf-h1-proxy.h
  • deps/curl/lib/cf-h2-proxy.c
  • deps/curl/lib/cf-haproxy.c
  • deps/curl/lib/cf-https-connect.c
  • deps/curl/lib/cf-ip-happy.c
  • deps/curl/lib/cf-socket.c
  • deps/curl/lib/cf-socket.h
  • deps/curl/lib/cfilters.c
  • deps/curl/lib/cfilters.h
  • deps/curl/lib/config-mac.h
  • deps/curl/lib/config-os400.h
  • deps/curl/lib/config-plan9.h
  • deps/curl/lib/config-riscos.h
  • deps/curl/lib/config-win32.h
  • deps/curl/lib/conncache.c
  • deps/curl/lib/conncache.h
  • deps/curl/lib/connect.c
  • deps/curl/lib/connect.h
  • deps/curl/lib/content_encoding.c
  • deps/curl/lib/cookie.c
  • deps/curl/lib/cookie.h
  • deps/curl/lib/cshutdn.c
  • deps/curl/lib/cshutdn.h
  • deps/curl/lib/curl_addrinfo.c
  • deps/curl/lib/curl_addrinfo.h
  • deps/curl/lib/curl_config-cmake.h.in
  • deps/curl/lib/curl_config.h.in
  • deps/curl/lib/curl_fnmatch.c
  • deps/curl/lib/curl_fopen.c
  • deps/curl/lib/curl_get_line.c
  • deps/curl/lib/curl_gssapi.c
  • deps/curl/lib/curl_gssapi.h
  • deps/curl/lib/curl_hmac.h
  • deps/curl/lib/curl_ldap.h
  • deps/curl/lib/curl_md5.h
  • deps/curl/lib/curl_ntlm_core.c
  • deps/curl/lib/curl_ntlm_core.h
  • deps/curl/lib/curl_printf.h
  • deps/curl/lib/curl_range.c
  • deps/curl/lib/curl_rtmp.c
  • deps/curl/lib/curl_rtmp.h
  • deps/curl/lib/curl_sasl.c
  • deps/curl/lib/curl_sasl.h
  • deps/curl/lib/curl_setup.h
  • deps/curl/lib/curl_setup_once.h
  • deps/curl/lib/curl_sha256.h
  • deps/curl/lib/curl_sha512_256.c
  • deps/curl/lib/curl_share.c
  • deps/curl/lib/curl_share.h
  • deps/curl/lib/curl_sspi.c
  • deps/curl/lib/curl_sspi.h
  • deps/curl/lib/curl_threads.h
  • deps/curl/lib/curl_trc.c
  • deps/curl/lib/curl_trc.h
  • deps/curl/lib/curlx/base64.c
  • deps/curl/lib/curlx/basename.c
  • deps/curl/lib/curlx/basename.h
  • deps/curl/lib/curlx/curlx.h
  • deps/curl/lib/curlx/dynbuf.c
  • deps/curl/lib/curlx/fopen.c
  • deps/curl/lib/curlx/fopen.h
  • deps/curl/lib/curlx/inet_ntop.c
  • deps/curl/lib/curlx/inet_ntop.h
  • deps/curl/lib/curlx/inet_pton.c
  • deps/curl/lib/curlx/inet_pton.h
  • deps/curl/lib/curlx/multibyte.c
  • deps/curl/lib/curlx/multibyte.h
  • deps/curl/lib/curlx/nonblock.c
  • deps/curl/lib/curlx/snprintf.c
  • deps/curl/lib/curlx/snprintf.h
  • deps/curl/lib/curlx/strcopy.c
  • deps/curl/lib/curlx/strdup.c
  • deps/curl/lib/curlx/strdup.h
  • deps/curl/lib/curlx/strerr.c
  • deps/curl/lib/curlx/strparse.c
  • deps/curl/lib/curlx/strparse.h
  • deps/curl/lib/curlx/timediff.c
  • deps/curl/lib/curlx/timediff.h
  • deps/curl/lib/curlx/timeval.c
  • deps/curl/lib/curlx/timeval.h
  • deps/curl/lib/curlx/version_win32.c
  • deps/curl/lib/curlx/version_win32.h
  • deps/curl/lib/curlx/wait.c
  • deps/curl/lib/curlx/wait.h
  • deps/curl/lib/curlx/warnless.c
  • deps/curl/lib/curlx/warnless.h
  • deps/curl/lib/curlx/winapi.c
  • deps/curl/lib/cw-out.c
  • deps/curl/lib/cw-pause.c
  • deps/curl/lib/dict.c
  • deps/curl/lib/dict.h
  • deps/curl/lib/doh.c
  • deps/curl/lib/doh.h
  • deps/curl/lib/dynhds.c
  • deps/curl/lib/dynhds.h
  • deps/curl/lib/easy.c
  • deps/curl/lib/easy_lock.h
  • deps/curl/lib/escape.c
  • deps/curl/lib/escape.h
  • deps/curl/lib/fake_addrinfo.c
  • deps/curl/lib/fake_addrinfo.h
  • deps/curl/lib/file.c
  • deps/curl/lib/file.h
  • deps/curl/lib/formdata.c
  • deps/curl/lib/formdata.h
  • deps/curl/lib/ftp.c
  • deps/curl/lib/ftp.h
  • deps/curl/lib/ftplistparser.h
  • deps/curl/lib/functypes.h
  • deps/curl/lib/getinfo.c
  • deps/curl/lib/gopher.c
  • deps/curl/lib/gopher.h
  • deps/curl/lib/hash.c
  • deps/curl/lib/hash.h
  • deps/curl/lib/headers.c
  • deps/curl/lib/headers.h
  • deps/curl/lib/hmac.c
  • deps/curl/lib/hostip.c
  • deps/curl/lib/hostip.h
  • deps/curl/lib/hostip4.c
  • deps/curl/lib/hostip6.c
  • deps/curl/lib/hsts.c
  • deps/curl/lib/hsts.h
  • deps/curl/lib/http.c
  • deps/curl/lib/http.h
  • deps/curl/lib/http1.c
  • deps/curl/lib/http1.h
  • deps/curl/lib/http2.c
  • deps/curl/lib/http2.h
  • deps/curl/lib/http_aws_sigv4.c
  • deps/curl/lib/http_chunks.c
  • deps/curl/lib/http_digest.c
  • deps/curl/lib/http_negotiate.c
  • deps/curl/lib/http_proxy.c
  • deps/curl/lib/httpsrr.c
  • deps/curl/lib/idn.c
  • deps/curl/lib/idn.h
  • deps/curl/lib/if2ip.c
  • deps/curl/lib/imap.c
  • deps/curl/lib/imap.h
  • deps/curl/lib/ldap.c
  • deps/curl/lib/libcurl.rc
  • deps/curl/lib/llist.h
  • deps/curl/lib/md4.c
  • deps/curl/lib/md5.c
  • deps/curl/lib/memdebug.c
  • deps/curl/lib/mime.c
  • deps/curl/lib/mime.h
  • deps/curl/lib/mprintf.c
  • deps/curl/lib/mqtt.c
  • deps/curl/lib/mqtt.h
  • deps/curl/lib/multi.c
  • deps/curl/lib/multi_ev.c
  • deps/curl/lib/multi_ntfy.c
  • deps/curl/lib/multihandle.h
  • deps/curl/lib/multiif.h
  • deps/curl/lib/netrc.c
  • deps/curl/lib/netrc.h
  • deps/curl/lib/noproxy.c
  • deps/curl/lib/openldap.c
  • deps/curl/lib/parsedate.c
  • deps/curl/lib/parsedate.h
  • deps/curl/lib/pingpong.c
  • deps/curl/lib/pingpong.h
  • deps/curl/lib/pop3.c
  • deps/curl/lib/pop3.h
  • deps/curl/lib/progress.c
  • deps/curl/lib/rand.c
  • deps/curl/lib/rand.h
  • deps/curl/lib/ratelimit.c
  • deps/curl/lib/ratelimit.h
  • deps/curl/lib/request.c
  • deps/curl/lib/request.h
  • deps/curl/lib/rtsp.c
  • deps/curl/lib/rtsp.h
  • deps/curl/lib/select.c
  • deps/curl/lib/select.h
  • deps/curl/lib/sendf.c
  • deps/curl/lib/sendf.h
  • deps/curl/lib/setopt.c
  • deps/curl/lib/setopt.h
  • deps/curl/lib/setup-os400.h
  • deps/curl/lib/setup-vms.h
  • deps/curl/lib/setup-win32.h
  • deps/curl/lib/sha256.c
  • deps/curl/lib/sigpipe.h
  • deps/curl/lib/slist.c
  • deps/curl/lib/smb.c
  • deps/curl/lib/smb.h
  • deps/curl/lib/smtp.c
  • deps/curl/lib/smtp.h
  • deps/curl/lib/socketpair.c
  • deps/curl/lib/socketpair.h
  • deps/curl/lib/socks.c
  • deps/curl/lib/socks_gssapi.c
  • deps/curl/lib/socks_sspi.c
  • deps/curl/lib/splay.c
  • deps/curl/lib/splay.h
  • deps/curl/lib/strcase.c
  • deps/curl/lib/strcase.h
  • deps/curl/lib/strequal.c
  • deps/curl/lib/strerror.c
  • deps/curl/lib/system_win32.c
  • deps/curl/lib/system_win32.h
  • deps/curl/lib/telnet.c
  • deps/curl/lib/telnet.h
  • deps/curl/lib/tftp.c
  • deps/curl/lib/tftp.h
  • deps/curl/lib/transfer.c
  • deps/curl/lib/uint-hash.c
  • deps/curl/lib/uint-table.h
  • deps/curl/lib/url.c
  • deps/curl/lib/url.h
  • deps/curl/lib/urlapi-int.h
  • deps/curl/lib/urlapi.c
  • deps/curl/lib/urldata.h
  • deps/curl/lib/vauth/cleartext.c
  • deps/curl/lib/vauth/cram.c
  • deps/curl/lib/vauth/digest.c
  • deps/curl/lib/vauth/digest.h
  • deps/curl/lib/vauth/digest_sspi.c
  • deps/curl/lib/vauth/gsasl.c
  • deps/curl/lib/vauth/krb5_gssapi.c
  • deps/curl/lib/vauth/krb5_sspi.c
  • deps/curl/lib/vauth/ntlm.c
  • deps/curl/lib/vauth/ntlm_sspi.c
  • deps/curl/lib/vauth/oauth2.c
  • deps/curl/lib/vauth/spnego_gssapi.c
  • deps/curl/lib/vauth/spnego_sspi.c
  • deps/curl/lib/vauth/vauth.c
  • deps/curl/lib/vauth/vauth.h
  • deps/curl/lib/version.c
  • deps/curl/lib/vquic/curl_ngtcp2.c
  • deps/curl/lib/vquic/curl_ngtcp2.h
  • deps/curl/lib/vquic/curl_osslq.c
  • deps/curl/lib/vquic/curl_quiche.c
  • deps/curl/lib/vquic/curl_quiche.h
  • deps/curl/lib/vquic/vquic-tls.c
  • deps/curl/lib/vquic/vquic-tls.h
  • deps/curl/lib/vquic/vquic.c
  • deps/curl/lib/vquic/vquic.h
  • deps/curl/lib/vquic/vquic_int.h
  • deps/curl/lib/vssh/libssh.c
  • deps/curl/lib/vssh/libssh2.c
  • deps/curl/lib/vssh/ssh.h
  • deps/curl/lib/vssh/vssh.c
  • deps/curl/lib/vssh/vssh.h
  • deps/curl/lib/vtls/apple.c
  • deps/curl/lib/vtls/apple.h
  • deps/curl/lib/vtls/cipher_suite.c
  • deps/curl/lib/vtls/cipher_suite.h
  • deps/curl/lib/vtls/gtls.c
  • deps/curl/lib/vtls/gtls.h
  • deps/curl/lib/vtls/hostcheck.c
  • deps/curl/lib/vtls/hostcheck.h
  • deps/curl/lib/vtls/keylog.c
  • deps/curl/lib/vtls/keylog.h
  • deps/curl/lib/vtls/mbedtls.c
  • deps/curl/lib/vtls/mbedtls.h
  • deps/curl/lib/vtls/openssl.c
  • deps/curl/lib/vtls/openssl.h
  • deps/curl/lib/vtls/rustls.c
  • deps/curl/lib/vtls/rustls.h
  • deps/curl/lib/vtls/schannel.c
  • deps/curl/lib/vtls/schannel.h
  • deps/curl/lib/vtls/schannel_int.h
  • deps/curl/lib/vtls/schannel_verify.c
  • deps/curl/lib/vtls/vtls.c
  • deps/curl/lib/vtls/vtls.h
  • deps/curl/lib/vtls/vtls_int.h
  • deps/curl/lib/vtls/vtls_scache.c
💤 Files with no reviewable changes (6)
  • deps/curl/lib/cf-haproxy.c
  • deps/curl/lib/curl_share.c
  • deps/curl/lib/config-plan9.h
  • deps/curl/lib/dynhds.c
  • deps/curl/lib/curl_setup_once.h
  • deps/curl/lib/config-riscos.h

Comment on lines +312 to +323
CURLcode Curl_altsvc_ctrl(struct Curl_easy *data, const long ctrl)
{
DEBUGASSERT(asi);
asi->flags = ctrl;
DEBUGASSERT(data);
if(!ctrl)
return CURLE_BAD_FUNCTION_ARGUMENT;

if(!data->asi) {
data->asi = Curl_altsvc_init();
if(!data->asi)
return CURLE_OUT_OF_MEMORY;
}
data->asi->flags = ctrl;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
rg -n -C2 '\bCURLOPT_ALTSVC_CTRL\b|\bCurl_altsvc_ctrl\b|CURLALTSVC_'

Repository: nodesource/nsolid

Length of output: 3907


🏁 Script executed:

# Search for documentation, tests, and usage patterns of CURLOPT_ALTSVC_CTRL
rg -i 'altsvc|alt-svc' --type md --type txt -A 3 -B 1 | head -100

Repository: nodesource/nsolid

Length of output: 5953


🏁 Script executed:

# Look for test files that might test CURLOPT_ALTSVC_CTRL
fd -e c -e h -e txt test | xargs rg -l 'ALTSVC_CTRL' 2>/dev/null || echo "No test files found with ALTSVC_CTRL"

Repository: nodesource/nsolid

Length of output: 97


🏁 Script executed:

# Check if there are any comments or documentation in the curl headers explaining the behavior
rg -B5 -A5 'CURLOPT_ALTSVC_CTRL' deps/curl/include/

Repository: nodesource/nsolid

Length of output: 1477


🏁 Script executed:

# Search for any curl documentation files mentioning altsvc or reset behavior
find deps/curl -type f \( -name "*.md" -o -name "*.txt" -o -name "*.rst" \) | xargs rg -l -i 'altsvc' 2>/dev/null | head -5

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

# Look at how other control options handle 0
rg -B5 -A8 'case CURLOPT_' deps/curl/lib/setopt.c | grep -A8 'BITMASK\|_CTRL' | head -60

Repository: nodesource/nsolid

Length of output: 535


🏁 Script executed:

# Check if there are other similar control setters and how they handle zero
ast-grep --pattern 'if(!$VAR)
    return CURLE_BAD_FUNCTION_ARGUMENT;' deps/curl/lib/

Repository: nodesource/nsolid

Length of output: 5241


🏁 Script executed:

# Look at the full context of the altsvc.c file to see if there are any comments about expected usage
sed -n '300,330p' deps/curl/lib/altsvc.c

Repository: nodesource/nsolid

Length of output: 701


🏁 Script executed:

# Search curl's official documentation or CHANGELOG in the repo
find deps/curl -type f -name "CHANGELOG*" -o -name "NEWS*" | xargs grep -i altsvc 2>/dev/null | head -20

Repository: nodesource/nsolid

Length of output: 43


Reconsider the zero-mask check to provide a disable/reset path.

The current code rejects ctrl == 0 with CURLE_BAD_FUNCTION_ARGUMENT, which prevents resetting data->asi->flags once initialized. This deviates from the pattern used by similar bitmask controls like CURLOPT_HSTS_CTRL, which checks for specific bits rather than rejecting zero outright. Either allow zero to reset the flags to a neutral state, or document explicitly why zero is invalid. Without a reset path, callers cannot disable alt-svc after enabling it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/altsvc.c` around lines 312 - 323, The function Curl_altsvc_ctrl
currently rejects ctrl == 0 which prevents resetting data->asi->flags; change
the input validation in Curl_altsvc_ctrl so zero is accepted as a valid
"reset/disable" value and is assigned to data->asi->flags, and instead validate
only against unknown bits (e.g., compute a ALTSVC_CTRL_VALID mask and return
CURLE_BAD_FUNCTION_ARGUMENT if (ctrl & ~ALTSVC_CTRL_VALID) != 0); ensure
data->asi is still allocated as before and that data->asi->flags = ctrl handles
the zero case.

Comment on lines 329 to +335
data->state.async.dns =
Curl_dnscache_mk_entry(data, ares->temp_ai,
Curl_dnscache_mk_entry(data, &ares->temp_ai,
data->state.async.hostname, 0,
data->state.async.port, FALSE);
if(data->state.async.dns)
ares->temp_ai = NULL; /* temp_ai now owned by entry */
if(!data->state.async.dns) {
result = CURLE_OUT_OF_MEMORY;
goto out;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Keep the OOM exit on the shared cleanup path.

Line 335 jumps past the async_ares_cleanup(data) call at Line 363. If HTTPS RR state was already accumulated, ares->hinfo stays live until a later lookup overwrites it, leaking the nested allocations on this failure path.

Suggested fix
       data->state.async.dns =
         Curl_dnscache_mk_entry(data, &ares->temp_ai,
                                data->state.async.hostname, 0,
                                data->state.async.port, FALSE);
       if(!data->state.async.dns) {
         result = CURLE_OUT_OF_MEMORY;
+        async_ares_cleanup(data);
         goto out;
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data->state.async.dns =
Curl_dnscache_mk_entry(data, ares->temp_ai,
Curl_dnscache_mk_entry(data, &ares->temp_ai,
data->state.async.hostname, 0,
data->state.async.port, FALSE);
if(data->state.async.dns)
ares->temp_ai = NULL; /* temp_ai now owned by entry */
if(!data->state.async.dns) {
result = CURLE_OUT_OF_MEMORY;
goto out;
data->state.async.dns =
Curl_dnscache_mk_entry(data, &ares->temp_ai,
data->state.async.hostname, 0,
data->state.async.port, FALSE);
if(!data->state.async.dns) {
result = CURLE_OUT_OF_MEMORY;
async_ares_cleanup(data);
goto out;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/asyn-ares.c` around lines 329 - 335, The OOM path after
Curl_dnscache_mk_entry currently jumps to the early "out" label and skips
async_ares_cleanup, leaking ares->hinfo and nested allocations; change the
control flow so that when Curl_dnscache_mk_entry returns NULL you set result =
CURLE_OUT_OF_MEMORY and jump to the common cleanup that runs async_ares_cleanup
(or call async_ares_cleanup before jumping out) instead of jumping directly to
out. Locate the block around Curl_dnscache_mk_entry and replace the direct goto
out with a goto or call that ensures async_ares_cleanup(data) runs (referencing
Curl_dnscache_mk_entry, async_ares_cleanup, and ares->hinfo).

Comment on lines +490 to 493
VERBOSE(i = 0);
for(a = bs->running; a; a = a->next) {
CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result);
if(a->result)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Increment the attempt index in this final trace dump.

Line 492 will log every entry as baller 0 because i is initialized once and never advanced in this loop.

🔧 Suggested fix
-      VERBOSE(i = 0);
+      VERBOSE(i = -1);
       for(a = bs->running; a; a = a->next) {
+        VERBOSE(++i);
         CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
VERBOSE(i = 0);
for(a = bs->running; a; a = a->next) {
CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result);
if(a->result)
VERBOSE(i = -1);
for(a = bs->running; a; a = a->next) {
VERBOSE(++i);
CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result);
if(a->result)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/cf-ip-happy.c` around lines 490 - 493, The final trace loop
uses a local index i (set by VERBOSE(i = 0)) but never increments it, so every
trace prints "baller 0"; inside the loop that iterates for(a = bs->running; a; a
= a->next) in cf-ip-happy.c, increment i each iteration (e.g., i++ at the end of
the loop body) before or after calling CURL_TRC_CF so the trace message shows
the correct attempt index for each entry.

Comment on lines +315 to +317
#ifdef SOCK_CLOEXEC
addr->socktype |= SOCK_CLOEXEC;
#endif
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the full socket_open function context around lines 315-317
cat -n deps/curl/lib/cf-socket.c | sed -n '300,380p'

Repository: nodesource/nsolid

Length of output: 3474


🏁 Script executed:

# Also search for where fopensocket callback is invoked
rg -n "fopensocket" deps/curl/lib/cf-socket.c -A 5 -B 2

Repository: nodesource/nsolid

Length of output: 1964


🏁 Script executed:

# Check for SOCK_NONBLOCK handling to verify the claim about it avoiding internal flags
rg -n "SOCK_NONBLOCK" deps/curl/lib/cf-socket.c -B 3 -A 3

Repository: nodesource/nsolid

Length of output: 1473


Keep SOCK_CLOEXEC out of the public opensocket callback input.

Line 315 mutates addr->socktype before the fopensocket callback at line 332 sees it. This changes the callback-visible value from plain SOCK_STREAM/SOCK_DGRAM to a flag-augmented one on SOCK_CLOEXEC platforms, breaking existing callbacks that switch or compare on address->socktype. The nearby SOCK_NONBLOCK handling (lines 1072–1073) explicitly avoids this pattern with if(!data->set.fopensocket) before setting the flag.

Suggested fix
 static CURLcode socket_open(struct Curl_easy *data,
                             struct Curl_sockaddr_ex *addr,
                             curl_socket_t *sockfd)
 {
   char errbuf[STRERROR_LEN];
+  int socktype = addr->socktype;
-
-#ifdef SOCK_CLOEXEC
-  addr->socktype |= SOCK_CLOEXEC;
-#endif
 
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
   if(data->set.fopensocket) {
@@
   }
   else {
     /* opensocket callback not set, so create the socket now */
+#ifdef SOCK_CLOEXEC
+    socktype |= SOCK_CLOEXEC;
+#endif
 `#ifdef` DEBUGBUILD
     if((addr->family == AF_INET6) && getenv("CURL_DBG_SOCK_FAIL_IPV6")) {
       failf(data, "CURL_DBG_SOCK_FAIL_IPV6: failed to open socket");
       return CURLE_COULDNT_CONNECT;
     }
 `#endif`
-    *sockfd = CURL_SOCKET(addr->family, addr->socktype, addr->protocol);
+    *sockfd = CURL_SOCKET(addr->family, socktype, addr->protocol);
     if((*sockfd == CURL_SOCKET_BAD) && (SOCKERRNO == SOCKENOMEM))
       return CURLE_OUT_OF_MEMORY;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#ifdef SOCK_CLOEXEC
addr->socktype |= SOCK_CLOEXEC;
#endif
static CURLcode socket_open(struct Curl_easy *data,
struct Curl_sockaddr_ex *addr,
curl_socket_t *sockfd)
{
char errbuf[STRERROR_LEN];
int socktype = addr->socktype;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
if(data->set.fopensocket) {
/* opensocket callback provided */
*sockfd = data->set.fopensocket(data->set.opensocket_client,
(struct sockaddr *) addr, sockfd);
}
else {
/* opensocket callback not set, so create the socket now */
`#ifdef` SOCK_CLOEXEC
socktype |= SOCK_CLOEXEC;
`#endif`
`#ifdef` DEBUGBUILD
if((addr->family == AF_INET6) && getenv("CURL_DBG_SOCK_FAIL_IPV6")) {
failf(data, "CURL_DBG_SOCK_FAIL_IPV6: failed to open socket");
return CURLE_COULDNT_CONNECT;
}
`#endif`
*sockfd = CURL_SOCKET(addr->family, socktype, addr->protocol);
if((*sockfd == CURL_SOCKET_BAD) && (SOCKERRNO == SOCKENOMEM))
return CURLE_OUT_OF_MEMORY;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/cf-socket.c` around lines 315 - 317, The code mutates
addr->socktype to add SOCK_CLOEXEC before invoking the user fopen socket
callback, which exposes a flag-augmented value to that callback; change this so
the public fopen socket callback (data->set.fopensocket / the fopensocket
invocation at the call site) sees the original plain socket type (e.g.,
SOCK_STREAM/SOCK_DGRAM). Either apply SOCK_CLOEXEC only after checking if
data->set.fopensocket is set (like the SOCK_NONBLOCK pattern) or pass a local
copy of addr->socktype to the callback and set the CLOEXEC flag on the actual
addr->socktype only when creating the socket; ensure references to
addr->socktype and the fopensocket callback name are used to locate and adjust
the logic.

Comment on lines +357 to 374
#ifdef USE_SO_NOSIGPIPE
if(Curl_sock_nosigpipe(*sockfd) < 0) {
failf(data, "setsockopt enable SO_NOSIGPIPE: %s",
curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
sclose(*sockfd);
*sockfd = CURL_SOCKET_BAD;
return CURLE_COULDNT_CONNECT;
}
#endif /* USE_SO_NOSIGPIPE */

#if defined(HAVE_FCNTL) && !defined(SOCK_CLOEXEC)
if(fcntl(*sockfd, F_SETFD, FD_CLOEXEC) < 0) {
failf(data, "fcntl set CLOEXEC: %s",
curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
close(*sockfd);
sclose(*sockfd);
*sockfd = CURL_SOCKET_BAD;
return CURLE_COULDNT_CONNECT;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n deps/curl/lib/cf-socket.c | sed -n '340,390p'

Repository: nodesource/nsolid

Length of output: 2070


🏁 Script executed:

cat -n deps/curl/lib/cf-socket.c | sed -n '300,400p'

Repository: nodesource/nsolid

Length of output: 4162


🏁 Script executed:

rg "fopensocket" deps/curl/lib/cf-socket.c -A 3 -B 3

Repository: nodesource/nsolid

Length of output: 1657


🏁 Script executed:

rg "fclosesocket" deps/curl/lib/cf-socket.c -A 2 -B 2

Repository: nodesource/nsolid

Length of output: 330


🏁 Script executed:

wc -l deps/curl/lib/cf-socket.c

Repository: nodesource/nsolid

Length of output: 91


🏁 Script executed:

rg "^[a-zA-Z_].*\(.*\)\s*$" deps/curl/lib/cf-socket.c --before-context 0 | head -20

Repository: nodesource/nsolid

Length of output: 588


🏁 Script executed:

cat -n deps/curl/lib/cf-socket.c | sed -n '320,380p'

Repository: nodesource/nsolid

Length of output: 2829


🏁 Script executed:

cat -n deps/curl/lib/cf-socket.c | sed -n '250,375p' | grep -E "^[[:space:]]*[0-9]+\s+[a-zA-Z_].*\{|^[[:space:]]*[0-9]+\s+static|^[[:space:]]*[0-9]+\s+sclose|^[[:space:]]*[0-9]+\s+fclosesocket"

Repository: nodesource/nsolid

Length of output: 554


🏁 Script executed:

python3 << 'EOF'
with open('deps/curl/lib/cf-socket.c', 'r') as f:
    lines = f.readlines()
    # Print lines 250-380 with line numbers
    for i in range(249, min(380, len(lines))):
        print(f"{i+1:4d}: {lines[i]}", end='')
EOF

Repository: nodesource/nsolid

Length of output: 5271


🏁 Script executed:

rg "sclose" deps/curl/lib/cf-socket.c -B 3 -A 1 | head -60

Repository: nodesource/nsolid

Length of output: 583


🏁 Script executed:

rg "define fclosesocket|fclosesocket\s*\(" deps/curl/lib/ -B 2 -A 2

Repository: nodesource/nsolid

Length of output: 382


🏁 Script executed:

rg "fclosesocket|sclose" deps/curl/include/ -B 1 -A 1

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

rg "fclosesocket" --type c | head -20

Repository: nodesource/nsolid

Length of output: 575


🏁 Script executed:

fd "sockrc\|socket" deps/curl/lib/ -type f

Repository: nodesource/nsolid

Length of output: 231


🏁 Script executed:

rg "define.*fclosesocket|fclosesocket.*=" deps/curl/ --type c --type h

Repository: nodesource/nsolid

Length of output: 214


🏁 Script executed:

rg -l "fclosesocket" deps/curl/ | xargs rg "sclose" -l | head -5

Repository: nodesource/nsolid

Length of output: 86


🏁 Script executed:

rg "fclosesocket" --type c --type h

Repository: nodesource/nsolid

Length of output: 575


🏁 Script executed:

sed -n '350,380p' deps/curl/lib/cf-socket.c | cat -n

Repository: nodesource/nsolid

Length of output: 1333


🏁 Script executed:

sed -n '300,420p' deps/curl/lib/cf-socket.c | cat -n

Repository: nodesource/nsolid

Length of output: 4769


🏁 Script executed:

rg "socket_open|Curl_socket_open" deps/curl/lib/cf-socket.c -B 5 -A 20 | head -100

Repository: nodesource/nsolid

Length of output: 3152


🏁 Script executed:

rg "sclose\(" deps/curl/lib/cf-socket.c -B 5 -A 0 | head -80

Repository: nodesource/nsolid

Length of output: 608


🏁 Script executed:

ast-grep --pattern 'static $_ $func($_) {
  $$$
}' --lang c deps/curl/lib/cf-socket.c | head -50

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

head -n 380 deps/curl/lib/cf-socket.c | tail -n 80

Repository: nodesource/nsolid

Length of output: 2883


🏁 Script executed:

grep -n "^[a-zA-Z_]" deps/curl/lib/cf-socket.c | awk -F: '$1 < 360 {line=$1; name=$0} END {print line, name}'

Repository: nodesource/nsolid

Length of output: 120


🏁 Script executed:

awk 'NR>=250 && NR<=380' deps/curl/lib/cf-socket.c

Repository: nodesource/nsolid

Length of output: 4485


🏁 Script executed:

rg "socket\(" deps/curl/lib/cf-socket.c -B 2 -A 5 | head -80

Repository: nodesource/nsolid

Length of output: 1360


🏁 Script executed:

find deps/curl -name "*.h" -o -name "*.c" | xargs grep -l "fclosesocket" 2>/dev/null

Repository: nodesource/nsolid

Length of output: 153


🏁 Script executed:

rg "fopensocket|fclosesocket" deps/curl/include/curl.h -B 2 -A 2

Repository: nodesource/nsolid

Length of output: 125


🏁 Script executed:

head -c 15000 deps/curl/lib/cf-socket.c | tail -c 2000 | cat -n

Repository: nodesource/nsolid

Length of output: 2607


🏁 Script executed:

python3 << 'PYEOF'
with open('deps/curl/lib/cf-socket.c', 'r') as f:
    lines = f.readlines()
    # Print lines 280-390
    for i in range(279, min(390, len(lines))):
        print(f"{i+1:4d}: {lines[i]}", end='')
PYEOF

Repository: nodesource/nsolid

Length of output: 4299


🏁 Script executed:

rg "fopensocket" deps/curl/ -B 2 -A 2 | head -100

Repository: nodesource/nsolid

Length of output: 3104


🏁 Script executed:

rg "fclosesocket" deps/curl/ -B 2 -A 2 | head -100

Repository: nodesource/nsolid

Length of output: 1826


🏁 Script executed:

rg "typedef.*socket\|callback.*socket\|socket.*callback" deps/curl/include/curl.h -B 1 -A 3

Repository: nodesource/nsolid

Length of output: 125


🏁 Script executed:

sed -n '355,365p' deps/curl/lib/cf-socket.c | nl

Repository: nodesource/nsolid

Length of output: 103


🏁 Script executed:

rg "USE_SO_NOSIGPIPE" deps/curl/lib/cf-socket.c -B 10 -A 15

Repository: nodesource/nsolid

Length of output: 2004


🏁 Script executed:

rg "USE_SO_NOSIGPIPE" deps/curl/lib/cf-socket.c -B 50 | head -80

Repository: nodesource/nsolid

Length of output: 2691


🏁 Script executed:

rg "Curl_socket\|socket\(" deps/curl/lib/cf-socket.c -B 3 -A 3 | head -80

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

rg "fclosesocket\|sclose" deps/curl/lib/ --type c | grep -v "^Binary" | head -40

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

rg "^#define\s+fclosesocket|^fclosesocket" deps/curl/lib/ -B 1 -A 3

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

find deps/curl/lib -name "*.h" -o -name "*.c" | xargs grep -n "fclosesocket\|socket.*callback" | head -40

Repository: nodesource/nsolid

Length of output: 2985


🏁 Script executed:

ls -la deps/curl/lib/*.h

Repository: nodesource/nsolid

Length of output: 9755


🏁 Script executed:

rg "fclosesocket" deps/curl/lib/url.h -B 3 -A 3

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

rg "fopensocket" deps/curl/lib/ -B 5 -A 5 | head -100

Repository: nodesource/nsolid

Length of output: 6165


🏁 Script executed:

cat deps/curl/lib/url.h | grep -n "fclosesocket\|FCLOSESOCKET" -i | head -20

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

rg "closesocket|CLOSESOCKET" deps/curl/lib/ --type h -B 2 -A 2 | head -50

Repository: nodesource/nsolid

Length of output: 2403


🏁 Script executed:

wc -l deps/curl/lib/url.h

Repository: nodesource/nsolid

Length of output: 84


🏁 Script executed:

rg "callback|fopen|fclose" deps/curl/lib/url.h -i -B 1 -A 1 | head -80

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

rg "CURLOPTTYPE_FUNCTIONPOINT.*socket|fopensocket|fclosesocket" deps/curl/include/curl.h -B 1 -A 1

Repository: nodesource/nsolid

Length of output: 125


🏁 Script executed:

rg "typedef.*fclosesocket|define.*fclosesocket|fclosesocket.*=" deps/curl/ --type c --type h

Repository: nodesource/nsolid

Length of output: 214


🏁 Script executed:

rg "fclosesocket" --type c --type h

Repository: nodesource/nsolid

Length of output: 575


🏁 Script executed:

rg "fopensocket" --type c --type h -B 5 -A 5

Repository: nodesource/nsolid

Length of output: 6165


🏁 Script executed:

rg "define.*sclose|^sclose\s*\(" deps/curl/ --type c --type h -B 2 -A 2

Repository: nodesource/nsolid

Length of output: 1045


🏁 Script executed:

rg "sclose" deps/curl/lib/cf-socket.c -B 5 -A 0

Repository: nodesource/nsolid

Length of output: 608


🏁 Script executed:

rg "Curl_socket_close|socket_close|Curl_close" deps/curl/lib/cf-socket.c -B 2 -A 3

Repository: nodesource/nsolid

Length of output: 2612


🏁 Script executed:

rg "close.*socket|socket.*close" deps/curl/lib/ --type c | head -30

Repository: nodesource/nsolid

Length of output: 2504


🏁 Script executed:

rg "curl_easy_cleanup|easy_cleanup" deps/curl/lib/ --type c -A 20 | head -80

Repository: nodesource/nsolid

Length of output: 4840


🏁 Script executed:

rg "socket_close|SocketClose|SOCKET_CLOSE" deps/curl/lib/ -B 3 -A 10 | head -100

Repository: nodesource/nsolid

Length of output: 5290


🏁 Script executed:

rg "Curl_socket\s*\(" deps/curl/lib/ --type c -B 3 -A 15 | head -100

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

rg "static.*close|^close_" deps/curl/lib/cf-socket.c -B 2 -A 20 | head -150

Repository: nodesource/nsolid

Length of output: 1411


🏁 Script executed:

rg "struct.*conn|Curl_conn|conn_close" deps/curl/lib/cf-socket.c -B 1 -A 3 | head -50

Repository: nodesource/nsolid

Length of output: 2023


🏁 Script executed:

grep -n "^[a-zA-Z_].*{" deps/curl/lib/cf-socket.c | head -20

Repository: nodesource/nsolid

Length of output: 259


🏁 Script executed:

python3 << 'PYEOF'
with open('deps/curl/lib/cf-socket.c', 'r') as f:
    content = f.read()
    lines = content.split('\n')
    
# Find line numbers with USE_SO_NOSIGPIPE
for i, line in enumerate(lines):
    if 'USE_SO_NOSIGPIPE' in line:
        # Print context around it
        start = max(0, i - 40)
        end = min(len(lines), i + 25)
        for j in range(start, end):
            print(f"{j+1:4d}: {lines[j]}")
        break
PYEOF

Repository: nodesource/nsolid

Length of output: 2460


🏁 Script executed:

rg "socket_conn_close|sock_close|conn->sockfd|socket.*close" deps/curl/lib/cf-socket.c -B 3 -A 3 | head -100

Repository: nodesource/nsolid

Length of output: 2613


🏁 Script executed:

grep -n "^[a-zA-Z_].*Curl_" deps/curl/lib/cf-socket.c | head -20

Repository: nodesource/nsolid

Length of output: 1357


🏁 Script executed:

python3 << 'PYEOF'
with open('deps/curl/lib/cf-socket.c', 'r') as f:
    lines = f.readlines()
    
# Find lines containing "close"
for i, line in enumerate(lines):
    if 'close' in line.lower() and ('static' in line or 'cf_socket' in line):
        print(f"{i+1:4d}: {line.rstrip()}")
PYEOF

Repository: nodesource/nsolid

Length of output: 440


🏁 Script executed:

rg "socket_conn_close" deps/curl/lib/cf-socket.c -B 5 -A 20

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

grep -n "^[a-zA-Z_].*close" deps/curl/lib/cf-socket.c

Repository: nodesource/nsolid

Length of output: 295


🏁 Script executed:

wc -l deps/curl/lib/cf-socket.c
# It's likely small enough to read entirely
cat deps/curl/lib/cf-socket.c | wc -l

Repository: nodesource/nsolid

Length of output: 96


🏁 Script executed:

cat deps/curl/lib/cf-socket.c

Repository: nodesource/nsolid

Length of output: 50373


Use the callback-aware close path on socket-setup failures.

Lines 361 and 371 use sclose() directly to close the socket on setup failures, even if it was created via the fopensocket callback. This bypasses the fclosesocket callback that may be registered for proper cleanup. Use socket_close(data, data->conn, TRUE, *sockfd) instead to invoke callback-based socket closing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/cf-socket.c` around lines 357 - 374, Replace the direct
sclose(*sockfd) calls in the socket setup failure paths (inside the
Curl_sock_nosigpipe and fcntl error blocks) with the callback-aware close helper
socket_close(data, data->conn, TRUE, *sockfd) so any registered fclosesocket
callback gets invoked; after invoking socket_close set *sockfd = CURL_SOCKET_BAD
and return CURLE_COULDNT_CONNECT as before. Ensure you update both failure
branches where sclose is used (the block around Curl_sock_nosigpipe and the
block around fcntl(F_SETFD, FD_CLOEXEC)) and keep the existing failf messages
and error returns unchanged.

Comment on lines +39 to +42
#ifdef CURLVERBOSE
int Curl_nghttp2_fr_print(const nghttp2_frame *frame, char *buffer,
size_t blen);
#endif
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
from pathlib import Path

for path in Path('.').rglob('*.[ch]'):
    try:
        lines = path.read_text(errors='ignore').splitlines()
    except Exception:
        continue

    includes = [line.strip() for line in lines if line.strip().startswith('#include')]
    if any('"http2.h"' in inc for inc in includes):
        pos = next(i for i, inc in enumerate(includes) if '"http2.h"' in inc)
        if not any('nghttp2/nghttp2.h' in inc for inc in includes[:pos]):
            print(path)
PY

Repository: nodesource/nsolid

Length of output: 161


🏁 Script executed:

#!/bin/bash
# Check the current includes in http2.h around the region in question
echo "=== deps/curl/lib/http2.h (first 50 lines) ===" 
head -n 50 deps/curl/lib/http2.h | cat -n

echo -e "\n=== Checking for nghttp2 include in http2.h ===" 
grep -n "nghttp2" deps/curl/lib/http2.h || echo "Not found"

echo -e "\n=== deps/curl/lib/http.c - include section ===" 
head -n 100 deps/curl/lib/http.c | grep -n "#include" | head -20

Repository: nodesource/nsolid

Length of output: 3295


🏁 Script executed:

#!/bin/bash
# Check more includes in http.c to find where http2.h is included
echo "=== deps/curl/lib/http.c - all includes ===" 
head -n 150 deps/curl/lib/http.c | grep -n "#include"

echo -e "\n=== Looking for nghttp2 and http2 in http.c ===" 
head -n 150 deps/curl/lib/http.c | grep -n "nghttp2\|http2"

Repository: nodesource/nsolid

Length of output: 1342


🏁 Script executed:

#!/bin/bash
# Check which files include nghttp2/nghttp2.h
echo "=== Checking nghttp2 includes in the flagged files ===" 
for file in deps/curl/lib/cf-https-connect.c deps/curl/lib/curl_trc.c deps/curl/lib/http.c deps/curl/lib/multi.c; do
  echo "--- $file ---"
  grep -n "nghttp2/nghttp2.h" "$file" || echo "NOT FOUND"
done

echo -e "\n=== Check full include order in http.c ===" 
head -n 90 deps/curl/lib/http.c | grep -n "#include"

Repository: nodesource/nsolid

Length of output: 1499


🏁 Script executed:

#!/bin/bash
# Check if nghttp2/nghttp2.h is included indirectly through other headers
echo "=== Tracing includes in urldata.h ===" 
grep -n "nghttp2" deps/curl/lib/urldata.h | head -20

echo -e "\n=== Check if http2.h forward-declares nghttp2_frame ===" 
grep -A5 -B5 "nghttp2_frame" deps/curl/lib/http2.h | head -30

echo -e "\n=== Look for typedef/forward declaration in http2.h ===" 
cat deps/curl/lib/http2.h | head -100

Repository: nodesource/nsolid

Length of output: 3396


🏁 Script executed:

#!/bin/bash
# Trace where nghttp2/nghttp2.h is actually included from
echo "=== Check curl_setup.h ===" 
grep -n "nghttp2" deps/curl/lib/curl_setup.h | head -10

echo -e "\n=== Check which header in the chain includes nghttp2 ===" 
# Look in likely candidates
for header in curl_setup.h urldata.h curl_trc.h transfer.h; do
  echo "--- $header ---"
  grep -l "nghttp2/nghttp2.h" "deps/curl/lib/$header" 2>/dev/null || echo "not here"
done

echo -e "\n=== Simulate compile: what pulls in nghttp2? ===" 
python3 <<'PYEOF'
import subprocess
import re

# Find all .h files and check which one includes nghttp2/nghttp2.h
result = subprocess.run(['find', 'deps/curl/lib', '-name', '*.h', '-type', 'f'], 
                       capture_output=True, text=True)
headers = result.stdout.strip().split('\n')

found = []
for hdr in headers:
    try:
        with open(hdr, 'r', errors='ignore') as f:
            content = f.read()
            if '<nghttp2/nghttp2.h>' in content:
                found.append(hdr)
    except:
        pass

if found:
    print("Headers that include <nghttp2/nghttp2.h>:")
    for h in found:
        print(f"  {h}")
else:
    print("No header found with <nghttp2/nghttp2.h>")
PYEOF

Repository: nodesource/nsolid

Length of output: 375


🏁 Script executed:

#!/bin/bash
# Verify the include chain: does dynhds.h include nghttp2?
echo "=== dynhds.h content (first 50 lines) ===" 
head -n 50 deps/curl/lib/dynhds.h | cat -n

echo -e "\n=== Confirm dynhds.h includes nghttp2/nghttp2.h ===" 
grep -n "nghttp2/nghttp2.h" deps/curl/lib/dynhds.h

echo -e "\n=== Verify include order in http.c around dynhds and http2 ===" 
sed -n '68,78p' deps/curl/lib/http.c | cat -n

Repository: nodesource/nsolid

Length of output: 2635


🏁 Script executed:

#!/bin/bash
# Verify that dynhds.h is indeed included before http2.h in all flagged files
echo "=== Check if dynhds.h comes before http2.h in the flagged files ===" 

for file in deps/curl/lib/cf-https-connect.c deps/curl/lib/curl_trc.c deps/curl/lib/http.c deps/curl/lib/multi.c; do
  echo "--- $file ---"
  dynhds_line=$(grep -n '"dynhds.h"' "$file" 2>/dev/null | cut -d: -f1)
  http2_line=$(grep -n '"http2.h"' "$file" 2>/dev/null | cut -d: -f1)
  
  if [ -n "$dynhds_line" ] && [ -n "$http2_line" ]; then
    if [ "$dynhds_line" -lt "$http2_line" ]; then
      echo "  dynhds.h @ line $dynhds_line comes BEFORE http2.h @ line $http2_line"
    else
      echo "  dynhds.h @ line $dynhds_line comes AFTER http2.h @ line $http2_line"
    fi
  else
    echo "  dynhds.h found: $([ -n "$dynhds_line" ] && echo "yes" || echo "no"), http2.h found: $([ -n "$http2_line" ] && echo "yes" || echo "no")"
  fi
done

Repository: nodesource/nsolid

Length of output: 438


🏁 Script executed:

#!/bin/bash
# Critical: Check if cf-https-connect.c, curl_trc.c, and multi.c actually use http2.h functions
echo "=== Check what these files do with http2.h ===" 

for file in deps/curl/lib/cf-https-connect.c deps/curl/lib/curl_trc.c deps/curl/lib/multi.c; do
  echo "--- $file ---"
  if grep -q "Curl_nghttp2" "$file" 2>/dev/null; then
    echo "  ✗ USES nghttp2 functions"
    grep -n "Curl_nghttp2" "$file" | head -5
  else
    echo "  OK: Does not use nghttp2 functions directly"
  fi
done

echo -e "\n=== Check if http2.h is under `#ifdef` CURLVERBOSE in these files ===" 
for file in deps/curl/lib/cf-https-connect.c deps/curl/lib/curl_trc.c deps/curl/lib/multi.c; do
  echo "--- $file ---"
  # Check context around http2.h include
  line_num=$(grep -n '"http2.h"' "$file" 2>/dev/null | cut -d: -f1)
  if [ -n "$line_num" ]; then
    start=$((line_num - 3))
    end=$((line_num + 3))
    sed -n "${start},${end}p" "$file" | cat -n
  fi
done

Repository: nodesource/nsolid

Length of output: 1106


Make http2.h include <nghttp2/nghttp2.h> to declare nghttp2_frame.

The new Curl_nghttp2_fr_print declaration under CURLVERBOSE uses the nghttp2_frame typedef, but http2.h does not include the nghttp2 header. Three files that include http2.h (cf-https-connect.c, curl_trc.c, multi.c) do not pull in nghttp2/nghttp2.h through other headers, creating a compilation failure when CURLVERBOSE is enabled.

♻️ Recommended fix
 `#ifdef` USE_NGHTTP2
+#include <nghttp2/nghttp2.h>
 
 /* value for MAX_CONCURRENT_STREAMS we use until we get an updated setting
    from the peer */
 `#define` DEFAULT_MAX_CONCURRENT_STREAMS 100
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/http2.h` around lines 39 - 42, Add a direct include of
nghttp2's public header so the nghttp2_frame type is declared where
Curl_nghttp2_fr_print is declared: modify deps/curl/lib/http2.h to `#include`
<nghttp2/nghttp2.h> (preferably guarded by the existing macro that enables
nghttp2, e.g. surround the include and the Curl_nghttp2_fr_print prototype with
the same USE_NGHTTP2/CURL_DISABLE_HTTP2 conditional) so callers of http2.h (like
cf-https-connect.c, curl_trc.c, multi.c) get the nghttp2_frame typedef when
CURLVERBOSE is enabled.

Comment on lines +219 to +236
curlx_dyn_init(&line, 2000);

if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
return NULL;

while(*str) {
if((*str == '\\' || *str == '"') &&
curlx_dyn_addn(&line, "\\", 1))
return NULL;
if(curlx_dyn_addn(&line, str, 1))
return NULL;
str++;
}

if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
return NULL;

return curlx_dyn_ptr(&line);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Free the dynbuf on imap_atom() failure paths.

curlx_dyn_init(&line, 2000) hands ownership to this helper, but every later return NULL skips curlx_dyn_free(&line). A failed curlx_dyn_addn() will leak the partially built buffer.

Proposed fix
 static char *imap_atom(const char *str, bool escape_only)
 {
   struct dynbuf line;
   size_t nclean;
   size_t len;
@@
   curlx_dyn_init(&line, 2000);
 
-  if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
-    return NULL;
+  if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
+    goto fail;
 
   while(*str) {
     if((*str == '\\' || *str == '"') &&
-       curlx_dyn_addn(&line, "\\", 1))
-      return NULL;
-    if(curlx_dyn_addn(&line, str, 1))
-      return NULL;
+       curlx_dyn_addn(&line, "\\", 1))
+      goto fail;
+    if(curlx_dyn_addn(&line, str, 1))
+      goto fail;
     str++;
   }
 
-  if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
-    return NULL;
+  if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
+    goto fail;
 
   return curlx_dyn_ptr(&line);
+fail:
+  curlx_dyn_free(&line);
+  return NULL;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
curlx_dyn_init(&line, 2000);
if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
return NULL;
while(*str) {
if((*str == '\\' || *str == '"') &&
curlx_dyn_addn(&line, "\\", 1))
return NULL;
if(curlx_dyn_addn(&line, str, 1))
return NULL;
str++;
}
if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
return NULL;
return curlx_dyn_ptr(&line);
curlx_dyn_init(&line, 2000);
if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
goto fail;
while(*str) {
if((*str == '\\' || *str == '"') &&
curlx_dyn_addn(&line, "\\", 1))
goto fail;
if(curlx_dyn_addn(&line, str, 1))
goto fail;
str++;
}
if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
goto fail;
return curlx_dyn_ptr(&line);
fail:
curlx_dyn_free(&line);
return NULL;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/imap.c` around lines 219 - 236, The dynbuf initialized by
curlx_dyn_init(&line, 2000) must be freed on all error paths in imap_atom():
when any curlx_dyn_addn() fails you should call curlx_dyn_free(&line) before
returning NULL (or refactor to a single cleanup label and jump there on error),
ensuring curlx_dyn_free(&line) is called for every early return and only return
curlx_dyn_ptr(&line) on success.

Comment on lines +1179 to +1214
/* Detect IMAP listings vs. downloading a single email */
static bool is_custom_fetch_listing_match(const char *params)
{
/* match " 1:* (FLAGS ..." or " 1,2,3 (FLAGS ..." */
if(*params++ != ' ')
return FALSE;

while(ISDIGIT(*params)) {
params++;
if(*params == 0)
return FALSE;
}
if(*params == ':')
return true;
if(*params == ',')
return true;
return FALSE;
}

static bool is_custom_fetch_listing(struct IMAP *imap)
{
/* filter out "UID FETCH 1:* (FLAGS ..." queries to list emails */
if(!imap->custom)
return FALSE;
else if(curl_strequal(imap->custom, "FETCH") && imap->custom_params) {
const char *p = imap->custom_params;
return is_custom_fetch_listing_match(p);
}
else if(curl_strequal(imap->custom, "UID") && imap->custom_params) {
if(curl_strnequal(imap->custom_params, " FETCH ", 7)) {
const char *p = imap->custom_params + 6;
return is_custom_fetch_listing_match(p);
}
}
return FALSE;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This matcher currently black-holes range/comma FETCH responses.

is_custom_fetch_listing() only looks at the message set, so commands like UID FETCH 1:* (FLAGS) and UID FETCH 1:* BODY[] both hit the empty branch in imap_state_listsearch_resp(). The state machine then consumes each untagged FETCH response and returns only the final tagged OK, so callers get neither the listing rows nor the body data.

Also applies to: 1232-1234

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/imap.c` around lines 1179 - 1214, The matcher currently only
inspects the message-set and thus treats any FETCH with a range/comma as a
"listing", which causes BODY[] responses to be swallowed; update
is_custom_fetch_listing() (and its helper is_custom_fetch_listing_match()) to
not just recognize the message-set but also verify the fetch data-item token
that follows and only return true for listing-style queries (e.g. when the
data-item is FLAGS/ (FLAGS ...)), otherwise return FALSE so UID/FETCH ... BODY[]
responses are handled normally by imap_state_listsearch_resp().

Comment on lines +1697 to +1709
static void imap_easy_reset(struct IMAP *imap)
{
Curl_safefree(imap->mailbox);
Curl_safefree(imap->uid);
Curl_safefree(imap->mindex);
Curl_safefree(imap->section);
Curl_safefree(imap->partial);
Curl_safefree(imap->query);
Curl_safefree(imap->custom);
Curl_safefree(imap->custom_params);
/* Clear the transfer mode for the next request */
imap->transfer = PPTRANSFER_BODY;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reset uidvalidity state between requests.

imap_done() calls imap_easy_reset() after every transfer. Leaving uidvalidity_set intact makes the next request on the same easy handle inherit the previous URL's UIDVALIDITY constraint, which can spuriously reselect or reject a mailbox.

Proposed fix
 static void imap_easy_reset(struct IMAP *imap)
 {
   Curl_safefree(imap->mailbox);
   Curl_safefree(imap->uid);
   Curl_safefree(imap->mindex);
   Curl_safefree(imap->section);
   Curl_safefree(imap->partial);
   Curl_safefree(imap->query);
   Curl_safefree(imap->custom);
   Curl_safefree(imap->custom_params);
+  imap->uidvalidity = 0;
+  imap->uidvalidity_set = FALSE;
   /* Clear the transfer mode for the next request */
   imap->transfer = PPTRANSFER_BODY;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/imap.c` around lines 1697 - 1709, The imap_easy_reset function
currently frees string fields but doesn't clear the UIDVALIDITY tracking, so
subsequent requests on the same easy handle inherit the previous request's
UIDVALIDITY constraint; update imap_easy_reset to reset the UIDVALIDITY state by
clearing imap->uidvalidity_set (and reset imap->uidvalidity as well) so the next
transfer starts without the previous UIDVALIDITY constraint, referencing the
imap_easy_reset function and the imap->uidvalidity_set/imap->uidvalidity fields.

Comment on lines +558 to 564
case MTYPE_LONGLONGU:
iptr->val.numu = va_arg(arglist, uint64_t);
break;

case FORMAT_LONGLONG:
iptr->val.nums = (mp_intmax_t)va_arg(arglist, mp_intmax_t);
case MTYPE_LONGLONG:
iptr->val.nums = va_arg(arglist, int64_t);
break;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file structure and the lines in question
wc -l deps/curl/lib/mprintf.c

Repository: nodesource/nsolid

Length of output: 89


🏁 Script executed:

# Examine lines 555-570 to see the MTYPE_LONGLONGU and MTYPE_LONGLONG cases
sed -n '555,570p' deps/curl/lib/mprintf.c

Repository: nodesource/nsolid

Length of output: 411


🏁 Script executed:

# Also check the %lln handling around lines 1043-1047
sed -n '1040,1050p' deps/curl/lib/mprintf.c

Repository: nodesource/nsolid

Length of output: 387


🏁 Script executed:

# Find all references to MTYPE_LONGLONG to understand the data structure
grep -n "MTYPE_LONGLONG" deps/curl/lib/mprintf.c | head -20

Repository: nodesource/nsolid

Length of output: 412


🏁 Script executed:

# Look for the definition of the iptr->val structure
grep -B5 -A5 "val.nums\|val.numu" deps/curl/lib/mprintf.c | head -40

Repository: nodesource/nsolid

Length of output: 948


🏁 Script executed:

# Check the union/struct definition for the format argument value holder
sed -n '1,150p' deps/curl/lib/mprintf.c | grep -A10 "union\|typedef"

Repository: nodesource/nsolid

Length of output: 408


🏁 Script executed:

# Search for where MTYPE_LONGLONG is set - look for the format specifiers
sed -n '390,430p' deps/curl/lib/mprintf.c | cat -n

Repository: nodesource/nsolid

Length of output: 1468


🏁 Script executed:

# Look more broadly at how the type is determined based on format specifiers
sed -n '370,440p' deps/curl/lib/mprintf.c | cat -n

Repository: nodesource/nsolid

Length of output: 2311


🏁 Script executed:

# Check what format characters trigger MTYPE_LONGLONG
rg -n -B2 -A2 "MTYPE_LONGLONG|MTYPE_LONGLONGU" deps/curl/lib/mprintf.c | head -60

Repository: nodesource/nsolid

Length of output: 1712


🏁 Script executed:

# Look for the '%lln' format handling to understand the output side
sed -n '1010,1030p' deps/curl/lib/mprintf.c | cat -n

Repository: nodesource/nsolid

Length of output: 754


🏁 Script executed:

# Verify the actual format specifiers used - look for '%lld', '%llu', '%lln'
rg -n "lld|llu|lln" deps/curl/lib/mprintf.c

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

# Search for where MTYPE_INTPTR is set and how it interacts with FLAGS_LONGLONG
sed -n '380,440p' deps/curl/lib/mprintf.c | grep -B3 -A3 "MTYPE_INTPTR\|case 'n'"

Repository: nodesource/nsolid

Length of output: 222


🏁 Script executed:

# Check how FLAGS_LONGLONG is set for the 'n' format
sed -n '280,380p' deps/curl/lib/mprintf.c | tail -50 | cat -n

Repository: nodesource/nsolid

Length of output: 1636


🏁 Script executed:

# Look at the complete 'n' case handling and FLAGS parsing
sed -n '330,370p' deps/curl/lib/mprintf.c | cat -n

Repository: nodesource/nsolid

Length of output: 1399


🏁 Script executed:

# Check if there's any typedef mapping int64_t to long long
grep -n "typedef.*int64_t\|int64_t.*=" deps/curl/lib/mprintf.c | head -10

Repository: nodesource/nsolid

Length of output: 116


🏁 Script executed:

# Look for any platform-specific handling of int64_t
grep -rn "int64_t\|long long" deps/curl/include/ | grep -i "define\|typedef" | head -20

Repository: nodesource/nsolid

Length of output: 1325


🏁 Script executed:

# Verify the actual flow: check what format specifiers trigger MTYPE_LONGLONG
# Look at a specific example - where does 'll' modifier get parsed?
sed -n '240,330p' deps/curl/lib/mprintf.c | cat -n

Repository: nodesource/nsolid

Length of output: 3234


🏁 Script executed:

# Check the standard C headers that define int64_t
grep -rn "int64_t" deps/curl/include/ | grep -E "define|typedef" | head -20

Repository: nodesource/nsolid

Length of output: 43


🏁 Script executed:

# Check if there's any `#include` of standard int types
head -50 deps/curl/lib/mprintf.c | grep -E "#include|int64_t"

Repository: nodesource/nsolid

Length of output: 225


🏁 Script executed:

# Check curl_setup.h for int64_t and stdint.h
head -100 deps/curl/lib/curl_setup.h | grep -E "#include|int64_t"

Repository: nodesource/nsolid

Length of output: 135


🏁 Script executed:

# Look for stdint.h or similar includes in curl_setup.h
grep -n "stdint\|inttypes\|int64_t" deps/curl/lib/curl_setup.h | head -20

Repository: nodesource/nsolid

Length of output: 230


🏁 Script executed:

# Get a broader view of includes in curl_setup.h
sed -n '1,200p' deps/curl/lib/curl_setup.h | grep "#include"

Repository: nodesource/nsolid

Length of output: 160


Use long long at the %ll varargs boundary.

Format specifiers %lld, %llu, and %lln require passing and receiving long long/unsigned long long per the C standard. On LP64 systems, int64_t is typically long (a different type), making va_arg(arglist, int64_t) undefined behavior even though both are 64-bit.

Lines 559, 563: Replace va_arg(arglist, int64_t) and va_arg(arglist, uint64_t) with va_arg(arglist, long long) and va_arg(arglist, unsigned long long), then cast the result to the union field's type.

Line 1046: When FLAGS_LONGLONG is set, cast the pointer to long long * instead of int64_t *.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deps/curl/lib/mprintf.c` around lines 558 - 564, The va_arg usage for %ll
boundaries is incorrect: in the switch cases MTYPE_LONGLONGU and MTYPE_LONGLONG
replace va_arg(arglist, uint64_t)/va_arg(arglist, int64_t) with va_arg(arglist,
unsigned long long)/va_arg(arglist, long long) and then cast the returned long
long/unsigned long long to the union fields iptr->val.numu and iptr->val.nums
respectively; additionally, where FLAGS_LONGLONG causes a pointer to be
stored/retrieved (the code that currently casts to int64_t *), change that cast
to long long * (or unsigned long long * as appropriate) before assigning/copying
so the varargs boundary uses long long types per the %ll specifiers.

@jaz239
Copy link

jaz239 commented Mar 14, 2026

Hey @santigimeno can you please make sure this PR is attached to a ticket? Currently it is not

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants