Skip to content

#299: Hand a throwing fnmatch or host-key callback to the coroutine - #37

Merged
EdmondDantes merged 2 commits into
true-asyncfrom
299-curl-callback-exception-handoff
Sep 10, 2026
Merged

EdmondDantes merged 2 commits into
true-asyncfrom
299-curl-callback-exception-handoff

Conversation

@EdmondDantes

Copy link
Copy Markdown

Closes true-async/php-async#299.

CURLOPT_FNMATCH_FUNCTION and CURLOPT_SSH_HOSTKEYFUNCTION were the last two curl callbacks without the async exception handoff that curl_prereqfunction, curl_debug, curl_progress and curl_xferinfo already have. An exception left in EG(exception) survived the remaining libcurl iterations and escaped outside the coroutine as an uncaught fatal error, while the awaiter was woken with Async\AsyncCancellation: Graceful shutdown.

Both callbacks now store the exception on the async event with the code libcurl would have produced had the callback returned instead of thrown:

  • fnmatch reports the generic abort. libcurl distinguishes only a match from a non-match there and has no result code for a failing callback (lib/ftplistparser.c, ftp_pl_insert_finfo).
  • a refused host key reports CURLE_PEER_FAILED_VERIFICATION, which is what libcurl reports for anything but CURLKHMATCH_OK (lib/vssh/libssh2.c).

Testing

  • ext/async/tests/curl/072-fnmatch_exception.phpt (php-async PR) covers the fnmatch half against the FTP server from ext/ftp/tests/server.inc. Red before the change, green after; the failing diff shows both the wrong exception at the awaiter and the uncaught fatal.
  • ext/curl/tests + ext/async/tests + ext/async/fuzzy-tests/_generated, ZTS debug, libcurl 8.12.1: 2116 passed, 0 failed.
  • ext/curl/interface.c compiles clean against libcurl 8.5.0 and 7.87.0.

The host-key half has no test: the libcurl on the development machine carries neither sftp nor scp, so the callback cannot be reached there.

CURLOPT_FNMATCH_FUNCTION and CURLOPT_SSH_HOSTKEYFUNCTION were the last two
curl callbacks without the async exception handoff. An exception left in
EG(exception) survived the remaining libcurl iterations and escaped outside
the coroutine as an uncaught error, while the awaiter was woken with a
cancellation.

Both now store the exception on the async event with the code libcurl would
have produced: the generic abort for fnmatch, which libcurl has no result
code of its own for, and CURLE_PEER_FAILED_VERIFICATION for a refused host
key.
The two handoff blocks overwrite the callback result, and the reason was
not on the page: a warning raised as an exception reaches them with the
callback's own answer already in rval. The host-key block also takes the
wrong-return error above it, not only a throw from the callback.
@EdmondDantes
EdmondDantes merged commit 62a91f3 into true-async Sep 10, 2026
1 check passed
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.

1 participant