read timeout

Michael Weiser <[email protected]> Thu, 21 Dec 2006 17:51:29 +0100
Newsgroups gmane.comp.web.wget.patches
Message-ID <[email protected]>
--sm4nu43k4a2Rpi4c
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi,

please find attached a patch to get the --read-timeout option working
with connections that hang in a read operation due to the server going
offline or changing IP address.

When a server goes offline in the middle of an HTTP request, the TCP
connection will hang (almost?) indefinitely. Because it isn't closed or
rejected but simply doesn't respond to IP packets any more, the select
in fd_read will time out normally but then call sock_read which will
block forever.

There are a couple of scenarios in which this would accour:

- the server actually doing down and staying down. After some time a
  router should start to generate "destination host unreachable" ICMP
  messages which in turn should reset the HTTP TCP connection but I'm not
  sure about that.

- a firewall is activated on the server which drops all incoming
  packets. This wouldn't normally happen to a real server but can be used
  for testing.

- a NAT router box inbetween wget and the server loses the connection
  information. This would be true for most DSL router boxes which get
  disconnected periodically by the provider, then reconnect and get a
  different IP. This invalidates all old connections which therefore will
  hang on the client and server side until they're somehow timed out.

I added SO_SNDTIMEO and SO_RCVTIMEO socket options to the socket and put
some handling for it into http.c and retr.c.

Now I'd like to know if this actually makes any sense or just works
around a braindead misunderstanding of mine. ;)

I'm not subscribed to this list, so please Cc: to
[email protected].
-- 
Thanks,
Michael

--sm4nu43k4a2Rpi4c
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="wget-1.10.2-readtimeout.patch"

--- wget-1.10.2/src/connect.c.orig	2006-12-21 17:03:39.000000000 +0100
+++ wget-1.10.2/src/connect.c	2006-12-21 17:08:05.000000000 +0100
@@ -294,6 +294,26 @@
   }
 #endif
 
+  if (opt.read_timeout) {
+    struct timeval tmout;
+    int err;
+
+    tmout.tv_sec = (long) opt.read_timeout;
+    tmout.tv_usec = 1000000 * (opt.read_timeout - (long) opt.read_timeout);
+
+    err = setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, &tmout, sizeof(tmout));
+#ifdef ENABLE_DEBUG
+    if (err < 0) 
+      DEBUGP (("Failed setting SO_SNDTIMEO: %s", strerror (errno)));
+#endif
+
+    err = setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, &tmout, sizeof(tmout));
+#ifdef ENABLE_DEBUG
+    if (err < 0) 
+      DEBUGP (("Failed setting SO_RCVTIMEO: %s", strerror (errno)));
+#endif
+  }
+
   /* For very small rate limits, set the buffer size (and hence,
      hopefully, the kernel's TCP window size) to the per-second limit.
      That way we should never have to sleep for more than 1s between
--- wget-1.10.2/src/http.c.orig	2005-08-09 00:54:16.000000000 +0200
+++ wget-1.10.2/src/http.c	2006-12-21 17:27:42.000000000 +0100
@@ -1960,6 +1960,9 @@
 			  hs->restval, &hs->rd_size, &hs->len, &hs->dltime,
 			  flags);
 
+  if (hs->res < 0 && errno == ETIMEDOUT)
+    hs->res = -3;
+
   if (hs->res >= 0)
     CLOSE_FINISH (sock);
   else
@@ -1979,6 +1982,8 @@
   }
   if (hs->res == -2)
     return FWRITEERR;
+  if (hs->res == -3)
+    return READERR;
   return RETRFINISHED;
 }
 
--- wget-1.10.2/src/retr.c.orig	2005-06-25 17:07:11.000000000 +0200
+++ wget-1.10.2/src/retr.c	2006-12-21 17:30:45.000000000 +0100
@@ -290,6 +290,11 @@
 
       if (progress_interactive && ret < 0 && errno == ETIMEDOUT)
 	ret = 0;		/* interactive timeout, handled above */
+      else if (ret < 0 && errno == EWOULDBLOCK) {
+	ret = -1;		/* socket read/write timeout */
+	errno = ETIMEDOUT;
+	break;
+      }
       else if (ret <= 0)
 	break;			/* EOF or read error */
 

--sm4nu43k4a2Rpi4c--