cw-out: handle error codes for 0len writes

Handle errors returned by the callback the same for 0-length writes as
for all the others.

Closes #18428
This commit is contained in:
Stefan Eissing 2025-08-29 10:20:12 +02:00 committed by Daniel Stenberg
parent 21c288902d
commit fa9151b41a
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2

View file

@ -195,6 +195,50 @@ static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype,
}
}
static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx,
struct Curl_easy *data,
curl_write_callback wcb,
void *wcb_data,
cw_out_type otype,
const char *buf, size_t blen,
size_t *pnwritten)
{
size_t nwritten;
CURLcode result;
*pnwritten = 0;
Curl_set_in_callback(data, TRUE);
nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data);
Curl_set_in_callback(data, FALSE);
CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu",
blen, (otype == CW_OUT_HDS) ? "header" : "body",
nwritten);
if(CURL_WRITEFUNC_PAUSE == nwritten) {
if(data->conn && data->conn->handler->flags & PROTOPT_NONETWORK) {
/* Protocols that work without network cannot be paused. This is
actually only FILE:// just now, and it cannot pause since the
transfer is not done using the "normal" procedure. */
failf(data, "Write callback asked for PAUSE when not supported");
return CURLE_WRITE_ERROR;
}
ctx->paused = TRUE;
CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
result = Curl_xfer_pause_recv(data, TRUE);
return result ? result : CURLE_AGAIN;
}
else if(CURL_WRITEFUNC_ERROR == nwritten) {
failf(data, "client returned ERROR on write of %zu bytes", blen);
return CURLE_WRITE_ERROR;
}
else if(nwritten != blen) {
failf(data, "Failure writing output to destination, "
"passed %zu returned %zd", blen, nwritten);
return CURLE_WRITE_ERROR;
}
*pnwritten = nwritten;
return CURLE_OK;
}
static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
struct Curl_easy *data,
cw_out_type otype,
@ -206,6 +250,7 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
void *wcb_data;
size_t max_write, min_write;
size_t wlen, nwritten;
CURLcode result;
/* If we errored once, we do not invoke the client callback again */
if(ctx->errored)
@ -221,44 +266,18 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
*pconsumed = 0;
if(otype == CW_OUT_BODY_0LEN) {
DEBUGASSERT(!blen);
Curl_set_in_callback(data, TRUE);
nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data);
Curl_set_in_callback(data, FALSE);
CURL_TRC_WRITE(data, "[OUT] wrote %zu BODY bytes -> %zu",
blen, nwritten);
return cw_out_cb_write(ctx, data, wcb, wcb_data, otype,
buf, blen, &nwritten);
}
else {
while(blen && !ctx->paused) {
if(!flush_all && blen < min_write)
break;
wlen = max_write ? CURLMIN(blen, max_write) : blen;
Curl_set_in_callback(data, TRUE);
nwritten = wcb((char *)CURL_UNCONST(buf), 1, wlen, wcb_data);
Curl_set_in_callback(data, FALSE);
CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu",
wlen, (otype == CW_OUT_BODY) ? "body" : "header",
nwritten);
if(CURL_WRITEFUNC_PAUSE == nwritten) {
if(data->conn && data->conn->handler->flags & PROTOPT_NONETWORK) {
/* Protocols that work without network cannot be paused. This is
actually only FILE:// just now, and it cannot pause since the
transfer is not done using the "normal" procedure. */
failf(data, "Write callback asked for PAUSE when not supported");
return CURLE_WRITE_ERROR;
}
ctx->paused = TRUE;
CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
return Curl_xfer_pause_recv(data, TRUE);
}
else if(CURL_WRITEFUNC_ERROR == nwritten) {
failf(data, "client returned ERROR on write of %zu bytes", wlen);
return CURLE_WRITE_ERROR;
}
else if(nwritten != wlen) {
failf(data, "Failure writing output to destination, "
"passed %zu returned %zd", wlen, nwritten);
return CURLE_WRITE_ERROR;
}
result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype,
buf, wlen, &nwritten);
if(result)
return result;
*pconsumed += nwritten;
blen -= nwritten;
buf += nwritten;
@ -274,14 +293,14 @@ static CURLcode cw_out_buf_flush(struct cw_out_ctx *ctx,
{
CURLcode result = CURLE_OK;
if(curlx_dyn_len(&cwbuf->b)) {
if(curlx_dyn_len(&cwbuf->b) || (cwbuf->type == CW_OUT_BODY_0LEN)) {
size_t consumed;
result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all,
curlx_dyn_ptr(&cwbuf->b),
curlx_dyn_len(&cwbuf->b),
&consumed);
if(result)
if(result && (result != CURLE_AGAIN))
return result;
if(consumed) {
@ -394,8 +413,9 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
size_t consumed;
result = cw_out_ptr_flush(ctx, data, otype, flush_all,
buf, blen, &consumed);
if(result)
if(result && (result != CURLE_AGAIN))
return result;
result = CURLE_OK;
if(consumed < blen) {
/* did not write all, append the rest */
result = cw_out_append(ctx, data, otype,