diff --git a/src/ChangeLog b/src/ChangeLog
index 5af1bd4a..8bd9f243 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,12 @@
+2003-11-08  Hrvoje Niksic  <hniksic@xemacs.org>
+
+	* progress.c (update_speed_ring): Clear the speed ring when the
+	download stalls.
+
+	* retr.c (get_contents): Specify 0.95s read timeout, so that the
+	progress gauge can be updated even when data arrives very slowly
+	or stalls.
+
 2003-11-08  Hrvoje Niksic  <hniksic@xemacs.org>
 
 	* utils.c (wtimer_allocate): Bless the use of wtimer_read on a
diff --git a/src/progress.c b/src/progress.c
index 8ae09c32..0f2e84d9 100644
--- a/src/progress.c
+++ b/src/progress.c
@@ -51,6 +51,7 @@ so, delete this exception statement from your version.  */
 
 struct progress_implementation {
   char *name;
+  int interactive;
   void *(*create) PARAMS ((long, long));
   void (*update) PARAMS ((void *, long, double));
   void (*finish) PARAMS ((void *, double));
@@ -70,8 +71,8 @@ static void bar_finish PARAMS ((void *, double));
 static void bar_set_params PARAMS ((const char *));
 
 static struct progress_implementation implementations[] = {
-  { "dot", dot_create, dot_update, dot_finish, dot_set_params },
-  { "bar", bar_create, bar_update, bar_finish, bar_set_params }
+  { "dot", 0, dot_create, dot_update, dot_finish, dot_set_params },
+  { "bar", 1, bar_create, bar_update, bar_finish, bar_set_params }
 };
 static struct progress_implementation *current_impl;
 static int current_impl_locked;
@@ -168,6 +169,17 @@ progress_create (long initial, long total)
   return current_impl->create (initial, total);
 }
 
+/* Return non-zero if the progress gauge is "interactive", i.e. if it
+   can profit from being called regularly even in absence of data.
+   The progress bar is interactive because it regularly updates the
+   ETA and current update.  */
+
+int
+progress_interactive_p (void *progress)
+{
+  return current_impl->interactive;
+}
+
 /* Inform the progress gauge of newly received bytes.  DLTIME is the
    time in milliseconds since the beginning of the download.  */
 
@@ -425,6 +437,11 @@ static volatile sig_atomic_t received_sigwinch;
    past.  */
 #define DLSPEED_SAMPLE_MIN 150
 
