1
0
mirror of https://github.com/moparisthebest/curl synced 2024-11-11 20:15:03 -05:00

CURLOPT_XFERINFOFUNCTION: introducing a new progress callback

CURLOPT_XFERINFOFUNCTION is now the preferred progress callback function
and CURLOPT_PROGRESSFUNCTION is considered deprecated.

This new callback uses pure 'curl_off_t' arguments to pass on full
resolution sizes. It otherwise retains the same characteristics: the
same call rate, the same meanings for the arguments and the return code
is used the same way.

The progressfunc.c example is updated to show how to use the new
callback for newer libcurls while supporting the older one if built with
an older libcurl or even built with a newer libcurl while running with
an older.
This commit is contained in:
Daniel Stenberg 2013-06-15 14:57:01 +02:00
parent 90695fb2c5
commit 12d01cb6fa
8 changed files with 140 additions and 29 deletions

View File

@ -16,9 +16,8 @@
1.3 struct lifreq 1.3 struct lifreq
1.4 signal-based resolver timeouts 1.4 signal-based resolver timeouts
1.5 get rid of PATH_MAX 1.5 get rid of PATH_MAX
1.6 progress callback without doubles 1.6 Happy Eyeball dual stack connect
1.7 Happy Eyeball dual stack connect 1.7 Modified buffer size approach
1,8 Modified buffer size approach
2. libcurl - multi interface 2. libcurl - multi interface
2.1 More non-blocking 2.1 More non-blocking
@ -158,16 +157,7 @@
we need libssh2 to properly tell us when we pass in a too small buffer and we need libssh2 to properly tell us when we pass in a too small buffer and
its current API (as of libssh2 1.2.7) doesn't. its current API (as of libssh2 1.2.7) doesn't.
1.6 progress callback without doubles 1.6 Happy Eyeball dual stack connect
The progress callback was introduced way back in the days and the choice to
use doubles in the arguments was possibly good at the time. Today the doubles
only confuse users and make the amounts less precise. We should introduce
another progress callback option that take precedence over the old one and
have both co-exist for a forseeable time until we can remove the double-using
one.
1.7 Happy Eyeball dual stack connect
In order to make alternative technologies not suffer when transitioning, like In order to make alternative technologies not suffer when transitioning, like
when introducing IPv6 as an alternative to IPv4 and there are more than one when introducing IPv6 as an alternative to IPv4 and there are more than one
@ -179,7 +169,7 @@
http://tools.ietf.org/html/rfc6555 http://tools.ietf.org/html/rfc6555
1.8 Modified buffer size approach 1.7 Modified buffer size approach
Current libcurl allocates a fixed 16K size buffer for download and an Current libcurl allocates a fixed 16K size buffer for download and an
additional 16K for upload. They are always unconditionally part of the easy additional 16K for upload. They are always unconditionally part of the easy

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -30,9 +30,10 @@ struct myprogress {
CURL *curl; CURL *curl;
}; };
static int progress(void *p, /* this is how the CURLOPT_XFERINFOFUNCTION callback works */
double dltotal, double dlnow, static int xferinfo(void *p,
double ultotal, double ulnow) curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{ {
struct myprogress *myp = (struct myprogress *)p; struct myprogress *myp = (struct myprogress *)p;
CURL *curl = myp->curl; CURL *curl = myp->curl;
@ -48,7 +49,9 @@ static int progress(void *p,
fprintf(stderr, "TOTAL TIME: %f \r\n", curtime); fprintf(stderr, "TOTAL TIME: %f \r\n", curtime);
} }
fprintf(stderr, "UP: %g of %g DOWN: %g of %g\r\n", fprintf(stderr, "UP: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T
" DOWN: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T
"\r\n",
ulnow, ultotal, dlnow, dltotal); ulnow, ultotal, dlnow, dltotal);
if(dlnow > STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES) if(dlnow > STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES)
@ -56,6 +59,19 @@ static int progress(void *p,
return 0; return 0;
} }
/* for libcurl older than 7.32.0 (CURLOPT_PROGRESSFUNCTION) */
static int older_progress(void *p,
double dltotal, double dlnow,
double ultotal, double ulnow)
{
return xferinfo(p,
(curl_off_t)dltotal,
(curl_off_t)dlnow,
(curl_off_t)ultotal,
(curl_off_t)ulnow);
}
int main(void) int main(void)
{ {
CURL *curl; CURL *curl;
@ -68,9 +84,28 @@ int main(void)
prog.curl = curl; prog.curl = curl;
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/"); curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/");
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, older_progress);
/* pass the struct pointer into the progress function */ /* pass the struct pointer into the progress function */
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &prog); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &prog);
#if LIBCURL_VERSION_NUM >= 0x072000
/* xferinfo was introduced in 7.32.0, no earlier libcurl versions will
compile as they won't have the symbols around.
If built with a newer libcurl, but running with an older libcurl:
curl_easy_setopt() will fail in run-time trying to set the new
callback, making the older callback get used.
New libcurls will prefer the new callback and instead use that one even
if both callbacks are set. */
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo);
/* pass the struct pointer into the xferinfo function, note that this is
an alias to CURLOPT_PROGRESSDATA */
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog);
#endif
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
res = curl_easy_perform(curl); res = curl_easy_perform(curl);

