diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index bbf456d1fe..e39e96d2b6 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -368,6 +368,32 @@ out: return result; } +static timediff_t async_ares_poll_timeout(struct async_ares_ctx *ares, + timediff_t timeout_ms) +{ + struct timeval *ares_calced, time_buf, max_timeout; + int itimeout_ms; + +#if TIMEDIFF_T_MAX > INT_MAX + itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX : + ((timeout_ms < 0) ? -1 : (int)timeout_ms); +#else + itimeout_ms = (int)timeout_ms; +#endif + max_timeout.tv_sec = itimeout_ms / 1000; + max_timeout.tv_usec = (itimeout_ms % 1000) * 1000; + + /* c-ares tells us the shortest timeout of any operation on channel */ + ares_calced = ares_timeout(ares->channel, &max_timeout, &time_buf); + /* use the timeout period ares returned to us above if less than one + second is left, otherwise use 1000ms to make sure the progress callback + gets called frequent enough */ + if(!ares_calced->tv_sec) + return (timediff_t)(ares_calced->tv_usec / 1000); + else + return 1000; +} + /* * Curl_async_await() * @@ -383,67 +409,44 @@ CURLcode Curl_async_await(struct Curl_easy *data, struct Curl_dns_entry **dns) { struct async_ares_ctx *ares = &data->state.async.ares; + struct curltime start = *Curl_pgrs_now(data); CURLcode result = CURLE_OK; - timediff_t timeout_ms; DEBUGASSERT(dns); *dns = NULL; /* clear on entry */ - timeout_ms = Curl_timeleft_ms(data); - if(timeout_ms < 0) { - /* already expired! */ - connclose(data->conn, "Timed out before name resolve started"); - return CURLE_OPERATION_TIMEDOUT; - } - if(!timeout_ms) - timeout_ms = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve */ - - /* Wait for the name resolve query to complete. */ + /* Wait for the name resolve query to complete or time out. */ while(!result) { - struct timeval *real_timeout, time_buf, max_timeout; - int itimeout_ms; - timediff_t call_timeout_ms; + timediff_t timeout_ms; -#if TIMEDIFF_T_MAX > INT_MAX - itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX : (int)timeout_ms; -#else - itimeout_ms = (int)timeout_ms; -#endif + timeout_ms = Curl_timeleft_ms(data); + if(!timeout_ms) { /* no applicable timeout from `data`*/ + timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &start); + if(elapsed_ms < (CURL_TIMEOUT_RESOLVE * 1000)) + timeout_ms = (CURL_TIMEOUT_RESOLVE * 1000) - elapsed_ms; + else + timeout_ms = -1; + } - max_timeout.tv_sec = itimeout_ms / 1000; - max_timeout.tv_usec = (itimeout_ms % 1000) * 1000; + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } - real_timeout = ares_timeout(ares->channel, &max_timeout, &time_buf); - - /* use the timeout period ares returned to us above if less than one - second is left, otherwise use 1000ms to make sure the progress callback - gets called frequent enough */ - if(!real_timeout->tv_sec) - call_timeout_ms = (timediff_t)(real_timeout->tv_usec / 1000); - else - call_timeout_ms = 1000; - - if(Curl_ares_perform(ares->channel, call_timeout_ms) < 0) - return CURLE_UNRECOVERABLE_POLL; + if(Curl_ares_perform(ares->channel, + async_ares_poll_timeout(ares, timeout_ms)) < 0) { + result = CURLE_UNRECOVERABLE_POLL; + break; + } result = Curl_async_is_resolved(data, dns); if(result || data->state.async.done) break; - if(Curl_pgrsUpdate(data)) + if(Curl_pgrsUpdate(data)) { result = CURLE_ABORTED_BY_CALLBACK; - else { - struct curltime now = curlx_now(); /* update in loop */ - timediff_t elapsed_ms = curlx_ptimediff_ms(&now, Curl_pgrs_now(data)); - if(elapsed_ms <= 0) - timeout_ms -= 1; /* always deduct at least 1 */ - else if(elapsed_ms > timeout_ms) - timeout_ms = -1; - else - timeout_ms -= elapsed_ms; + break; } - if(timeout_ms < 0) - result = CURLE_OPERATION_TIMEDOUT; } /* Operation complete, if the lookup was successful we now have the entry