multi: timeout handles even without connection
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
This commit is contained in:
parent
6e8a6039b8
commit
a1ec035afc
65
lib/multi.c
65
lib/multi.c
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -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:
|
||||
@ -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)) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user