mirror of
https://github.com/curl/curl.git
synced 2026-04-11 12:01:42 +08:00
timeout handling: auto-detect effective timeout
When checking a transfer for being expired via `Curl_timeleft_ms()`, eleminate the `bool connecting` parameter and have the function check the `mstate` of the transfer instead. Advantages: * eleminate the caller needing awareness if the transfer is connecting or in a later state * fix pingpong timeout handling to check the correct timeout during "proto_connect" phases * avoid using "connecting" timeouts during establishing a secondary connection (e.g. FTP) since this would use the timestamp from the original, primary connect and thus be wrong Reported-by: Wyuer on github Fixes #20347 Closes #20354
This commit is contained in:
parent
3d354f55b7
commit
8ce16e7bf2
@ -392,7 +392,7 @@ CURLcode Curl_async_await(struct Curl_easy *data,
|
||||
DEBUGASSERT(entry);
|
||||
*entry = NULL; /* clear on entry */
|
||||
|
||||
timeout_ms = Curl_timeleft_ms(data, TRUE);
|
||||
timeout_ms = Curl_timeleft_ms(data);
|
||||
if(timeout_ms < 0) {
|
||||
/* already expired! */
|
||||
connclose(data->conn, "Timed out before name resolve started");
|
||||
|
||||
@ -571,7 +571,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
|
||||
|
||||
do {
|
||||
|
||||
if(Curl_timeleft_ms(data, TRUE) < 0) {
|
||||
if(Curl_timeleft_ms(data) < 0) {
|
||||
failf(data, "Proxy CONNECT aborted due to timeout");
|
||||
result = CURLE_OPERATION_TIMEDOUT;
|
||||
goto out;
|
||||
|
||||
@ -1058,7 +1058,7 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
|
||||
}
|
||||
DEBUGASSERT(ts->authority);
|
||||
|
||||
if(Curl_timeleft_ms(data, TRUE) < 0) {
|
||||
if(Curl_timeleft_ms(data) < 0) {
|
||||
failf(data, "Proxy CONNECT aborted due to timeout");
|
||||
result = CURLE_OPERATION_TIMEDOUT;
|
||||
goto out;
|
||||
|
||||
@ -496,8 +496,8 @@ out:
|
||||
bool more_possible;
|
||||
|
||||
/* when do we need to be called again? */
|
||||
next_expire_ms = Curl_timeleft_ms(data, TRUE);
|
||||
if(next_expire_ms <= 0) {
|
||||
next_expire_ms = Curl_timeleft_ms(data);
|
||||
if(next_expire_ms < 0) {
|
||||
failf(data, "Connection timeout after %" FMT_OFF_T " ms",
|
||||
curlx_ptimediff_ms(Curl_pgrs_now(data),
|
||||
&data->progress.t_startsingle));
|
||||
@ -699,7 +699,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
|
||||
if(!dns)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
if(Curl_timeleft_ms(data, TRUE) < 0) {
|
||||
if(Curl_timeleft_ms(data) < 0) {
|
||||
/* a precaution, no need to continue if time already is up */
|
||||
failf(data, "Connection time-out");
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
|
||||
@ -1955,7 +1955,7 @@ static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
|
||||
#endif
|
||||
|
||||
/* check if the generic timeout possibly is set shorter */
|
||||
other_ms = Curl_timeleft_ms(data, FALSE);
|
||||
other_ms = Curl_timeleft_ms(data);
|
||||
if(other_ms && (other_ms < timeout_ms))
|
||||
/* note that this also works fine for when other_ms happens to be negative
|
||||
due to it already having elapsed */
|
||||
|
||||
@ -177,8 +177,6 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
|
||||
|
||||
*done = FALSE;
|
||||
if(!Curl_shutdown_started(data, sockindex)) {
|
||||
CURL_TRC_M(data, "shutdown start on%s connection",
|
||||
sockindex ? " secondary" : "");
|
||||
Curl_shutdown_start(data, sockindex, 0);
|
||||
}
|
||||
else {
|
||||
@ -552,7 +550,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
|
||||
goto out;
|
||||
else {
|
||||
/* check allowed time left */
|
||||
const timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
|
||||
const timediff_t timeout_ms = Curl_timeleft_ms(data);
|
||||
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
|
||||
int rc;
|
||||
|
||||
|
||||
@ -100,25 +100,27 @@ enum alpnid Curl_str2alpnid(const struct Curl_str *cstr)
|
||||
* transfer/connection. If the value is 0, there is no timeout (ie there is
|
||||
* infinite time left). If the value is negative, the timeout time has already
|
||||
* elapsed.
|
||||
* @param data the transfer to check on
|
||||
* @param duringconnect TRUE iff connect timeout is also taken into account.
|
||||
* @unittest: 1303
|
||||
*/
|
||||
timediff_t Curl_timeleft_now_ms(struct Curl_easy *data,
|
||||
const struct curltime *pnow,
|
||||
bool duringconnect)
|
||||
const struct curltime *pnow)
|
||||
{
|
||||
timediff_t timeleft_ms = 0;
|
||||
timediff_t ctimeleft_ms = 0;
|
||||
timediff_t ctimeout_ms;
|
||||
|
||||
/* The duration of a connect and the total transfer are calculated from two
|
||||
different time-stamps. It can end up with the total timeout being reached
|
||||
before the connect timeout expires and we must acknowledge whichever
|
||||
timeout that is reached first. The total timeout is set per entire
|
||||
operation, while the connect timeout is set per connect. */
|
||||
if((!data->set.timeout || data->set.connect_only) && !duringconnect)
|
||||
if(Curl_shutdown_started(data, FIRSTSOCKET))
|
||||
return Curl_shutdown_timeleft(data, data->conn, FIRSTSOCKET);
|
||||
else if(Curl_is_connecting(data)) {
|
||||
timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
|
||||
data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
|
||||
ctimeleft_ms = ctimeout_ms -
|
||||
curlx_ptimediff_ms(pnow, &data->progress.t_startsingle);
|
||||
if(!ctimeleft_ms)
|
||||
ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
|
||||
}
|
||||
else if(!data->set.timeout || data->set.connect_only) {
|
||||
return 0; /* no timeout in place or checked, return "no limit" */
|
||||
}
|
||||
|
||||
if(data->set.timeout) {
|
||||
timeleft_ms = data->set.timeout -
|
||||
@ -127,25 +129,16 @@ timediff_t Curl_timeleft_now_ms(struct Curl_easy *data,
|
||||
timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
|
||||
}
|
||||
|
||||
if(!duringconnect)
|
||||
return timeleft_ms; /* no connect check, this is it */
|
||||
ctimeout_ms = (data->set.connecttimeout > 0) ?
|
||||
data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
|
||||
ctimeleft_ms = ctimeout_ms -
|
||||
curlx_ptimediff_ms(pnow, &data->progress.t_startsingle);
|
||||
if(!ctimeleft_ms)
|
||||
ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
|
||||
if(!timeleft_ms)
|
||||
return ctimeleft_ms; /* no general timeout, this is it */
|
||||
|
||||
/* return minimal time left or max amount already expired */
|
||||
return (ctimeleft_ms < timeleft_ms) ? ctimeleft_ms : timeleft_ms;
|
||||
return timeleft_ms;
|
||||
else if(!timeleft_ms)
|
||||
return ctimeleft_ms;
|
||||
return CURLMIN(ctimeleft_ms, timeleft_ms);
|
||||
}
|
||||
|
||||
timediff_t Curl_timeleft_ms(struct Curl_easy *data,
|
||||
bool duringconnect)
|
||||
timediff_t Curl_timeleft_ms(struct Curl_easy *data)
|
||||
{
|
||||
return Curl_timeleft_now_ms(data, Curl_pgrs_now(data), duringconnect);
|
||||
return Curl_timeleft_now_ms(data, Curl_pgrs_now(data));
|
||||
}
|
||||
|
||||
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
|
||||
@ -162,6 +155,8 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
|
||||
/* Set a timer, unless we operate on the admin handle */
|
||||
if(data->mid)
|
||||
Curl_expire_ex(data, conn->shutdown.timeout_ms, EXPIRE_SHUTDOWN);
|
||||
CURL_TRC_M(data, "shutdown start on%s connection",
|
||||
sockindex ? " secondary" : "");
|
||||
}
|
||||
|
||||
timediff_t Curl_shutdown_timeleft(struct Curl_easy *data,
|
||||
@ -204,8 +199,11 @@ void Curl_shutdown_clear(struct Curl_easy *data, int sockindex)
|
||||
|
||||
bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
|
||||
{
|
||||
struct curltime *pt = &data->conn->shutdown.start[sockindex];
|
||||
return (pt->tv_sec > 0) || (pt->tv_usec > 0);
|
||||
if(data->conn) {
|
||||
struct curltime *pt = &data->conn->shutdown.start[sockindex];
|
||||
return (pt->tv_sec > 0) || (pt->tv_usec > 0);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* retrieves ip address and port from a sockaddr structure. note it calls
|
||||
|
||||
@ -36,11 +36,9 @@ enum alpnid Curl_str2alpnid(const struct Curl_str *str);
|
||||
|
||||
/* generic function that returns how much time there is left to run, according
|
||||
to the timeouts set */
|
||||
timediff_t Curl_timeleft_ms(struct Curl_easy *data,
|
||||
bool duringconnect);
|
||||
timediff_t Curl_timeleft_ms(struct Curl_easy *data);
|
||||
timediff_t Curl_timeleft_now_ms(struct Curl_easy *data,
|
||||
const struct curltime *pnow,
|
||||
bool duringconnect);
|
||||
const struct curltime *pnow);
|
||||
|
||||
#define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
|
||||
|
||||
|
||||
@ -76,6 +76,10 @@ static void cshutdn_run_once(struct Curl_easy *data,
|
||||
/* We expect to be attached when called */
|
||||
DEBUGASSERT(data->conn == conn);
|
||||
|
||||
if(!Curl_shutdown_started(data, FIRSTSOCKET)) {
|
||||
Curl_shutdown_start(data, FIRSTSOCKET, 0);
|
||||
}
|
||||
|
||||
cshutdn_run_conn_handler(data, conn);
|
||||
|
||||
if(conn->bits.shutdown_filters) {
|
||||
|
||||
@ -304,8 +304,8 @@ static CURLcode doh_probe_run(struct Curl_easy *data,
|
||||
goto error;
|
||||
}
|
||||
|
||||
timeout_ms = Curl_timeleft_ms(data, TRUE);
|
||||
if(timeout_ms <= 0) {
|
||||
timeout_ms = Curl_timeleft_ms(data);
|
||||
if(timeout_ms < 0) {
|
||||
result = CURLE_OPERATION_TIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -686,7 +686,7 @@ static CURLcode getftpresponse(struct Curl_easy *data,
|
||||
|
||||
while(!*ftpcodep && !result) {
|
||||
/* check and reset timeout value every lap */
|
||||
timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE);
|
||||
timediff_t timeout = Curl_pp_state_timeout(data, pp);
|
||||
timediff_t interval_ms;
|
||||
|
||||
if(timeout <= 0) {
|
||||
|
||||
@ -125,7 +125,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
|
||||
else
|
||||
break;
|
||||
|
||||
timeout_ms = Curl_timeleft_ms(data, FALSE);
|
||||
timeout_ms = Curl_timeleft_ms(data);
|
||||
if(timeout_ms < 0) {
|
||||
result = CURLE_OPERATION_TIMEDOUT;
|
||||
break;
|
||||
|
||||
10
lib/multi.c
10
lib/multi.c
@ -351,6 +351,11 @@ static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data)
|
||||
#define multi_warn_debug(x, y) Curl_nop_stmt
|
||||
#endif
|
||||
|
||||
bool Curl_is_connecting(struct Curl_easy *data)
|
||||
{
|
||||
return data->mstate < MSTATE_DO;
|
||||
}
|
||||
|
||||
static CURLMcode multi_xfers_add(struct Curl_multi *multi,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
@ -1720,14 +1725,13 @@ static bool multi_handle_timeout(struct Curl_easy *data,
|
||||
bool *stream_error,
|
||||
CURLcode *result)
|
||||
{
|
||||
bool connect_timeout = data->mstate < MSTATE_DO;
|
||||
timediff_t timeout_ms;
|
||||
|
||||
timeout_ms = Curl_timeleft_ms(data, connect_timeout);
|
||||
timeout_ms = Curl_timeleft_ms(data);
|
||||
if(timeout_ms < 0) {
|
||||
/* Handle timed out */
|
||||
struct curltime since;
|
||||
if(connect_timeout)
|
||||
if(Curl_is_connecting(data))
|
||||
since = data->progress.t_startsingle;
|
||||
else
|
||||
since = data->progress.t_startop;
|
||||
|
||||
@ -40,6 +40,7 @@ bool Curl_multiplex_wanted(const struct Curl_multi *multi);
|
||||
void Curl_set_in_callback(struct Curl_easy *data, bool value);
|
||||
bool Curl_is_in_callback(struct Curl_easy *data);
|
||||
CURLcode Curl_preconnect(struct Curl_easy *data);
|
||||
bool Curl_is_connecting(struct Curl_easy *data);
|
||||
|
||||
void Curl_multi_connchanged(struct Curl_multi *multi);
|
||||
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "urldata.h"
|
||||
#include "cfilters.h"
|
||||
#include "connect.h"
|
||||
#include "multiif.h"
|
||||
#include "sendf.h"
|
||||
#include "curl_trc.h"
|
||||
#include "select.h"
|
||||
@ -40,9 +41,9 @@
|
||||
/* Returns timeout in ms. 0 or negative number means the timeout has already
|
||||
triggered */
|
||||
timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
|
||||
struct pingpong *pp, bool disconnecting)
|
||||
struct pingpong *pp)
|
||||
{
|
||||
timediff_t timeout_ms; /* in milliseconds */
|
||||
timediff_t timeout_ms, xfer_timeout_ms;
|
||||
timediff_t response_time = data->set.server_response_timeout ?
|
||||
data->set.server_response_timeout : RESP_TIMEOUT;
|
||||
|
||||
@ -55,19 +56,10 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
|
||||
full response to arrive before we bail out */
|
||||
timeout_ms = response_time -
|
||||
curlx_ptimediff_ms(Curl_pgrs_now(data), &pp->response);
|
||||
|
||||
if(data->set.timeout && !disconnecting) {
|
||||
/* if timeout is requested, find out how much overall remains */
|
||||
timediff_t timeout2_ms = Curl_timeleft_ms(data, FALSE);
|
||||
/* pick the lowest number */
|
||||
timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
|
||||
}
|
||||
|
||||
if(disconnecting) {
|
||||
timediff_t total_left_ms = Curl_timeleft_ms(data, FALSE);
|
||||
timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0));
|
||||
}
|
||||
|
||||
/* transfer timeout can be 0, which means no timeout applies */
|
||||
xfer_timeout_ms = Curl_timeleft_ms(data);
|
||||
if(xfer_timeout_ms && (xfer_timeout_ms < timeout_ms))
|
||||
return xfer_timeout_ms;
|
||||
return timeout_ms;
|
||||
}
|
||||
|
||||
@ -82,7 +74,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
int rc;
|
||||
timediff_t interval_ms;
|
||||
timediff_t timeout_ms = Curl_pp_state_timeout(data, pp, disconnecting);
|
||||
timediff_t timeout_ms = Curl_pp_state_timeout(data, pp);
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(timeout_ms <= 0) {
|
||||
|
||||
@ -91,7 +91,7 @@ void Curl_pp_init(struct pingpong *pp, const struct curltime *pnow);
|
||||
/* Returns timeout in ms. 0 or negative number means the timeout has already
|
||||
triggered */
|
||||
timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
|
||||
struct pingpong *pp, bool disconnecting);
|
||||
struct pingpong *pp);
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
|
||||
@ -131,7 +131,7 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf,
|
||||
|
||||
*pnread = 0;
|
||||
for(;;) {
|
||||
timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
|
||||
timediff_t timeout_ms = Curl_timeleft_ms(data);
|
||||
if(timeout_ms < 0) {
|
||||
/* we already got the timeout */
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
|
||||
@ -156,10 +156,9 @@ static CURLcode tftp_set_timeouts(struct tftp_conn *state)
|
||||
{
|
||||
time_t timeout;
|
||||
timediff_t timeout_ms;
|
||||
bool start = (state->state == TFTP_STATE_START);
|
||||
|
||||
/* Compute drop-dead time */
|
||||
timeout_ms = Curl_timeleft_ms(state->data, start);
|
||||
timeout_ms = Curl_timeleft_ms(state->data);
|
||||
|
||||
if(timeout_ms < 0) {
|
||||
/* time-out, bail out, go home */
|
||||
@ -1142,8 +1141,7 @@ static timediff_t tftp_state_timeout(struct tftp_conn *state,
|
||||
if(event)
|
||||
*event = TFTP_EVENT_NONE;
|
||||
|
||||
timeout_ms = Curl_timeleft_ms(state->data,
|
||||
(state->state == TFTP_STATE_START));
|
||||
timeout_ms = Curl_timeleft_ms(state->data);
|
||||
if(timeout_ms < 0) {
|
||||
state->error = TFTP_ERR_TIMEOUT;
|
||||
state->state = TFTP_STATE_FIN;
|
||||
|
||||
@ -386,7 +386,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data)
|
||||
goto out;
|
||||
|
||||
if(k->keepon) {
|
||||
if(Curl_timeleft_ms(data, FALSE) < 0) {
|
||||
if(Curl_timeleft_ms(data) < 0) {
|
||||
if(k->size != -1) {
|
||||
failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
|
||||
" milliseconds with %" FMT_OFF_T " out of %"
|
||||
|
||||
@ -3039,7 +3039,7 @@ static CURLcode resolve_server(struct Curl_easy *data,
|
||||
{
|
||||
struct hostname *ehost;
|
||||
int eport;
|
||||
timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
|
||||
timediff_t timeout_ms = Curl_timeleft_ms(data);
|
||||
const char *peertype = "host";
|
||||
CURLcode result;
|
||||
|
||||
|
||||
@ -2420,7 +2420,7 @@ static CURLcode myssh_block_statemach(struct Curl_easy *data,
|
||||
if(result)
|
||||
break;
|
||||
|
||||
left_ms = Curl_timeleft_ms(data, FALSE);
|
||||
left_ms = Curl_timeleft_ms(data);
|
||||
if(left_ms < 0) {
|
||||
failf(data, "Operation timed out");
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
|
||||
@ -3205,7 +3205,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
|
||||
if(result)
|
||||
break;
|
||||
|
||||
left_ms = Curl_timeleft_ms(data, FALSE);
|
||||
left_ms = Curl_timeleft_ms(data);
|
||||
if(left_ms < 0) {
|
||||
failf(data, "Operation timed out");
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
|
||||
@ -1797,7 +1797,7 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
remaining = MAX_RENEG_BLOCK_TIME - elapsed;
|
||||
|
||||
if(blocking) {
|
||||
timeout_ms = Curl_timeleft_ms(data, FALSE);
|
||||
timeout_ms = Curl_timeleft_ms(data);
|
||||
|
||||
if(timeout_ms < 0) {
|
||||
result = CURLE_OPERATION_TIMEDOUT;
|
||||
@ -1950,7 +1950,7 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
while(len > *pnwritten) {
|
||||
size_t this_write = 0;
|
||||
int what;
|
||||
timediff_t timeout_ms = Curl_timeleft_ms(data, FALSE);
|
||||
timediff_t timeout_ms = Curl_timeleft_ms(data);
|
||||
if(timeout_ms < 0) {
|
||||
/* we already got the timeout */
|
||||
failf(data, "schannel: timed out sending data "
|
||||
|
||||
2
lib/ws.c
2
lib/ws.c
@ -1699,7 +1699,7 @@ static CURLcode ws_send_raw_blocking(struct Curl_easy *data,
|
||||
|
||||
CURL_TRC_WS(data, "ws_send_raw_blocking() partial, %zu left to send",
|
||||
buflen);
|
||||
left_ms = Curl_timeleft_ms(data, FALSE);
|
||||
left_ms = Curl_timeleft_ms(data);
|
||||
if(left_ms < 0) {
|
||||
failf(data, "[WS] Timeout waiting for socket becoming writable");
|
||||
return CURLE_SEND_ERROR;
|
||||
|
||||
@ -148,7 +148,8 @@ static CURLcode test_unit1303(const char *arg)
|
||||
NOW(run[i].now_s, run[i].now_us);
|
||||
TIMEOUTS(run[i].timeout_ms, run[i].connecttimeout_ms);
|
||||
easy->progress.now = now;
|
||||
timeout = Curl_timeleft_now_ms(easy, &now, run[i].connecting);
|
||||
easy->mstate = run[i].connecting ? MSTATE_INIT : MSTATE_DO;
|
||||
timeout = Curl_timeleft_now_ms(easy, &now);
|
||||
if(timeout != run[i].result)
|
||||
fail(run[i].comment);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user