Simplified http.c.
authorWerner Koch <wk@gnupg.org>
Thu, 5 Aug 2010 18:47:25 +0000 (18:47 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 5 Aug 2010 18:47:25 +0000 (18:47 +0000)
common/ChangeLog
common/estream.h
common/http.c
common/http.h

index 05611f9..2096ffd 100644 (file)
@@ -1,3 +1,17 @@
+2010-08-05  Werner Koch  <wk@g10code.com>
+
+       * estream.h (es_asprintf, es_vasprintf): Add lost prototyps.
+
+       * http.c: Require estream and make HTTP_USE_ESTREAM obsolete.  It
+       make the code unreadable and we require estream anyway for GnuPG.
+       (http_wait_response): Get use of cookies right.
+       (send_request): s/xtryasprintf/es_asprintf/ to allow standalone
+       use of the code.
+       (insert_escapes, connect_server): s/sprintf/snprintf/.
+       (parse_response): s/my_read_line/es_read_line/.
+       (my_read_line): Remove.
+       (write_server): Use pth_write.
+
 2010-07-26  Werner Koch  <wk@g10code.com>
 
        * estream.c (es_func_fp_write) [W32]: Write smaller chunks.
index 8f45cce..f7ef162 100644 (file)
@@ -352,6 +352,11 @@ int es_vfprintf_unlocked (estream_t ES__RESTRICT stream,
                           const char *ES__RESTRICT format, va_list ap)
      _ESTREAM_GCC_A_PRINTF(2,0);
 
+char *es_asprintf (const char *ES__RESTRICT format, ...)
+     _ESTREAM_GCC_A_PRINTF(1,2);
+char *es_vasprintf (const char *ES__RESTRICT format, va_list ap)
+     _ESTREAM_GCC_A_PRINTF(1,0);
+
 int es_setvbuf (estream_t ES__RESTRICT stream,
                char *ES__RESTRICT buf, int mode, size_t size);
 void es_setbuf (estream_t ES__RESTRICT stream, char *ES__RESTRICT buf);
index ab159e2..4ce7420 100644 (file)
 /* Simple HTTP client implementation.  We try to keep the code as
    self-contained as possible.  There are some contraints however:
 
+  - estream is required.  We now require estream because it provides a
+    very useful and portable asprintf implementation and the fopencookie
+    function.
   - stpcpy is required
   - fixme: list other requirements.
 
 
-  - With HTTP_USE_ESTREAM defined, all I/O is done through estream.
   - With HTTP_USE_GNUTLS support for https is provided (this also
     requires estream).
   - With HTTP_NO_WSASTARTUP the socket initialization is not done
@@ -129,18 +131,13 @@ typedef unsigned long longcounter_t;
 # define counter_strtoul(a) strtoul ((a), NULL, 10)
 #endif
 
-/* Define a prefix to map stream functions to the estream library. */
-#ifdef HTTP_USE_ESTREAM
-#define P_ES(a)  es_ ## a
-#else
-#define P_ES(a)  a
+#if defined(HTTP_USE_ESTREAM) && defined (__GNUC__)
+# warning HTTP_USE_ESTREAM is an obsolete macro
 #endif
+
 #ifndef HTTP_USE_GNUTLS
 typedef void * gnutls_session_t;
 #endif
-#if defined(HTTP_USE_GNUTLS) && !defined(HTTP_USE_ESTREAM)
-#error Use of GNUTLS also requires support for Estream
-#endif
 
 static gpg_error_t do_parse_uri (parsed_uri_t uri, int only_local_part);
 static int remove_escapes (char *string);
@@ -156,7 +153,6 @@ static int connect_server (const char *server, unsigned short port,
                            unsigned int flags, const char *srvtag);
 static gpg_error_t write_server (int sock, const char *data, size_t length);
 
-#ifdef HTTP_USE_ESTREAM
 static ssize_t cookie_read (void *cookie, void *buffer, size_t size);
 static ssize_t cookie_write (void *cookie, const void *buffer, size_t size);
 static int cookie_close (void *cookie);
@@ -187,8 +183,6 @@ struct cookie_s
 };
 typedef struct cookie_s *cookie_t;
 
-#endif /*HTTP_USE_ESTREAM*/
-
 #ifdef HTTP_USE_GNUTLS
 static gpg_error_t (*tls_callback) (http_t, gnutls_session_t, int);
 #endif /*HTTP_USE_GNUTLS*/
@@ -211,15 +205,10 @@ struct http_context_s
   int sock;
   unsigned int in_data:1;
   unsigned int is_http_0_9:1;
