diff --git a/lib/cshutdn.c b/lib/cshutdn.c index 751d95ad6e..9d4a5ec67a 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -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 */ diff --git a/lib/ftp.c b/lib/ftp.c index 4dde4bdb1d..1dbc56ef69 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -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", diff --git a/lib/multi.c b/lib/multi.c index 868590414c..ae221d4f46 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -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; } diff --git a/lib/pingpong.c b/lib/pingpong.c index bae6dd273f..69bf421b75 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -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; }