mirror of
https://github.com/curl/curl.git
synced 2026-04-14 22:01:41 +03:00
h2+h3: align stream close handling
For HTTP/2, add error code description to close failures. For HTTP/3, add special handling like in HTTP/2 when streams have been rejected or an error comes during the response body and we are not interested in the body. Closes #20207
This commit is contained in:
parent
3b1c2a1510
commit
0f042efcb1
4 changed files with 66 additions and 48 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
61
lib/http2.c
61
lib/http2.c
|
|
@ -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;
|
||||
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 */
|
||||
}
|
||||
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->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->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);
|
||||
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));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue