multi: add notifications API

Add infrastructure to colled and dispatch notifications for transfers
and the multi handle in general. Applications can register a callback
and en-/disable notification type the are interested in.

Without a callback installed, notifications are not collected. Same when
a notification type has not been enabled.

Memory allocation failures on adding notifications lead to a general
multi failure state and result in CURLM_OUT_OF_MEMORY returned from
curl_multi_perform() and curl_multi_socket*() invocations.

Closes #18432
This commit is contained in:
Stefan Eissing 2025-09-01 11:58:16 +02:00 committed by Daniel Stenberg
parent f4e83a0adc
commit 357808f4ad
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
24 changed files with 767 additions and 44 deletions

View file

@ -78,6 +78,8 @@ man_MANS = \
curl_multi_get_offt.3 \
curl_multi_info_read.3 \
curl_multi_init.3 \
curl_multi_notify_disable.3 \
curl_multi_notify_enable.3 \
curl_multi_perform.3 \
curl_multi_poll.3 \
curl_multi_remove_handle.3 \

View file

@ -0,0 +1,66 @@
---
c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
SPDX-License-Identifier: curl
Title: curl_multi_notify_disable
Section: 3
Source: libcurl
See-also:
- CURLMOPT_NOTIFYFUNCTION (3)
- CURLMOPT_NOTIFYDATA (3)
- curl_multi_notify_enable (3)
Protocol:
- All
Added-in: 8.17.0
---
# NAME
curl_multi_notify_disable - disable a notification type
# SYNOPSIS
~~~c
#include <curl/curl.h>
CURLMcode curl_multi_notify_disable(CURLM *multi_handle,
unsigned int notification);
~~~
# DESCRIPTION
Disables collecting the given notification type in the multi handle. A
callback function installed via CURLMOPT_NOTIFYFUNCTION(3) is no longer
called when this notification happens.
Only when a notification callback is installed *and* a notification
is enabled are these collected and dispatched to the callback.
Several notification types can be enabled at the same time. Disabling
an already disabled notification is not an error.
A notification can be enabled again via curl_multi_notify_enable(3).
# %PROTOCOLS%
# EXAMPLE
~~~c
int main(void)
{
int rc;
CURLM *multi = curl_multi_init();
rc = curl_multi_notify_disable(multi, CURLM_NTFY_INFO_READ);
}
~~~
# %AVAILABILITY%
# RETURN VALUE
This function returns a CURLMcode indicating success or error.
CURLM_OK (0) means everything was OK, non-zero means an error occurred, see
libcurl-errors(3).
The return code is for the whole multi stack. Problems still might have
occurred on individual transfers even when one of these functions return OK.

View file

@ -0,0 +1,66 @@
---
c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
SPDX-License-Identifier: curl
Title: curl_multi_notify_enable
Section: 3
Source: libcurl
See-also:
- CURLMOPT_NOTIFYFUNCTION (3)
- CURLMOPT_NOTIFYDATA (3)
- curl_multi_notify_disable (3)
Protocol:
- All
Added-in: 8.17.0
---
# NAME
curl_multi_notify_enable - enable a notification type
# SYNOPSIS
~~~c
#include <curl/curl.h>
CURLMcode curl_multi_notify_enable(CURLM *multi_handle,
unsigned int notification);
~~~
# DESCRIPTION
Enables collecting the given notification type in the multi handle. A
callback function installed via CURLMOPT_NOTIFYFUNCTION(3) is called
when this notification happens.
Only when a notification callback is installed *and* a notification
is enabled are these collected and dispatched to the callback.
Several notification types can be enabled at the same time. Enabling
an already enabled notification is not an error.
A notification can be disabled again via curl_multi_notify_disable(3).
# %PROTOCOLS%
# EXAMPLE
~~~c
int main(void)
{
int rc;
CURLM *multi = curl_multi_init();
rc = curl_multi_notify_enable(multi, CURLM_NTFY_INFO_READ);
}
~~~
# %AVAILABILITY%
# RETURN VALUE
This function returns a CURLMcode indicating success or error.
CURLM_OK (0) means everything was OK, non-zero means an error occurred, see
libcurl-errors(3).
The return code is for the whole multi stack. Problems still might have
occurred on individual transfers even when one of these functions return OK.