+/* The time after which the download starts to be considered
+   "stalled", i.e. the current bandwidth is not printed and the recent
+   download speeds are scratched.  */
+#define STALL_START_TIME 5000
+
 struct bar_progress {
   long initial_length;		/* how many bytes have been downloaded
 				   previously. */
@@ -466,8 +483,12 @@ struct bar_progress {
 				   position. */
   long recent_bytes;		/* bytes downloaded so far. */
 
+  int stalled;			/* set when no data arrives for longer
+				   than STALL_START_TIME, then reset
+				   when new data arrives. */
+
   /* create_image() uses these to make sure that ETA information
-     doesn't flash. */
+     doesn't flicker. */
   double last_eta_time;		/* time of the last update to download
 				   speed and ETA, measured since the
 				   beginning of download. */
@@ -615,6 +636,38 @@ update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
   if (recent_age < DLSPEED_SAMPLE_MIN)
     return;
 
+  if (howmuch == 0)
+    {
+      /* If we're not downloading anything, we might be stalling,
+	 i.e. not downloading anything for an extended period of time.
+	 Since 0-reads do not enter the history ring, recent_age
+	 effectively measures the time since last read.  */
+      if (recent_age >= STALL_START_TIME)
+	{
+	  /* If we're stalling, reset the ring contents because it's
+	     stale and because it will make bar_update stop printing
+	     the (bogus) current bandwidth.  */
+	  bp->stalled = 1;
+	  xzero (*hist);
+	  bp->recent_bytes = 0;
+	}
+      return;
+    }
+
+  /* We now have a non-zero amount of to store to the speed ring.  */
+
+  /* If the stall status was acquired, reset it. */
+  if (bp->stalled)
+    {
+      bp->stalled = 0;
+      /* "recent_age" includes the the entired stalled period, which
+	 could be very long.  Don't update the speed ring with that
+	 value because the current bandwidth would start too small.
+	 Start with an arbitrary (but more reasonable) time value and
+	 let it level out.  */
+      recent_age = 1000;
+    }
+
   /* Store "recent" bytes and download time to history ring at the
      position POS.  */
 
@@ -637,7 +690,7 @@ update_speed_ring (struct bar_progress *bp, long howmuch, double dltime)
   if (++hist->pos == DLSPEED_HISTORY_SIZE)
     hist->pos = 0;
 
-#if 0
+#if 1
   /* Sledgehammer check to verify that the totals are accurate. */
   {
     int i;
diff --git a/src/progress.h b/src/progress.h
index dad06f59..5437a1d5 100644
--- a/src/progress.h
+++ b/src/progress.h
@@ -35,6 +35,7 @@ void set_progress_implementation PARAMS ((const char *));
 void progress_schedule_redirect PARAMS ((void));
 
 void *progress_create PARAMS ((long, long));
+int progress_interactive_p PARAMS ((void *));
 void progress_update PARAMS ((void *, long, double));
 void progress_finish PARAMS ((void *, double));
 
diff --git a/src/retr.c b/src/retr.c
index 5e4212f8..4d03fd4e 100644
--- a/src/retr.c
+++ b/src/retr.c
@@ -161,13 +161,25 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
   static char dlbuf[16384];
   int dlbufsize = sizeof (dlbuf);
 
-  void *progress = NULL;
   struct wget_timer *timer = wtimer_allocate ();
+  double last_successful_read_tm;
+
+  /* The progress gauge, set according to the user preferences. */
+  void *progress = NULL;
+
+  /* Non-zero if the progress gauge is interactive, i.e. if it can
+     continually update the display.  When true, smaller timeout
+     values are used so that the gauge can update the display when
+     data arrives slowly. */
+  int progress_interactive = 0;
 
   *len = restval;
 
   if (opt.verbose)
-    progress = progress_create (restval, expected);
+    {
+      progress = progress_create (restval, expected);
+      progress_interactive = progress_interactive_p (progress);
+    }
 
   if (rbuf && RBUF_FD (rbuf) == fd)
     {
@@ -192,6 +204,7 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
   if (opt.limit_rate)
     limit_bandwidth_reset ();
   wtimer_reset (timer);
+  last_successful_read_tm = 0;
 
   /* Use a smaller buffer for low requested bandwidths.  For example,
      with --limit-rate=2k, it doesn't make sense to slurp in 16K of
@@ -209,24 +222,53 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
     {
       int amount_to_read = (use_expected
 			    ? MIN (expected - *len, dlbufsize) : dlbufsize);
-      res = xread (fd, dlbuf, amount_to_read, -1);
-
-      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 by disk performance.  */
-      fflush (fp);
-      if (ferror (fp))
+      double tmout = opt.read_timeout;
+      if (progress_interactive)
 	{
-	  res = -2;
-	  goto out;
+	  double waittm;
+	  /* For interactive progress gauges, always specify a ~1s
+	     timeout, so that the gauge can be updated regularly even
+	     when the data arrives very slowly or stalls.  */
+	  tmout = 0.95;
+	  waittm = (wtimer_read (timer) - last_successful_read_tm) / 1000;
+	  if (waittm + tmout > opt.read_timeout)
+	    {
+	      /* Don't allow waiting for data to exceed read timeout. */
+	      tmout = opt.read_timeout - waittm;
+	      if (tmout < 0)
+		{
+		  /* We've already exceeded the timeout. */
+		  res = -1;
+		  errno = ETIMEDOUT;
+		  break;
+		}
+	    }
 	}
+      res = xread (fd, dlbuf, amount_to_read, tmout);
+
+      if (res == 0 || (res < 0 && errno != ETIMEDOUT))
+	break;
+      else if (res < 0)
+	res = 0;		/* timeout */
 
       wtimer_update (timer);
+      if (res > 0)
+	{
+	  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 by disk
+	     performance.  */
+	  fflush (fp);
+	  if (ferror (fp))
+	    {
+	      res = -2;
+	      goto out;
+	    }
+	  last_successful_read_tm = wtimer_read (timer);
+	}
+
       if (opt.limit_rate)
 	limit_bandwidth (res, timer);