Skip to content

Commit

Permalink
multi: timeout handles even without connection
Browse files Browse the repository at this point in the history
When there is a "change" in a multi handle and pending handles are moved
back to the main list to be retested if they can proceed further (for
example a previous transfer completed or a connection has a confirmed
multiplexed state), the timeout check in multi_runsingle() would not
trigger because it required an established connection.

This could make a pending tranfer go back to pending state even though
it had been "in progress" for a longer time than permitted. By removing
the requirement for an associated connection, the timeout check will be
done proper even for transfers that has not yet been assigned one.

Ref #13227
Reported-by: Rahul Krishna M
Closes #13276
  • Loading branch information
bagder committed Apr 12, 2024
1 parent de66e8a commit dd47d69
Showing 1 changed file with 27 additions and 38 deletions.
65 changes: 27 additions & 38 deletions lib/multi.c
Expand Up @@ -1744,47 +1744,48 @@ static bool multi_handle_timeout(struct Curl_easy *data,
CURLcode *result,
bool connect_timeout)
{
timediff_t timeout_ms;
timeout_ms = Curl_timeleft(data, now, connect_timeout);
timediff_t timeout_ms = Curl_timeleft(data, now, connect_timeout);

if(timeout_ms < 0) {
/* Handle timed out */
struct curltime since;
if(connect_timeout)
since = data->progress.t_startsingle;
else
since = data->progress.t_startop;
if(data->mstate == MSTATE_RESOLVING)
failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
" milliseconds",
Curl_timediff(*now, data->progress.t_startsingle));
" milliseconds", Curl_timediff(*now, since));
else if(data->mstate == MSTATE_CONNECTING)
failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
" milliseconds",
Curl_timediff(*now, data->progress.t_startsingle));
" milliseconds", Curl_timediff(*now, since));
else {
struct SingleRequest *k = &data->req;
if(k->size != -1) {
failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
" milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
CURL_FORMAT_CURL_OFF_T " bytes received",
Curl_timediff(*now, data->progress.t_startsingle),
k->bytecount, k->size);
Curl_timediff(*now, since), k->bytecount, k->size);
}
else {
failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
" milliseconds with %" CURL_FORMAT_CURL_OFF_T
" bytes received",
Curl_timediff(*now, data->progress.t_startsingle),
k->bytecount);
" bytes received", Curl_timediff(*now, since), k->bytecount);
}
}

/* Force connection closed if the connection has indeed been used */
if(data->mstate > MSTATE_DO) {
streamclose(data->conn, "Disconnected with pending data");
*stream_error = TRUE;
}
*result = CURLE_OPERATION_TIMEDOUT;
(void)multi_done(data, *result, TRUE);
if(data->conn) {
/* Force connection closed if the connection has indeed been used */
if(data->mstate > MSTATE_DO) {
streamclose(data->conn, "Disconnect due to timeout");
*stream_error = TRUE;
}
(void)multi_done(data, *result, TRUE);
}
return TRUE;
}

return (timeout_ms < 0);
return FALSE;
}

/*
Expand Down Expand Up @@ -1942,23 +1943,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
return CURLM_INTERNAL_ERROR;
}

if(data->conn &&
(data->mstate >= MSTATE_CONNECT) &&
(data->mstate < MSTATE_COMPLETED)) {
/* Check for overall operation timeout here but defer handling the
* connection timeout to later, to allow for a connection to be set up
* in the window since we last checked timeout. This prevents us
* tearing down a completed connection in the case where we were slow
* to check the timeout (e.g. process descheduled during this loop).
* We set connect_timeout=FALSE to do this. */

/* we need to wait for the connect state as only then is the start time
stored, but we must not check already completed handles */
if(multi_handle_timeout(data, nowp, &stream_error, &result, FALSE)) {
/* Skip the statemachine and go directly to error handling section. */
goto statemachine_end;
}
}
/* Wait for the connect state as only then is the start time stored, but
we must not check already completed handles */
if((data->mstate >= MSTATE_CONNECT) && (data->mstate < MSTATE_COMPLETED) &&
multi_handle_timeout(data, nowp, &stream_error, &result, FALSE))
/* Skip the statemachine and go directly to error handling section. */
goto statemachine_end;

switch(data->mstate) {
case MSTATE_INIT:
Expand Down Expand Up @@ -2629,8 +2619,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
return CURLM_INTERNAL_ERROR;
}

if(data->conn &&
data->mstate >= MSTATE_CONNECT &&
if(data->mstate >= MSTATE_CONNECT &&
data->mstate < MSTATE_DO &&
rc != CURLM_CALL_MULTI_PERFORM &&
!multi_ischanged(multi, false)) {
Expand Down

0 comments on commit dd47d69

Please sign in to comment.