protocol handler blocking disconnect workaround

- set the default shutdown timeout of 2 seconds for the internal
  admin handle when using that to disconnect the protocol
- pingpong: add checking the over all timeout of the transfer
  during disconnect
- pingpong: on poll timeout in blocking wait, error with
  CURLE_OPERATION_TIMEDOUT when disconnecting
- multi: readd the timeout/signal "inheritance" of added
  transfers to the admin handle - as it was before.
This commit is contained in:
Stefan Eissing 2025-02-28 12:34:40 +01:00
parent 5432614ed3
commit aa3ede6d27
No known key found for this signature in database
4 changed files with 29 additions and 3 deletions

View File

@ -63,7 +63,14 @@ static void cshutdn_run_conn_handler(struct Curl_easy *data,
Curl_http_auth_cleanup_negotiate(conn);
if(conn->handler && conn->handler->disconnect) {
unsigned int timeout_ms = data->set.server_response_timeout;
/* Some disconnect handlers do a blocking wait on server responses.
* FTP/IMAP/SMTP and SFTP are among them. When using the internal
* handle, set an overall short timeout so we do not hang for the
* default 120 seconds. */
if(data->state.internal) {
data->set.timeout = DEFAULT_SHUTDOWN_TIMEOUT_MS;
(void)Curl_pgrsTime(data, TIMER_STARTOP);
}
/* This is set if protocol-specific cleanups should be made */
DEBUGF(infof(data, "connection #%" FMT_OFF_T
@ -71,9 +78,7 @@ static void cshutdn_run_conn_handler(struct Curl_easy *data,
conn->connection_id, conn->bits.aborted));
/* There are protocol handlers that block on retrieving
* server responses here (FTP). Set a short timeout. */
data->set.server_response_timeout = CURLMIN(timeout_ms, 2 * 1000);
conn->handler->disconnect(data, conn, conn->bits.aborted);
data->set.server_response_timeout = timeout_ms;
}
/* possible left-overs from the async name resolvers */

View File

@ -3132,6 +3132,8 @@ static CURLcode ftp_block_statemach(struct Curl_easy *data,
CURLcode result = CURLE_OK;
while(ftpc->state != FTP_STOP) {
if(ftpc->shutdown)
CURL_TRC_FTP(data, "in shutdown, waiting for server response");
result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
if(result)
break;
@ -4043,6 +4045,7 @@ static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
CURLcode result = CURLE_OK;
if(conn->proto.ftpc.ctl_valid) {
CURL_TRC_FTP(data, "sending QUIT to close session");
result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
if(result) {
failf(data, "Failure sending QUIT command: %s",

View File

@ -417,6 +417,15 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
Curl_cpool_xfer_init(data);
multi_warn_debug(multi, data);
/* The admin handle only ever has default timeouts set. To improve the
state somewhat we clone the timeouts from each added handle so that the
admin handle always has the same timeouts as the most recently added
easy handle. */
multi->admin->set.timeout = data->set.timeout;
multi->admin->set.server_response_timeout =
data->set.server_response_timeout;
multi->admin->set.no_signal = data->set.no_signal;
CURL_TRC_M(data, "added, transfers=%u", multi->num_easy);
return CURLM_OK;
}

View File

@ -29,6 +29,7 @@
#include "urldata.h"
#include "cfilters.h"
#include "connect.h"
#include "sendf.h"
#include "select.h"
#include "progress.h"
@ -74,6 +75,11 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
}
if(disconnecting) {
timediff_t total_left_ms = Curl_timeleft(data, NULL, FALSE);
timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0));
}
return timeout_ms;
}
@ -96,6 +102,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
}
DEBUGF(infof(data, "pp_statematch, timeout=%" FMT_TIMEDIFF_T, timeout_ms));
if(block) {
interval_ms = 1000; /* use 1 second timeout intervals */
if(timeout_ms < interval_ms)
@ -135,6 +142,8 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
}
else if(rc)
result = pp->statemachine(data, data->conn);
else if(disconnecting)
return CURLE_OPERATION_TIMEDOUT;
return result;
}