View File

@ -377,10 +377,54 @@ function that performs transfers.
\fICURLOPT_NOPROGRESS\fP must be set to 0 to make this function actually \fICURLOPT_NOPROGRESS\fP must be set to 0 to make this function actually
get called. get called.
.IP CURLOPT_XFERINFOFUNCTION
Pass a pointer to a function that matches the following prototype:
.nf
\fBint function(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow);\fP
.fi
This function gets called by libcurl instead of its internal equivalent with a
frequent interval. While data is being transferred it will be called very
frequently, and during slow periods like when nothing is being transferred it
can slow down to about one call per second.
\fIclientp\fP is the pointer set with \fICURLOPT_XFERINFODATA\fP, it is only
passed along from the application to the callback.
The callback gets told how much data libcurl will transfer and has
transferred, in number of bytes. \fIdltotal\fP is the total number of bytes
libcurl expects to download in this transfer. \fIdlnow\fP is the number of
bytes downloaded so far. \fIultotal\fP is the total number of bytes libcurl
expects to upload in this transfer. \fIulnow\fP is the number of bytes
uploaded so far.
Unknown/unused argument values passed to the callback will be set to zero
(like if you only download data, the upload size will remain 0). Many times
the callback will be called one or more times first, before it knows the data
sizes so a program must be made to handle that.
Returning a non-zero value from this callback will cause libcurl to abort the
transfer and return \fICURLE_ABORTED_BY_CALLBACK\fP.
If you transfer data with the multi interface, this function will not be
called during periods of idleness unless you call the appropriate libcurl
function that performs transfers.
\fICURLOPT_NOPROGRESS\fP must be set to 0 to make this function actually
get called.
(Added in 7.32.0)
.IP CURLOPT_PROGRESSDATA .IP CURLOPT_PROGRESSDATA
Pass a pointer that will be untouched by libcurl and passed as the first Pass a pointer that will be untouched by libcurl and passed as the first
argument in the progress callback set with \fICURLOPT_PROGRESSFUNCTION\fP. argument in the progress callback set with \fICURLOPT_PROGRESSFUNCTION\fP.
The default value of this parameter is unspecified. The default value of this parameter is unspecified.
.IP CURLOPT_XFERINFODATA
Pass a pointer that will be untouched by libcurl and passed as the first
argument in the progress callback set with \fICURLOPT_XFERINFOFUNCTION\fP.
The default value of this parameter is unspecified. This option is an alias
for CURLOPT_PROGRESSDATA. (Added in 7.32.0)
.IP CURLOPT_HEADERFUNCTION .IP CURLOPT_HEADERFUNCTION
Pass a pointer to a function that matches the following prototype: Pass a pointer to a function that matches the following prototype:
\fBsize_t function( void *ptr, size_t size, size_t nmemb, void \fBsize_t function( void *ptr, size_t size, size_t nmemb, void

View File

@ -428,7 +428,7 @@ CURLOPT_POSTREDIR 7.19.1
CURLOPT_PREQUOTE 7.9.5 CURLOPT_PREQUOTE 7.9.5
CURLOPT_PRIVATE 7.10.3 CURLOPT_PRIVATE 7.10.3
CURLOPT_PROGRESSDATA 7.1 CURLOPT_PROGRESSDATA 7.1
CURLOPT_PROGRESSFUNCTION 7.1 CURLOPT_PROGRESSFUNCTION 7.1 7.32.0
CURLOPT_PROTOCOLS 7.19.4 CURLOPT_PROTOCOLS 7.19.4
CURLOPT_PROXY 7.1 CURLOPT_PROXY 7.1
CURLOPT_PROXYAUTH 7.10.7 CURLOPT_PROXYAUTH 7.10.7
@ -525,6 +525,8 @@ CURLOPT_WRITEDATA 7.9.7
CURLOPT_WRITEFUNCTION 7.1 CURLOPT_WRITEFUNCTION 7.1
CURLOPT_WRITEHEADER 7.1 CURLOPT_WRITEHEADER 7.1
CURLOPT_WRITEINFO 7.1 CURLOPT_WRITEINFO 7.1
CURLOPT_XFERINFODATA 7.32.0
CURLOPT_XFERINFOFUNCTION 7.32.0
CURLPAUSE_ALL 7.18.0 CURLPAUSE_ALL 7.18.0
CURLPAUSE_CONT 7.18.0 CURLPAUSE_CONT 7.18.0
CURLPAUSE_RECV 7.18.0 CURLPAUSE_RECV 7.18.0

View File

@ -156,12 +156,22 @@ struct curl_httppost {
HTTPPOST_CALLBACK posts */ HTTPPOST_CALLBACK posts */
}; };
/* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered
deprecated but was the only choice up until 7.31.0 */
typedef int (*curl_progress_callback)(void *clientp, typedef int (*curl_progress_callback)(void *clientp,
double dltotal, double dltotal,
double dlnow, double dlnow,
double ultotal, double ultotal,
double ulnow); double ulnow);
/* This is the CURLOPT_XFERINFOFUNCTION callback proto. It was introduced in
7.32.0, it avoids floating point and provides more detailed information. */
typedef int (*curl_xferinfo_callback)(void *clientp,
curl_off_t dltotal,
curl_off_t dlnow,
curl_off_t ultotal,
curl_off_t ulnow);
#ifndef CURL_MAX_WRITE_SIZE #ifndef CURL_MAX_WRITE_SIZE
/* Tests have proven that 20K is a very bad buffer size for uploads on /* Tests have proven that 20K is a very bad buffer size for uploads on
Windows, while 16K for some odd reason performed a lot better. Windows, while 16K for some odd reason performed a lot better.
@ -968,13 +978,16 @@ typedef enum {
/* 55 = OBSOLETE */ /* 55 = OBSOLETE */
/* Function that will be called instead of the internal progress display /* DEPRECATED
* Function that will be called instead of the internal progress display
* function. This function should be defined as the curl_progress_callback * function. This function should be defined as the curl_progress_callback
* prototype defines. */ * prototype defines. */
CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56),
/* Data passed to the progress callback */ /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION
callbacks */
CINIT(PROGRESSDATA, OBJECTPOINT, 57), CINIT(PROGRESSDATA, OBJECTPOINT, 57),
#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA
/* We want the referrer field set automatically when following locations */ /* We want the referrer field set automatically when following locations */
CINIT(AUTOREFERER, LONG, 58), CINIT(AUTOREFERER, LONG, 58),
@ -1533,6 +1546,11 @@ typedef enum {
/* Enable/disable SASL initial response */ /* Enable/disable SASL initial response */
CINIT(SASL_IR, LONG, 218), CINIT(SASL_IR, LONG, 218),
/* Function that will be called instead of the internal progress display
* function. This function should be defined as the curl_xferinfo_callback
* prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */
CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219),
CURLOPT_LASTENTRY /* the last unused */ CURLOPT_LASTENTRY /* the last unused */
} CURLoption; } CURLoption;

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -357,12 +357,21 @@ int Curl_pgrsUpdate(struct connectdata *conn)
} /* Calculations end */ } /* Calculations end */
if(!(data->progress.flags & PGRS_HIDE)) { if(!(data->progress.flags & PGRS_HIDE)) {
/* progress meter has not been shut off */ /* progress meter has not been shut off */
if(data->set.fprogress) { if(data->set.fxferinfo) {
/* There's a callback set, so we call that instead of writing /* There's a callback set, call that */
anything ourselves. This really is the way to go. */ result= data->set.fxferinfo(data->set.progress_client,
data->progress.size_dl,
data->progress.downloaded,
data->progress.size_ul,
data->progress.uploaded);
if(result)
failf(data, "Callback aborted");
return result;
}
else if(data->set.fprogress) {
/* The older deprecated callback is set, call that */
result= data->set.fprogress(data->set.progress_client, result= data->set.fprogress(data->set.progress_client,
(double)data->progress.size_dl, (double)data->progress.size_dl,
(double)data->progress.downloaded, (double)data->progress.downloaded,

View File

@ -1609,8 +1609,20 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
data->progress.callback = TRUE; /* no longer internal */ data->progress.callback = TRUE; /* no longer internal */
else else
data->progress.callback = FALSE; /* NULL enforces internal */ data->progress.callback = FALSE; /* NULL enforces internal */
break;
case CURLOPT_XFERINFOFUNCTION:
/*
* Transfer info callback function
*/
data->set.fxferinfo = va_arg(param, curl_xferinfo_callback);
if(data->set.fxferinfo)
data->progress.callback = TRUE; /* no longer internal */
else
data->progress.callback = FALSE; /* NULL enforces internal */
break; break;
case CURLOPT_PROGRESSDATA: case CURLOPT_PROGRESSDATA:
/* /*
* Custom client data to pass to the progress callback * Custom client data to pass to the progress callback

View File

@ -1428,7 +1428,8 @@ struct UserDefined {
curl_read_callback fread_func; /* function that reads the input */ curl_read_callback fread_func; /* function that reads the input */
int is_fread_set; /* boolean, has read callback been set to non-NULL? */ int is_fread_set; /* boolean, has read callback been set to non-NULL? */
int is_fwrite_set; /* boolean, has write callback been set to non-NULL? */ int is_fwrite_set; /* boolean, has write callback been set to non-NULL? */
curl_progress_callback fprogress; /* function for progress information */ curl_progress_callback fprogress; /* OLD and deprecated progress callback */
curl_xferinfo_callback fxferinfo; /* progress callback */
curl_debug_callback fdebug; /* function that write informational data */ curl_debug_callback fdebug; /* function that write informational data */
curl_ioctl_callback ioctl_func; /* function for I/O control */ curl_ioctl_callback ioctl_func; /* function for I/O control */
curl_sockopt_callback fsockopt; /* function for setting socket options */ curl_sockopt_callback fsockopt; /* function for setting socket options */