From b83729a339f5904832e4e524eb2e9499dbdb53e9 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Tue, 26 Dec 2023 01:55:54 -0500 Subject: [PATCH] quiche: return CURLE_HTTP3 on send to invalid stream Prior to this change if a send failed on a stream in an invalid state (according to quiche) and not marked as closed (according to libcurl) then the send function would return CURLE_SEND_ERROR. We already have similar code for ngtcp2 to return CURLE_HTTP3 in this case. Caught by test test_07_upload.py: test_07_22_upload_parallel_fail. Fixes https://github.com/curl/curl/issues/12590 Closes https://github.com/curl/curl/pull/12597 --- lib/vquic/curl_ngtcp2.c | 2 ++ lib/vquic/curl_quiche.c | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index fb1ef60a0a..f4edf2d636 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -1894,6 +1894,8 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, sent = (ssize_t)len; goto out; } + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> stream closed", stream->id, len); *err = CURLE_HTTP3; sent = -1; goto out; diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 91395876b4..227adbdd9a 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -1078,6 +1078,28 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; stream = H3_STREAM_CTX(data); } + else if(stream->closed) { + if(stream->resp_hds_complete) { + /* sending request body on a stream that has been closed by the + * server. If the server has send us a final response, we should + * silently discard the send data. + * This happens for example on redirects where the server, instead + * of reading the full request body just closed the stream after + * sending the 30x response. + * This is sort of a race: had the transfer loop called recv first, + * it would see the response and stop/discard sending on its own- */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" + "on closed stream with response", stream->id); + *err = CURLE_OK; + nwritten = (ssize_t)len; + goto out; + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> stream closed", stream->id, len); + *err = CURLE_HTTP3; + nwritten = -1; + goto out; + } else { bool eof = (stream->upload_left >= 0 && (curl_off_t)len >= stream->upload_left); @@ -1095,20 +1117,11 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, nwritten = -1; goto out; } - else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE && - stream->closed && stream->resp_hds_complete) { - /* sending request body on a stream that has been closed by the - * server. If the server has send us a final response, we should - * silently discard the send data. - * This happens for example on redirects where the server, instead - * of reading the full request body just closed the stream after - * sending the 30x response. - * This is sort of a race: had the transfer loop called recv first, - * it would see the response and stop/discard sending on its own- */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" - "on closed stream with response", stream->id); - *err = CURLE_OK; - nwritten = (ssize_t)len; + else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> invalid stream state", stream->id, len); + *err = CURLE_HTTP3; + nwritten = -1; goto out; } else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { -- 2.47.3