]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/http.c
Merge changes from CUPS 1.6svn-r10267.
[thirdparty/cups.git] / cups / http.c
index 9cd7b9002666c2de4eede44f2aaa0d8328a00d34..69ebd2803861c49f92893655e710163bdba12e52 100644 (file)
@@ -3,7 +3,7 @@
  *
  *   HTTP routines for CUPS.
  *
- *   Copyright 2007-2011 by Apple Inc.
+ *   Copyright 2007-2012 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   This file contains Kerberos support code, copyright 2006 by
  *
  * Contents:
  *
- *   httpAddCredential()       - Allocates and adds a single credential to an
- *                               array.
- *   _httpBIOMethods()         - Get the OpenSSL BIO methods for HTTP
- *                               connections.
- *   httpBlocking()            - Set blocking/non-blocking behavior on a
- *                               connection.
- *   httpCheck()               - Check to see if there is a pending response
- *                               from the server.
- *   httpClearCookie()         - Clear the cookie value(s).
- *   httpClearFields()         - Clear HTTP request fields.
- *   httpClose()               - Close an HTTP connection.
- *   httpConnect()             - Connect to a HTTP server.
- *   httpConnectEncrypt()      - Connect to a HTTP server using encryption.
- *   httpCopyCredentials()     - Copy the credentials associated with an
- *                               encrypted connection.
- *   _httpConvertCredentials() - Convert credentials to the internal format.
- *   _httpCreate()             - Create an unconnected HTTP connection.
- *   httpDelete()              - Send a DELETE request to the server.
- *   _httpDisconnect()         - Disconnect a HTTP connection.
- *   httpEncryption()          - Set the required encryption on the link.
- *   httpError()               - Get the last error on a connection.
- *   httpFlush()               - Flush data from a HTTP connection.
- *   httpFlushWrite()          - Flush data in write buffer.
- *   _httpFreeCredentials()    - Free internal credentials.
- *   httpFreeCredentials()     - Free an array of credentials.
- *   httpGet()                 - Send a GET request to the server.
- *   httpGetAuthString()       - Get the current authorization string.
- *   httpGetBlocking()         - Get the blocking/non-block state of a
- *                               connection.
- *   httpGetCookie()           - Get any cookie data from the response.
- *   httpGetFd()               - Get the file descriptor associated with a
- *                               connection.
- *   httpGetField()            - Get a field value from a request/response.
- *   httpGetLength()           - Get the amount of data remaining from the
- *                               content-length or transfer-encoding fields.
- *   httpGetLength2()          - Get the amount of data remaining from the
- *                               content-length or transfer-encoding fields.
- *   httpGetStatus()           - Get the status of the last HTTP request.
- *   httpGetSubField()         - Get a sub-field value.
- *   httpGetSubField2()        - Get a sub-field value.
- *   httpGets()                - Get a line of text from a HTTP connection.
- *   httpHead()                - Send a HEAD request to the server.
- *   httpInitialize()          - Initialize the HTTP interface library and set
- *                               the default HTTP proxy (if any).
- *   httpOptions()             - Send an OPTIONS request to the server.
- *   _httpPeek()               - Peek at data from a HTTP connection.
- *   httpPost()                - Send a POST request to the server.
- *   httpPrintf()              - Print a formatted string to a HTTP connection.
- *   httpPut()                 - Send a PUT request to the server.
- *   httpRead()                - Read data from a HTTP connection.
- *   httpRead2()               - Read data from a HTTP connection.
- *   _httpReadCDSA()           - Read function for the CDSA library.
- *   _httpReadGNUTLS()         - Read function for the GNU TLS library.
- *   httpReconnect()           - Reconnect to a HTTP server.
- *   httpSetAuthString()       - Set the current authorization string.
- *   httpSetCredentials()      - Set the credentials associated with an
- *                               encrypted connection.
- *   httpSetCookie()           - Set the cookie value(s).
- *   httpSetExpect()           - Set the Expect: header in a request.
- *   httpSetField()            - Set the value of an HTTP header.
- *   httpSetLength()           - Set the content-length and content-encoding.
- *   _httpSetTimeout()         - Set read/write timeouts and an optional
- *                               callback.
- *   httpTrace()               - Send an TRACE request to the server.
- *   _httpUpdate()             - Update the current HTTP status for incoming
- *                               data.
- *   httpUpdate()              - Update the current HTTP state for incoming
- *                               data.
- *   _httpWait()               - Wait for data available on a connection (no
- *                               flush).
- *   httpWait()                - Wait for data available on a connection.
- *   httpWrite()               - Write data to a HTTP connection.
- *   httpWrite2()              - Write data to a HTTP connection.
- *   _httpWriteCDSA()          - Write function for the CDSA library.
- *   _httpWriteGNUTLS()        - Write function for the GNU TLS library.
- *   http_bio_ctrl()           - Control the HTTP connection.
- *   http_bio_free()           - Free OpenSSL data.
- *   http_bio_new()            - Initialize an OpenSSL BIO structure.
- *   http_bio_puts()           - Send a string for OpenSSL.
- *   http_bio_read()           - Read data for OpenSSL.
- *   http_bio_write()          - Write data for OpenSSL.
- *   http_debug_hex()          - Do a hex dump of a buffer.
- *   http_field()              - Return the field index for a field name.
- *   http_read_ssl()           - Read from a SSL/TLS connection.
- *   http_locking_cb()         - Lock/unlock a thread's mutex.
- *   http_send()               - Send a request with all fields and the trailing
- *                               blank line.
- *   http_set_credentials()    - Set the SSL/TLS credentials.
- *   http_setup_ssl()          - Set up SSL/TLS support on a connection.
- *   http_shutdown_ssl()       - Shut down SSL/TLS on a connection.
- *   http_threadid_cb()        - Return the current thread ID.
- *   http_upgrade()            - Force upgrade to TLS encryption.
- *   http_write()              - Write a buffer to a HTTP connection.
- *   http_write_chunk()        - Write a chunked buffer.
- *   http_write_ssl()          - Write to a SSL/TLS connection.
+ *   httpAddCredential()      - Allocates and adds a single credential to an
+ *                             array.
+ *   _httpBIOMethods()       - Get the OpenSSL BIO methods for HTTP
+ *                             connections.
+ *   httpBlocking()          - Set blocking/non-blocking behavior on a
+ *                             connection.
+ *   httpCheck()             - Check to see if there is a pending response
+ *                             from the server.
+ *   httpClearCookie()       - Clear the cookie value(s).
+ *   httpClearFields()       - Clear HTTP request fields.
+ *   httpClose()             - Close an HTTP connection.
+ *   httpConnect()           - Connect to a HTTP server.
+ *   httpConnectEncrypt()     - Connect to a HTTP server using encryption.
+ *   httpCopyCredentials()    - Copy the credentials associated with an
+ *                             encrypted connection.
+ *   _httpCreate()           - Create an unconnected HTTP connection.
+ *   _httpCreateCredentials() - Create credentials in the internal format.
+ *   httpDelete()            - Send a DELETE request to the server.
+ *   _httpDisconnect()       - Disconnect a HTTP connection.
+ *   httpEncryption()        - Set the required encryption on the link.
+ *   httpError()             - Get the last error on a connection.
+ *   httpFlush()             - Flush data from a HTTP connection.
+ *   httpFlushWrite()        - Flush data in write buffer.
+ *   _httpFreeCredentials()   - Free internal credentials.
+ *   httpFreeCredentials()    - Free an array of credentials.
+ *   httpGet()               - Send a GET request to the server.
+ *   httpGetAuthString()      - Get the current authorization string.
+ *   httpGetBlocking()       - Get the blocking/non-block state of a
+ *                             connection.
+ *   httpGetCookie()         - Get any cookie data from the response.
+ *   httpGetFd()             - Get the file descriptor associated with a
+ *                             connection.
+ *   httpGetField()          - Get a field value from a request/response.
+ *   httpGetLength()         - Get the amount of data remaining from the
+ *                             content-length or transfer-encoding fields.
+ *   httpGetLength2()        - Get the amount of data remaining from the
+ *                             content-length or transfer-encoding fields.
+ *   httpGets()              - Get a line of text from a HTTP connection.
+ *   httpGetState()          - Get the current state of the HTTP request.
+ *   httpGetStatus()         - Get the status of the last HTTP request.
+ *   httpGetSubField()       - Get a sub-field value.
+ *   httpGetSubField2()       - Get a sub-field value.
+ *   httpGetVersion()        - Get the HTTP version at the other end.
+ *   httpHead()              - Send a HEAD request to the server.
+ *   httpInitialize()        - Initialize the HTTP interface library and set
+ *                             the default HTTP proxy (if any).
+ *   httpOptions()           - Send an OPTIONS request to the server.
+ *   _httpPeek()             - Peek at data from a HTTP connection.
+ *   httpPost()              - Send a POST request to the server.
+ *   httpPrintf()            - Print a formatted string to a HTTP connection.
+ *   httpPut()               - Send a PUT request to the server.
+ *   httpRead()              - Read data from a HTTP connection.
+ *   httpRead2()             - Read data from a HTTP connection.
+ *   _httpReadCDSA()         - Read function for the CDSA library.
+ *   _httpReadGNUTLS()       - Read function for the GNU TLS library.
+ *   httpReconnect()         - Reconnect to a HTTP server.
+ *   httpReconnect2()        - Reconnect to a HTTP server with timeout and
+ *                             optional cancel.
+ *   httpSetAuthString()      - Set the current authorization string.
+ *   httpSetCredentials()     - Set the credentials associated with an
+ *                             encrypted connection.
+ *   httpSetCookie()         - Set the cookie value(s).
+ *   httpSetExpect()         - Set the Expect: header in a request.
+ *   httpSetField()          - Set the value of an HTTP header.
+ *   httpSetLength()         - Set the content-length and content-encoding.
+ *   httpSetTimeout()        - Set read/write timeouts and an optional
+ *                             callback.
+ *   httpTrace()             - Send an TRACE request to the server.
+ *   _httpUpdate()           - Update the current HTTP status for incoming
+ *                             data.
+ *   httpUpdate()            - Update the current HTTP state for incoming
+ *                             data.
+ *   _httpWait()             - Wait for data available on a connection (no
+ *                             flush).
+ *   httpWait()              - Wait for data available on a connection.
+ *   httpWrite()             - Write data to a HTTP connection.
+ *   httpWrite2()            - Write data to a HTTP connection.
+ *   _httpWriteCDSA()        - Write function for the CDSA library.
+ *   _httpWriteGNUTLS()       - Write function for the GNU TLS library.
+ *   http_bio_ctrl()         - Control the HTTP connection.
+ *   http_bio_free()         - Free OpenSSL data.
+ *   http_bio_new()          - Initialize an OpenSSL BIO structure.
+ *   http_bio_puts()         - Send a string for OpenSSL.
+ *   http_bio_read()         - Read data for OpenSSL.
+ *   http_bio_write()        - Write data for OpenSSL.
+ *   http_debug_hex()        - Do a hex dump of a buffer.
+ *   http_field()            - Return the field index for a field name.
+ *   http_read_ssl()         - Read from a SSL/TLS connection.
+ *   http_send()             - Send a request with all fields and the trailing
+ *                             blank line.
+ *   http_set_credentials()   - Set the SSL/TLS credentials.
+ *   http_set_timeout()       - Set the socket timeout values.
+ *   http_set_wait()         - Set the default wait value for reads.
+ *   http_setup_ssl()        - Set up SSL/TLS support on a connection.
+ *   http_shutdown_ssl()      - Shut down SSL/TLS on a connection.
+ *   http_upgrade()          - Force upgrade to TLS encryption.
+ *   http_write()            - Write a buffer to a HTTP connection.
+ *   http_write_chunk()       - Write a chunked buffer.
+ *   http_write_ssl()        - Write to a SSL/TLS connection.
  */
 
 /*
 
 #include "cups-private.h"
 #include <fcntl.h>
+#include <math.h>
 #ifdef WIN32
 #  include <tchar.h>
 #else
 #  include <sys/resource.h>
 #endif /* WIN32 */
 #ifdef HAVE_POLL