-#ifdef HTTP_USE_ESTREAM
   estream_t fp_read;
   estream_t fp_write;
   void *write_cookie;
   void *read_cookie;
-#else /*!HTTP_USE_ESTREAM*/
-  FILE *fp_read;
-  FILE *fp_write;
-#endif /*!HTTP_USE_ESTREAM*/
   void *tls_context;
   parsed_uri_t uri;
   http_req_t req_type;
@@ -375,9 +364,9 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url,
       if (!hd->fp_read && !hd->fp_write && hd->sock != -1)
         sock_close (hd->sock);
       if (hd->fp_read)
-        P_ES(fclose) (hd->fp_read);
+        es_fclose (hd->fp_read);
       if (hd->fp_write)
-        P_ES(fclose) (hd->fp_write);
+        es_fclose (hd->fp_write);
       http_release_parsed_uri (hd->uri);
       xfree (hd);
     }
@@ -392,17 +381,12 @@ http_start_data (http_t hd)
 {
   if (!hd->in_data)
     {
-#ifdef HTTP_USE_ESTREAM
       es_fputs ("\r\n", hd->fp_write);
       es_fflush (hd->fp_write);
-#else
-      fflush (hd->fp_write);
-      write_server (hd->sock, "\r\n", 2);
-#endif
       hd->in_data = 1;
     }
   else
-    P_ES(fflush) (hd->fp_write);
+    es_fflush (hd->fp_write);
 }
 
 
@@ -410,42 +394,21 @@ gpg_error_t
 http_wait_response (http_t hd)
 {
   gpg_error_t err;
+  cookie_t cookie;
 
   /* Make sure that we are in the data. */
   http_start_data (hd);        
 
-  /* We dup the socket, to cope with the fact that fclose closes the
-     underlying socket. In TLS mode we don't do that because we can't
-     close the socket gnutls is working on; instead we make sure that
-     the fclose won't close the socket in this case. */
-#ifdef HTTP_USE_ESTREAM
-  if (hd->write_cookie)
-    {
-      /* The write cookie is only set in the TLS case. */
-      cookie_t cookie = hd->write_cookie;
-      cookie->keep_socket = 1;
-    }
-  else
-#endif /*HTTP_USE_ESTREAM*/
-    {
-#ifdef HAVE_W32_SYSTEM
-      HANDLE handle = (HANDLE)hd->sock;
-      if (!DuplicateHandle (GetCurrentProcess(), handle,
-                           GetCurrentProcess(), &handle, 0,
-                           TRUE, DUPLICATE_SAME_ACCESS ))
-       return gpg_error_from_syserror ();
-      hd->sock = (int)handle;
-#else
-      hd->sock = dup (hd->sock);
-#endif
-      if (hd->sock == -1)
-        return gpg_error_from_syserror ();
-    }
-  P_ES(fclose) (hd->fp_write);
+  /* Close the write stream but keep the socket open.  */
+  cookie = hd->write_cookie;
+  if (!cookie)
+    return gpg_error (GPG_ERR_INTERNAL);
+
+  cookie->keep_socket = 1;
+  es_fclose (hd->fp_write);
   hd->fp_write = NULL;
-#ifdef HTTP_USE_ESTREAM
+  /* The close has released the cookie and thus we better set it to NULL.  */
   hd->write_cookie = NULL;
-#endif
 
   /* Shutdown one end of the socket is desired.  As per HTTP/1.0 this
      is not required but some very old servers (e.g. the original pksd
@@ -454,29 +417,22 @@ http_wait_response (http_t hd)
     shutdown (hd->sock, 1);
   hd->in_data = 0;
 
-#ifdef HTTP_USE_ESTREAM
-  {
-    cookie_t cookie;
-
-    cookie = xtrycalloc (1, sizeof *cookie);
-    if (!cookie)
-      return gpg_error_from_syserror ();
-    cookie->fd = hd->sock;
-    if (hd->uri->use_tls)
-      cookie->tls_session = hd->tls_context;
+  /* Create a new cookie and a stream for reading.  */
+  cookie = xtrycalloc (1, sizeof *cookie);
+  if (!cookie)
+    return gpg_error_from_syserror ();
+  cookie->fd = hd->sock;
+  if (hd->uri->use_tls)
+    cookie->tls_session = hd->tls_context;
 
-    hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
-    if (!hd->fp_read)
-      {
-        xfree (cookie);
-        return gpg_error_from_syserror ();
-      }
-  }
-#else /*!HTTP_USE_ESTREAM*/
-  hd->fp_read = fdopen (hd->sock, "r");
+  hd->read_cookie = cookie;
+  hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
   if (!hd->fp_read)
