aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/curl.14
-rw-r--r--docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.35
-rw-r--r--docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.36
-rw-r--r--lib/multi.c60
-rw-r--r--lib/progress.c75
-rw-r--r--lib/progress.h6
-rw-r--r--lib/transfer.c54
-rw-r--r--lib/transfer.h3
-rw-r--r--lib/urldata.h8
9 files changed, 125 insertions, 96 deletions
diff --git a/docs/curl.1 b/docs/curl.1
index f0ce3a791..7a6cbbd5a 100644
--- a/docs/curl.1
+++ b/docs/curl.1
@@ -1024,10 +1024,6 @@ The given speed is measured in bytes/second, unless a suffix is appended.
Appending 'k' or 'K' will count the number as kilobytes, 'm' or M' makes it
megabytes, while 'g' or 'G' makes it gigabytes. Examples: 200K, 3m and 1G.
-The given rate is the average speed counted during the entire transfer. It
-means that curl might use higher transfer speeds in short bursts, but over
-time it uses no more than the given rate.
-
If you also use the \fI-Y, --speed-limit\fP option, that option will take
precedence and might cripple the rate-limiting slightly, to help keeping the
speed-limit logic working.
diff --git a/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.3 b/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.3
index 031f2cd39..c99ff61e3 100644
--- a/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.3
+++ b/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.3
@@ -31,9 +31,8 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_MAX_RECV_SPEED_LARGE,
curl_off_t speed);
.SH DESCRIPTION
Pass a curl_off_t as parameter. If a download exceeds this \fIspeed\fP
-(counted in bytes per second) on cumulative average during the transfer, the
-transfer will pause to keep the average rate less than or equal to the
-parameter value. Defaults to unlimited speed.
+(counted in bytes per second) the transfer will pause to keep the speed less
+than or equal to the parameter value. Defaults to unlimited speed.
This option doesn't affect transfer speeds done with FILE:// URLs.
.SH DEFAULT
diff --git a/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.3 b/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.3
index c2c6336f1..7f3efe57c 100644
--- a/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.3
+++ b/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.3
@@ -31,9 +31,9 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_MAX_SEND_SPEED_LARGE,
curl_off_t maxspeed);
.SH DESCRIPTION
Pass a curl_off_t as parameter with the \fImaxspeed\fP. If an upload exceeds
-this speed (counted in bytes per second) on cumulative average during the
-transfer, the transfer will pause to keep the average rate less than or equal
-to the parameter value. Defaults to unlimited speed.
+this speed (counted in bytes per second) the transfer will pause to keep the
+speed less than or equal to the parameter value. Defaults to unlimited
+speed.
This option doesn't affect transfer speeds done with FILE:// URLs.
.SH DEFAULT
diff --git a/lib/multi.c b/lib/multi.c
index e1325f029..8e4091687 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -1801,9 +1801,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
result = Curl_speedcheck(data, now);
if(( (data->set.max_send_speed == 0) ||
- (data->progress.ulspeed < data->set.max_send_speed)) &&
+ (Curl_pgrsLimitWaitTime(data->progress.uploaded,
+ data->progress.ul_limit_size,
+ data->set.max_send_speed,
+ data->progress.ul_limit_start,
+ now) <= 0)) &&
( (data->set.max_recv_speed == 0) ||
- (data->progress.dlspeed < data->set.max_recv_speed)))
+ (Curl_pgrsLimitWaitTime(data->progress.downloaded,
+ data->progress.dl_limit_size,
+ data->set.max_recv_speed,
+ data->progress.dl_limit_start,
+ now) <= 0)))
multistate(data, CURLM_STATE_PERFORM);
break;
@@ -1814,35 +1822,31 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
bool comeback = FALSE;
/* check if over send speed */
- if((data->set.max_send_speed > 0) &&
- (data->progress.ulspeed > data->set.max_send_speed)) {
- int buffersize;
-
- multistate(data, CURLM_STATE_TOOFAST);
-
- /* calculate upload rate-limitation timeout. */
- buffersize = (int)(data->set.buffer_size ?
- data->set.buffer_size : BUFSIZE);
- timeout_ms = Curl_sleep_time(data->set.max_send_speed,
- data->progress.ulspeed, buffersize);
- Curl_expire_latest(data, timeout_ms);
- break;
+ if(data->set.max_send_speed > 0) {
+ timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded,
+ data->progress.ul_limit_size,
+ data->set.max_send_speed,
+ data->progress.ul_limit_start,
+ now);
+ if(timeout_ms > 0) {
+ multistate(data, CURLM_STATE_TOOFAST);
+ Curl_expire_latest(data, timeout_ms);
+ break;
+ }
}
/* check if over recv speed */
- if((data->set.max_recv_speed > 0) &&
- (data->progress.dlspeed > data->set.max_recv_speed)) {
- int buffersize;
-
- multistate(data, CURLM_STATE_TOOFAST);
-
- /* Calculate download rate-limitation timeout. */
- buffersize = (int)(data->set.buffer_size ?
- data->set.buffer_size : BUFSIZE);
- timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
- data->progress.dlspeed, buffersize);
- Curl_expire_latest(data, timeout_ms);
- break;
+ if(data->set.max_recv_speed > 0) {
+ timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded,
+ data->progress.dl_limit_size,
+ data->set.max_recv_speed,
+ data->progress.dl_limit_start,
+ now);
+ if(timeout_ms > 0) {
+ multistate(data, CURLM_STATE_TOOFAST);
+ Curl_expire_latest(data, timeout_ms);
+ break;
+ }
}
/* read/write data if it is ready to do so */
diff --git a/lib/progress.c b/lib/progress.c
index 760ca1cc3..0f67ef250 100644
--- a/lib/progress.c
+++ b/lib/progress.c
@@ -216,18 +216,93 @@ void Curl_pgrsStartNow(struct Curl_easy *data)
{
data->progress.speeder_c = 0; /* reset the progress meter display */
data->progress.start = Curl_tvnow();
+ data->progress.ul_limit_start.tv_sec = 0;
+ data->progress.ul_limit_start.tv_usec = 0;
+ data->progress.dl_limit_start.tv_sec = 0;
+ data->progress.dl_limit_start.tv_usec = 0;
/* clear all bits except HIDE and HEADERS_OUT */
data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT;
}
+/*
+ * This is used to handle speed limits, calculating how much milliseconds we
+ * need to wait until we're back under the speed limit, if needed.
+ *
+ * The way it works is by having a "starting point" (time & amount of data
+ * transfered by then) used in the speed computation, to be used instead of the
+ * start of the transfer.
+ * This starting point is regularly moved as transfer goes on, to keep getting
+ * accurate values (instead of average over the entire tranfer).
+ *
+ * This function takes the current amount of data transfered, the amount at the
+ * starting point, the limit (in bytes/s), the time of the starting point and
+ * the current time.
+ *
+ * Returns -1 if no waiting is needed (not enough data transfered since
+ * starting point yet), 0 when no waiting is needed but the starting point
+ * should be reset (to current), or the number of milliseconds to wait to get
+ * back under the speed limit.
+ */
+long Curl_pgrsLimitWaitTime(curl_off_t cursize,
+ curl_off_t startsize,
+ curl_off_t limit,
+ struct timeval start,
+ struct timeval now)
+{
+ curl_off_t size = cursize - startsize;
+ long minimum, actual;
+
+ /* we don't have a starting point yet -- return 0 so it gets (re)set */
+ if(start.tv_sec == 0 && start.tv_usec == 0)
+ return 0;
+
+ /* not enough data yet */
+ if(size < limit)
+ return -1;
+
+ minimum = (long) (CURL_OFF_T_C(1000) * size / limit);
+ actual = Curl_tvdiff(now, start);
+
+ if(actual < minimum)
+ return minimum - actual;
+ else
+ return 0;
+}
+
void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
{
+ struct timeval now = Curl_tvnow();
+
data->progress.downloaded = size;
+
+ /* download speed limit */
+ if((data->set.max_recv_speed > 0) &&
+ (Curl_pgrsLimitWaitTime(data->progress.downloaded,
+ data->progress.dl_limit_size,
+ data->set.max_recv_speed,
+ data->progress.dl_limit_start,
+ now) == 0)) {
+ data->progress.dl_limit_start = now;
+ data->progress.dl_limit_size = size;
+ }
}
void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size)
{
+ struct timeval now = Curl_tvnow();
+
data->progress.uploaded = size;
+
+ /* upload speed limit */
+ if((data->set.max_send_speed > 0) &&
+ (Curl_pgrsLimitWaitTime(data->progress.uploaded,
+ data->progress.ul_limit_size,
+ data->set.max_send_speed,
+ data->progress.ul_limit_start,
+ now) == 0)) {
+ data->progress.ul_limit_start = now;
+ data->progress.ul_limit_size = size;
+ }
}
void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size)
diff --git a/lib/progress.h b/lib/progress.h
index a77b7ce59..155ff04fe 100644
--- a/lib/progress.h
+++ b/lib/progress.h
@@ -49,7 +49,11 @@ void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size);
int Curl_pgrsUpdate(struct connectdata *);
void Curl_pgrsResetTimesSizes(struct Curl_easy *data);
void Curl_pgrsTime(struct Curl_easy *data, timerid timer);
-
+long Curl_pgrsLimitWaitTime(curl_off_t cursize,
+ curl_off_t startsize,
+ curl_off_t limit,
+ struct timeval start,
+ struct timeval now);
/* Don't show progress for sizes smaller than: */
#define LEAST_SIZE_PROGRESS BUFSIZE
diff --git a/lib/transfer.c b/lib/transfer.c
index e4a2835f8..5d5ee6be0 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -1264,60 +1264,6 @@ int Curl_single_getsock(const struct connectdata *conn,
return bitmap;
}
-/*
- * Determine optimum sleep time based on configured rate, current rate,
- * and packet size.
- * Returns value in milliseconds.
- *
- * The basic idea is to adjust the desired rate up/down in this method
- * based on whether we are running too slow or too fast. Then, calculate
- * how many milliseconds to wait for the next packet to achieve this new
- * rate.
- */
-long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
- int pkt_size)
-{
- curl_off_t min_sleep = 0;
- curl_off_t rv = 0;
-
- if(rate_bps == 0)
- return 0;
-
- /* If running faster than about .1% of the desired speed, slow
- * us down a bit. Use shift instead of division as the 0.1%
- * cutoff is arbitrary anyway.
- */
- if(cur_rate_bps > (rate_bps + (rate_bps >> 10))) {
- /* running too fast, decrease target rate by 1/64th of rate */
- rate_bps -= rate_bps >> 6;
- min_sleep = 1;
- }
- else if(cur_rate_bps < (rate_bps - (rate_bps >> 10))) {
- /* running too slow, increase target rate by 1/64th of rate */
- rate_bps += rate_bps >> 6;
- }
-
- /* Determine number of milliseconds to wait until we do
- * the next packet at the adjusted rate. We should wait
- * longer when using larger packets, for instance.
- */
- rv = ((curl_off_t)(pkt_size * 1000) / rate_bps);
-
- /* Catch rounding errors and always slow down at least 1ms if
- * we are running too fast.
- */
- if(rv < min_sleep)
- rv = min_sleep;
-
- /* Bound value to fit in 'long' on 32-bit platform. That's
- * plenty long enough anyway!
- */
- if(rv > 0x7fffffff)
- rv = 0x7fffffff;
-
- return (long)rv;
-}
-
/* Curl_init_CONNECT() gets called each time the handle switches to CONNECT
which means this gets called once for each subsequent redirect etc */
void Curl_init_CONNECT(struct Curl_easy *data)
diff --git a/lib/transfer.h b/lib/transfer.h
index 0058f8c86..518967260 100644
--- a/lib/transfer.h
+++ b/lib/transfer.h
@@ -64,8 +64,5 @@ Curl_setup_transfer (struct connectdata *data,
curl_off_t *writecountp /* return number of bytes written */
);
-long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
- int pkt_size);
-
#endif /* HEADER_CURL_TRANSFER_H */
diff --git a/lib/urldata.h b/lib/urldata.h
index 1f4d2551e..3ac050b53 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1173,6 +1173,14 @@ struct Progress {
struct timeval t_startsingle;
struct timeval t_startop;
struct timeval t_acceptdata;
+
+ /* upload speed limit */
+ struct timeval ul_limit_start;
+ curl_off_t ul_limit_size;
+ /* download speed limit */
+ struct timeval dl_limit_start;
+ curl_off_t dl_limit_size;
+
#define CURR_TIME (5+1) /* 6 entries for 5 seconds */
curl_off_t speeder[ CURR_TIME ];