From 91386937ff120d11f7bf24dc487f00751362a61c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 5 Feb 2007 22:51:32 +0000 Subject: [PATCH] - Michael Wallner provided a patch that adds support for CURLOPT_TIMEOUT_MS and CURLOPT_CONNECTTIMEOUT_MS that, as their names should hint, do the timeouts with millisecond resolution instead. The only restriction to that is the alarm() (sometimes) used to abort name resolves as that uses full seconds. I fixed the FTP response timeout part of the patch. Internally we now count and keep the timeouts in milliseconds but it also means we multiply set timeouts with 1000. The effect of this is that no timeout can be set to more than 2^31 milliseconds (on 32 bit systems), which equals 24.86 days. We probably couldn't before either since the code did *1000 on the timeout values on several places already. --- CHANGES | 13 +++++++++++++ RELEASE-NOTES | 4 ++-- docs/libcurl/curl_easy_setopt.3 | 10 +++++++++- include/curl/curl.h | 4 ++++ lib/connect.c | 18 +++++++++--------- lib/ftp.c | 24 +++++++++++++----------- lib/gtls.c | 6 +++--- lib/hostares.c | 12 +++--------- lib/hostthre.c | 4 ++-- lib/http.c | 11 ++++++----- lib/socks.c | 10 +++++----- lib/ssluse.c | 10 +++++----- lib/telnet.c | 2 +- lib/tftp.c | 2 +- lib/transfer.c | 6 +++--- lib/url.c | 18 ++++++++++++++++-- lib/urldata.h | 6 +++--- 17 files changed, 98 insertions(+), 62 deletions(-) diff --git a/CHANGES b/CHANGES index ea6e5e556..33ae1ab3e 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,19 @@ Changelog +Daniel (5 February 2007) +- Michael Wallner added support for CURLOPT_TIMEOUT_MS and + CURLOPT_CONNECTTIMEOUT_MS that, as their names suggest, do the timeouts with + millisecond resolution. The only restriction to that is the alarm() + (sometimes) used to abort name resolves as that uses full seconds. I fixed + the FTP response timeout part of the patch. + + Internally we now count and keep the timeouts in milliseconds but it also + means we multiply set timeouts with 1000. The effect of this is that no + timeout can be set to more than 2^31 milliseconds (on 32 bit systems), which + equals 24.86 days. We probably couldn't before either since the code did + *1000 on the timeout values on several places already. + Daniel (3 February 2007) - Yang Tse fixed the cookie expiry date in several test cases that started to fail since they used "1 feb 2007"... diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 3c5e860ad..bbe226975 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -11,7 +11,7 @@ Curl and libcurl 7.16.2 This release includes the following changes: - o + o Added CURLOPT_TIMEOUT_MS and CURLOPT_CONNECTTIMEOUT_MS This release includes the following bugfixes: @@ -33,6 +33,6 @@ New curl mirrors: This release would not have looked like this without help, code, reports and advice from friends like these: - Yang Tse, Manfred Schwarb + Yang Tse, Manfred Schwarb, Michael Wallner Thanks! (and sorry if I forgot to mention someone) diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 08e7405e2..23d012de9 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -21,7 +21,7 @@ .\" * $Id$ .\" ************************************************************************** .\" -.TH curl_easy_setopt 3 "2 Nov 2006" "libcurl 7.16.1" "libcurl Manual" +.TH curl_easy_setopt 3 "5 Feb 2007" "libcurl 7.16.2" "libcurl Manual" .SH NAME curl_easy_setopt \- set options for a curl easy handle .SH SYNOPSIS @@ -1078,6 +1078,10 @@ SIGALRM to enable time-outing system calls. In unix-like systems, this might cause signals to be used unless \fICURLOPT_NOSIGNAL\fP is set. +.IP CURLOPT_TIMEOUT_MS +Like \fICURLOPT_TIMEOUT\fP but takes number of milliseconds instead. If +libcurl is built to use the standard system name resolver, that part will +still use full-second resolution for timeouts. (Added in 7.16.2) .IP CURLOPT_LOW_SPEED_LIMIT Pass a long as parameter. It contains the transfer speed in bytes per second that the transfer should be below during \fICURLOPT_LOW_SPEED_TIME\fP seconds @@ -1135,6 +1139,10 @@ timeouts). See also the \fICURLOPT_TIMEOUT\fP option. In unix-like systems, this might cause signals to be used unless \fICURLOPT_NOSIGNAL\fP is set. +.IP CURLOPT_CONNECTTIMEOUT_MS +Like \fICURLOPT_CONNECTTIMEOUT\fP but takes number of milliseconds instead. If +libcurl is built to use the standard system name resolver, that part will +still use full-second resolution for timeouts. (Added in 7.16.2) .IP CURLOPT_IPRESOLVE Allows an application to select what kind of IP addresses to use when resolving host names. This is only interesting when using host names that diff --git a/include/curl/curl.h b/include/curl/curl.h index f41067614..2c79f7dac 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1054,6 +1054,10 @@ typedef enum { /* Send CCC (Clear Command Channel) after authentication */ CINIT(FTP_SSL_CCC, LONG, 154), + /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ + CINIT(TIMEOUT_MS, LONG, 155), + CINIT(CONNECTTIMEOUT_MS, LONG, 156), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/connect.c b/lib/connect.c index 2b3897204..37db6e228 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2007, 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 @@ -558,15 +558,15 @@ CURLcode Curl_is_connected(struct connectdata *conn, /* 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*1000; + allow_total = allow = data->set.timeout; else - allow = data->set.connecttimeout*1000; + allow = data->set.connecttimeout; } else if(data->set.timeout) { - allow_total = allow = data->set.timeout*1000; + allow_total = allow = data->set.timeout; } else if(data->set.connecttimeout) { - allow = data->set.connecttimeout*1000; + allow = data->set.connecttimeout; } if(has_passed > allow ) { @@ -826,14 +826,14 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ /* get the most strict timeout of the ones converted to milliseconds */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) - timeout_ms = data->set.timeout*1000; + timeout_ms = data->set.timeout; else - timeout_ms = data->set.connecttimeout*1000; + timeout_ms = data->set.connecttimeout; } else if(data->set.timeout) - timeout_ms = data->set.timeout*1000; + timeout_ms = data->set.timeout; else - timeout_ms = data->set.connecttimeout*1000; + timeout_ms = data->set.connecttimeout; /* subtract the passed time */ timeout_ms -= has_passed; diff --git a/lib/ftp.c b/lib/ftp.c index 479eeffe3..29004ead3 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -184,7 +184,7 @@ static CURLcode AllowServerConnect(struct connectdata *conn) struct SessionHandle *data = conn->data; curl_socket_t sock = conn->sock[SECONDARYSOCKET]; struct timeval now = Curl_tvnow(); - long timespent = Curl_tvdiff(Curl_tvnow(), now)/1000; + long timespent = Curl_tvdiff(Curl_tvnow(), now); long timeout = data->set.connecttimeout?data->set.connecttimeout: (data->set.timeout?data->set.timeout: 0); @@ -198,7 +198,7 @@ static CURLcode AllowServerConnect(struct connectdata *conn) /* We allow the server 60 seconds to connect to us, or a custom timeout. Note the typecast here. */ - timeout_ms = (timeout?(int)timeout:60) * 1000; + timeout_ms = (timeout?(int)timeout:60000); switch (Curl_select(sock, CURL_SOCKET_BAD, timeout_ms)) { case -1: /* error */ @@ -444,7 +444,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ bool keepon=TRUE; ssize_t gotbytes; char *ptr; - long timeout; /* timeout in seconds */ + long timeout; /* timeout in milliseconds */ int interval_ms; struct SessionHandle *data = conn->data; char *line_start; @@ -473,16 +473,16 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ the response for any given ftp response, not for the time from connect to the given ftp response. */ timeout = data->set.ftp_response_timeout - /* timeout time */ - Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */ + Curl_tvdiff(Curl_tvnow(), now); /* spent time */ else if(data->set.timeout) /* if timeout is requested, find out how much remaining time we have */ timeout = data->set.timeout - /* timeout time */ - Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */ + Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */ else /* Even without a requested timeout, we only wait response_time seconds for the full response to arrive before we bail out */ timeout = ftpc->response_time - - Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */ + Curl_tvdiff(Curl_tvnow(), now); /* spent time */ if(timeout <=0 ) { failf(data, "FTP response timeout"); @@ -491,6 +491,8 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ if(!ftpc->cache) { interval_ms = 1 * 1000; /* use 1 second timeout intervals */ + if(timeout < interval_ms) + interval_ms = timeout; switch (Curl_select(sockfd, CURL_SOCKET_BAD, interval_ms)) { case -1: /* select() error, stop reading */ @@ -2751,16 +2753,16 @@ static long ftp_state_timeout(struct connectdata *conn) time. Also, use ftp->response because FTP_RESPONSE_TIMEOUT is supposed to govern the response for any given ftp response, not for the time from connect to the given ftp response. */ - timeout_ms = data->set.ftp_response_timeout*1000 - /* timeout time */ + timeout_ms = data->set.ftp_response_timeout - /* timeout time */ Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */ else if(data->set.timeout) /* if timeout is requested, find out how much remaining time we have */ - timeout_ms = data->set.timeout*1000 - /* timeout time */ + timeout_ms = data->set.timeout - /* timeout time */ Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */ else /* Without a requested timeout, we only wait 'response_time' seconds for the full response to arrive before we bail out */ - timeout_ms = ftpc->response_time*1000 - + timeout_ms = ftpc->response_time - Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */ return timeout_ms; @@ -2904,7 +2906,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn, /* We always support persistant connections on ftp */ conn->bits.close = FALSE; - ftpc->response_time = 3600; /* set default response time-out */ + ftpc->response_time = 3600000; /* set default response time-out */ #ifndef CURL_DISABLE_HTTP if (conn->bits.tunnel_proxy && conn->bits.httpproxy) { @@ -3063,7 +3065,7 @@ CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status, bool premature */ long old_time = ftpc->response_time; - ftpc->response_time = 60; /* give it only a minute for now */ + ftpc->response_time = 60000; /* give it only a minute for now */ result = Curl_GetFTPResponse(&nread, conn, &ftpcode); diff --git a/lib/gtls.c b/lib/gtls.c index 250ecada4..977263b12 100644 --- a/lib/gtls.c +++ b/lib/gtls.c @@ -144,12 +144,12 @@ static CURLcode handshake(struct connectdata *conn, long has_passed; if(duringconnect && data->set.connecttimeout) - timeout_ms = data->set.connecttimeout*1000; + timeout_ms = data->set.connecttimeout; if(data->set.timeout) { /* get the strictest timeout of the ones converted to milliseconds */ - if((data->set.timeout*1000) < timeout_ms) - timeout_ms = data->set.timeout*1000; + if(data->set.timeout) < timeout_ms) + timeout_ms = data->set.timeout; } /* Evaluate in milliseconds how much time that has passed */ diff --git a/lib/hostares.c b/lib/hostares.c index 1db0f4320..2738edcdf 100644 --- a/lib/hostares.c +++ b/lib/hostares.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2007, 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 @@ -183,7 +183,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, { CURLcode rc=CURLE_OK; struct SessionHandle *data = conn->data; - long timeout = CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */ + long timeout; /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ @@ -191,14 +191,8 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, timeout = conn->data->set.connecttimeout; else if(conn->data->set.timeout) timeout = conn->data->set.timeout; - - /* We convert the number of seconds into number of milliseconds here: */ - if(timeout < 2147483) - /* maximum amount of seconds that can be multiplied with 1000 and - still fit within 31 bits */ - timeout *= 1000; else - timeout = 0x7fffffff; /* ridiculous amount of time anyway */ + timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ /* Wait for the name resolve query to complete. */ while (1) { diff --git a/lib/hostthre.c b/lib/hostthre.c index 12e31ea34..f88bf15ec 100644 --- a/lib/hostthre.c +++ b/lib/hostthre.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2007, 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 @@ -600,7 +600,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, ticks = GetTickCount(); /* wait for the thread to resolve the name */ - status = WaitForSingleObject(td->event_resolved, 1000UL*timeout); + status = WaitForSingleObject(td->event_resolved, timeout); /* mark that we are now done waiting */ ReleaseMutex(td->mutex_waiting); diff --git a/lib/http.c b/lib/http.c index c07053bdd..ad42273cd 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1121,7 +1121,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, ssize_t gotbytes; char *ptr; long timeout = - data->set.timeout?data->set.timeout:3600; /* in seconds */ + data->set.timeout?data->set.timeout:3600000; /* in milliseconds */ char *line_start; char *host_port; curl_socket_t tunnelsocket = conn->sock[sockindex]; @@ -1225,15 +1225,16 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, /* if timeout is requested, find out how much remaining time we have */ long check = timeout - /* timeout time */ - Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */ - if(check <=0 ) { + Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */ + if(check <= 0) { failf(data, "Proxy CONNECT aborted due to timeout"); error = SELECT_TIMEOUT; /* already too little time */ break; } - /* timeout each second and check the timeout */ - switch (Curl_select(tunnelsocket, CURL_SOCKET_BAD, 1000)) { + /* loop every second at least, less if the timeout is near */ + switch (Curl_select(tunnelsocket, CURL_SOCKET_BAD, + check<1000?check:1000)) { case -1: /* select() error, stop reading */ error = SELECT_ERROR; failf(data, "Proxy CONNECT aborted due to select() error"); diff --git a/lib/socks.c b/lib/socks.c index 3319e697e..32d0c4b54 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2007, 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 @@ -120,14 +120,14 @@ CURLcode Curl_SOCKS4(const char *proxy_name, /* get timeout */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) - timeout = data->set.timeout*1000; + timeout = data->set.timeout; else - timeout = data->set.connecttimeout*1000; + timeout = data->set.connecttimeout; } else if(data->set.timeout) - timeout = data->set.timeout*1000; + timeout = data->set.timeout; else if(data->set.connecttimeout) - timeout = data->set.connecttimeout*1000; + timeout = data->set.connecttimeout; else timeout = DEFAULT_CONNECT_TIMEOUT; diff --git a/lib/ssluse.c b/lib/ssluse.c index 55afb2446..4f5b7f94d 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -1459,17 +1459,17 @@ Curl_ossl_connect_step2(struct connectdata *conn, 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*1000; + *timeout_ms = data->set.timeout; else - *timeout_ms = data->set.connecttimeout*1000; + *timeout_ms = data->set.connecttimeout; } else if(data->set.timeout) - *timeout_ms = data->set.timeout*1000; + *timeout_ms = data->set.timeout; else if(data->set.connecttimeout) - *timeout_ms = data->set.connecttimeout*1000; + *timeout_ms = data->set.connecttimeout; else /* no particular time-out has been set */ - *timeout_ms= DEFAULT_CONNECT_TIMEOUT; + *timeout_ms = DEFAULT_CONNECT_TIMEOUT; /* Evaluate in milliseconds how much time that has passed */ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); diff --git a/lib/telnet.c b/lib/telnet.c index 97d22b788..14501c624 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -1387,7 +1387,7 @@ CURLcode Curl_telnet(struct connectdata *conn, bool *done) if(data->set.timeout) { struct timeval now; /* current time */ now = Curl_tvnow(); - if(Curl_tvdiff(now, conn->created)/1000 >= data->set.timeout) { + if(Curl_tvdiff(now, conn->created) >= data->set.timeout) { failf(data, "Time-out"); code = CURLE_OPERATION_TIMEOUTED; keepon = FALSE; diff --git a/lib/tftp.c b/lib/tftp.c index 2a28b0cb9..b1a34a5df 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -195,7 +195,7 @@ void tftp_set_timeouts(tftp_state_data_t *state) else { /* Compute drop-dead time */ - maxtime = (time_t)(data->set.timeout?data->set.timeout:3600); + maxtime = (time_t)(data->set.timeout?data->set.timeout/1000L:3600); state->max_time = state->start_time+maxtime; /* Set per-block timeout to 10% of total */ diff --git a/lib/transfer.c b/lib/transfer.c index bff16b4ff..91e468b64 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1547,13 +1547,13 @@ CURLcode Curl_readwrite(struct connectdata *conn, return result; if (data->set.timeout && - ((Curl_tvdiff(k->now, k->start)/1000) >= data->set.timeout)) { + (Curl_tvdiff(k->now, k->start) >= data->set.timeout)) { if (k->size != -1) { - failf(data, "Operation timed out after %d seconds with %" + failf(data, "Operation timed out after %d milliseconds with %" FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received", data->set.timeout, k->bytecount, k->size); } else { - failf(data, "Operation timed out after %d seconds with %" + failf(data, "Operation timed out after %d milliseconds with %" FORMAT_OFF_T " bytes received", data->set.timeout, k->bytecount); } diff --git a/lib/url.c b/lib/url.c index 8b49033f3..076f50ebd 100644 --- a/lib/url.c +++ b/lib/url.c @@ -734,7 +734,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, * An FTP option that specifies how quickly an FTP response must be * obtained before it is considered failure. */ - data->set.ftp_response_timeout = va_arg( param , long ); + data->set.ftp_response_timeout = va_arg( param , long ) * 1000; break; case CURLOPT_FTPLISTONLY: /* @@ -1242,12 +1242,21 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, * The maximum time you allow curl to use for a single transfer * operation. */ + data->set.timeout = va_arg(param, long) * 1000L; + break; + + case CURLOPT_TIMEOUT_MS: data->set.timeout = va_arg(param, long); break; + case CURLOPT_CONNECTTIMEOUT: /* * The maximum time you allow curl to use to connect. */ + data->set.connecttimeout = va_arg(param, long) * 1000L; + break; + + case CURLOPT_CONNECTTIMEOUT_MS: data->set.connecttimeout = va_arg(param, long); break; @@ -3828,9 +3837,14 @@ else { /* if timeout is not set, use the connect timeout */ shortest = data->set.connecttimeout; + if(shortest < 1000) + /* the alarm() function only provide integer second resolution, so if + we want to wait less than one second we must bail out already now. */ + return CURLE_OPERATION_TIMEDOUT; + /* alarm() makes a signal get sent when the timeout fires off, and that will abort system calls */ - prev_alarm = alarm((unsigned int) shortest); + prev_alarm = alarm((unsigned int) (shortest ? shortest/1000L : shortest)); /* We can expect the conn->created time to be "now", as that was just recently set in the beginning of this function and nothing slow has been done since then until now. */ diff --git a/lib/urldata.h b/lib/urldata.h index 3bb3327ba..acc9d1ba6 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1176,9 +1176,9 @@ struct UserDefined { void *progress_client; /* pointer to pass to the progress callback */ void *ioctl_client; /* pointer to pass to the ioctl callback */ - long timeout; /* in seconds, 0 means no timeout */ - long connecttimeout; /* in seconds, 0 means no timeout */ - long ftp_response_timeout; /* in seconds, 0 means no timeout */ + long timeout; /* in milliseconds, 0 means no timeout */ + long connecttimeout; /* in milliseconds, 0 means no timeout */ + long ftp_response_timeout; /* in milliseconds, 0 means no timeout */ curl_off_t infilesize; /* size of file to upload, -1 means unknown */ long low_speed_limit; /* bytes/second */ long low_speed_time; /* number of seconds */