From 1b701c746f66b8fd5bf3017c36254dbde8456df2 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 7 Feb 2008 22:25:04 +0000 Subject: [PATCH] - Refactored a lot of timeout code into a few functions in an attempt to make them all use the same (hopefully correct) logic to make it less error-prone and easier to introduce library-wide where it should be used. --- CHANGES | 5 ++ RELEASE-NOTES | 3 +- lib/connect.c | 138 +++++++++++++++++++++++++++++--------------------- lib/connect.h | 14 +++-- lib/ftp.c | 39 ++------------ lib/gtls.c | 20 +------- lib/qssl.c | 13 +---- lib/socks.c | 26 +--------- lib/ssluse.c | 29 ++--------- lib/tftp.c | 17 ++++--- 10 files changed, 123 insertions(+), 181 deletions(-) diff --git a/CHANGES b/CHANGES index 96fa0af1e..9dac29165 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,11 @@ Changelog +Daniel S (7 Feb 2008) +- Refactored a lot of timeout code into a few functions in an attempt to make + them all use the same (hopefully correct) logic to make it less error-prone + and easier to introduce library-wide where it should be used. + Yang Tse (6 Feb 2008) - Fix an issue in strdup replacement function when dealing with absolutely huge strings. Only systems without a standard strdup would be affected. diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 09c7c7513..75b434301 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -32,6 +32,7 @@ New curl mirrors: This release would not have looked like this without help, code, reports and advice from friends like these: - Michal Marek, Dmitry Kurochkin, Niklas Angebrand + Michal Marek, Dmitry Kurochkin, Niklas Angebrand, Günter Knauf, Yang Tse, + Dan Fandrich Thanks! (and sorry if I forgot to mention someone) diff --git a/lib/connect.c b/lib/connect.c index e9f9cf150..393c85603 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -101,6 +101,66 @@ singleipconnect(struct connectdata *conn, long timeout_ms, bool *connected); +/* + * Curl_timeleft() returns the amount of milliseconds left allowed for the + * transfer/connection. If the value is negative, the timeout time has already + * elapsed. + * + * If 'nowp' is non-NULL, it points to the current time. + * 'duringconnect' is FALSE if not during a connect, as then of course the + * connect timeout is not taken into account! + */ +long Curl_timeleft(struct connectdata *conn, + struct timeval *nowp, + bool duringconnect) +{ + struct SessionHandle *data = conn->data; + int timeout_set = 0; + long timeout_ms = duringconnect?DEFAULT_CONNECT_TIMEOUT:0; + struct timeval now; + + /* if a timeout is set, use the most restrictive one */ + + if(data->set.timeout > 0) + timeout_set |= 1; + if(duringconnect && (data->set.connecttimeout > 0)) + timeout_set |= 2; + + switch (timeout_set) { + case 1: + timeout_ms = data->set.timeout; + break; + case 2: + timeout_ms = data->set.connecttimeout; + break; + case 3: + if(data->set.timeout < data->set.connecttimeout) + timeout_ms = data->set.timeout; + else + timeout_ms = data->set.connecttimeout; + break; + default: + /* use the default */ + if(!duringconnect) + /* if we're not during connect, there's no default timeout so if we're + at zero we better just return zero and not make it a negative number + by the math below */ + return 0; + break; + } + + if(!nowp) { + now = Curl_tvnow(); + nowp = &now; + } + + /* substract elapsed time */ + timeout_ms -= Curl_tvdiff(*nowp, data->progress.t_startsingle); + + return timeout_ms; +} + + /* * Curl_nonblock() set the given socket to either blocking or non-blocking * mode based on the 'nonblock' boolean argument. This function is highly @@ -533,42 +593,33 @@ CURLcode Curl_is_connected(struct connectdata *conn, CURLcode code = CURLE_OK; curl_socket_t sockfd = conn->sock[sockindex]; long allow = DEFAULT_CONNECT_TIMEOUT; - long allow_total = 0; - long has_passed; DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); *connected = FALSE; /* a very negative world view is best */ - /* Evaluate in milliseconds how much time that has passed */ - has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); - - /* subtract the most strict timeout of the ones */ - if(data->set.timeout && data->set.connecttimeout) { - if(data->set.timeout < data->set.connecttimeout) - allow_total = allow = data->set.timeout; - else - allow = data->set.connecttimeout; - } - else if(data->set.timeout) { - allow_total = allow = data->set.timeout; - } - else if(data->set.connecttimeout) { - allow = data->set.connecttimeout; - } - - if(has_passed > allow ) { - /* time-out, bail out, go home */ - failf(data, "Connection time-out after %ld ms", has_passed); - return CURLE_OPERATION_TIMEDOUT; - } if(conn->bits.tcpconnect) { /* we are connected already! */ + long allow_total = 0; + + /* subtract the most strict timeout of the ones */ + if(data->set.timeout) + allow_total = data->set.timeout; + Curl_expire(data, allow_total); *connected = TRUE; return CURLE_OK; } + /* figure out how long time we have left to connect */ + allow = Curl_timeleft(conn, NULL, TRUE); + + if(allow < 0) { + /* time-out, bail out, go home */ + failf(data, "Connection time-out"); + return CURLE_OPERATION_TIMEDOUT; + } + Curl_expire(data, allow); /* check for connect without timeout as we want to return immediately */ @@ -821,7 +872,6 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ int num_addr; Curl_addrinfo *ai; Curl_addrinfo *curr_addr; - int timeout_set = 0; struct timeval after; struct timeval before = Curl_tvnow(); @@ -834,39 +884,13 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ *connected = FALSE; /* default to not connected */ - /* if a timeout is set, use the most restrictive one */ + /* get the timeout left */ + timeout_ms = Curl_timeleft(conn, &before, TRUE); - if(data->set.timeout > 0) - timeout_set += 1; - if(data->set.connecttimeout > 0) - timeout_set += 2; - - switch (timeout_set) { - case 1: - timeout_ms = data->set.timeout; - break; - case 2: - timeout_ms = data->set.connecttimeout; - break; - case 3: - if(data->set.timeout < data->set.connecttimeout) - timeout_ms = data->set.timeout; - else - timeout_ms = data->set.connecttimeout; - break; - default: - timeout_ms = DEFAULT_CONNECT_TIMEOUT; - break; - } - - if(timeout_set > 0) { - /* if a timeout was already set, substract elapsed time */ - timeout_ms -= Curl_tvdiff(before, data->progress.t_startsingle); - if(timeout_ms < 0) { - /* a precaution, no need to continue if time already is up */ - failf(data, "Connection time-out"); - return CURLE_OPERATION_TIMEDOUT; - } + if(timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "Connection time-out"); + return CURLE_OPERATION_TIMEDOUT; } Curl_expire(data, timeout_ms); diff --git a/lib/connect.h b/lib/connect.h index 3bfe722ea..78c0191ba 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -31,14 +31,20 @@ CURLcode Curl_is_connected(struct connectdata *conn, bool *connected); CURLcode Curl_connecthost(struct connectdata *conn, - const struct Curl_dns_entry *host, /* connect to this */ + const struct Curl_dns_entry *host, /* connect to + this */ curl_socket_t *sockconn, /* not set if error */ Curl_addrinfo **addr, /* the one we used */ - bool *connected /* truly connected? */ - ); + bool *connected); /* truly connected? */ CURLcode Curl_store_ip_addr(struct connectdata *conn); +/* generic function that returns how much time there's left to run, according + to the timeouts set */ +long Curl_timeleft(struct connectdata *conn, + struct timeval *nowp, + bool duringconnect); + #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ #endif diff --git a/lib/ftp.c b/lib/ftp.c index 5fc9669ab..061189c98 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -300,43 +300,14 @@ static bool isBadFtpString(const char *string) */ static CURLcode AllowServerConnect(struct connectdata *conn) { - long timeout_ms; struct SessionHandle *data = conn->data; curl_socket_t sock = conn->sock[SECONDARYSOCKET]; - int timeout_set = 0; + long timeout_ms = Curl_timeleft(conn, NULL, TRUE); - /* if a timeout is set, use the most restrictive one */ - - if(data->set.timeout > 0) - timeout_set += 1; - if(data->set.connecttimeout > 0) - timeout_set += 2; - - switch (timeout_set) { - case 1: - timeout_ms = data->set.timeout; - break; - case 2: - timeout_ms = data->set.connecttimeout; - break; - case 3: - if(data->set.timeout < data->set.connecttimeout) - timeout_ms = data->set.timeout; - else - timeout_ms = data->set.connecttimeout; - break; - default: - timeout_ms = 60000; /* 60 seconds default timeout */ - break; - } - - if(timeout_set > 0) { - /* if a timeout was already set, substract elapsed time */ - timeout_ms -= Curl_tvdiff(Curl_tvnow(), conn->now); - if(timeout_ms < 0) { - failf(data, "Timed out before server could connect to us"); - return CURLE_OPERATION_TIMEDOUT; - } + if(timeout_ms < 0) { + /* if a timeout was already reached, bail out */ + failf(data, "Timed out before server could connect to us"); + return CURLE_OPERATION_TIMEDOUT; } switch (Curl_socket_ready(sock, CURL_SOCKET_BAD, (int)timeout_ms)) { diff --git a/lib/gtls.c b/lib/gtls.c index 49b0fc739..d317d2bb0 100644 --- a/lib/gtls.c +++ b/lib/gtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -154,23 +154,7 @@ static CURLcode handshake(struct connectdata *conn, rc = gnutls_handshake(session); if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { - long timeout_ms = DEFAULT_CONNECT_TIMEOUT; - long has_passed; - - if(duringconnect && data->set.connecttimeout) - timeout_ms = data->set.connecttimeout; - - if(data->set.timeout) { - /* get the strictest timeout of the ones converted to milliseconds */ - if(data->set.timeout < timeout_ms) - timeout_ms = data->set.timeout; - } - - /* Evaluate in milliseconds how much time that has passed */ - has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); - - /* subtract the passed time */ - timeout_ms -= has_passed; + long timeout_ms = Curl_connecttimeleft(conn, NULL, duringconnect); if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ diff --git a/lib/qssl.c b/lib/qssl.c index 4ced8b744..e65a0e2f6 100644 --- a/lib/qssl.c +++ b/lib/qssl.c @@ -172,17 +172,8 @@ static CURLcode Curl_qsossl_handshake(struct connectdata * conn, int sockindex) if(!data->set.ssl.verifyhost) h->exitPgm = Curl_qsossl_trap_cert; - if(data->set.connecttimeout) { - timeout_ms = data->set.connecttimeout; - - if(data->set.timeout) - if(timeout_ms > data->set.timeout) - timeout_ms = data->set.timeout; - } - else if(data->set.timeout) - timeout_ms = data->set.timeout; - else - timeout_ms = DEFAULT_CONNECT_TIMEOUT; + /* figure out how long time we should wait at maximum */ + timeout_ms = Curl_timeleft(conn, NULL, TRUE); /* SSL_Handshake() timeout resolution is second, so round up. */ diff --git a/lib/socks.c b/lib/socks.c index dc159ad38..b78a04a45 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -138,18 +138,7 @@ CURLcode Curl_SOCKS4(const char *proxy_name, struct SessionHandle *data = conn->data; /* get timeout */ - if(data->set.timeout && data->set.connecttimeout) { - if(data->set.timeout < data->set.connecttimeout) - timeout = data->set.timeout; - else - timeout = data->set.connecttimeout; - } - else if(data->set.timeout) - timeout = data->set.timeout; - else if(data->set.connecttimeout) - timeout = data->set.connecttimeout; - else - timeout = DEFAULT_CONNECT_TIMEOUT; + timeout = Curl_timeleft(conn, NULL, TRUE); Curl_nonblock(sock, FALSE); @@ -403,18 +392,7 @@ CURLcode Curl_SOCKS5(const char *proxy_name, } /* get timeout */ - if(data->set.timeout && data->set.connecttimeout) { - if(data->set.timeout < data->set.connecttimeout) - timeout = data->set.timeout; - else - timeout = data->set.connecttimeout; - } - else if(data->set.timeout) - timeout = data->set.timeout; - else if(data->set.connecttimeout) - timeout = data->set.connecttimeout; - else - timeout = DEFAULT_CONNECT_TIMEOUT; + timeout = Curl_timeleft(conn, NULL, TRUE); Curl_nonblock(sock, TRUE); diff --git a/lib/ssluse.c b/lib/ssluse.c index 0083a4153..e8a2e03c9 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -1448,40 +1448,17 @@ ossl_connect_step2(struct connectdata *conn, { struct SessionHandle *data = conn->data; int err; - long has_passed; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); - /* Find out if any timeout is set. If not, use 300 seconds. - Otherwise, figure out the most strict timeout of the two possible one - and then how much time that has elapsed to know how much time we - allow for the connect call */ - if(data->set.timeout && data->set.connecttimeout) { - /* get the most strict timeout of the ones converted to milliseconds */ - if(data->set.timeoutset.connecttimeout) - *timeout_ms = data->set.timeout; - else - *timeout_ms = data->set.connecttimeout; - } - else if(data->set.timeout) - *timeout_ms = data->set.timeout; - else if(data->set.connecttimeout) - *timeout_ms = data->set.connecttimeout; - else - /* no particular time-out has been set */ - *timeout_ms = DEFAULT_CONNECT_TIMEOUT; - - /* Evaluate in milliseconds how much time that has passed */ - has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); - - /* subtract the passed time */ - *timeout_ms -= has_passed; + /* Find out how much more time we're allowed */ + *timeout_ms = Curl_timeleft(conn, NULL, TRUE); if(*timeout_ms < 0) { - /* a precaution, no need to continue if time already is up */ + /* no need to continue if time already is up */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } diff --git a/lib/tftp.c b/lib/tftp.c index 94e5fc3fe..3f7d6442c 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -114,7 +114,7 @@ typedef enum { TFTP_ERR_ILLEGAL, TFTP_ERR_UNKNOWNID, TFTP_ERR_EXISTS, - TFTP_ERR_NOSUCHUSER, /* This will never be triggered by this code */ + TFTP_ERR_NOSUCHUSER, /* This will never be triggered by this code */ /* The remaining error codes are internal to curl */ TFTP_ERR_NONE = -100, @@ -194,12 +194,14 @@ static void tftp_set_timeouts(tftp_state_data_t *state) struct SessionHandle *data = state->conn->data; time_t maxtime, timeout; + long timeout_ms; time(&state->start_time); + if(state->state == TFTP_STATE_START) { /* Compute drop-dead time */ - maxtime = (time_t)(data->set.connecttimeout/1000L? - data->set.connecttimeout/1000L:30); + timeout_ms = Curl_timeleft(state->conn, NULL, TRUE); + maxtime = (time_t)(timeout_ms + 500) / 1000; state->max_time = state->start_time+maxtime; /* Set per-block timeout to total */ @@ -219,10 +221,13 @@ static void tftp_set_timeouts(tftp_state_data_t *state) } else { - /* Compute drop-dead time */ - maxtime = (time_t)(data->set.timeout/1000L? - data->set.timeout/1000L:3600); + timeout_ms = Curl_timeleft(state->conn, NULL, TRUE); + if(timeout_ms > 0) + maxtime = (time_t)(timeout_ms + 500) / 1000; + else + maxtime = 3600; + state->max_time = state->start_time+maxtime; /* Set per-block timeout to 10% of total */