-#  include <sys/poll.h>
+#  include <poll.h>
 #endif /* HAVE_POLL */
 
 
-/*
- * Some operating systems have done away with the Fxxxx constants for
- * the fcntl() call; this works around that "feature"...
- */
-
-#ifndef FNONBLK
-#  define FNONBLK O_NONBLOCK
-#endif /* !FNONBLK */
-
-
 /*
  * Local functions...
  */
@@ -164,23 +159,14 @@ static int                http_read_ssl(http_t *http, char *buf, int len);
 #  if defined(HAVE_CDSASSL) && defined(HAVE_SECCERTIFICATECOPYDATA)
 static int             http_set_credentials(http_t *http);
 #  endif /* HAVE_CDSASSL ** HAVE_SECCERTIFICATECOPYDATA */
+#endif /* HAVE_SSL */
+static void            http_set_timeout(int fd, double timeout);
+static void            http_set_wait(http_t *http);
+#ifdef HAVE_SSL
 static int             http_setup_ssl(http_t *http);
 static void            http_shutdown_ssl(http_t *http);
 static int             http_upgrade(http_t *http);
 static int             http_write_ssl(http_t *http, const char *buf, int len);
-
-#  ifdef HAVE_GNUTLS
-#    ifdef HAVE_PTHREAD_H
-GCRY_THREAD_OPTION_PTHREAD_IMPL;
-#    endif /* HAVE_PTHREAD_H */
-
-#  elif defined(HAVE_LIBSSL)
-static _cups_mutex_t   *http_locks;    /* OpenSSL lock mutexes */
-
-static void            http_locking_cb(int mode, int type, const char *file,
-                                       int line);
-static unsigned long   http_threadid_cb(void);
-#  endif /* HAVE_GNUTLS */
 #endif /* HAVE_SSL */
 
 
@@ -272,7 +258,7 @@ static BIO_METHOD   http_bio_methods =
  *
  * Use @code cupsArrayNew(NULL, NULL)@ to create a credentials array.
  *
- * @since CUPS 1.5@
+ * @since CUPS 1.5/Mac OS X 10.7@
  */
 
 int                                    /* O - 0 on success, -1 on error */
@@ -324,7 +310,10 @@ httpBlocking(http_t *http,         /* I - Connection to server */
              int    b)                 /* I - 1 = blocking, 0 = non-blocking */
 {
   if (http)
+  {
     http->blocking = b;
+    http_set_wait(http);
+  }
 }
 
 
@@ -505,7 +494,7 @@ httpConnectEncrypt(
  * 'httpCopyCredentials()' - Copy the credentials associated with an encrypted
  *                          connection.
  *
- * @since CUPS 1.5@
+ * @since CUPS 1.5/Mac OS X 10.7@
  */
 
 int                                    /* O - Status of call (0 = success) */
@@ -569,64 +558,6 @@ httpCopyCredentials(
 }
 
 
-/*
- * '_httpConvertCredentials()' - Convert credentials to the internal format.
- */
-
-http_tls_credentials_t                 /* O - Internal credentials */
-_httpConvertCredentials(
-    cups_array_t *credentials)         /* I - Array of credentials */
-{
-  if (!credentials)
-    return (NULL);
-
-#  ifdef HAVE_LIBSSL
-  return (NULL);
-
-#  elif defined(HAVE_GNUTLS)
-  return (NULL);
-
-#  elif defined(HAVE_CDSASSL) && defined(HAVE_SECCERTIFICATECOPYDATA)
-  CFMutableArrayRef    peerCerts;      /* Peer credentials reference */
-  SecCertificateRef    secCert;        /* Certificate reference */
-  CFDataRef            data;           /* Credential data reference */
-  http_credential_t    *credential;    /* Credential data */
-
-
-  if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
-                                       cupsArrayCount(credentials),
-                                       &kCFTypeArrayCallBacks)) == NULL)
-    return (NULL);
-
-  for (credential = (http_credential_t *)cupsArrayFirst(credentials);
-       credential;
-       credential = (http_credential_t *)cupsArrayNext(credentials))
-  {
-    if ((data = CFDataCreate(kCFAllocatorDefault, credential->data,
-                            credential->datalen)))
-    {
-      if ((secCert = SecCertificateCreateWithData(kCFAllocatorDefault, data))
-              != NULL)
-      {
-       CFArrayAppendValue(peerCerts, secCert);
-       CFRelease(secCert);
-      }
-
-      CFRelease(data);
-    }
-  }
-
-  return (peerCerts);
-
-#  elif defined(HAVE_SSPISSL)
-  return (NULL);
-
-#  else
-  return (NULL);
-#  endif /* HAVE_LIBSSL */
-}
-
-
 /*
  * '_httpCreate()' - Create an unconnected HTTP connection.
  */
@@ -667,6 +598,7 @@ _httpCreate(
 
   if ((http = calloc(sizeof(http_t), 1)) == NULL)
   {
+    _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
     httpAddrFreeList(addrlist);
     return (NULL);
   }
@@ -692,6 +624,8 @@ _httpCreate(
   else
     http->encryption = encryption;
 
+  http_set_wait(http);
+
  /*
   * Return the new structure...
   */
@@ -700,6 +634,64 @@ _httpCreate(
 }
 
 
+/*
+ * '_httpCreateCredentials()' - Create credentials in the internal format.
+ */
+
+http_tls_credentials_t                 /* O - Internal credentials */
+_httpCreateCredentials(
+    cups_array_t *credentials)         /* I - Array of credentials */
+{
+  if (!credentials)
+    return (NULL);
+
+#  ifdef HAVE_LIBSSL
+  return (NULL);
+
+#  elif defined(HAVE_GNUTLS)
+  return (NULL);
+
+#  elif defined(HAVE_CDSASSL) && defined(HAVE_SECCERTIFICATECOPYDATA)
+  CFMutableArrayRef    peerCerts;      /* Peer credentials reference */
+  SecCertificateRef    secCert;        /* Certificate reference */
+  CFDataRef            data;           /* Credential data reference */
+  http_credential_t    *credential;    /* Credential data */
+
+
+  if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
+                                       cupsArrayCount(credentials),
+                                       &kCFTypeArrayCallBacks)) == NULL)
+    return (NULL);
+
+  for (credential = (http_credential_t *)cupsArrayFirst(credentials);
+       credential;
+       credential = (http_credential_t *)cupsArrayNext(credentials))
+  {
+    if ((data = CFDataCreate(kCFAllocatorDefault, credential->data,
+                            credential->datalen)))
+    {
+      if ((secCert = SecCertificateCreateWithData(kCFAllocatorDefault, data))
+              != NULL)
+      {
+       CFArrayAppendValue(peerCerts, secCert);
+       CFRelease(secCert);
+      }
+
+      CFRelease(data);
+    }
+  }
+
+  return (peerCerts);
+
+#  elif defined(HAVE_SSPISSL)
+  return (NULL);
+
+#  else
+  return (NULL);
+#  endif /* HAVE_LIBSSL */
+}
+
+
 /*
  * 'httpDelete()' - Send a DELETE request to the server.
  */
