]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
The httpAddrConnect and httpConnect* APIs now try connecting to multiple
authormsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>
Fri, 11 Dec 2015 18:59:09 +0000 (18:59 +0000)
committermsweet <msweet@a1ca3aef-8c08-0410-bb20-df032aa958be>
Fri, 11 Dec 2015 18:59:09 +0000 (18:59 +0000)
addresses in parallel (<rdar://problem/20643153>)

git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@13014 a1ca3aef-8c08-0410-bb20-df032aa958be

CHANGES.txt
cups/http-addrlist.c
cups/http.c

index 3e3edb78bf0186d9ab0da73dc1a25771b4d48764..983c389ddb678c885185ca3920866b69868adcd4 100644 (file)
@@ -1,8 +1,10 @@
-CHANGES.txt - 2.2b1 - 2015-11-18
+CHANGES.txt - 2.2b1 - 2015-12-11
 --------------------------------
 
 CHANGES IN CUPS V2.2b1
 
+       - The httpAddrConnect and httpConnect* APIs now try connecting to
+         multiple addresses in parallel (<rdar://problem/20643153>)
        - The cupsd domain socket is no longer world-accessible on OS X
          (<rdar://problem/7542560>)
        - Interface scripts are no longer supported for security reasons
index 0b3f083a9924fb7300dab2965aa89d9a3d828f87..8a17f6fa66d9b021c0a8174e2798097bbf47b72a 100644 (file)
@@ -68,14 +68,19 @@ httpAddrConnect2(
   http_addr_t          peer;           /* Peer address */
   int                  flags,          /* Socket flags */
                        remaining;      /* Remaining timeout */
+  int                  i,              /* Looping var */
+                       nfds,           /* Number of file descriptors */
+                       fds[100],       /* Socket file descriptors */
+                       result;         /* Result from select() or poll() */
+  http_addrlist_t      *addrs[100];    /* Addresses */
 #  ifdef HAVE_POLL
-  struct pollfd                pfd;            /* Polled file descriptor */
+  struct pollfd                pfds[100];      /* Polled file descriptors */
 #  else
+  int                  max_fd = -1;    /* Highest file descriptor */
   fd_set               input_set,      /* select() input set */
                        output_set;     /* select() output set */
   struct timeval       timeout;        /* Timeout */
 #  endif /* HAVE_POLL */
-  int                  nfds;           /* Result from select()/poll() */
 #endif /* O_NONBLOCK */
 #ifdef DEBUG
   char                 temp[256];      /* Temporary address string */
@@ -95,35 +100,39 @@ httpAddrConnect2(
   if (cancel && *cancel)
     return (NULL);
 
-  if (msec <= 0 || getenv("CUPS_DISABLE_ASYNC_CONNECT"))
+  if (msec <= 0)
     msec = INT_MAX;
 
  /*
   * Loop through each address until we connect or run out of addresses...
   */
 
-  while (addrlist)
+  for (nfds = 0; addrlist && nfds < (int)(sizeof(fds) / sizeof(fds[0])); addrlist = addrlist->next)
   {
     if (cancel && *cancel)
+    {
+      while (nfds > 0)
+      {
+        nfds --;
+       httpAddrClose(NULL, fds[nfds]);
+      }
+
       return (NULL);
+    }
 
    /*
     * Create the socket...
     */
 
-    DEBUG_printf(("2httpAddrConnect2: Trying %s:%d...",
-                 httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
-                 httpAddrPort(&(addrlist->addr))));
+    DEBUG_printf(("2httpAddrConnect2: Trying %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr))));
 
-    if ((*sock = (int)socket(httpAddrFamily(&(addrlist->addr)), SOCK_STREAM,
-                             0)) < 0)
+    if ((fds[nfds] = (int)socket(httpAddrFamily(&(addrlist->addr)), SOCK_STREAM, 0)) < 0)
     {
      /*
       * Don't abort yet, as this could just be an issue with the local
       * system not being configured with IPv4/IPv6/domain socket enabled...
       */
 
-      addrlist = addrlist->next;
       continue;
     }
 
@@ -132,16 +141,16 @@ httpAddrConnect2(
     */
 
     val = 1;
-    setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val));
+    setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val));
 
 #ifdef SO_REUSEPORT
     val = 1;
-    setsockopt(*sock, SOL_SOCKET, SO_REUSEPORT, CUPS_SOCAST &val, sizeof(val));
+    setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEPORT, CUPS_SOCAST &val, sizeof(val));
 #endif /* SO_REUSEPORT */
 
 #ifdef SO_NOSIGPIPE
     val = 1;
-    setsockopt(*sock, SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val));
+    setsockopt(fds[nfds], SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val));
 #endif /* SO_NOSIGPIPE */
 
    /*
@@ -150,14 +159,14 @@ httpAddrConnect2(
     */
 
     val = 1;
-    setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, CUPS_SOCAST &val, sizeof(val));
+    setsockopt(fds[nfds], IPPROTO_TCP, TCP_NODELAY, CUPS_SOCAST &val, sizeof(val));
 
 #ifdef FD_CLOEXEC
    /*
     * Close this socket when starting another process...
     */
 
-    fcntl(*sock, F_SETFD, FD_CLOEXEC);
+    fcntl(fds[nfds], F_SETFD, FD_CLOEXEC);
 #endif /* FD_CLOEXEC */
 
 #ifdef O_NONBLOCK
@@ -167,142 +176,157 @@ httpAddrConnect2(
 
     DEBUG_printf(("httpAddrConnect2: Setting non-blocking connect()"));
 
-    flags = fcntl(*sock, F_GETFL, 0);
-    if (msec != INT_MAX)
-    {
-      DEBUG_puts("httpAddrConnect2: Setting non-blocking connect()");
-
-      fcntl(*sock, F_SETFL, flags | O_NONBLOCK);
-    }
+    flags = fcntl(fds[nfds], F_GETFL, 0);
+    fcntl(fds[nfds], F_SETFL, flags | O_NONBLOCK);
 #endif /* O_NONBLOCK */
 
    /*
     * Then connect...
     */
 
-    if (!connect(*sock, &(addrlist->addr.addr), (socklen_t)httpAddrLength(&(addrlist->addr))))
+    if (!connect(fds[nfds], &(addrlist->addr.addr), (socklen_t)httpAddrLength(&(addrlist->addr))))
     {
-      DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...",
-                   httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
-                   httpAddrPort(&(addrlist->addr))));
+      DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr))));
 
 #ifdef O_NONBLOCK
