h2+h3: align stream close handling
No binary (build failed)
—
(log yok)
| @@ -588,6 +588,10 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, | |
| drain_tunnel(cf, data, &ctx->tunnel); | |
| } | |
| break; | |
| + | case NGHTTP2_RST_STREAM: |
| + | if(frame->rst_stream.error_code) |
| + | ctx->tunnel.reset = TRUE; |
| + | break; |
| default: | |
| break; | |
| } | |
| @@ -747,6 +751,8 @@ static int proxy_h2_on_stream_close(nghttp2_session *session, | |
| stream_id, nghttp2_http2_strerror(error_code), error_code); | |
| ctx->tunnel.closed = TRUE; | |
| ctx->tunnel.error = error_code; | |
| + | if(error_code) |
| + | ctx->tunnel.reset = TRUE; |
| return 0; | |
| } | |
| @@ -1223,20 +1229,11 @@ static CURLcode h2_handle_tunnel_close(struct Curl_cfilter *cf, | |
| struct cf_h2_proxy_ctx *ctx = cf->ctx; | |
| *pnread = 0; | |
| - | if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { |
| - | CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " |
| - | "connection", ctx->tunnel.stream_id); |
| - | failf(data, "proxy server refused HTTP/2 stream"); |
| - | return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ |
| - | } |
| - | else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) { |
| - | failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", |
| - | ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error), |
| - | ctx->tunnel.error); |
| - | return CURLE_HTTP2_STREAM; |
| - | } |
| - | else if(ctx->tunnel.reset) { |
| - | failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id); |
| + | if(ctx->tunnel.error) { |
| + | failf(data, "HTTP/2 stream %" PRIu32 " reset by %s (error 0x%" PRIx32 |
| + | " %s)", ctx->tunnel.stream_id, |
| + | ctx->tunnel.reset ? "server" : "curl", |
| + | ctx->tunnel.error, nghttp2_http2_strerror(ctx->tunnel.error)); |
| return CURLE_RECV_ERROR; | |
| } | |
| @@ -146,6 +146,7 @@ struct h2_stream_ctx { | |
| BIT(resp_hds_complete); /* we have a complete, final response */ | |
| BIT(closed); /* TRUE on stream close */ | |
| BIT(reset); /* TRUE on stream reset */ | |
| + | BIT(reset_by_server); /* TRUE on stream reset by server */ |
| BIT(close_handled); /* TRUE if stream closure is handled by libcurl */ | |
| BIT(bodystarted); | |
| BIT(body_eos); /* the complete body has been added to `sendbuf` and | |
| @@ -1011,10 +1012,8 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, | |
| } | |
| break; | |
| case NGHTTP2_RST_STREAM: | |
| - | stream->closed = TRUE; |
| - | if(frame->rst_stream.error_code) { |
| - | stream->reset = TRUE; |
| - | } |
| + | if(frame->rst_stream.error_code) |
| + | stream->reset_by_server = TRUE; |
| Curl_multi_mark_dirty(data); | |
| break; | |
| case NGHTTP2_WINDOW_UPDATE: | |
| @@ -1332,9 +1331,8 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, | |
| stream->closed = TRUE; | |
| stream->error = error_code; | |
| - | if(stream->error) { |
| + | if(stream->error) |
| stream->reset = TRUE; | |
| - | } |
| if(stream->error) | |
| CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)", | |
| @@ -1686,36 +1684,31 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, | |
| CURLcode result; | |
| *pnlen = 0; | |
| - | if(stream->error == NGHTTP2_REFUSED_STREAM) { |
| - | CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " |
| - | "connection", stream->id); |
| - | connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ |
| - | data->state.refused_stream = TRUE; |
| - | return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ |
| - | } |
| - | else if(stream->error != NGHTTP2_NO_ERROR) { |
| - | if(stream->resp_hds_complete && data->req.no_body) { |
| - | CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " |
| - | "not want a body anyway, ignore: %s (err %u)", |
| - | stream->id, nghttp2_http2_strerror(stream->error), |
| - | stream->error); |
| - | stream->close_handled = TRUE; |
| - | return CURLE_OK; |
| - | } |
| - | failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", |
| - | stream->id, nghttp2_http2_strerror(stream->error), |
| - | stream->error); |
| - | return CURLE_HTTP2_STREAM; |
| - | } |
| - | else if(stream->reset) { |
| - | failf(data, "HTTP/2 stream %u was reset", stream->id); |
| - | return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2; |
| - | } |
| - | |
| - | if(!stream->bodystarted) { |
| - | failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " |
| - | " all response header fields, treated as error", |
| - | stream->id); |
| + | if(stream->reset) { |
| + | if(stream->error == NGHTTP2_REFUSED_STREAM) { |
| + | infof(data, "HTTP/2 stream %d refused by server, try again on a new " |
| + | "connection", stream->id); |
| + | connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ |
| + | data->state.refused_stream = TRUE; |
| + | return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ |
| + | } |
| + | else if(stream->resp_hds_complete && data->req.no_body) { |
| + | CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " |
| + | "not want a body anyway, ignore: %s (err %u)", |
| + | stream->id, nghttp2_http2_strerror(stream->error), |
| + | stream->error); |
| + | stream->close_handled = TRUE; |
| + | return CURLE_OK; |
| + | } |
| + | failf(data, "HTTP/2 stream %" PRIu32 " reset by %s (error 0x%" PRIx32 |
| + | " %s)", stream->id, stream->reset_by_server ? "server" : "curl", |
| + | stream->error, nghttp2_http2_strerror(stream->error)); |
| + | return stream->error ? CURLE_HTTP2_STREAM : |
| + | (data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2); |
| + | } |
| + | else if(!stream->bodystarted) { |
| + | failf(data, "HTTP/2 stream %d was closed cleanly, but before getting " |
| + | " all response header fields, treated as error", stream->id); |
| return CURLE_HTTP2_STREAM; | |
| } | |
| @@ -1373,6 +1373,20 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, | |
| (void)cf; | |
| *pnread = 0; | |
| if(stream->reset) { | |
| + | if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { |
| + | infof(data, "HTTP/3 stream %" PRId64 " refused by server, try again " |
| + | "on a new connection", stream->id); |
| + | connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ |
| + | data->state.refused_stream = TRUE; |
| + | return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ |
| + | } |
| + | else if(stream->resp_hds_complete && data->req.no_body) { |
| + | CURL_TRC_CF(data, cf, "[%" PRId64 "] error after response headers, " |
| + | "but we did not want a body anyway, ignore error 0x%" |
| + | PRIx64 " %s", stream->id, stream->error3, |
| + | vquic_h3_err_str(stream->error3)); |
| + | return CURLE_OK; |
| + | } |
| failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 | |
| " %s)", stream->id, stream->error3, | |
| vquic_h3_err_str(stream->error3)); | |
| @@ -855,6 +855,20 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, | |
| DEBUGASSERT(stream); | |
| *pnread = 0; | |
| if(stream->reset) { | |
| + | if(stream->error3 == CURL_H3_ERR_REQUEST_REJECTED) { |
| + | infof(data, "HTTP/3 stream %" PRIu64 " refused by server, try again " |
| + | "on a new connection", stream->id); |
| + | connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ |
| + | data->state.refused_stream = TRUE; |
| + | return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ |
| + | } |
| + | else if(stream->resp_hds_complete && data->req.no_body) { |
| + | CURL_TRC_CF(data, cf, "[%" PRIu64 "] error after response headers, " |
| + | "but we did not want a body anyway, ignore error 0x%" |
| + | PRIx64 " %s", stream->id, stream->error3, |
| + | vquic_h3_err_str(stream->error3)); |
| + | return CURLE_OK; |
| + | } |
| failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 | |
| " %s)", stream->id, stream->error3, | |
| vquic_h3_err_str(stream->error3)); | |