-    return gpg_error_from_syserror ();
-#endif /*!HTTP_USE_ESTREAM*/
+    {
+      xfree (cookie);
+      hd->read_cookie = NULL;
+      return gpg_error_from_syserror ();
+    }
 
   err = parse_response (hd);
   return err;
@@ -515,9 +471,9 @@ http_close (http_t hd, int keep_read_stream)
   if (!hd->fp_read && !hd->fp_write && hd->sock != -1)
     sock_close (hd->sock);
   if (hd->fp_read && !keep_read_stream)
-    P_ES(fclose) (hd->fp_read);
+    es_fclose (hd->fp_read);
   if (hd->fp_write)
-    P_ES(fclose) (hd->fp_write);
+    es_fclose (hd->fp_write);
   http_release_parsed_uri (hd->uri);
   while (hd->headers)
     {
@@ -531,29 +487,18 @@ http_close (http_t hd, int keep_read_stream)
 }
 
 
-#ifdef HTTP_USE_ESTREAM
 estream_t
 http_get_read_ptr (http_t hd)
 {
   return hd?hd->fp_read:NULL;
 }
+
 estream_t
 http_get_write_ptr (http_t hd)
 {
   return hd?hd->fp_write:NULL;
 }
-#else /*!HTTP_USE_ESTREAM*/
-FILE *
-http_get_read_ptr (http_t hd)
-{
-  return hd?hd->fp_read:NULL;
-}
-FILE *
-http_get_write_ptr (http_t hd)
-{
-  return hd?hd->fp_write:NULL;
-}
-#endif /*!HTTP_USE_ESTREAM*/
+
 unsigned int
 http_get_status_code (http_t hd)
 {
@@ -794,7 +739,7 @@ insert_escapes (char *buffer, const char *string,
        {
          if (buffer)
            {
-             sprintf (buffer, "%%%02X", *s);
+             snprintf (buffer, 4, "%%%02X", *s);
              buffer += 3;
            }
          n += 3;
@@ -1017,7 +962,7 @@ send_request (http_t hd, const char *auth,
 
   if (http_proxy && *http_proxy)
     {
-      request = xtryasprintf 
+      request = es_asprintf 
         ("%s http://%s:%hu%s%s HTTP/1.0\r\n%s%s",
          hd->req_type == HTTP_REQ_GET ? "GET" :
          hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
@@ -1035,7 +980,7 @@ send_request (http_t hd, const char *auth,
       else
         snprintf (portstr, sizeof portstr, ":%u", port);
 
-      request = xtryasprintf 
+      request = es_asprintf 
         ("%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
          hd->req_type == HTTP_REQ_GET ? "GET" :
          hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
@@ -1053,7 +998,6 @@ send_request (http_t hd, const char *auth,
     }
 
 
-#ifdef HTTP_USE_ESTREAM
   /* First setup estream so that we can write even the first line
      using estream.  This is also required for the sake of gnutls. */
   {
@@ -1066,16 +1010,15 @@ send_request (http_t hd, const char *auth,
         goto leave;
       }
     cookie->fd = hd->sock;
+    hd->write_cookie = cookie;
     if (hd->uri->use_tls)
-      {
-        cookie->tls_session = tls_session;
-        hd->write_cookie = cookie;
-      }
+      cookie->tls_session = tls_session;
 
     hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
     if (!hd->fp_write)
       {
         xfree (cookie);
+        hd->write_cookie = NULL;
         err = gpg_error_from_syserror ();
       }
     else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
@@ -1083,45 +1026,22 @@ send_request (http_t hd, const char *auth,
     else
       err = 0;
 
-  if(err==0)
-    for(;headers;headers=headers->next)
-      {
-       if ((es_fputs (headers->d, hd->fp_write) || es_fflush (hd->fp_write))
-           || (es_fputs("\r\n",hd->fp_write) || es_fflush(hd->fp_write)))
-         {
-           err = gpg_error_from_syserror ();
-           break;
-         }
-      }
-  }
-
- leave:
-
-#else /*!HTTP_USE_ESTREAM*/
-  /* We send out the start of the request through our own send
-     function and only then assign a stdio stream.  This allows for
-     better error reporting that through standard stdio means. */
-  err = write_server (hd->sock, request, strlen (request));
-  if (!err)
-    for (;headers;headers=headers->next)
-      {
-       err = write_server (hd->sock, headers->d, strlen(headers->d));
-       if (err)
-         break;
-       err = write_server (hd->sock, "\r\n", 2);
-       if (err)
-         break;
-      }
   if (!err)
     {
-      hd->fp_write = fdopen (hd->sock, "w");
-      if (!hd->fp_write)
-        err = gpg_error_from_syserror ();
+      for (;headers; headers=headers->next)
+        {
+          if ((es_fputs (headers->d, hd->fp_write) || es_fflush (hd->fp_write))
+              || (es_fputs("\r\n",hd->fp_write) || es_fflush(hd->fp_write)))
+            {
+              err = gpg_error_from_syserror ();
+              break;
+            }
+        }
     }
-
-#endif /*!HTTP_USE_ESTREAM*/
-
-  xfree (request);
+  }
+  
+ leave:
+  es_free (request);
   xfree (authstr);
   xfree (proxy_authstr);
 
@@ -1181,97 +1101,6 @@ build_rel_path (parsed_uri_t uri)
 }
 
 
-
-/*
-   Same as fgets() but if the buffer is too short a larger one will be
-   allocated up to some limit *MAX_LENGTH.  A line is considered a
-   byte stream ending in a LF.  Returns the length of the line. EOF is
-   indicated by a line of length zero. The last LF may be missing due
-   to an EOF.  If MAX_LENGTH is zero on return, the line has been
-   truncated.  If the returned buffer is NULL, not enough memory was
-   enable to increase it, the return value will also be 0 and some
-   bytes might have been lost which should be no problem becuase
-   out-of-memory is pretty fatal for most applications.
-
-   If a line has been truncated, the file pointer is internally moved
-   forward to the end of the line.
-
-   Note: The returned buffer is allocated with enough extra space to
-   append a CR,LF,Nul
- */
-static size_t
-my_read_line (
-#ifdef HTTP_USE_ESTREAM
-              estream_t fp,
-#else
-              FILE *fp,
-#endif
-              char **addr_of_buffer,
-              size_t *length_of_buffer, size_t *max_length)
-{
-  int c;
-  char *buffer = *addr_of_buffer;
-  size_t length = *length_of_buffer;
-  size_t nbytes = 0;
-  size_t maxlen = *max_length;
-  char *p;
-
-  if (!buffer) /* Must allocate a new buffer. */
-    {          
-      length = 256;
-      buffer = xtrymalloc (length);
-      *addr_of_buffer = buffer;
-      if (!buffer)
-       {
-         *length_of_buffer = *max_length = 0;
-         return 0;
-       }
-      *length_of_buffer = length;
-    }
-
-  length -= 3; /* Reserve 3 bytes (cr,lf,eol). */
-  p = buffer;
-  while ((c = P_ES(getc) (fp)) != EOF)
-    {
-      if (nbytes == length) /* Increase the buffer. */
-       {                       
-         if (length > maxlen) /* Limit reached. */
-           {
-             /* Skip the rest of the line. */
-             while (c != '\n' && (c = P_ES(getc) (fp)) != EOF)
-               ;
-             *p++ = '\n'; /* Always append a LF (we reserved some space). */
-             nbytes++;
-             *max_length = 0; /* Indicate truncation */
-             break; /*(the while loop)*/
-           }
-         length += 3; /* Adjust for the reserved bytes. */
-         length += length < 1024 ? 256 : 1024;
-         *addr_of_buffer = xtryrealloc (buffer, length);
-         if (!*addr_of_buffer)
-           {
-             int save_errno = errno;
-             xfree (buffer);
-             *length_of_buffer = *max_length = 0;
-             gpg_err_set_errno (save_errno);
-             return 0;
-           }
-         buffer = *addr_of_buffer;
-         *length_of_buffer = length;
-         length -= 3; /* And re-adjust for the reservation. */
-         p = buffer + nbytes;
-       }
-      *p++ = c;
-      nbytes++;
-      if (c == '\n')
-       break;
-    }
-  *p = 0; /* Make sure the line is a string. */
-
-  return nbytes;
-}
-
-
 /* Transform a header name into a standard capitalized format; e.g.
    "Content-Type".  Conversion stops at the colon.  As usual we don't
    use the localized versions of ctype.h. */
@@ -1418,7 +1247,7 @@ parse_response (http_t hd)
   do
     {
       maxlen = MAX_LINELEN;
-      len = my_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
+      len = es_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
       line = hd->buffer;
       if (!line)
        return gpg_error_from_syserror (); /* Out of core. */
@@ -1462,7 +1291,7 @@ parse_response (http_t hd)
   do
     {
       maxlen = MAX_LINELEN;
-      len = my_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
+      len = es_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
       line = hd->buffer;
       if (!line)
        return gpg_error_from_syserror (); /* Out of core. */
@@ -1664,7 +1493,7 @@ connect_server (const char *server, unsigned short port,
       struct addrinfo hints, *res, *ai;
       char portstr[35];
 
-      sprintf (portstr, "%hu", port);
+      snprintf (portstr, sizeof portstr, "%hu", port);
       memset (&hints, 0, sizeof (hints));
       hints.ai_socktype = SOCK_STREAM;
       if (getaddrinfo (serverlist[srv].target, portstr, &hints, &res))
@@ -1778,22 +1607,25 @@ static gpg_error_t
 write_server (int sock, const char *data, size_t length)
 {
   int nleft;
+  int nwritten;
 
   /* FIXME: We would better use pth I/O functions.  */
   nleft = length;
   while (nleft > 0)
     {
-#ifdef HAVE_W32_SYSTEM
-      int nwritten;
-      
+#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_PTH)
       nwritten = send (sock, data, nleft, 0);
       if ( nwritten == SOCKET_ERROR ) 
         {
           log_info ("network write failed: ec=%d\n", (int)WSAGetLastError ());
           return gpg_error (GPG_ERR_NETWORK);
         }
-#else /*!HAVE_W32_SYSTEM*/
-      int nwritten = write (sock, data, nleft);
+#else /*!HAVE_W32_SYSTEM || HAVE_PTH*/
+# ifdef HAVE_PTH
+      nwritten = pth_write (sock, data, nleft);
+# else
+      nwritten = write (sock, data, nleft);
+# endif
       if (nwritten == -1)
        {
          if (errno == EINTR)
@@ -1810,7 +1642,7 @@ write_server (int sock, const char *data, size_t length)
          log_info ("network write failed: %s\n", strerror (errno));
          return gpg_error_from_syserror ();
        }
-#endif /*!HAVE_W32_SYSTEM*/
+#endif /*!HAVE_W32_SYSTEM || HAVE_PTH*/
       nleft -= nwritten;
       data += nwritten;
     }
@@ -1820,7 +1652,6 @@ write_server (int sock, const char *data, size_t length)
 
 
 \f
-#ifdef HTTP_USE_ESTREAM
 /* Read handler for estream.  */
 static ssize_t
 cookie_read (void *cookie, void *buffer, size_t size)
@@ -1961,7 +1792,6 @@ cookie_close (void *cookie)
   xfree (c);
   return 0;
 }
-#endif /*HTTP_USE_ESTREAM*/
 
 
 
@@ -1999,9 +1829,7 @@ main (int argc, char **argv)
 #endif /*HTTP_USE_GNUTLS*/
   header_t hdr;
 
-#ifdef HTTP_USE_ESTREAM
   es_init ();
-#endif
   log_set_prefix ("http-test", 1 | 4);
   if (argc == 1)
     {
@@ -2100,7 +1928,7 @@ main (int argc, char **argv)
   switch (http_get_status_code (hd))
     {
     case 200:
-      while ((c = P_ES(getc) (http_get_read_ptr (hd))) != EOF)
+      while ((c = es_getc (http_get_read_ptr (hd))) != EOF)
         putchar (c);
       break;
     case 301:
index b00d87a..2dd612e 100644 (file)
@@ -21,9 +21,7 @@
 #define GNUPG_COMMON_HTTP_H 
 
 #include <gpg-error.h>
-#ifdef HTTP_USE_ESTREAM
 #include "../common/estream.h"
-#endif
 
 struct uri_tuple_s {
   struct uri_tuple_s *next;
@@ -100,13 +98,8 @@ gpg_error_t http_open_document (http_t *r_hd,
                                const char *srvtag,
                                strlist_t headers);
 
-#ifdef HTTP_USE_ESTREAM
 estream_t http_get_read_ptr (http_t hd);
 estream_t http_get_write_ptr (http_t hd);
-#else /*!HTTP_USE_ESTREAM*/
-FILE *http_get_read_ptr (http_t hd);
-FILE *http_get_write_ptr (http_t hd);
-#endif /*!HTTP_USE_ESTREAM*/
 unsigned int http_get_status_code (http_t hd);
 const char *http_get_header (http_t hd, const char *name);