-      fcntl(*sock, F_SETFL, flags);
+      fcntl(fds[nfds], F_SETFL, flags);
 #endif /* O_NONBLOCK */
 
+      *sock = fds[nfds];
+
+      while (nfds > 0)
+      {
+        nfds --;
+       httpAddrClose(NULL, fds[nfds]);
+      }
+
       return (addrlist);
     }
 
-#ifdef O_NONBLOCK
-#  ifdef WIN32
-    if (WSAGetLastError() == WSAEINPROGRESS ||
-        WSAGetLastError() == WSAEWOULDBLOCK)
-#  else
-    if (errno == EINPROGRESS || errno == EWOULDBLOCK)
-#  endif /* WIN32 */
+#ifdef WIN32
+    if (WSAGetLastError() != WSAEINPROGRESS && WSAGetLastError() != WSAEWOULDBLOCK)
+#else
+    if (errno != EINPROGRESS && errno != EWOULDBLOCK)
+#endif /* WIN32 */
     {
-      DEBUG_puts("1httpAddrConnect2: Finishing async connect()");
+      DEBUG_printf(("1httpAddrConnect2: Unable to connect to %s:%d: %s", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr)), strerror(errno)));
+      httpAddrClose(NULL, fds[nfds]);
+      continue;
+    }
+
+    fcntl(fds[nfds], F_SETFL, flags);
+
+#ifndef HAVE_POLL
+    if (fds[nfds] > max_fd)
+      max_fd = fds[nfds];
+#endif /* !HAVE_POLL */
 
-      fcntl(*sock, F_SETFL, flags);
+    addrs[nfds] = addrlist;
+    nfds ++;
+  }
+
+#ifdef O_NONBLOCK
+  DEBUG_puts("1httpAddrConnect2: Finishing async connect()");
 