View file

@ -72,6 +72,14 @@ Max simultaneously open connections. See CURLMOPT_MAX_TOTAL_CONNECTIONS(3)
Signal that the network has changed. See CURLMOPT_NETWORK_CHANGED(3)
## CURLMOPT_NOTIFYDATA
Custom pointer passed to the notify callback. See CURLMOPT_NOTIFYDATA(3)
## CURLMOPT_NOTIFYFUNCTION
Callback that receives notifications. See CURLMOPT_NOTIFYFUNCTION(3)
## CURLMOPT_PIPELINING
Enable HTTP multiplexing. See CURLMOPT_PIPELINING(3)

View file

@ -0,0 +1,72 @@
---
c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
SPDX-License-Identifier: curl
Title: CURLMOPT_NOTIFYDATA
Section: 3
Source: libcurl
See-also:
- CURLMOPT_NOTIFYFUNCTION (3)
- curl_multi_notify_disable (3)
- curl_multi_notify_enable (3)
Protocol:
- All
Added-in: 8.17.0
---
# NAME
CURLMOPT_NOTIFYDATA - custom pointer passed to the notification callback
# SYNOPSIS
~~~c
#include <curl/curl.h>
CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_NOTIFYDATA, void *pointer);
~~~
# DESCRIPTION
A data *pointer* to pass to the notification callback set with the
CURLMOPT_NOTIFYFUNCTION(3) option.
This pointer is not touched by libcurl but is only passed in as the
notification callback's **clientp** argument.
# DEFAULT
NULL
# %PROTOCOLS%
# EXAMPLE
~~~c
struct priv {
void *ours;
};
static void ntfy_cb(CURLM *multi, unsigned int notification,
CURL *easy, void *ntfyp)
{
struct priv *p = ntfyp;
printf("my ptr: %p\n", p->ours);
/* ... */
}
int main(void)
{
struct priv setup;
CURLM *multi = curl_multi_init();
/* ... use socket callback and custom pointer */
curl_multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, ntfy_cb);
curl_multi_setopt(multi, CURLMOPT_NOTIFYDATA, &setup);
curl_multi_notify_enable(multi, CURLM_NTFY_INFO_READ);
}
~~~
# %AVAILABILITY%
# RETURN VALUE
Returns CURLM_OK.

View file

