mirror of
https://github.com/curl/curl.git
synced 2026-08-05 01:06:13 +03:00
connection: shutdown TLS (for FTP) better
This adds connection shutdown infrastructure and first use for FTP. FTP
data connections, when not encountering an error, are now shut down in a
blocking way with a 2sec timeout.
- add cfilter `Curl_cft_shutdown` callback
- keep a shutdown start timestamp and timeout at connectdata
- provide shutdown timeout default and member in
`data->set.shutdowntimeout`.
- provide methods for starting, interrogating and clearing
shutdown timers
- provide `Curl_conn_shutdown_blocking()` to shutdown the
`sockindex` filter chain in a blocking way. Use that in FTP.
- add `Curl_conn_cf_poll()` to wait for socket events during
shutdown of a connection filter chain.
This gets the monitoring sockets and events via the filters
"adjust_pollset()" methods. This gives correct behaviour when
shutting down a TLS connection through a HTTP/2 proxy.
- Implement shutdown for all socket filters
- for HTTP/2 and h2 proxying to send GOAWAY
- for TLS backends to the best of their capabilities
- for tcp socket filter to make a final, nonblocking
receive to avoid unwanted RST states
- add shutdown forwarding to happy eyeballers and
https connect ballers when applicable.
Closes #13904
This commit is contained in:
parent
7d934267ab
commit
c31041b17e
33 changed files with 1161 additions and 465 deletions
71
lib/http2.c
71
lib/http2.c
|
|
@ -133,11 +133,14 @@ struct cf_h2_ctx {
|
|||
struct Curl_hash streams; /* hash of `data->id` to `h2_stream_ctx` */
|
||||
size_t drain_total; /* sum of all stream's UrlState drain */
|
||||
uint32_t max_concurrent_streams;
|
||||
uint32_t goaway_error;
|
||||
int32_t last_stream_id;
|
||||
uint32_t goaway_error; /* goaway error code from server */
|
||||
int32_t remote_max_sid; /* max id processed by server */
|
||||
int32_t local_max_sid; /* max id processed by us */
|
||||
BIT(conn_closed);
|
||||
BIT(goaway);
|
||||
BIT(rcvd_goaway);
|
||||
BIT(sent_goaway);
|
||||
BIT(enable_push);
|
||||
BIT(shutdown);
|
||||
BIT(nw_out_blocked);
|
||||
};
|
||||
|
||||
|
|
@ -437,7 +440,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
|
|||
Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
|
||||
Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
|
||||
Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
|
||||
ctx->last_stream_id = 2147483647;
|
||||
ctx->remote_max_sid = 2147483647;
|
||||
|
||||
rc = nghttp2_session_callbacks_new(&cbs);
|
||||
if(rc) {
|
||||
|
|
@ -967,6 +970,10 @@ static int push_promise(struct Curl_cfilter *cf,
|
|||
rv = CURL_PUSH_DENY;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* success, remember max stream id processed */
|
||||
if(newstream->id > ctx->local_max_sid)
|
||||
ctx->local_max_sid = newstream->id;
|
||||
}
|
||||
else {
|
||||
CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it");
|
||||
|
|
@ -1252,12 +1259,12 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
break;
|
||||
}
|
||||
case NGHTTP2_GOAWAY:
|
||||
ctx->goaway = TRUE;
|
||||
ctx->rcvd_goaway = TRUE;
|
||||
ctx->goaway_error = frame->goaway.error_code;
|
||||
ctx->last_stream_id = frame->goaway.last_stream_id;
|
||||
ctx->remote_max_sid = frame->goaway.last_stream_id;
|
||||
if(data) {
|
||||
infof(data, "received GOAWAY, error=%u, last_stream=%u",
|
||||
ctx->goaway_error, ctx->last_stream_id);
|
||||
ctx->goaway_error, ctx->remote_max_sid);
|
||||
Curl_multi_connchanged(data->multi);
|
||||
}
|
||||
break;
|
||||
|
|
@ -1878,7 +1885,7 @@ static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
}
|
||||
else if(stream->reset ||
|
||||
(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
|
||||
(ctx->goaway && ctx->last_stream_id < stream->id)) {
|
||||
(ctx->rcvd_goaway && ctx->remote_max_sid < stream->id)) {
|
||||
CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id);
|
||||
*err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2;
|
||||
nread = -1;
|
||||
|
|
@ -2358,6 +2365,7 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
|
|||
struct easy_pollset *ps)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct cf_call_data save;
|
||||
curl_socket_t sock;
|
||||
bool want_recv, want_send;
|
||||
|
||||
|
|
@ -2368,7 +2376,6 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
|
|||
Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
|
||||
if(want_recv || want_send) {
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
|
||||
struct cf_call_data save;
|
||||
bool c_exhaust, s_exhaust;
|
||||
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
|
|
@ -2383,6 +2390,14 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
|
|||
Curl_pollset_set(data, ps, sock, want_recv, want_send);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
}
|
||||
else if(ctx->sent_goaway && !ctx->shutdown) {
|
||||
/* shutdown in progress */
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
want_send = nghttp2_session_want_write(ctx->h2);
|
||||
want_recv = nghttp2_session_want_read(ctx->h2);
|
||||
Curl_pollset_set(data, ps, sock, want_recv, want_send);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
|
||||
|
|
@ -2446,6 +2461,7 @@ static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
CF_DATA_SAVE(save, cf, data);
|
||||
cf_h2_ctx_clear(ctx);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
cf->connected = FALSE;
|
||||
}
|
||||
if(cf->next)
|
||||
cf->next->cft->do_close(cf->next, data);
|
||||
|
|
@ -2462,6 +2478,42 @@ static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
}
|
||||
}
|
||||
|
||||
static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data, bool *done)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
CURLcode result;
|
||||
int rv;
|
||||
|
||||
if(!cf->connected || !ctx->h2 || ctx->shutdown) {
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
if(!ctx->sent_goaway) {
|
||||
rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE,
|
||||
ctx->local_max_sid, 0,
|
||||
(const uint8_t *)"shutown", sizeof("shutown"));
|
||||
if(rv) {
|
||||
failf(data, "nghttp2_submit_goaway() failed: %s(%d)",
|
||||
nghttp2_strerror(rv), rv);
|
||||
return CURLE_SEND_ERROR;
|
||||
}
|
||||
ctx->sent_goaway = TRUE;
|
||||
}
|
||||
/* GOAWAY submitted, process egress and ingress until nghttp2 is done. */
|
||||
result = CURLE_OK;
|
||||
if(nghttp2_session_want_write(ctx->h2))
|
||||
result = h2_progress_egress(cf, data);
|
||||
if(!result && nghttp2_session_want_read(ctx->h2))
|
||||
result = h2_progress_ingress(cf, data, 0);
|
||||
|
||||
*done = !result && !nghttp2_session_want_write(ctx->h2) &&
|
||||
!nghttp2_session_want_read(ctx->h2);
|
||||
ctx->shutdown = (result || *done);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode http2_data_pause(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool pause)
|
||||
|
|
@ -2632,6 +2684,7 @@ struct Curl_cftype Curl_cft_nghttp2 = {
|
|||
cf_h2_destroy,
|
||||
cf_h2_connect,
|
||||
cf_h2_close,
|
||||
cf_h2_shutdown,
|
||||
Curl_cf_def_get_host,
|
||||
cf_h2_adjust_pollset,
|
||||
cf_h2_data_pending,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue