lib: add CURL_WRITEFUNC_ERROR to signal write callback error

Prior to this change if the user wanted to signal an error from their
write callbacks they would have to use logic to return a value different
from the number of bytes (nmemb) passed to the callback. Also, the
inclination of some users has been to just return 0 to signal error,
which is incorrect as that may be the number of bytes passed to the
callback.

To remedy this the user can now return CURL_WRITEFUNC_ERROR instead.

Ref: https://github.com/curl/curl/issues/9873

Closes https://github.com/curl/curl/pull/9874
This commit is contained in:
Jay Satiro 2022-11-08 18:49:21 -05:00
parent 988c1c12f5
commit 6d75115406
7 changed files with 35 additions and 35 deletions

View File

@ -51,10 +51,12 @@ the header line is null-terminated!
The pointer named \fIuserdata\fP is the one you set with the
\fICURLOPT_HEADERDATA(3)\fP option.
This callback function must return the number of bytes actually taken care of.
If that amount differs from the amount passed in to your function, it will signal
an error to the library. This will cause the transfer to get aborted and the
libcurl function in progress will return \fICURLE_WRITE_ERROR\fP.
Your callback should return the number of bytes actually taken care of. If
that amount differs from the amount passed to your callback function, it will
signal an error condition to the library. This will cause the transfer to get
aborted and the libcurl function used will return \fICURLE_WRITE_ERROR\fP.
You can also abort the transfer by returning CURL_WRITEFUNC_ERROR. (7.87.0)
A complete HTTP header that is passed to this function can be up to
\fICURL_MAX_HTTP_HEADER\fP (100K) bytes and includes the final line terminator.

View File

@ -59,6 +59,13 @@ process any pending RTP data before marking the request as finished.
The \fICURLOPT_INTERLEAVEDATA(3)\fP is passed in the \fIuserdata\fP argument in
the callback.
Your callback should return the number of bytes actually taken care of. If
that amount differs from the amount passed to your callback function, it will
signal an error condition to the library. This will cause the transfer to get
aborted and the libcurl function used will return \fICURLE_WRITE_ERROR\fP.
You can also abort the transfer by returning CURL_WRITEFUNC_ERROR. (7.87.0)
.SH DEFAULT
NULL, the interleave data is then passed to the regular write function:
\fICURLOPT_WRITEFUNCTION(3)\fP.

View File

@ -63,6 +63,8 @@ that amount differs from the amount passed to your callback function, it will
signal an error condition to the library. This will cause the transfer to get
aborted and the libcurl function used will return \fICURLE_WRITE_ERROR\fP.
You can also abort the transfer by returning CURL_WRITEFUNC_ERROR. (7.87.0)
If your callback function returns CURL_WRITEFUNC_PAUSE it will cause this
transfer to become paused. See \fIcurl_easy_pause(3)\fP for further details.

View File

@ -187,6 +187,7 @@ CURL_WAIT_POLLIN 7.28.0
CURL_WAIT_POLLOUT 7.28.0
CURL_WAIT_POLLPRI 7.28.0
CURL_WIN32 7.69.0
CURL_WRITEFUNC_ERROR 7.87.0
CURL_WRITEFUNC_PAUSE 7.18.0
CURL_ZERO_TERMINATED 7.56.0
CURLALTSVC_H1 7.64.1

View File