@ -0,0 +1,129 @@
---
c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
SPDX-License-Identifier: curl
Title: CURLMOPT_NOTIFYFUNCTION
Section: 3
Source: libcurl
See-also:
- CURLMOPT_NOTIFYDATA (3)
- curl_multi_socket_action (3)
- curl_multi_notify_disable (3)
- curl_multi_notify_enable (3)
Protocol:
- All
Added-in: 8.17.0
---
# NAME
CURLMOPT_NOTIFYFUNCTION - callback receiving notifications
# SYNOPSIS
~~~c
#include <curl/curl.h>
void ntfy_callback(CURLM *multi, /* multi handle */
unsigned int notification, /* notification type */
CURL *easy, /* easy handle */
void *ntfyp); /* private ntfy pointer */
CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_NOTIFYFUNCTION, ntfy_callback);
~~~
# DESCRIPTION
Pass a pointer to your callback function, which should match the prototype
shown above.
When the multi handle processes transfers, changes can be observed
by receiving notifications about them. This can eliminate the need to
constantly interrogate the multi handle to observe such changes to
act on them.
Notifications are collected and dispatched to the application's callback
function at an appropriate time.
The notify callback is different from other callbacks in that it
can use more libcurl API functions. Apart from curl_multi_perform(3),
curl_multi_socket(3), curl_multi_socket_action(3), curl_multi_socket_all(3)
and curl_multi_cleanup(3) it may call all other methods on the
multi and easy handles. This includes adding and removing easy
handles to/from the multi handle.
This callback may get invoked at any time when interacting with libcurl.
This may even happen after all transfers are done and *may also*
happen *during* a call to curl_multi_cleanup(3) when cached connections
are shut down.
# CALLBACK ARGUMENTS
*multi* identifies the multi handle that triggered the notification.
**notification** is the type of notification, e.g. what happened. The
following types are available:
## CURLM_NTFY_INFO_READ
When enabled via curl_multi_notify_enable(3), this informs the application
that there are new messages to be processed via curl_multi_info_read(3).
This notification happens whenever a message is added to an empty
message stack in the multi handle and not for subsequent additions. The
notification callback is then expected to read all available message,
emptying the stack, so a subsequent addition triggers the notification
again.
The *easy* handle passed is an internal handle.
## CURLM_NTFY_EASY_DONE
When enabled via curl_multi_notify_enable(3), this notification is triggered
when a an easy handle has finished. This happens both for
successful and failed transfers.
The *easy* handle passed is the transfer that is done. This *may* be
an internal handle when DoH or other features are used.
*easy* identifies the transfer involved. This may be one of the
application's own easy handle or an internal handle.
**ntfyp** is set with CURLMOPT_NOTIFYDATA(3).
# DEFAULT
NULL (no callback)
# %PROTOCOLS%
# EXAMPLE
~~~c
struct priv {
void *ours;
};
static void ntfy_cb(CURLM *multi, unsigned int notification,
CURL *easy, void *ntfyp)
{
struct priv *p = ntfyp;
printf("my ptr: %p\n", p->ours);
/* ... */
}
int main(void)
{
struct priv setup;
CURLM *multi = curl_multi_init();
/* ... use socket callback and custom pointer */
curl_multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, ntfy_cb);
curl_multi_setopt(multi, CURLMOPT_NOTIFYDATA, &setup);
curl_multi_notify_enable(multi, CURLM_NTFY_INFO_READ);
}
~~~
# %AVAILABILITY%
# RETURN VALUE
Returns CURLM_OK.

View file

@ -114,6 +114,8 @@ man_MANS = \
CURLMOPT_MAX_TOTAL_CONNECTIONS.3 \
CURLMOPT_MAXCONNECTS.3 \
CURLMOPT_NETWORK_CHANGED.3 \
CURLMOPT_NOTIFYDATA.3 \
CURLMOPT_NOTIFYFUNCTION.3 \
CURLMOPT_PIPELINING.3 \
CURLMOPT_PIPELINING_SERVER_BL.3 \
CURLMOPT_PIPELINING_SITE_BL.3 \

View file

@ -545,6 +545,8 @@ CURLM_BAD_SOCKET 7.15.4
CURLM_CALL_MULTI_PERFORM 7.9.6
CURLM_CALL_MULTI_SOCKET 7.15.5
CURLM_INTERNAL_ERROR 7.9.6
CURLM_NTFY_EASY_DONE 8.17.0
CURLM_NTFY_INFO_READ 8.17.0
CURLM_OK 7.9.6
CURLM_OUT_OF_MEMORY 7.9.6
CURLM_RECURSIVE_API_CALL 7.59.0
@ -568,6 +570,8 @@ CURLMOPT_MAX_PIPELINE_LENGTH 7.30.0
CURLMOPT_MAX_TOTAL_CONNECTIONS 7.30.0
CURLMOPT_MAXCONNECTS 7.16.3
CURLMOPT_NETWORK_CHANGED 8.16.0
CURLMOPT_NOTIFYDATA 8.17.0
CURLMOPT_NOTIFYFUNCTION 8.17.0
CURLMOPT_PIPELINING 7.16.0
CURLMOPT_PIPELINING_SERVER_BL 7.30.0
CURLMOPT_PIPELINING_SITE_BL 7.30.0