]> sjero.net Git - wget/blobdiff - src/retr.c
[svn] Try to reuse connections that return error codes.
[wget] / src / retr.c
index e177f7a5a692715108dd0dafc23c955cd31ffc9f..eedabd8d6e0348248c9a9499dc1d0abdcbd3cb7c 100644 (file)
@@ -129,32 +129,28 @@ limit_bandwidth (long bytes, struct wget_timer *timer)
   limit_data.chunk_start = wtimer_read (timer);
 }
 
-#define MIN(i, j) ((i) <= (j) ? (i) : (j))
+#ifndef MIN
+# define MIN(i, j) ((i) <= (j) ? (i) : (j))
+#endif
 
 /* Reads the contents of file descriptor FD, until it is closed, or a
    read error occurs.  The data is read in 8K chunks, and stored to
-   stream fp, which should have been open for writing.  If BUF is
-   non-NULL and its file descriptor is equal to FD, flush RBUF first.
-   This function will *not* use the rbuf_* functions!
+   stream fp, which should have been open for writing.
 
    The EXPECTED argument is passed to show_progress() unchanged, but
    otherwise ignored.
 
    If opt.verbose is set, the progress is also shown.  RESTVAL
-   represents a value from which to start downloading (which will be
-   shown accordingly).  If RESTVAL is non-zero, the stream should have
-   been open for appending.
+   (RESTart VALue) is the position from which the download starts,
+   needed for progress display.
 
    The function exits and returns codes of 0, -1 and -2 if the
    connection was closed, there was a read error, or if it could not
-   write to the output stream, respectively.
+   write to the output stream, respectively.  */
 
-   IMPORTANT: The function flushes the contents of the buffer in
-   rbuf_flush() before actually reading from fd.  If you wish to read
-   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, double *elapsed)
+fd_read_body (int fd, FILE *out, long *len, long restval, long expected,
+             int use_expected, double *elapsed)
 {
   int res = 0;
 
@@ -181,26 +177,6 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
       progress_interactive = progress_interactive_p (progress);
     }
 
-  if (rbuf && RBUF_FD (rbuf) == fd)
-    {
-      int sz = 0;
-      while ((res = rbuf_flush (rbuf, dlbuf, sizeof (dlbuf))) != 0)
-       {
-         fwrite (dlbuf, 1, res, fp);
-         *len += res;
-         sz += res;
-       }
-      if (sz)
-       fflush (fp);
-      if (ferror (fp))
-       {
-         res = -2;
-         goto out;
-       }
-      if (progress)
-       progress_update (progress, sz, 0);
-    }
-
   if (opt.limit_rate)
     limit_bandwidth_reset ();
   wtimer_reset (timer);
@@ -213,7 +189,7 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
   if (opt.limit_rate && opt.limit_rate < dlbufsize)
     dlbufsize = opt.limit_rate;
 