@@ -1069,7 +1061,7 @@ httpGetLength2(http_t *http)              /* I - Connection to server */
   if (!http)
     return (-1);
 
-  if (!strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked"))
+  if (!_cups_strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked"))
   {
     DEBUG_puts("4httpGetLength2: chunked request!");
 
@@ -1118,245 +1110,91 @@ httpGetLength2(http_t *http)           /* I - Connection to server */
 
 
 /*
- * 'httpGetStatus()' - Get the status of the last HTTP request.
- *
- * @since CUPS 1.2/Mac OS X 10.5@
- */
-
-http_status_t                          /* O - HTTP status */
-httpGetStatus(http_t *http)            /* I - Connection to server */
-{
-  return (http ? http->status : HTTP_ERROR);
-}
-
-
-/*
- * 'httpGetSubField()' - Get a sub-field value.
- *
- * @deprecated@
+ * 'httpGets()' - Get a line of text from a HTTP connection.
  */
 
-char *                                 /* O - Value or NULL */
-httpGetSubField(http_t       *http,    /* I - Connection to server */
-                http_field_t field,    /* I - Field index */
-                const char   *name,    /* I - Name of sub-field */
-               char         *value)    /* O - Value string */
+char *                                 /* O - Line or NULL */
+httpGets(char   *line,                 /* I - Line to read into */
+         int    length,                        /* I - Max length of buffer */
+        http_t *http)                  /* I - Connection to server */
 {
-  return (httpGetSubField2(http, field, name, value, HTTP_MAX_VALUE));
-}
-
-
-/*
- * 'httpGetSubField2()' - Get a sub-field value.
- *
- * @since CUPS 1.2/Mac OS X 10.5@
- */
+  char *lineptr,                       /* Pointer into line */
+       *lineend,                       /* End of line */
+       *bufptr,                        /* Pointer into input buffer */
+       *bufend;                        /* Pointer to end of buffer */
+  int  bytes,                          /* Number of bytes read */
+       eol;                            /* End-of-line? */
 
-char *                                 /* O - Value or NULL */
-httpGetSubField2(http_t       *http,   /* I - Connection to server */
-                 http_field_t field,   /* I - Field index */
-                 const char   *name,   /* I - Name of sub-field */
-                char         *value,   /* O - Value string */
-                int          valuelen) /* I - Size of value buffer */
-{
-  const char   *fptr;                  /* Pointer into field */
-  char         temp[HTTP_MAX_VALUE],   /* Temporary buffer for name */
-               *ptr,                   /* Pointer into string buffer */
-               *end;                   /* End of value buffer */
 
-  DEBUG_printf(("2httpGetSubField2(http=%p, field=%d, name=\"%s\", value=%p, "
-                "valuelen=%d)", http, field, name, value, valuelen));
+  DEBUG_printf(("2httpGets(line=%p, length=%d, http=%p)", line, length, http));
 
-  if (!http || !name || !value || valuelen < 2 ||
-      field <= HTTP_FIELD_UNKNOWN || field >= HTTP_FIELD_MAX)
+  if (http == NULL || line == NULL)
     return (NULL);
 
-  end = value + valuelen - 1;
+ /*
+  * Read a line from the buffer...
+  */
 
-  for (fptr = http->fields[field]; *fptr;)
+  http->error = 0;
+  lineptr     = line;
+  lineend     = line + length - 1;
+  eol         = 0;
+
+  while (lineptr < lineend)
   {
    /*
-    * Skip leading whitespace...
+    * Pre-load the buffer as needed...
     */
 
-    while (_cups_isspace(*fptr))
-      fptr ++;
+#ifdef WIN32
+    WSASetLastError(0);
+#else
+    errno = 0;
+#endif /* WIN32 */
 
-    if (*fptr == ',')
+    while (http->used == 0)
     {
-      fptr ++;
-      continue;
-    }
+     /*
+      * No newline; see if there is more data to be read...
+      */
 
-   /*
-    * Get the sub-field name...
-    */
+      while (!_httpWait(http, http->wait_value, 1))
+      {
+       if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+         continue;
 
-    for (ptr = temp;
-         *fptr && *fptr != '=' && !_cups_isspace(*fptr) &&
-            ptr < (temp + sizeof(temp) - 1);
-         *ptr++ = *fptr++);
+        DEBUG_puts("3httpGets: Timed out!");
+#ifdef WIN32
+        http->error = WSAETIMEDOUT;
+#else
+        http->error = ETIMEDOUT;
+#endif /* WIN32 */
+        return (NULL);
+      }
 
-    *ptr = '\0';
+#ifdef HAVE_SSL
+      if (http->tls)
+       bytes = http_read_ssl(http, http->buffer + http->used,
+                             HTTP_MAX_BUFFER - http->used);
+      else
+#endif /* HAVE_SSL */
+        bytes = recv(http->fd, http->buffer + http->used,
+                    HTTP_MAX_BUFFER - http->used, 0);
 
-    DEBUG_printf(("4httpGetSubField2: name=\"%s\"", temp));
+      DEBUG_printf(("4httpGets: read %d bytes...", bytes));
 
-   /*
-    * Skip trailing chars up to the '='...
-    */
+#ifdef DEBUG
+      http_debug_hex("httpGets", http->buffer + http->used, bytes);
+#endif /* DEBUG */
 
-    while (_cups_isspace(*fptr))
-      fptr ++;
+      if (bytes < 0)
+      {
+       /*
+       * Nope, can't get a line this time...
+       */
 
-    if (!*fptr)
-      break;
-
-    if (*fptr != '=')
-      continue;
-
-   /*
-    * Skip = and leading whitespace...
-    */
-
-    fptr ++;
-
-    while (_cups_isspace(*fptr))
-      fptr ++;
-
-    if (*fptr == '\"')
-    {
-     /*
-      * Read quoted string...
-      */
-
-      for (ptr = value, fptr ++;
-           *fptr && *fptr != '\"' && ptr < end;
-          *ptr++ = *fptr++);
-
-      *ptr = '\0';
-
-      while (*fptr && *fptr != '\"')
-        fptr ++;
-
-      if (*fptr)
-        fptr ++;
-    }
-    else
-    {
-     /*
-      * Read unquoted string...
-      */
-
-      for (ptr = value;
-           *fptr && !_cups_isspace(*fptr) && *fptr != ',' && ptr < end;
-          *ptr++ = *fptr++);
-
-      *ptr = '\0';
-
-      while (*fptr && !_cups_isspace(*fptr) && *fptr != ',')
-        fptr ++;
-    }
-
-    DEBUG_printf(("4httpGetSubField2: value=\"%s\"", value));
-
-   /*
-    * See if this is the one...
-    */
-
-    if (!strcmp(name, temp))
-    {
-      DEBUG_printf(("3httpGetSubField2: Returning \"%s\"", value));
-      return (value);
-    }
-  }
-
-  value[0] = '\0';
-
-  DEBUG_puts("3httpGetSubField2: Returning NULL");
-
-  return (NULL);
-}
-
-
-/*
- * 'httpGets()' - Get a line of text from a HTTP connection.
- */
-
-char *                                 /* O - Line or NULL */
-httpGets(char   *line,                 /* I - Line to read into */
-         int    length,                        /* I - Max length of buffer */
-        http_t *http)                  /* I - Connection to server */
-{
-  char *lineptr,                       /* Pointer into line */
-       *lineend,                       /* End of line */
-       *bufptr,                        /* Pointer into input buffer */
-       *bufend;                        /* Pointer to end of buffer */
-  int  bytes,                          /* Number of bytes read */
-       eol;                            /* End-of-line? */
-
-
-  DEBUG_printf(("2httpGets(line=%p, length=%d, http=%p)", line, length, http));
-
-  if (http == NULL || line == NULL)
-    return (NULL);
-
- /*
-  * Read a line from the buffer...
-  */
-
-  http->error = 0;
-  lineptr     = line;
-  lineend     = line + length - 1;
-  eol         = 0;
-
-  while (lineptr < lineend)
-  {
-   /*
-    * Pre-load the buffer as needed...
-    */
-
-#ifdef WIN32
-    WSASetLastError(0);
-#else
-    errno = 0;
-#endif /* WIN32 */
-
-    while (http->used == 0)
-    {
-     /*
-      * No newline; see if there is more data to be read...
-      */
-
-      if (!_httpWait(http, http->blocking ? 30000 : 10000, 1))
-      {
-        DEBUG_puts("3httpGets: Timed out!");
-#ifdef WIN32
-        http->error = WSAETIMEDOUT;
-#else
-        http->error = ETIMEDOUT;
-#endif /* WIN32 */
-        return (NULL);
-      }
-
-#ifdef HAVE_SSL
-      if (http->tls)
-       bytes = http_read_ssl(http, http->buffer + http->used,
-                             HTTP_MAX_BUFFER - http->used);
-      else
-#endif /* HAVE_SSL */
-        bytes = recv(http->fd, http->buffer + http->used,
-                    HTTP_MAX_BUFFER - http->used, 0);
-
-      DEBUG_printf(("4httpGets: read %d bytes...", bytes));
-
-      if (bytes < 0)
-      {
-       /*
-       * Nope, can't get a line this time...
-       */
-
-#ifdef WIN32
-        DEBUG_printf(("3httpGets: recv() error %d!", WSAGetLastError()));
+#ifdef WIN32
+        DEBUG_printf(("3httpGets: recv() error %d!", WSAGetLastError()));
 
         if (WSAGetLastError() == WSAEINTR)
          continue;
@@ -1455,6 +1293,189 @@ httpGets(char   *line,                  /* I - Line to read into */
 }
 
 
+/*
+ * 'httpGetState()' - Get the current state of the HTTP request.
+ */
+
+http_state_t                           /* O - HTTP state */
+httpGetState(http_t *http)             /* I - Connection to server */
+{
+  return (http ? http->state : HTTP_ERROR);
+}
+
+
+/*
+ * 'httpGetStatus()' - Get the status of the last HTTP request.
+ *
+ * @since CUPS 1.2/Mac OS X 10.5@
+ */
+
+http_status_t                          /* O - HTTP status */
+httpGetStatus(http_t *http)            /* I - Connection to server */
+{
+  return (http ? http->status : HTTP_ERROR);
+}
+
+
+/*
+ * 'httpGetSubField()' - Get a sub-field value.
+ *
+ * @deprecated@
+ */
+
+char *                                 /* O - Value or NULL */
+httpGetSubField(http_t       *http,    /* I - Connection to server */
+                http_field_t field,    /* I - Field index */
+                const char   *name,    /* I - Name of sub-field */
+               char         *value)    /* O - Value string */
+{
+  return (httpGetSubField2(http, field, name, value, HTTP_MAX_VALUE));
+}
+
+
+/*
+ * 'httpGetSubField2()' - Get a sub-field value.
+ *
+ * @since CUPS 1.2/Mac OS X 10.5@
+ */
+
+char *                                 /* O - Value or NULL */
+httpGetSubField2(http_t       *http,   /* I - Connection to server */
+                 http_field_t field,   /* I - Field index */
+                 const char   *name,   /* I - Name of sub-field */
+                char         *value,   /* O - Value string */
+                int          valuelen) /* I - Size of value buffer */
+{
+  const char   *fptr;                  /* Pointer into field */
+  char         temp[HTTP_MAX_VALUE],   /* Temporary buffer for name */
+               *ptr,                   /* Pointer into string buffer */
+               *end;                   /* End of value buffer */
+
+  DEBUG_printf(("2httpGetSubField2(http=%p, field=%d, name=\"%s\", value=%p, "
+                "valuelen=%d)", http, field, name, value, valuelen));
+
+  if (!http || !name || !value || valuelen < 2 ||
+      field <= HTTP_FIELD_UNKNOWN || field >= HTTP_FIELD_MAX)
+    return (NULL);
+
+  end = value + valuelen - 1;
+
+  for (fptr = http->fields[field]; *fptr;)
+  {
+   /*
+    * Skip leading whitespace...
+    */
+
+    while (_cups_isspace(*fptr))
+      fptr ++;
+
+    if (*fptr == ',')
+    {
+      fptr ++;
+      continue;
+    }
+
+   /*
+    * Get the sub-field name...
+    */
+
+    for (ptr = temp;
+         *fptr && *fptr != '=' && !_cups_isspace(*fptr) &&
+            ptr < (temp + sizeof(temp) - 1);
+         *ptr++ = *fptr++);
+
+    *ptr = '\0';
+
+    DEBUG_printf(("4httpGetSubField2: name=\"%s\"", temp));
+
+   /*
+    * Skip trailing chars up to the '='...
+    */
+
+    while (_cups_isspace(*fptr))
+      fptr ++;
+
+    if (!*fptr)
+      break;
+
+    if (*fptr != '=')
+      continue;
+
+   /*
+    * Skip = and leading whitespace...
+    */
+
+    fptr ++;
+
+    while (_cups_isspace(*fptr))
+      fptr ++;
+
+    if (*fptr == '\"')
+    {
+     /*
+      * Read quoted string...
+      */
+
+      for (ptr = value, fptr ++;
+           *fptr && *fptr != '\"' && ptr < end;
+          *ptr++ = *fptr++);
+
+      *ptr = '\0';
+
+      while (*fptr && *fptr != '\"')
+        fptr ++;
+
+      if (*fptr)
+        fptr ++;
+    }
+    else
+    {
+     /*
+      * Read unquoted string...
+      */
+
+      for (ptr = value;
+           *fptr && !_cups_isspace(*fptr) && *fptr != ',' && ptr < end;
+          *ptr++ = *fptr++);
+
+      *ptr = '\0';
+
+      while (*fptr && !_cups_isspace(*fptr) && *fptr != ',')
+        fptr ++;
+    }
+
+    DEBUG_printf(("4httpGetSubField2: value=\"%s\"", value));
+
+   /*
+    * See if this is the one...
+    */
+
+    if (!strcmp(name, temp))
+    {
+      DEBUG_printf(("3httpGetSubField2: Returning \"%s\"", value));
+      return (value);
+    }
+  }
+
+  value[0] = '\0';
+
+  DEBUG_puts("3httpGetSubField2: Returning NULL");
+
+  return (NULL);
+}
+
+
+/*
+ * 'httpGetVersion()' - Get the HTTP version at the other end.
+ */
+
+http_version_t                         /* O - Version number */
+httpGetVersion(http_t *http)           /* I - Connection to server */
+{
+  return (http ? http->version : HTTP_1_0);
+}
+
+
 /*
  * 'httpHead()' - Send a HEAD request to the server.
  */
@@ -1518,14 +1539,6 @@ httpInitialize(void)
 #endif /* WIN32 */
 
 #ifdef HAVE_GNUTLS
- /*
-  * Make sure we handle threading properly...
-  */
-
-#  ifdef HAVE_PTHREAD_H
-  gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
-#  endif /* HAVE_PTHREAD_H */
-
  /*
   * Initialize GNU TLS...
   */
@@ -1540,19 +1553,6 @@ httpInitialize(void)
   SSL_load_error_strings();
   SSL_library_init();
 
- /*
-  * Set the threading callbacks...
-  */
-
-  http_locks = calloc(CRYPTO_num_locks(), sizeof(_cups_mutex_t));
-#  ifdef HAVE_PTHREAD_H
-  for (i = 0; i < CRYPTO_num_locks(); i ++)
-    pthread_mutex_init(http_locks + i, NULL);
-#  endif /* HAVE_PTHREAD_H */
-
-  CRYPTO_set_id_callback(http_threadid_cb);
-  CRYPTO_set_locking_callback(http_locking_cb);
-
  /*
   * Using the current time is a dubious random seed, but on some systems
   * it is the best we can do (on others, this seed isn't even used...)
@@ -1668,8 +1668,16 @@ _httpPeek(http_t *http,                  /* I - Connection to server */
     * Buffer small reads for better performance...
     */
 
-    if (!http->blocking && !httpWait(http, 10000))
-      return (0);
+    if (!http->blocking)
+    {
+      while (!httpWait(http, http->wait_value))
+      {
+       if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+         continue;
+
+       return (0);
+      }
+    }
 
     if (http->data_remaining > sizeof(http->buffer))
       bytes = sizeof(http->buffer);
@@ -1924,73 +1932,83 @@ httpRead2(http_t *http,                 /* I - Connection to server */
     * Buffer small reads for better performance...
     */
 
-    if (!http->blocking && !httpWait(http, 10000))
-      return (0);
+    ssize_t    buflen;                 /* Length of read for buffer */
+
+    if (!http->blocking)
+    {
+      while (!httpWait(http, http->wait_value))
+      {
+       if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+         continue;
+
+       return (0);
+      }
+    }
 
     if (http->data_remaining > sizeof(http->buffer))
-      bytes = sizeof(http->buffer);
+      buflen = sizeof(http->buffer);
     else
-      bytes = http->data_remaining;
+      buflen = http->data_remaining;
+
+    DEBUG_printf(("2httpRead2: Reading %d bytes into buffer.", (int)buflen));
 
+    do
+    {
 #ifdef HAVE_SSL
-    if (http->tls)
-      bytes = http_read_ssl(http, http->buffer, bytes);
-    else
+      if (http->tls)
+       bytes = http_read_ssl(http, http->buffer, buflen);
+      else
 #endif /* HAVE_SSL */
-    {
-      DEBUG_printf(("2httpRead2: reading %d bytes from socket into buffer...",
-                    (int)bytes));
-
-      bytes = recv(http->fd, http->buffer, bytes, 0);
+      bytes = recv(http->fd, http->buffer, buflen, 0);
 
-      DEBUG_printf(("2httpRead2: read %d bytes from socket into buffer...",
-                    (int)bytes));
-    }
-
-    if (bytes > 0)
-      http->used = bytes;
-    else if (bytes < 0)
-    {
-#ifdef WIN32
-      if (WSAGetLastError() != WSAEINTR)
-      {
-        http->error = WSAGetLastError();
-        return (-1);
-      }
-      else if (WSAGetLastError() == WSAEWOULDBLOCK)
+      if (bytes < 0)
       {
-        if (!http->timeout_cb || !(*http->timeout_cb)(http, http->timeout_data))
+#ifdef WIN32
+       if (WSAGetLastError() != WSAEINTR)
        {
-         http->error = WSAEWOULDBLOCK;
+         http->error = WSAGetLastError();
          return (-1);
        }
-      }
+       else if (WSAGetLastError() == WSAEWOULDBLOCK)
+       {
+         if (!http->timeout_cb ||
+             !(*http->timeout_cb)(http, http->timeout_data))
+         {
+           http->error = WSAEWOULDBLOCK;
+           return (-1);
+         }
+       }
 #else
-      if (errno == EWOULDBLOCK || errno == EAGAIN)
-      {
-        if (http->timeout_cb && !(*http->timeout_cb)(http, http->timeout_data))
+       if (errno == EWOULDBLOCK || errno == EAGAIN)
        {
-         http->error = errno;
-         return (-1);
+         if (http->timeout_cb && !(*http->timeout_cb)(http, http->timeout_data))
+         {
+           http->error = errno;
+           return (-1);
+         }
+         else if (!http->timeout_cb && errno != EAGAIN)
+         {
+           http->error = errno;
+           return (-1);
+         }
        }
-       else if (!http->timeout_cb && errno != EAGAIN)
+       else if (errno != EINTR)
        {
          http->error = errno;
          return (-1);
        }
-      }
-      else if (errno != EINTR)
-      {
-        http->error = errno;
-        return (-1);
-      }
 #endif /* WIN32 */
+      }
     }
-    else
-    {
-      http->error = EPIPE;
-      return (0);
-    }
+    while (bytes < 0);
+
+    DEBUG_printf(("2httpRead2: Read " CUPS_LLFMT " bytes into buffer.",
+                  CUPS_LLCAST bytes));
+#ifdef DEBUG
+    http_debug_hex("httpRead2", http->buffer, (int)bytes);
+#endif /* DEBUG */
+
+    http->used = bytes;
   }
 
   if (http->used > 0)
@@ -2012,16 +2030,53 @@ httpRead2(http_t *http,                 /* I - Connection to server */
 #ifdef HAVE_SSL
   else if (http->tls)
   {
-    if (!http->blocking && !httpWait(http, 10000))
-      return (0);
+    if (!http->blocking)
+    {
+      while (!httpWait(http, http->wait_value))
+      {
+       if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+         continue;
+
+       return (0);
+      }
+    }
 
-    bytes = (ssize_t)http_read_ssl(http, buffer, (int)length);
+    while ((bytes = (ssize_t)http_read_ssl(http, buffer, (int)length)) < 0)
+    {
+#ifdef WIN32
+      if (WSAGetLastError() == WSAEWOULDBLOCK)
+      {
+        if (!http->timeout_cb || !(*http->timeout_cb)(http, http->timeout_data))
+         break;
+      }
+      else if (WSAGetLastError() != WSAEINTR)
+        break;
+#else
+      if (errno == EWOULDBLOCK || errno == EAGAIN)
+      {
+        if (http->timeout_cb && !(*http->timeout_cb)(http, http->timeout_data))
+         break;
+        else if (!http->timeout_cb && errno != EAGAIN)
+         break;
+      }
+      else if (errno != EINTR)
+        break;
+#endif /* WIN32 */
+    }
   }
 #endif /* HAVE_SSL */
   else
   {
-    if (!http->blocking && !httpWait(http, 10000))
-      return (0);
+    if (!http->blocking)
+    {
+      while (!httpWait(http, http->wait_value))
+      {
+       if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+         continue;
+
+       return (0);
+      }
+    }
 
     DEBUG_printf(("2httpRead2: reading " CUPS_LLFMT " bytes from socket...",
                   CUPS_LLCAST length));
@@ -2054,6 +2109,9 @@ httpRead2(http_t *http,                   /* I - Connection to server */
 
     DEBUG_printf(("2httpRead2: read " CUPS_LLFMT " bytes from socket...",
                   CUPS_LLCAST bytes));
+#ifdef DEBUG
+    http_debug_hex("httpRead2", buffer, (int)bytes);
+#endif /* DEBUG */
   }
 
   if (bytes > 0)
@@ -2099,10 +2157,6 @@ httpRead2(http_t *http,                  /* I - Connection to server */
     }
   }
 
-#ifdef DEBUG
-  http_debug_hex("httpRead2", buffer, (int)bytes);
-#endif /* DEBUG */
-
   return (bytes);
 }
 
@@ -2131,8 +2185,11 @@ _httpReadCDSA(
     * Make sure we have data before we read...
     */
 
-    if (!_httpWait(http, 10000, 0))
+    while (!_httpWait(http, http->wait_value, 0))
     {
+      if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+       continue;
+
       http->error = ETIMEDOUT;
       return (-1);
     }
@@ -2182,7 +2239,10 @@ _httpReadGNUTLS(
     size_t               length)       /* I - Number of bytes to read */
 {
   http_t       *http;                  /* HTTP connection */
+  ssize_t      bytes;                  /* Bytes read */
+
 
+  DEBUG_printf(("6_httpReadGNUTLS(ptr=%p, data=%p, length=%d)", ptr, data, (int)length));
 
   http = (http_t *)ptr;
 
@@ -2192,14 +2252,19 @@ _httpReadGNUTLS(
     * Make sure we have data before we read...
     */
 
-    if (!_httpWait(http, 10000, 0))
+    while (!_httpWait(http, http->wait_value, 0))
     {
+      if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+       continue;
+
       http->error = ETIMEDOUT;
       return (-1);
     }
   }
 
-  return (recv(http->fd, data, length, 0));
+  bytes = recv(http->fd, data, length, 0);
+  DEBUG_printf(("6_httpReadGNUTLS: bytes=%d", (int)bytes));
+  return (bytes);
 }
 #endif /* HAVE_SSL && HAVE_GNUTLS */
 
@@ -2210,6 +2275,22 @@ _httpReadGNUTLS(
 
 int                                    /* O - 0 on success, non-zero on failure */
 httpReconnect(http_t *http)            /* I - Connection to server */
+{
+  DEBUG_printf(("httpReconnect(http=%p)", http));
+
+  return (httpReconnect2(http, 30000, NULL));
+}
+
+
+/*
+ * 'httpReconnect2()' - Reconnect to a HTTP server with timeout and optional
+ *                      cancel.
+ */
+
+int                                    /* O - 0 on success, non-zero on failure */
+httpReconnect2(http_t *http,           /* I - Connection to server */
+              int    msec,             /* I - Timeout in milliseconds */
+              int    *cancel)          /* I - Pointer to "cancel" variable */
 {
   http_addrlist_t      *addr;          /* Connected address */
 #ifdef DEBUG
@@ -2218,15 +2299,19 @@ httpReconnect(http_t *http)             /* I - Connection to server */
 #endif /* DEBUG */
 
 
-  DEBUG_printf(("httpReconnect(http=%p)", http));
+  DEBUG_printf(("httpReconnect2(http=%p, msec=%d, cancel=%p)", http, msec,
+                cancel));
 
   if (!http)
+  {
+    _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
     return (-1);
+  }
 
 #ifdef HAVE_SSL
   if (http->tls)
   {
-    DEBUG_puts("2httpReconnect: Shutting down SSL/TLS...");
+    DEBUG_puts("2httpReconnect2: Shutting down SSL/TLS...");
     http_shutdown_ssl(http);
   }
 #endif /* HAVE_SSL */
@@ -2237,7 +2322,7 @@ httpReconnect(http_t *http)               /* I - Connection to server */
 
   if (http->fd >= 0)
   {
-    DEBUG_printf(("2httpReconnect: Closing socket %d...", http->fd));
+    DEBUG_printf(("2httpReconnect2: Closing socket %d...", http->fd));
 
 #ifdef WIN32
     closesocket(http->fd);
@@ -2254,12 +2339,13 @@ httpReconnect(http_t *http)             /* I - Connection to server */
 
 #ifdef DEBUG
   for (current = http->addrlist; current; current = current->next)
-    DEBUG_printf(("2httpReconnect: Address %s:%d",
+    DEBUG_printf(("2httpReconnect2: Address %s:%d",
                   httpAddrString(&(current->addr), temp, sizeof(temp)),
                   _httpAddrPort(&(current->addr))));
 #endif /* DEBUG */
 
-  if ((addr = httpAddrConnect(http->addrlist, &(http->fd))) == NULL)
+  if ((addr = httpAddrConnect2(http->addrlist, &(http->fd), msec,
+                               cancel)) == NULL)
   {
    /*
     * Unable to connect...
@@ -2272,36 +2358,21 @@ httpReconnect(http_t *http)             /* I - Connection to server */
 #endif /* WIN32 */
     http->status = HTTP_ERROR;
 
-    DEBUG_printf(("1httpReconnect: httpAddrConnect failed: %s",
+    DEBUG_printf(("1httpReconnect2: httpAddrConnect failed: %s",
                   strerror(http->error)));
 
     return (-1);
   }
 
-  DEBUG_printf(("2httpReconnect: New socket=%d", http->fd));
+  DEBUG_printf(("2httpReconnect2: New socket=%d", http->fd));
 
-  if (http->timeout_value.tv_sec > 0)
-  {
-#ifdef WIN32
-    DWORD timeout_value = http->timeout_value.tv_sec * 1000 +
-                         http->timeout_value.tv_usec / 1000;
-                                       /* Timeout in milliseconds */
-
-    setsockopt(http->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout_value,
-               sizeof(timeout_value));
-    setsockopt(http->fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout_value,
-               sizeof(timeout_value));
-#else
-    setsockopt(http->fd, SOL_SOCKET, SO_RCVTIMEO, &(http->timeout_value),
-               sizeof(http->timeout_value));
-    setsockopt(http->fd, SOL_SOCKET, SO_SNDTIMEO, &(http->timeout_value),
-               sizeof(http->timeout_value));
-#endif /* WIN32 */
-  }
+  if (http->timeout_value > 0)
+    http_set_timeout(http->fd, http->timeout_value);
 
   http->hostaddr = &(addr->addr);
   http->error    = 0;
   http->status   = HTTP_CONTINUE;
+  http->state    = HTTP_WAITING;
 
 #ifdef HAVE_SSL
   if (http->encryption == HTTP_ENCRYPT_ALWAYS)
@@ -2325,7 +2396,7 @@ httpReconnect(http_t *http)               /* I - Connection to server */
     return (http_upgrade(http));
 #endif /* HAVE_SSL */
 
-  DEBUG_printf(("1httpReconnect: Connected to %s:%d...",
+  DEBUG_printf(("1httpReconnect2: Connected to %s:%d...",
                httpAddrString(http->hostaddr, temp, sizeof(temp)),
                _httpAddrPort(http->hostaddr)));
 
@@ -2398,7 +2469,7 @@ httpSetAuthString(http_t     *http,       /* I - Connection to server */
  * 'httpSetCredentials()' - Set the credentials associated with an encrypted
  *                         connection.
  *
- * @since CUPS 1.5@
+ * @since CUPS 1.5/Mac OS X 10.7@
  */
 
 int                                            /* O - Status of call (0 = success) */
@@ -2410,7 +2481,7 @@ httpSetCredentials(http_t *http,          /* I - Connection to server */
 
   _httpFreeCredentials(http->tls_credentials);
 
-  http->tls_credentials = _httpConvertCredentials(credentials);
+  http->tls_credentials = _httpCreateCredentials(credentials);
 
   return (http->tls_credentials ? 0 : -1);
 }
@@ -2554,46 +2625,33 @@ httpSetLength(http_t *http,             /* I - Connection to server */
 
 
 /*
- * '_httpSetTimeout()' - Set read/write timeouts and an optional callback.
+ * 'httpSetTimeout()' - Set read/write timeouts and an optional callback.
  *
  * The optional timeout callback receives both the HTTP connection and a user
- * data pointer and must return 1 to continue or 0 to error out.
+ * data pointer and must return 1 to continue or 0 to error (time) out.
+ *
+ * @since CUPS 1.5/Mac OS X 10.7@
  */
 
 void
-_httpSetTimeout(
-    http_t             *http,          /* I - Connection to server */
-    double             timeout,                /* I - Number of seconds for timeout,
+httpSetTimeout(
+    http_t            *http,           /* I - Connection to server */
+    double            timeout,         /* I - Number of seconds for timeout,
                                                must be greater than 0 */
-    _http_timeout_cb_t cb,             /* I - Callback function or NULL */
-    void               *user_data)     /* I - User data pointer */
+    http_timeout_cb_t cb,              /* I - Callback function or NULL */
+    void              *user_data)      /* I - User data pointer */
 {
   if (!http || timeout <= 0.0)
     return;
 
-  http->timeout_cb            = cb;
-  http->timeout_data          = user_data;
-  http->timeout_value.tv_sec  = (int)timeout;
-  http->timeout_value.tv_usec = (int)(timeout * 1000000) % 1000000;
+  http->timeout_cb    = cb;
+  http->timeout_data  = user_data;
+  http->timeout_value = timeout;
 
   if (http->fd >= 0)
-  {
-#ifdef WIN32
-    DWORD timeout_value = http->timeout_value.tv_sec * 1000 +
-                         http->timeout_value.tv_usec / 1000;
-                                       /* Timeout in milliseconds */
-
-    setsockopt(http->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout_value,
-               sizeof(timeout_value));
-    setsockopt(http->fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout_value,
-               sizeof(timeout_value));
-#else
-    setsockopt(http->fd, SOL_SOCKET, SO_RCVTIMEO, &(http->timeout_value),
-               sizeof(http->timeout_value));
-    setsockopt(http->fd, SOL_SOCKET, SO_SNDTIMEO, &(http->timeout_value),
-               sizeof(http->timeout_value));
-#endif /* WIN32 */
-  }
+    http_set_timeout(http->fd, timeout);
+
+  http_set_wait(http);
 }
 
 
@@ -2733,7 +2791,7 @@ _httpUpdate(http_t        *http,  /* I - Connection to server */
     * Be tolerants of servers that send unknown attribute fields...
     */
 
-    if (!strcasecmp(line, "expect"))
+    if (!_cups_strcasecmp(line, "expect"))
     {
      /*
       * "Expect: 100-continue" or similar...
@@ -2741,7 +2799,7 @@ _httpUpdate(http_t        *http,  /* I - Connection to server */
 
       http->expect = (http_status_t)atoi(value);
     }
-    else if (!strcasecmp(line, "cookie"))
+    else if (!_cups_strcasecmp(line, "cookie"))
     {
      /*
       * "Cookie: name=value[; name=value ...]" - replaces previous cookies...
@@ -3179,7 +3237,19 @@ _httpWriteGNUTLS(
     const void           *data,                /* I - Data buffer */
     size_t               length)       /* I - Number of bytes to write */
 {
-  return (send(((http_t *)ptr)->fd, data, length, 0));
+  ssize_t bytes;                       /* Bytes written */
+
+
+  DEBUG_printf(("6_httpWriteGNUTLS(ptr=%p, data=%p, length=%d)", ptr, data,
+                (int)length));
+#ifdef DEBUG
+  http_debug_hex("_httpWriteGNUTLS", data, (int)length);
+#endif /* DEBUG */
+
+  bytes = send(((http_t *)ptr)->fd, data, length, 0);
+  DEBUG_printf(("_httpWriteGNUTLS: bytes=%d", (int)bytes));
+
+  return (bytes);
 }
 #endif /* HAVE_SSL && HAVE_GNUTLS */
 
@@ -3300,8 +3370,11 @@ http_bio_read(BIO  *h,                   /* I - BIO data */
     * Make sure we have data before we read...
     */
 
-    if (!_httpWait(http, 10000, 0))
+    while (!_httpWait(http, http->wait_value, 0))
     {
+      if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+       continue;
+
 #ifdef WIN32
       http->error = WSAETIMEDOUT;
 #else
@@ -3350,7 +3423,7 @@ http_debug_hex(const char *prefix,        /* I - Prefix for line */
   if (_cups_debug_fd < 0 || _cups_debug_level < 6)
     return;
 
-  DEBUG_printf(("6%s: %d bytes:\n", prefix, bytes));
+  DEBUG_printf(("6%s: %d bytes:", prefix, bytes));
 
   snprintf(line, sizeof(line), "6%s: ", prefix);
   start = line + strlen(line);
@@ -3398,7 +3471,7 @@ http_field(const char *name)              /* I - String name */
 
 
   for (i = 0; i < HTTP_FIELD_MAX; i ++)
-    if (strcasecmp(name, http_fields[i]) == 0)
+    if (_cups_strcasecmp(name, http_fields[i]) == 0)
       return ((http_field_t)i);
 
   return (HTTP_FIELD_UNKNOWN);
@@ -3457,7 +3530,8 @@ http_read_ssl(http_t *http,               /* I - Connection to server */
 
 
   error = SSLRead(http->tls, buf, len, &processed);
-
+  DEBUG_printf(("6http_read_ssl: error=%d, processed=%d", (int)error,
+                (int)processed));
   switch (error)
   {
     case 0 :
@@ -3470,7 +3544,7 @@ http_read_ssl(http_t *http,               /* I - Connection to server */
        else
        {
          result = -1;
-         errno = EINTR;
+         errno  = EINTR;
        }
        break;
 
@@ -3481,12 +3555,13 @@ http_read_ssl(http_t *http,             /* I - Connection to server */
        else
        {
          result = -1;
-         errno = EPIPE;
+         errno  = EPIPE;
        }
        break;
   }
 
   return (result);
+
 #  elif defined(HAVE_SSPISSL)
   return _sspiRead((_sspi_struct_t*) http->tls, buf, len);
 #  endif /* HAVE_LIBSSL */
@@ -3494,25 +3569,6 @@ http_read_ssl(http_t *http,              /* I - Connection to server */
 #endif /* HAVE_SSL */
 
 
-#ifdef HAVE_LIBSSL
-/*
- * 'http_locking_cb()' - Lock/unlock a thread's mutex.
- */
-
-static void
-http_locking_cb(int        mode,       /* I - Lock mode */
-               int        type,        /* I - Lock type */
-               const char *file,       /* I - Source file */
-               int        line)        /* I - Line number */
-{
-  if (mode & CRYPTO_LOCK)
-    _cupsMutexLock(http_locks + type);
-  else
-    _cupsMutexUnlock(http_locks + type);
-}
-#endif /* HAVE_LIBSSL */
-
-
 /*
  * 'http_send()' - Send a request with all fields and the trailing blank line.
  */
@@ -3760,43 +3816,93 @@ http_set_credentials(http_t *http)      /* I - Connection to server */
   return (error);
 }
 #  endif /* HAVE_CDSASSL && HAVE_SECCERTIFICATECOPYDATA */
+#endif /* HAVE_SSL */
+
+
+/*
+ * 'http_set_timeout()' - Set the socket timeout values.
+ */
+
+static void
+http_set_timeout(int    fd,            /* I - File descriptor */
+                 double timeout)       /* I - Timeout in seconds */
+{
+#ifdef WIN32
+  DWORD tv = (DWORD)(timeout * 1000);
+                                     /* Timeout in milliseconds */
+
+  setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+  setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
+
+#else
+  struct timeval tv;                   /* Timeout in secs and usecs */
+
+  tv.tv_sec  = (int)timeout;
+  tv.tv_usec = (int)(1000000 * fmod(timeout, 1.0));
+
+  setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+  setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'http_set_wait()' - Set the default wait value for reads.
+ */
+
+static void
+http_set_wait(http_t *http)            /* I - Connection to server */
+{
+  if (http->blocking)
+  {
+    http->wait_value = (int)(http->timeout_value * 1000);
+
+    if (http->wait_value <= 0)
+      http->wait_value = 60000;
+  }
+  else
+    http->wait_value = 10000;
+}
 
 
+#ifdef HAVE_SSL
 /*
  * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
  */
 
-static int                             /* O - Status of connection */
+static int                             /* O - 0 on success, -1 on failure */
 http_setup_ssl(http_t *http)           /* I - Connection to server */
 {
   _cups_globals_t      *cg = _cupsGlobals();
                                        /* Pointer to library globals */
   int                  any_root;       /* Allow any root */
+  char                 hostname[256],  /* Hostname */
+                       *hostptr;       /* Pointer into hostname */
 
 #  ifdef HAVE_LIBSSL
-  SSL_CTX      *context;               /* Context for encryption */
-  BIO          *bio;                   /* BIO data */
+  SSL_CTX              *context;       /* Context for encryption */
+  BIO                  *bio;           /* BIO data */
+  const char           *message = NULL;/* Error message */
 #  elif defined(HAVE_GNUTLS)
+  int                  status;         /* Status of handshake */
   gnutls_certificate_client_credentials *credentials;
                                        /* TLS credentials */
 #  elif defined(HAVE_CDSASSL)
-  OSStatus     error;                  /* Error code */
-  const char   *message = NULL;        /* Error message */
-  char         *hostname;              /* Hostname */
+  OSStatus             error;          /* Error code */
+  const char           *message = NULL;/* Error message */
 #    ifdef HAVE_SECCERTIFICATECOPYDATA
-  cups_array_t *credentials;           /* Credentials array */
-  cups_array_t *names;                 /* CUPS distinguished names */
-  CFArrayRef   dn_array;               /* CF distinguished names array */
-  CFIndex      count;                  /* Number of credentials */
-  CFDataRef    data;                   /* Certificate data */
-  int          i;                      /* Looping var */
-  http_credential_t
-               *credential;            /* Credential data */
+  cups_array_t         *credentials;   /* Credentials array */
+  cups_array_t         *names;         /* CUPS distinguished names */
+  CFArrayRef           dn_array;       /* CF distinguished names array */
+  CFIndex              count;          /* Number of credentials */
+  CFDataRef            data;           /* Certificate data */
+  int                  i;              /* Looping var */
+  http_credential_t    *credential;    /* Credential data */
 #    endif /* HAVE_SECCERTIFICATECOPYDATA */
 #  elif defined(HAVE_SSPISSL)
-  TCHAR                username[256];          /* Username returned from GetUserName() */
-  TCHAR                commonName[256];        /* Common name for certificate */
-  DWORD                dwSize;                 /* 32 bit size */
+  TCHAR                        username[256];  /* Username returned from GetUserName() */
+  TCHAR                        commonName[256];/* Common name for certificate */
+  DWORD                        dwSize;         /* 32 bit size */
 #  endif /* HAVE_LIBSSL */
 
 
@@ -3807,11 +3913,28 @@ http_setup_ssl(http_t *http)            /* I - Connection to server */
   */
 
   if (httpAddrLocalhost(http->hostaddr))
+  {
     any_root = 1;
+    strlcpy(hostname, "localhost", sizeof(hostname));
+  }
   else
+  {
+   /*
+    * Otherwise use the system-wide setting and make sure the hostname we have
+    * does not end in a trailing dot.
+    */
+
     any_root = cg->any_root;
 
+    strlcpy(hostname, http->hostname, sizeof(hostname));
+    if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+        *hostptr == '.')
+      *hostptr = '\0';
+  }
+
 #  ifdef HAVE_LIBSSL
+  (void)any_root;
+
   context = SSL_CTX_new(SSLv23_client_method());
 
   SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
@@ -3820,16 +3943,19 @@ http_setup_ssl(http_t *http)            /* I - Connection to server */
   BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
 
   http->tls = SSL_new(context);
-  SSL_set_bio(http->tls_credentials, bio, bio);
+  SSL_set_bio(http->tls, bio, bio);
+
+  SSL_set_tlsext_host_name(http->tls, hostname);
 
   if (SSL_connect(http->tls) != 1)
   {
-#    ifdef DEBUG
     unsigned long      error;  /* Error code */
 
     while ((error = ERR_get_error()) != 0)
-      DEBUG_printf(("8http_setup_ssl: %s", ERR_error_string(error, NULL)));
-#    endif /* DEBUG */
+    {
+      message = ERR_error_string(error, NULL);
+      DEBUG_printf(("8http_setup_ssl: %s", message));
+    }
 
     SSL_CTX_free(context);
     SSL_free(http->tls);
@@ -3842,16 +3968,26 @@ http_setup_ssl(http_t *http)            /* I - Connection to server */
 #    endif /* WIN32 */
     http->status = HTTP_ERROR;
 
-    return (HTTP_ERROR);
+    if (!message)
+      message = _("Unable to establish a secure connection to host.");
+
+    _cupsSetError(IPP_PKI_ERROR, message, 1);
+
+    return (-1);
   }
 
 #  elif defined(HAVE_GNUTLS)
+  (void)any_root;
+
   credentials = (gnutls_certificate_client_credentials *)
                     malloc(sizeof(gnutls_certificate_client_credentials));
   if (credentials == NULL)
   {
-    http->error = errno;
+    DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
+                  strerror(errno)));
+    http->error  = errno;
     http->status = HTTP_ERROR;
+    _cupsSetHTTPError(HTTP_ERROR);
 
     return (-1);
   }
@@ -3860,22 +3996,32 @@ http_setup_ssl(http_t *http)            /* I - Connection to server */
 
   gnutls_init(&http->tls, GNUTLS_CLIENT);
   gnutls_set_default_priority(http->tls);
+  gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname,
+                         strlen(hostname));
   gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
   gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr)http);
   gnutls_transport_set_pull_function(http->tls, _httpReadGNUTLS);
   gnutls_transport_set_push_function(http->tls, _httpWriteGNUTLS);
 
-  if ((gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
+  while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
   {
-    http->error  = errno;
-    http->status = HTTP_ERROR;
+    DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
+                  status, gnutls_strerror(status)));
 
-    gnutls_deinit(http->tls);
-    gnutls_certificate_free_credentials(*credentials);
-    free(credentials);
-    http->tls = NULL;
+    if (gnutls_error_is_fatal(status))
+    {
+      http->error  = EIO;
+      http->status = HTTP_ERROR;
 
-    return (-1);
+      _cupsSetError(IPP_PKI_ERROR, gnutls_strerror(status), 0);
+
+      gnutls_deinit(http->tls);
+      gnutls_certificate_free_credentials(*credentials);
+      free(credentials);
+      http->tls = NULL;
+
+      return (-1);
+    }
   }
 
   http->tls_credentials = credentials;
@@ -3883,8 +4029,9 @@ http_setup_ssl(http_t *http)              /* I - Connection to server */
 #  elif defined(HAVE_CDSASSL)
   if ((error = SSLNewContext(false, &http->tls)))
   {
-    http->error  = error;
+    http->error  = errno;
     http->status = HTTP_ERROR;
+    _cupsSetHTTPError(HTTP_ERROR);
 
     return (-1);
   }
@@ -3898,13 +4045,6 @@ http_setup_ssl(http_t *http)             /* I - Connection to server */
     DEBUG_printf(("4http_setup_ssl: SSLSetIOFuncs, error=%d", (int)error));
   }
 
-  if (!error)
-  {
-    error = SSLSetProtocolVersionEnabled(http->tls, kSSLProtocol2, false);
-    DEBUG_printf(("4http_setup_ssl: SSLSetProtocolVersionEnabled, error=%d",
-                  (int)error));
-  }
-
   if (!error)
   {
     error = SSLSetAllowsAnyRoot(http->tls, any_root);
@@ -3926,6 +4066,27 @@ http_setup_ssl(http_t *http)             /* I - Connection to server */
                   cg->expired_root, (int)error));
   }
 
+#    ifdef HAVE_SSLSETPROTOCOLVERSIONMAX
+  if (!error)
+  {
+    error = SSLSetProtocolVersionMax(http->tls, kTLSProtocol1);
+    DEBUG_printf(("4http_setup_ssl: SSLSetProtocolVersionMax(kTLSProtocol1), "
+                  "error=%d", (int)error));
+  }
+#    endif /* HAVE_SSLSETPROTOCOLVERSIONMAX */
+
+ /*
+  * In general, don't verify certificates since things like the common name
+  * often do not match...
+  */
+
+  if (!error)
+  {
+    error = SSLSetEnableCertVerify(http->tls, false);
+    DEBUG_printf(("4http_setup_ssl: SSLSetEnableCertVerify, error=%d",
+                  (int)error));
+  }
+
 #    ifdef HAVE_SECCERTIFICATECOPYDATA
   if (!error)
   {
@@ -3951,24 +4112,21 @@ http_setup_ssl(http_t *http)            /* I - Connection to server */
 
   if (!error && cg->server_cert_cb != NULL)
   {
-    error = SSLSetEnableCertVerify(http->tls, false);
-    DEBUG_printf(("4http_setup_ssl: SSLSetEnableCertVerify, error=%d",
-                  (int)error));
-
-    if (!error)
-    {
-      error = SSLSetSessionOption(http->tls,
-                                 kSSLSessionOptionBreakOnServerAuth, true);
-      DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnServerAuth, "
-                    "error=%d", (int)error));
-    }
+    error = SSLSetSessionOption(http->tls,
+                               kSSLSessionOptionBreakOnServerAuth, true);
+    DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnServerAuth, "
+                 "error=%d", (int)error));
   }
 #    endif /* HAVE_SECCERTIFICATECOPYDATA */
 
+ /*
+  * Let the server know which hostname/domain we are trying to connect to
+  * in case it wants to serve up a certificate with a matching common name.
+  */
+
   if (!error)
   {
-    hostname = httpAddrLocalhost(http->hostaddr) ? "localhost" : http->hostname;
-    error    = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
+    error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
 
     DEBUG_printf(("4http_setup_ssl: SSLSetPeerDomainName, error=%d",
                   (int)error));
@@ -3982,7 +4140,7 @@ http_setup_ssl(http_t *http)              /* I - Connection to server */
     {
       error = SSLHandshake(http->tls);
 
-      DEBUG_printf(("4_httpWait: SSLHandshake returned %d.", (int)error));
+      DEBUG_printf(("4http_setup_ssl: SSLHandshake returned %d.", (int)error));
 
       switch (error)
       {
@@ -4007,8 +4165,8 @@ http_setup_ssl(http_t *http)              /* I - Connection to server */
                httpFreeCredentials(credentials);
              }
 
-             DEBUG_printf(("4_httpWait: Server certificate callback returned "
-                           "%d.", (int)error));
+             DEBUG_printf(("4http_setup_ssl: Server certificate callback "
+                           "returned %d.", (int)error));
            }
            break;
 
@@ -4048,7 +4206,7 @@ http_setup_ssl(http_t *http)              /* I - Connection to server */
                error = (cg->client_cert_cb)(http, http->tls, names,
                                             cg->client_cert_data);
 
-               DEBUG_printf(("4_httpWait: Client certificate callback "
+               DEBUG_printf(("4http_setup_ssl: Client certificate callback "
                              "returned %d.", (int)error));
              }
 
@@ -4127,7 +4285,10 @@ http_setup_ssl(http_t *http)             /* I - Connection to server */
   http->tls = _sspiAlloc();
 
   if (!http->tls)
+  {
+    _cupsSetHTTPError(HTTP_ERROR);
     return (-1);
+  }
 
   http->tls->sock = http->fd;
   dwSize          = sizeof(username) / sizeof(TCHAR);
@@ -4140,26 +4301,38 @@ http_setup_ssl(http_t *http)            /* I - Connection to server */
   {
     _sspiFree(http->tls_credentials);
     http->tls_credentials = NULL;
+
+    http->error  = EIO;
+    http->status = HTTP_ERROR;
+
+    _cupsSetError(IPP_PKI_ERROR,
+                  _("Unable to establish a secure connection to host."), 1);
+
     return (-1);
   }
 
-  _sspiSetAllowsAnyRoot(http->tls_credentials, TRUE);
+  _sspiSetAllowsAnyRoot(http->tls_credentials, any_root);
   _sspiSetAllowsExpiredCerts(http->tls_credentials, TRUE);
 
-  if (!_sspiConnect(http->tls_credentials, http->hostname))
+  if (!_sspiConnect(http->tls_credentials, hostname))
   {
     _sspiFree(http->tls_credentials);
     http->tls_credentials = NULL;
+
+    http->error  = EIO;
+    http->status = HTTP_ERROR;
+
+    _cupsSetError(IPP_PKI_ERROR,
+                  _("Unable to establish a secure connection to host."), 1);
+
     return (-1);
   }
 #  endif /* HAVE_CDSASSL */
 
   return (0);
 }
-#endif /* HAVE_SSL */
 
 
-#ifdef HAVE_SSL
 /*
  * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
  */
@@ -4170,11 +4343,11 @@ http_shutdown_ssl(http_t *http)         /* I - Connection to server */
 #  ifdef HAVE_LIBSSL
   SSL_CTX      *context;               /* Context for encryption */
 
-  context = SSL_get_SSL_CTX(http->tls_credentials);
+  context = SSL_get_SSL_CTX(http->tls);
 
-  SSL_shutdown(http->tls_credentials);
+  SSL_shutdown(http->tls);
   SSL_CTX_free(context);
-  SSL_free(http->tls_credentials);
+  SSL_free(http->tls);
 
 #  elif defined(HAVE_GNUTLS)
   gnutls_certificate_client_credentials *credentials;
@@ -4206,23 +4379,6 @@ http_shutdown_ssl(http_t *http)          /* I - Connection to server */
 #endif /* HAVE_SSL */
 
 
-#ifdef HAVE_LIBSSL
-/*
- * 'http_threadid_cb()' - Return the current thread ID.
- */
-
-static unsigned long                   /* O - Thread ID */
-http_threadid_cb(void)
-{
-#  ifdef HAVE_PTHREAD_H
-  return ((unsigned long)pthread_self());
-#  else
-  return (0);
-#  endif /* HAVE_PTHREAD_H */
-}
-#endif /* HAVE_LIBSSL */
-
-
 #ifdef HAVE_SSL
 /*
  * 'http_upgrade()' - Force upgrade to TLS encryption.
@@ -4331,6 +4487,64 @@ http_write(http_t     *http,             /* I - Connection to server */
 
   while (length > 0)
   {
+    DEBUG_printf(("3http_write: About to write %d bytes.", (int)length));
+
+    if (http->timeout_cb)
+    {
+#ifdef HAVE_POLL
+      struct pollfd    pfd;            /* Polled file descriptor */
+#else
+      fd_set           output_set;     /* Output ready for write? */
+      struct timeval   timeout;        /* Timeout value */
+#endif /* HAVE_POLL */
+      int              nfds;           /* Result from select()/poll() */
+
+      do
+      {
+#ifdef HAVE_POLL
+       pfd.fd     = http->fd;
+       pfd.events = POLLOUT;
+
+       while ((nfds = poll(&pfd, 1, http->wait_value)) < 0 &&
+              (errno == EINTR || errno == EAGAIN));
+
+#else
+       do
+       {
+         FD_ZERO(&output_set);
+         FD_SET(http->fd, &output_set);
+
+         timeout.tv_sec  = http->wait_value / 1000;
+         timeout.tv_usec = 1000 * (http->wait_value % 1000);
+
+         nfds = select(http->fd + 1, NULL, &output_set, NULL, &timeout);
+       }
+#  ifdef WIN32
+       while (nfds < 0 && (WSAGetLastError() == WSAEINTR ||
+                           WSAGetLastError() == WSAEWOULDBLOCK));
+#  else
+       while (nfds < 0 && (errno == EINTR || errno == EAGAIN));
+#  endif /* WIN32 */
+#endif /* HAVE_POLL */
+
+        if (nfds < 0)
+       {
+         http->error = errno;
+         return (-1);
+       }
+       else if (nfds == 0 && !(*http->timeout_cb)(http, http->timeout_data))
+       {
+#ifdef WIN32
+         http->error = WSAEWOULDBLOCK;
+#else
+         http->error = EWOULDBLOCK;
+#endif /* WIN32 */
+         return (-1);
+       }
+      }
+      while (nfds <= 0);
+    }
+
 #ifdef HAVE_SSL
     if (http->tls)
       bytes = http_write_ssl(http, buffer, length);
@@ -4338,6 +4552,9 @@ http_write(http_t     *http,              /* I - Connection to server */
 #endif /* HAVE_SSL */
     bytes = send(http->fd, buffer, length, 0);
 
+    DEBUG_printf(("3http_write: Write of %d bytes returned %d.", (int)length,
+                  (int)bytes));
+
     if (bytes < 0)
     {
 #ifdef WIN32