@ -256,6 +256,10 @@ typedef int (*curl_xferinfo_callback)(void *clientp,
will signal libcurl to pause receiving on the current transfer. */
#define CURL_WRITEFUNC_PAUSE 0x10000001
/* This is a magic return code for the write callback that, when returned,
will signal an error from the callback. */
#define CURL_WRITEFUNC_ERROR 0xFFFFFFFF
typedef size_t (*curl_write_callback)(char *buffer,
size_t size,
size_t nitems,

View File

@ -77,22 +77,14 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
const char *end = (char *)ptr + cb;
const char *scheme = NULL;
/*
* Once that libcurl has called back tool_header_cb() the returned value
* is checked against the amount that was intended to be written, if
* it does not match then it fails with CURLE_WRITE_ERROR. So at this
* point returning a value different from sz*nmemb indicates failure.
*/
size_t failure = (size && nmemb) ? 0 : 1;
if(!per->config)
return failure;
return CURL_WRITEFUNC_ERROR;
#ifdef DEBUGBUILD
if(size * nmemb > (size_t)CURL_MAX_HTTP_HEADER) {
warnf(per->config->global, "Header data exceeds single call write "
"limit!\n");
return failure;
return CURL_WRITEFUNC_ERROR;
}
#endif
@ -175,7 +167,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
if(outs->stream) {
/* indication of problem, get out! */
free(filename);
return failure;
return CURL_WRITEFUNC_ERROR;
}
outs->is_cd_filename = TRUE;
@ -185,12 +177,12 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
outs->alloc_filename = TRUE;
hdrcbdata->honor_cd_filename = FALSE; /* done now! */
if(!tool_create_output_file(outs, per->config))
return failure;
return CURL_WRITEFUNC_ERROR;
}
break;
}
if(!outs->stream && !tool_create_output_file(outs, per->config))
return failure;
return CURL_WRITEFUNC_ERROR;
}
if(hdrcbdata->config->writeout) {
char *value = memchr(ptr, ':', cb);
@ -210,7 +202,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
char *value = NULL;
if(!outs->stream && !tool_create_output_file(outs, per->config))
return failure;
return CURL_WRITEFUNC_ERROR;
if(hdrcbdata->global->isatty && hdrcbdata->global->styled_output)
value = memchr(ptr, ':', cb);

View File

@ -164,14 +164,6 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
intptr_t fhnd;
#endif
/*
* Once that libcurl has called back tool_write_cb() the returned value
* is checked against the amount that was intended to be written, if
* it does not match then it fails with CURLE_WRITE_ERROR. So at this
* point returning a value different from sz*nmemb indicates failure.
*/
const size_t failure = bytes ? 0 : 1;
#ifdef DEBUGBUILD
{
char *tty = curlx_getenv("CURL_ISATTY");
@ -185,13 +177,13 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
if(bytes > (size_t)CURL_MAX_HTTP_HEADER) {
warnf(config->global, "Header data size exceeds single call write "
"limit!\n");
return failure;
return CURL_WRITEFUNC_ERROR;
}
}
else {
if(bytes > (size_t)CURL_MAX_WRITE_SIZE) {
warnf(config->global, "Data size exceeds single call write limit!\n");
return failure;
return CURL_WRITEFUNC_ERROR;
}
}
@ -220,13 +212,13 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
}
if(check_fails) {
warnf(config->global, "Invalid output struct data for write callback\n");
return failure;
return CURL_WRITEFUNC_ERROR;
}
}
#endif
if(!outs->stream && !tool_create_output_file(outs, per->config))
return failure;
return CURL_WRITEFUNC_ERROR;
if(is_tty && (outs->bytes < 2000) && !config->terminal_binary_ok) {
/* binary output to terminal? */
@ -235,7 +227,7 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
"Use \"--output -\" to tell curl to output it to your terminal "
"anyway, or consider \"--output <FILE>\" to save to a file.\n");
config->synthetic_error = TRUE;
return failure;
return CURL_WRITEFUNC_ERROR;
}
}
@ -251,13 +243,13 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
wc_len = MultiByteToWideChar(CP_UTF8, 0, buffer, in_len, NULL, 0);
wc_buf = (wchar_t*) malloc(wc_len * sizeof(wchar_t));
if(!wc_buf)
return failure;
return CURL_WRITEFUNC_ERROR;
/* calculate buffer size for multi-byte characters */
wc_len = MultiByteToWideChar(CP_UTF8, 0, buffer, in_len, wc_buf, wc_len);
if(!wc_len) {
free(wc_buf);
return failure;
return CURL_WRITEFUNC_ERROR;
}
if(!WriteConsoleW(
@ -267,7 +259,7 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
&wc_len,
NULL)) {
free(wc_buf);
return failure;
return CURL_WRITEFUNC_ERROR;
}
free(wc_buf);
rc = bytes;
@ -289,7 +281,7 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
/* output buffering disabled */
int res = fflush(outs->stream);
if(res)
return failure;
return CURL_WRITEFUNC_ERROR;
}
return rc;