-  /* Read from fd while there is available data.
+  /* Read from FD while there is available data.
 
      Normally, if expected is 0, it means that it is not known how
      much data is expected.  However, if use_expected is specified,
@@ -243,7 +219,7 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
                }
            }
        }
-      res = xread (fd, dlbuf, amount_to_read, tmout);
+      res = fd_read (fd, dlbuf, amount_to_read, tmout);
 
       if (res == 0 || (res < 0 && errno != ETIMEDOUT))
        break;
@@ -253,17 +229,20 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
       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))
+         if (out)
            {
-             res = -2;
-             goto out;
+             fwrite (dlbuf, 1, res, out);
+             /* 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 (out);
+             if (ferror (out))
+               {
+                 res = -2;
+                 goto out;
+               }
            }
          last_successful_read_tm = wtimer_read (timer);
        }
@@ -292,6 +271,148 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
   return res;
 }
 \f
+/* Read a hunk of data from FD, up until a terminator.  The terminator
+   is whatever the TERMINATOR function determines it to be; for
+   example, it can be a line of data, or the head of an HTTP response.
+   The function returns the data read allocated with malloc.
+
+   In case of error, NULL is returned.  In case of EOF and no data
+   read, NULL is returned and errno set to 0.  In case of EOF with
+   data having been read, the data is returned, but it will
+   (obviously) not contain the terminator.
+
+   The idea is to be able to read a line of input, or otherwise a hunk
+   of text, such as the head of an HTTP request, without crossing the
+   boundary, so that the next call to fd_read etc. reads the data
+   after the hunk.  To achieve that, this function does the following:
+
+   1. Peek at available data.
+
+   2. Determine whether the peeked data, along with the previously
+      read data, includes the terminator.
+
+      2a. If yes, read the data until the end of the terminator, and
+          exit.
+
+      2b. If no, read the peeked data and goto 1.
+
+   The function is careful to assume as little as possible about the
+   implementation of peeking.  For example, every peek is followed by
+   a read.  If the read returns a different amount of data, the
+   process is retried until all data arrives safely.
+
+   BUFSIZE is the size of the initial buffer expected to read all the
+   data in the typical case.
+
+   This function should be used as a building block for other
+   functions -- see fd_read_line as a simple example.  */
+
+char *
+fd_read_hunk (int fd, hunk_terminator_t hunk_terminator, int bufsize)
+{
+  char *hunk = xmalloc (bufsize);
+  int tail = 0;                        /* tail position in HUNK */
+
+  while (1)
+    {
+      const char *end;
+      int pklen, rdlen, remain;
+
+      /* First, peek at the available data. */
+
+      pklen = fd_peek (fd, hunk + tail, bufsize - 1 - tail, -1);
+      if (pklen < 0)
+       {
+         xfree (hunk);
+         return NULL;
+       }
+      end = hunk_terminator (hunk, tail, pklen);
+      if (end)
+       {
+         /* The data contains the terminator: we'll drain the data up
+            to the end of the terminator.  */
+         remain = end - (hunk + tail);
+         if (remain == 0)
+           {
+             /* No more data needs to be read. */
+             hunk[tail] = '\0';
+             return hunk;
+           }
+         if (bufsize - 1 < tail + remain)
+           {
+             bufsize = tail + remain + 1;
+             hunk = xrealloc (hunk, bufsize);
+           }
+       }
+      else
+       /* No terminator: simply read the data we know is (or should
+          be) available.  */
+       remain = pklen;
+
+      /* Now, read the data.  Note that we make no assumptions about
+        how much data we'll get.  (Some TCP stacks are notorious for
+        read returning less data than the previous MSG_PEEK.)  */
+
+      rdlen = fd_read (fd, hunk + tail, remain, 0);
+      if (rdlen < 0)
+       {
+         xfree_null (hunk);
+         return NULL;
+       }
+      tail += rdlen;
+      hunk[tail] = '\0';
+
+      if (rdlen == 0)
+       {
+         if (tail == 0)
+           {
+             /* EOF without anything having been read */
+             xfree (hunk);
+             errno = 0;
+             return NULL;
+           }
+         else
+           /* EOF seen: return the data we've read. */
+           return hunk;
+       }
+      if (end && rdlen == remain)
+       /* The terminator was seen and the remaining data drained --
+          we got what we came for.  */
+       return hunk;
+
+      /* Keep looping until all the data arrives. */
+
+      if (tail == bufsize - 1)
+       {
+         bufsize <<= 1;
+         hunk = xrealloc (hunk, bufsize);
+       }
+    }
+}
+
+static const char *
+line_terminator (const char *hunk, int oldlen, int peeklen)
+{
+  const char *p = memchr (hunk + oldlen, '\n', peeklen);
+  if (p)
+    /* p+1 because we want the line to include '\n' */
+    return p + 1;
+  return NULL;
+}
+
+/* Read one line from FD and return it.  The line is allocated using
+   malloc.
+
+   If an error occurs, or if no data can be read, NULL is returned.
+   In the former case errno indicates the error condition, and in the
+   latter case, errno is NULL.  */
+
+char *
+fd_read_line (int fd)
+{
+  return fd_read_hunk (fd, line_terminator, 128);
+}
+\f
 /* Return a printed representation of the download rate, as
    appropriate for the speed.  If PAD is non-zero, strings will be
    padded to the width of 7 characters (xxxx.xx).  */
@@ -778,7 +899,7 @@ getproxy (struct url *u)
   rewritten_url = rewrite_shorthand_url (proxy);
   if (rewritten_url)
     {
-      strncpy (rewritten_storage, rewritten_url, sizeof(rewritten_storage));
+      strncpy (rewritten_storage, rewritten_url, sizeof (rewritten_storage));
       rewritten_storage[sizeof (rewritten_storage) - 1] = '\0';
       proxy = rewritten_storage;
     }