-      for (remaining = msec; remaining > 0; remaining -= 250)
+  for (remaining = msec; remaining > 0; remaining -= 250)
+  {
+    do
+    {
+      if (cancel && *cancel)
       {
-       do
-        {
-          if (cancel && *cancel)
-          {
-          /*
-           * Close this socket and return...
-           */
+       /*
+       * Close this socket and return...
+       */
 
-            DEBUG_puts("1httpAddrConnect2: Canceled connect()");
+       DEBUG_puts("1httpAddrConnect2: Canceled connect()");
 
-            httpAddrClose(NULL, *sock);
+       while (nfds > 0)
+       {
+         nfds --;
+         httpAddrClose(NULL, fds[nfds]);
+       }
 
-           *sock = -1;
+       *sock = -1;
 
-           return (NULL);
-          }
+       return (NULL);
+      }
 
 #  ifdef HAVE_POLL
-         pfd.fd     = *sock;
-         pfd.events = POLLIN | POLLOUT;
+      for (i = 0; i < nfds; i ++)
+      {
+        pfds[i].fd     = fds[i];
+        pfds[i].events = POLLIN | POLLOUT;
+      }
 
-          nfds = poll(&pfd, 1, remaining > 250 ? 250 : remaining);
+      result = poll(pfds, (nfds_t)nfds, remaining > 250 ? 250 : remaining);
 
-         DEBUG_printf(("1httpAddrConnect2: poll() returned %d (%d)", nfds,
-                       errno));
+      DEBUG_printf(("1httpAddrConnect2: poll() returned %d (%d)", result, errno));
 
 #  else
-         FD_ZERO(&input_set);
-         FD_SET(*sock, &input_set);
-         output_set = input_set;
+      FD_ZERO(&input_set);
+      for (i = 0; i < nfds; i ++)
+        FD_SET(fds[i], &input_set);
+      output_set = input_set;
 
-         timeout.tv_sec  = 0;
-         timeout.tv_usec = (remaining > 250 ? 250 : remaining) * 1000;
+      timeout.tv_sec  = 0;
+      timeout.tv_usec = (remaining > 250 ? 250 : remaining) * 1000;
 
-         nfds = select(*sock + 1, &input_set, &output_set, NULL, &timeout);
+      result = select(max_fd + 1, &input_set, &output_set, NULL, &timeout);
 
-         DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", nfds,
-                       errno));
+      DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", result, errno));
 #  endif /* HAVE_POLL */
-       }
+    }
 #  ifdef WIN32
-       while (nfds < 0 && (WSAGetLastError() == WSAEINTR ||
-                           WSAGetLastError() == WSAEWOULDBLOCK));
+    while (result < 0 && (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK));
 #  else
-       while (nfds < 0 && (errno == EINTR || errno == EAGAIN));
+    while (result < 0 && (errno == EINTR || errno == EAGAIN));
 #  endif /* WIN32 */
 
-        if (nfds > 0)
+    if (result > 0)
+    {
+      for (i = 0; i < nfds; i ++)
+      {
+#  ifdef HAVE_POLL
+        DEBUG_printf(("pfds[%d].revents=%x\n", i, pfds[i].revents));
+        if (pfds[i].revents)
+#  else
+        if (FD_ISSET(fds[i], &input))
+#  endif /* HAVE_POLL */
         {
-          len = sizeof(peer);
-          if (!getpeername(*sock, (struct sockaddr *)&peer, &len))
-          {
-           DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...",
-                         httpAddrString(&peer, temp, sizeof(temp)),
-                         httpAddrPort(&peer)));
-
-           return (addrlist);
-         }
+         *sock = fds[i];
+         len   = sizeof(peer);
+         if (!getpeername(fds[i], (struct sockaddr *)&peer, &len))
+         {
+           DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&peer, temp, sizeof(temp)), httpAddrPort(&peer)));
 
-          break;
-        }
+           addrlist = addrs[i];
+         }
+       }
+       else
+         httpAddrClose(NULL, fds[i]);
       }
+
+      return (addrlist);
     }
+  }
 #endif /* O_NONBLOCK */
 
-    DEBUG_printf(("1httpAddrConnect2: Unable to connect to %s:%d: %s",
-                 httpAddrString(&(addrlist->addr), temp, sizeof(temp)),
-                 httpAddrPort(&(addrlist->addr)), strerror(errno)));
-
-#ifndef WIN32
-    if (errno == EINPROGRESS)
-      errno = ETIMEDOUT;
-#endif /* !WIN32 */
-
-   /*
-    * Close this socket and move to the next address...
-    */
-
-    httpAddrClose(NULL, *sock);
-
-    *sock    = -1;
-    addrlist = addrlist->next;
+  while (nfds > 0)
+  {
+    nfds --;
+    httpAddrClose(NULL, fds[nfds]);
   }
 
-  if (!addrlist)
 #ifdef WIN32
-    _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, "Connection failed", 0);
+  _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, "Connection failed", 0);
 #else
-    _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, strerror(errno), 0);
+  _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, strerror(errno), 0);
 #endif /* WIN32 */
 
-  return (addrlist);
+  return (NULL);
 }
 
 
-
 /*
  * 'httpAddrCopyList()' - Copy an address list.
  *
index 4d1e7f32b3e5ba2398d43f37f39d997594d10385..de0ee46b8f6c92174808c36d8e6c6dc12c6cc430 100644 (file)
@@ -3,7 +3,7 @@
  *
  * HTTP routines for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2015 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * This file contains Kerberos support code, copyright 2006 by
@@ -2428,8 +2428,7 @@ httpReconnect2(http_t *http,              /* I - HTTP connection */
                   httpAddrPort(&(current->addr))));
 #endif /* DEBUG */
 
-  if ((addr = httpAddrConnect2(http->addrlist, &(http->fd), msec,
-                               cancel)) == NULL)
+  if ((addr = httpAddrConnect2(http->addrlist, &(http->fd), msec, cancel)) == NULL)
   {
    /*
     * Unable to connect...