GNU Wget is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
GNU Wget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
You should have received a copy of the GNU General Public License
along with Wget; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+In addition, as a special exception, the Free Software Foundation
+gives permission to link the code of its release of Wget with the
+OpenSSL project's "OpenSSL" library (or with modified versions of it
+that use the same license as the "OpenSSL" library), and distribute
+the linked executables. You must obey the GNU General Public License
+in all respects for all of the code used other than "OpenSSL". If you
+modify this file, you may extend this exception to your version of the
+file, but you are not obligated to do so. If you do not wish to do
+so, delete this exception statement from your version. */
#include <config.h>
\f
static struct {
- long bytes;
- long dltime;
+ long chunk_bytes;
+ double chunk_start;
+ double sleep_adjust;
} limit_data;
static void
limit_bandwidth_reset (void)
{
- limit_data.bytes = 0;
- limit_data.dltime = 0;
+ limit_data.chunk_bytes = 0;
+ limit_data.chunk_start = 0;
}
/* Limit the bandwidth by pausing the download for an amount of time.
- BYTES is the number of bytes received from the network, DELTA is
- how long it took to receive them, DLTIME the current download time,
- TIMER the timer, and ADJUSTMENT the previous. */
+ BYTES is the number of bytes received from the network, and DELTA
+ is the number of milliseconds it took to receive them. */
static void
-limit_bandwidth (long bytes, long delta)
+limit_bandwidth (long bytes, double *dltime, struct wget_timer *timer)
{
- long expected;
+ double delta_t = *dltime - limit_data.chunk_start;
+ double expected;
- limit_data.bytes += bytes;
- limit_data.dltime += delta;
+ limit_data.chunk_bytes += bytes;
- expected = (long)(1000.0 * limit_data.bytes / opt.limit_rate);
+ /* Calculate the amount of time we expect downloading the chunk
+ should take. If in reality it took less time, sleep to
+ compensate for the difference. */
+ expected = 1000.0 * limit_data.chunk_bytes / opt.limit_rate;
- if (expected > limit_data.dltime)
+ if (expected > delta_t)
{
- long slp = expected - limit_data.dltime;
+ double slp = expected - delta_t + limit_data.sleep_adjust;
+ double t0, t1;
if (slp < 200)
{
- DEBUGP (("deferring a %ld ms sleep (%ld/%ld) until later.\n",
- slp, limit_data.bytes, limit_data.dltime));
+ DEBUGP (("deferring a %.2f ms sleep (%ld/%.2f).\n",
+ slp, limit_data.chunk_bytes, delta_t));
return;
}
- DEBUGP (("sleeping %ld ms\n", slp));
- usleep (1000 * slp);
+ DEBUGP (("\nsleeping %.2f ms for %ld bytes, adjust %.2f ms\n",
+ slp, limit_data.chunk_bytes, limit_data.sleep_adjust));
+
+ t0 = *dltime;
+ usleep ((unsigned long) (1000 * slp));
+ t1 = wtimer_elapsed (timer);
+
+ /* Due to scheduling, we probably slept slightly longer (or
+ shorter) than desired. Calculate the difference between the
+ desired and the actual sleep, and adjust the next sleep by
+ that amount. */
+ limit_data.sleep_adjust = slp - (t1 - t0);
+
+ /* Since we've called wtimer_elapsed, we might as well update
+ the caller's dltime. */
+ *dltime = t1;
}
- limit_data.bytes = 0;
- limit_data.dltime = 0;
+ limit_data.chunk_bytes = 0;
+ limit_data.chunk_start = *dltime;
}
#define MIN(i, j) ((i) <= (j) ? (i) : (j))
from fd immediately, flush or discard the buffer. */
int
get_contents (int fd, FILE *fp, long *len, long restval, long expected,
- struct rbuf *rbuf, int use_expected, long *elapsed)
+ struct rbuf *rbuf, int use_expected, double *elapsed)
{
int res = 0;
- static char c[8192];
+
+ static char dlbuf[16384];
+ int dlbufsize = sizeof (dlbuf);
+
void *progress = NULL;
struct wget_timer *timer = wtimer_allocate ();
- long dltime = 0, last_dltime = 0;
+ double dltime = 0;
*len = restval;
if (rbuf && RBUF_FD (rbuf) == fd)
{
int sz = 0;
- while ((res = rbuf_flush (rbuf, c, sizeof (c))) != 0)
+ while ((res = rbuf_flush (rbuf, dlbuf, sizeof (dlbuf))) != 0)
{
- fwrite (c, sizeof (char), res, fp);
+ fwrite (dlbuf, 1, res, fp);
*len += res;
sz += res;
}
res = -2;
goto out;
}
- if (opt.verbose)
+ if (progress)
progress_update (progress, sz, 0);
}
limit_bandwidth_reset ();
wtimer_reset (timer);
+ /* If we're limiting the download, set our buffer size to the
+ limit. */
+ if (opt.limit_rate && opt.limit_rate < dlbufsize)
+ dlbufsize = opt.limit_rate;
+
/* Read from fd while there is available data.
Normally, if expected is 0, it means that it is not known how
while (!use_expected || (*len < expected))
{
int amount_to_read = (use_expected
- ? MIN (expected - *len, sizeof (c))
- : sizeof (c));
+ ? MIN (expected - *len, dlbufsize) : dlbufsize);
#ifdef HAVE_SSL
if (rbuf->ssl!=NULL)
- res = ssl_iread (rbuf->ssl, c, amount_to_read);
+ res = ssl_iread (rbuf->ssl, dlbuf, amount_to_read);
else
#endif /* HAVE_SSL */
- res = iread (fd, c, amount_to_read);
+ res = iread (fd, dlbuf, amount_to_read);
- if (res > 0)
+ if (res <= 0)
+ break;
+
+ fwrite (dlbuf, 1, res, fp);
+ /* Always flush the contents of the network packet. This should
+ not hinder performance: fast downloads will be received in
+ 16K chunks (which stdio would write out anyway), and slow
+ downloads won't be limited with disk performance. */
+ fflush (fp);
+ if (ferror (fp))
{
- fwrite (c, sizeof (char), res, fp);
- /* Always flush the contents of the network packet. This
- should not be adverse to performance, as the network
- packets typically won't be too tiny anyway. */
- fflush (fp);
- if (ferror (fp))
- {
- res = -2;
- goto out;
- }
-
- /* If bandwidth is not limited, one call to wtimer_elapsed
- is sufficient. */
- dltime = wtimer_elapsed (timer);
- if (opt.limit_rate)
- {
- limit_bandwidth (res, dltime - last_dltime);
- dltime = wtimer_elapsed (timer);
- last_dltime = dltime;
- }
-
- if (opt.verbose)
- progress_update (progress, res, dltime);
- *len += res;
+ res = -2;
+ goto out;
}
- else
- break;
+
+ dltime = wtimer_elapsed (timer);
+ if (opt.limit_rate)
+ limit_bandwidth (res, &dltime, timer);
+
+ if (progress)
+ progress_update (progress, res, dltime);
+ *len += res;
}
if (res < -1)
res = -1;
out:
- if (opt.verbose)
+ if (progress)
progress_finish (progress, dltime);
if (elapsed)
*elapsed = dltime;
appropriate for the speed. If PAD is non-zero, strings will be
padded to the width of 7 characters (xxxx.xx). */
char *
-retr_rate (long bytes, long msecs, int pad)
+retr_rate (long bytes, double msecs, int pad)
{
static char res[20];
static char *rate_names[] = {"B/s", "KB/s", "MB/s", "GB/s" };
UNITS is zero for B/s, one for KB/s, two for MB/s, and three for
GB/s. */
double
-calc_rate (long bytes, long msecs, int *units)
+calc_rate (long bytes, double msecs, int *units)
{
double dlrate;
assert (bytes >= 0);
if (msecs == 0)
- /* If elapsed time is 0, it means we're under the granularity of
- the timer. This often happens on systems that use time() for
- the timer. */
+ /* If elapsed time is exactly zero, it means we're under the
+ granularity of the timer. This often happens on systems that
+ use time() for the timer. */
msecs = wtimer_granularity ();
dlrate = (double)1000 * bytes / msecs;
else if (dlrate < 1024.0 * 1024.0 * 1024.0)
*units = 2, dlrate /= (1024.0 * 1024.0);
else
- /* Maybe someone will need this one day. More realistically, it
- will get tickled by buggy timers. */
+ /* Maybe someone will need this, one day. */
*units = 3, dlrate /= (1024.0 * 1024.0 * 1024.0);
return dlrate;
#define MAX_REDIRECTIONS 20
+#define SUSPEND_POST_DATA do { \
+ post_data_suspended = 1; \
+ saved_post_data = opt.post_data; \
+ saved_post_file_name = opt.post_file_name; \
+ opt.post_data = NULL; \
+ opt.post_file_name = NULL; \
+} while (0)
+
+#define RESTORE_POST_DATA do { \
+ if (post_data_suspended) \
+ { \
+ opt.post_data = saved_post_data; \
+ opt.post_file_name = saved_post_file_name; \
+ post_data_suspended = 0; \
+ } \
+} while (0)
+
/* Retrieve the given URL. Decides which loop to call -- HTTP, FTP,
FTP, proxy, etc. */
+/* #### This function should be rewritten so it doesn't return from
+ multiple points. */
+
uerr_t
retrieve_url (const char *origurl, char **file, char **newloc,
const char *refurl, int *dt)
char *local_file;
int redirection_count = 0;
+ int post_data_suspended = 0;
+ char *saved_post_data = NULL;
+ char *saved_post_file_name = NULL;
+
/* If dt is NULL, just ignore it. */
if (!dt)
dt = &dummy;
logprintf (LOG_NOTQUIET, _("Error parsing proxy URL %s: %s.\n"),
proxy, url_error (up_error_code));
xfree (url);
+ RESTORE_POST_DATA;
return PROXERR;
}
if (proxy_url->scheme != SCHEME_HTTP && proxy_url->scheme != u->scheme)
logprintf (LOG_NOTQUIET, _("Error in proxy URL %s: Must be HTTP.\n"), proxy);
url_free (proxy_url);
xfree (url);
+ RESTORE_POST_DATA;
return PROXERR;
}
}
url_free (u);
xfree (url);
xfree (mynewloc);
+ RESTORE_POST_DATA;
return result;
}
url_free (u);
xfree (url);
xfree (mynewloc);
+ RESTORE_POST_DATA;
return WRONGCODE;
}
url = mynewloc;
url_free (u);
u = newloc_parsed;
+
+ /* If we're being redirected from POST, we don't want to POST
+ again. Many requests answer POST with a redirection to an
+ index page; that redirection is clearly a GET. We "suspend"
+ POST data for the duration of the redirections, and restore
+ it when we're done. */
+ if (!post_data_suspended)
+ SUSPEND_POST_DATA;
+
goto redirected;
}
}
++global_download_count;
+ RESTORE_POST_DATA;
return result;
}
if (count <= opt.waitretry)
sleep (count - 1);
else
- sleep (opt.waitretry);
+ usleep (1000000L * opt.waitretry);
}
else if (opt.wait)
{
/* If random-wait is not specified, or if we are sleeping
between retries of the same download, sleep the fixed
interval. */
- sleep (opt.wait);
+ usleep (1000000L * opt.wait);
else
{
/* Sleep a random amount of time averaging in opt.wait
seconds. The sleeping amount ranges from 0 to
opt.wait*2, inclusive. */
- int waitsecs = random_number (opt.wait * 2 + 1);
-
- DEBUGP (("sleep_between_retrievals: norm=%ld,fuzz=%ld,sleep=%d\n",
- opt.wait, waitsecs - opt.wait, waitsecs));
-
- if (waitsecs)
- sleep (waitsecs);
+ double waitsecs = 2 * opt.wait * random_float ();
+ DEBUGP (("sleep_between_retrievals: avg=%f,sleep=%f\n",
+ opt.wait, waitsecs));
+ usleep (1000000L * waitsecs);
}
}
}