Index: lib/ftp.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/ftp.c,v
retrieving revision 1.342
diff -u -r1.342 ftp.c
--- lib/ftp.c	11 Dec 2005 23:37:59 -0000	1.342
+++ lib/ftp.c	22 Dec 2005 15:36:57 -0000
@@ -95,6 +95,7 @@
 #include "select.h"
 #include "parsedate.h" /* for the week day and month names */
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "multiif.h"
 
 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
 #include "inet_ntoa_r.h"
@@ -698,27 +699,24 @@
 }
 
 /* For the FTP "protocol connect" and "doing" phases only */
-CURLcode Curl_ftp_fdset(struct connectdata *conn,
-                        fd_set *read_fd_set,
-                        fd_set *write_fd_set,
-                        int *max_fdp)
+int Curl_ftp_getsock(struct connectdata *conn,
+                     curl_socket_t *socks,
+                     int numsocks)
 {
   struct FTP *ftp = conn->proto.ftp;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+
+  if(!numsocks)
+    return GETSOCK_BLANK;
+
+  socks[0] = conn->sock[FIRSTSOCKET];
 
   if(ftp->sendleft) {
     /* write mode */
-    FD_SET(sockfd, write_fd_set);
+    return GETSOCK_WRITESOCK(0);
   }
-  else {
-    /* read mode */
-    FD_SET(sockfd, read_fd_set);
-  }
-
-  if((int)sockfd > *max_fdp)
-    *max_fdp = (int)sockfd;
 
-  return CURLE_OK;
+  /* read mode */
+  return GETSOCK_READSOCK(0);
 }
 
 /* This is called after the FTP_QUOTE state is passed.
Index: lib/ftp.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/ftp.h,v
retrieving revision 1.22
diff -u -r1.22 ftp.h
--- lib/ftp.h	9 Feb 2005 13:06:40 -0000	1.22
+++ lib/ftp.h	22 Dec 2005 15:36:57 -0000
@@ -34,10 +34,9 @@
                              int *ftpcode);
 CURLcode Curl_ftp_nextconnect(struct connectdata *conn);
 CURLcode Curl_ftp_multi_statemach(struct connectdata *conn, bool *done);
-CURLcode Curl_ftp_fdset(struct connectdata *conn,
-                        fd_set *read_fd_set,
-                        fd_set *write_fd_set,
-                        int *max_fdp);
+int Curl_ftp_getsock(struct connectdata *conn,
+                     curl_socket_t *socks,
+                     int numsocks);
 CURLcode Curl_ftp_doing(struct connectdata *conn,
                         bool *dophase_done);
 #endif /* CURL_DISABLE_FTP */
Index: lib/hostares.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/hostares.c,v
retrieving revision 1.14
diff -u -r1.14 hostares.c
--- lib/hostares.c	19 Apr 2005 23:19:23 -0000	1.14
+++ lib/hostares.c	22 Dec 2005 15:36:57 -0000
@@ -105,15 +105,13 @@
  * Returns: CURLE_OK always!
  */
 
-CURLcode Curl_resolv_fdset(struct connectdata *conn,
-                           fd_set *read_fd_set,
-                           fd_set *write_fd_set,
-                           int *max_fdp)
+int Curl_resolv_getsock(struct connectdata *conn,
+                        curl_socket_t *socks,
+                        int numsocks)
 
 {
-  int max = ares_fds(conn->data->state.areschannel,
-                     read_fd_set, write_fd_set);
-  *max_fdp = max;
+  int max = ares_getsock(conn->data->state.areschannel,
+                         (int *)socks, numsocks);
 
   return CURLE_OK;
 }
Index: lib/hostip.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/hostip.h,v
retrieving revision 1.45
diff -u -r1.45 hostip.h
--- lib/hostip.h	11 Dec 2005 23:37:59 -0000	1.45
+++ lib/hostip.h	22 Dec 2005 15:36:57 -0000
@@ -159,6 +159,14 @@
 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
                               struct Curl_dns_entry **dnsentry);
 
+
+/* Curl_resolv_getsock() is a generic function that exists in multiple versions
+   depending on what name resolve technology we've built to use. The function
+   is called from the multi_getsock() function */
+int Curl_resolv_getsock(struct connectdata *conn,
+                        curl_socket_t *sock,
+                        int numsocks);
+#if 0
 /* Curl_resolv_fdset() is a generic function that exists in multiple versions
    depending on what name resolve technology we've built to use. The function
    is called from the curl_multi_fdset() function */
@@ -166,8 +174,11 @@
                            fd_set *read_fd_set,
                            fd_set *write_fd_set,
                            int *max_fdp);
+#endif
+
 /* unlock a previously resolved dns entry */
-void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns);
+void Curl_resolv_unlock(struct SessionHandle *data,
+                        struct Curl_dns_entry *dns);
 
 /* for debugging purposes only: */
 void Curl_scan_cache_used(void *user, void *ptr);
Index: lib/hostsyn.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/hostsyn.c,v
retrieving revision 1.5
diff -u -r1.5 hostsyn.c
--- lib/hostsyn.c	19 Apr 2005 23:19:23 -0000	1.5
+++ lib/hostsyn.c	22 Dec 2005 15:36:57 -0000
@@ -126,17 +126,15 @@
  * It is present here to keep #ifdefs out from multi.c
  */
 
-CURLcode Curl_resolv_fdset(struct connectdata *conn,
-                           fd_set *read_fd_set,
-                           fd_set *write_fd_set,
-                           int *max_fdp)
+int Curl_resolv_getsock(struct connectdata *conn,
+                        curl_socket_t *sock,
+                        int numsocks)
 {
   (void)conn;
-  (void)read_fd_set;
-  (void)write_fd_set;
-  (void)max_fdp;
+  (void)sock;
+  (void)numsocks;
 
-  return CURLE_OK;
+  return 0; /* no bits since we don't use any socks */
 }
 
 #endif /* truly sync */
Index: lib/multi.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/multi.c,v
retrieving revision 1.68
diff -u -r1.68 multi.c
--- lib/multi.c	8 Mar 2005 22:21:59 -0000	1.68
+++ lib/multi.c	22 Dec 2005 15:36:58 -0000
@@ -73,6 +73,18 @@
   CURLM_STATE_LAST /* not a true state, never use this */
 } CURLMstate;
 
+/* we support 16 sockets per easy handle. Set the corresponding bit to what
+   action we should wait for */
+#define MAX_SOCKSPEREASYHANDLE 16
+#define GETSOCK_READABLE (0x00ff)
+#define GETSOCK_WRITABLE (0xff00)
+
+struct socketstate {
+  curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
+  long action; /* socket action bitmap */
+  long timeout[MAX_SOCKSPEREASYHANDLE];
+};
+
 struct Curl_one_easy {
   /* first, two fields for the linked list of these */
   struct Curl_one_easy *next;
@@ -90,6 +102,8 @@
                                will be deleted when this handle is removed
                                from the multi-handle */
   int msg_num; /* number of messages left in 'msg' to return */
+
+  struct socketstate sockstate; /* for the socket API magic */
 };
 
 
@@ -111,6 +125,10 @@
 
   int num_msgs; /* total amount of messages in the easy handles */
 
+  /* callback function and user data pointer for the *socket() API */
+  curl_socket_callback socket_cb;
+  void *socket_userp;
+
   /* Hostname cache */
   struct curl_hash *hostcache;
 };
@@ -170,6 +188,7 @@
 {
   struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
   struct Curl_one_easy *easy;
+  int i;
 
   /* First, make some basic checks that the CURLM handle is a good handle */
   if(!GOOD_MULTI_HANDLE(multi))
@@ -180,12 +199,12 @@
     return CURLM_BAD_EASY_HANDLE;
 
   /* Now, time to add an easy handle to the multi stack */
-  easy = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy));
+  easy = (struct Curl_one_easy *)calloc(sizeof(struct Curl_one_easy), 1);
   if(!easy)
     return CURLM_OUT_OF_MEMORY;
 
-  /* clean it all first (just to be sure) */
-  memset(easy, 0, sizeof(struct Curl_one_easy));
+  for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
+    easy->sockstate.socks[i] = CURL_SOCKET_BAD;
 
   /* set the easy handle */
   easy->easy_handle = easy_handle;
@@ -209,6 +228,9 @@
 
   Curl_easy_addmulti(easy_handle, multi_handle);
 
+  /* make the SessionHandle struct refer back to this struct */
+  easy->easy_handle->set.one_easy = easy;
+
   /* increase the node-counter */
   multi->num_easy++;
 
@@ -252,6 +274,8 @@
     if(easy->next)
       easy->next->prev = easy->prev;
 
+    easy->easy_handle->set.one_easy = NULL; /* detached */
+
     /* NOTE NOTE NOTE
        We do not touch the easy handle here! */
     if (easy->msg)
@@ -266,6 +290,65 @@
     return CURLM_BAD_EASY_HANDLE; /* twasn't found */
 }
 
+static int waitconnect_getsock(struct connectdata *conn,
+                               curl_socket_t *sock,
+                               int numsocks)
+{
+  if(!numsocks)
+    return GETSOCK_BLANK;
+
+  sock[0] = conn->sock[FIRSTSOCKET];
+  return GETSOCK_WRITESOCK(0);
+}
+
+static int domore_getsock(struct connectdata *conn,
+                          curl_socket_t *sock,
+                          int numsocks)
+{
+  if(!numsocks)
+    return GETSOCK_BLANK;
+
+  /* When in DO_MORE state, we could be either waiting for us
+     to connect to a remote site, or we could wait for that site
+     to connect to us. It makes a difference in the way: if we
+     connect to the site we wait for the socket to become writable, if
+     the site connects to us we wait for it to become readable */
+  sock[0] = conn->sock[SECONDARYSOCKET];
+
+  return GETSOCK_WRITESOCK(0);
+}
+
+/* returns bitmapped flags for this handle and its sockets */
+static int multi_getsock(struct Curl_one_easy *easy,
+                         curl_socket_t *socks, /* points to numsocks number
+                                                 of sockets */
+                         int numsocks)
+{
+  switch(easy->state) {
+  default:
+    return 0;
+
+  case CURLM_STATE_WAITRESOLVE:
+    return Curl_resolv_getsock(easy->easy_conn, socks, numsocks);
+
+  case CURLM_STATE_PROTOCONNECT:
+    return Curl_protocol_getsock(easy->easy_conn, socks, numsocks);
+
+  case CURLM_STATE_DOING:
+    return Curl_doing_getsock(easy->easy_conn, socks, numsocks);
+
+  case CURLM_STATE_WAITCONNECT:
+    return waitconnect_getsock(easy->easy_conn, socks, numsocks);
+
+  case CURLM_STATE_DO_MORE:
+    return domore_getsock(easy->easy_conn, socks, numsocks);
+
+  case CURLM_STATE_PERFORM:
+    return Curl_single_getsock(easy->easy_conn, socks, numsocks);
+  }
+
+}
+
 CURLMcode curl_multi_fdset(CURLM *multi_handle,
                            fd_set *read_fd_set, fd_set *write_fd_set,
                            fd_set *exc_fd_set, int *max_fd)
@@ -276,266 +359,250 @@
   struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
   struct Curl_one_easy *easy;
   int this_max_fd=-1;
+  curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
+  int bitmap;
+  int i;
+  (void)exc_fd_set; /* not used */
 
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
 
-  *max_fd = -1; /* so far none! */
-
   easy=multi->easy.next;
   while(easy) {
-    switch(easy->state) {
-    default:
-      break;
-    case CURLM_STATE_WAITRESOLVE:
-      /* waiting for a resolve to complete */
-      Curl_resolv_fdset(easy->easy_conn, read_fd_set, write_fd_set,
-                        &this_max_fd);
-      if(this_max_fd > *max_fd)
-        *max_fd = this_max_fd;
-      break;
+    bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE);
 
-    case CURLM_STATE_PROTOCONNECT:
-      Curl_protocol_fdset(easy->easy_conn, read_fd_set, write_fd_set,
-                          &this_max_fd);
-      if(this_max_fd > *max_fd)
-        *max_fd = this_max_fd;
-      break;
+    for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
+      curl_socket_t s = CURL_SOCKET_BAD;
 
-    case CURLM_STATE_DOING:
-      Curl_doing_fdset(easy->easy_conn, read_fd_set, write_fd_set,
-                       &this_max_fd);
-      if(this_max_fd > *max_fd)
-        *max_fd = this_max_fd;
-      break;
-
-    case CURLM_STATE_WAITCONNECT:
-    case CURLM_STATE_DO_MORE:
-      {
-        /* when we're waiting for a connect, we wait for the socket to
-           become writable */
-        struct connectdata *conn = easy->easy_conn;
-        curl_socket_t sockfd;
-
-        if(CURLM_STATE_WAITCONNECT == easy->state) {
-          sockfd = conn->sock[FIRSTSOCKET];
-          FD_SET(sockfd, write_fd_set);
-        }
-        else {
-          /* When in DO_MORE state, we could be either waiting for us
-             to connect to a remote site, or we could wait for that site
-             to connect to us. It makes a difference in the way: if we
-             connect to the site we wait for the socket to become writable, if
-             the site connects to us we wait for it to become readable */
-          sockfd = conn->sock[SECONDARYSOCKET];
-          FD_SET(sockfd, write_fd_set);
-        }
-
-        if((int)sockfd > *max_fd)
-          *max_fd = (int)sockfd;
+      if(bitmap & GETSOCK_READSOCK(i)) {
+        FD_SET(sockbunch[i], read_fd_set);
+        s = sockbunch[i];
+      }
+      if(bitmap & GETSOCK_WRITESOCK(i)) {
+        FD_SET(sockbunch[i], write_fd_set);
+        s = sockbunch[i];
+      }
+      if(s == CURL_SOCKET_BAD)
+        /* this socket is unused, break out of loop */
+        break;
+      else {
+        if(s > this_max_fd)
+          this_max_fd = s;
       }
-      break;
-    case CURLM_STATE_PERFORM:
-      /* This should have a set of file descriptors for us to set.  */
-      /* after the transfer is done, go DONE */
-
-      Curl_single_fdset(easy->easy_conn,
-                        read_fd_set, write_fd_set,
-                        exc_fd_set, &this_max_fd);
-
-      /* remember the maximum file descriptor */
-      if(this_max_fd > *max_fd)
-        *max_fd = this_max_fd;
-
-      break;
     }
+
     easy = easy->next; /* check next handle */
   }
 
+  *max_fd = this_max_fd;
+
   return CURLM_OK;
 }
 
-CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
+static CURLMcode multi_runsingle(struct Curl_multi *multi,
+                                 struct Curl_one_easy *easy,
+                                 int *running_handles)
 {
-  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
-  struct Curl_one_easy *easy;
-  bool done;
-  CURLMcode result=CURLM_OK;
   struct Curl_message *msg = NULL;
   bool connected;
   bool async;
   bool protocol_connect;
   bool dophase_done;
+  bool done;
+  CURLMcode result = CURLM_OK;
 
-  *running_handles = 0; /* bump this once for every living handle */
-
-  if(!GOOD_MULTI_HANDLE(multi))
-    return CURLM_BAD_HANDLE;
-
-  easy=multi->easy.next;
-  while(easy) {
-    do {
-      if (CURLM_STATE_WAITCONNECT <= easy->state &&
-          easy->state <= CURLM_STATE_DO &&
-          easy->easy_handle->change.url_changed) {
-        char *gotourl;
-        Curl_posttransfer(easy->easy_handle);
-
-        easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
-        if(CURLE_OK == easy->result) {
-          gotourl = strdup(easy->easy_handle->change.url);
-          if(gotourl) {
-            easy->easy_handle->change.url_changed = FALSE;
-            easy->result = Curl_follow(easy->easy_handle, gotourl, FALSE);
-            if(CURLE_OK == easy->result)
-              multistate(easy, CURLM_STATE_CONNECT);
-            else
-              free(gotourl);
-          }
-          else {
-            easy->result = CURLE_OUT_OF_MEMORY;
-            multistate(easy, CURLM_STATE_COMPLETED);
-            break;
-          }
+  do {
+    if (CURLM_STATE_WAITCONNECT <= easy->state &&
+        easy->state <= CURLM_STATE_DO &&
+        easy->easy_handle->change.url_changed) {
+      char *gotourl;
+      Curl_posttransfer(easy->easy_handle);
+
+      easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
+      if(CURLE_OK == easy->result) {
+        gotourl = strdup(easy->easy_handle->change.url);
+        if(gotourl) {
+          easy->easy_handle->change.url_changed = FALSE;
+          easy->result = Curl_follow(easy->easy_handle, gotourl, FALSE);
+          if(CURLE_OK == easy->result)
+            multistate(easy, CURLM_STATE_CONNECT);
+          else
+            free(gotourl);
         }
-      }
-
-      easy->easy_handle->change.url_changed = FALSE;
-
-      switch(easy->state) {
-      case CURLM_STATE_INIT:
-        /* init this transfer. */
-        easy->result=Curl_pretransfer(easy->easy_handle);
-
-        if(CURLE_OK == easy->result) {
-          /* after init, go CONNECT */
-          multistate(easy, CURLM_STATE_CONNECT);
-          result = CURLM_CALL_MULTI_PERFORM;
-
-          easy->easy_handle->state.used_interface = Curl_if_multi;
+        else {
+          easy->result = CURLE_OUT_OF_MEMORY;
+          multistate(easy, CURLM_STATE_COMPLETED);
+          break;
         }
-        break;
+      }
+    }
 
-      case CURLM_STATE_CONNECT:
-        /* Connect. We get a connection identifier filled in. */
-        Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
-        easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn,
-                                    &async, &protocol_connect);
+    easy->easy_handle->change.url_changed = FALSE;
 
-        if(CURLE_OK == easy->result) {
-          if(async)
-            /* We're now waiting for an asynchronous name lookup */
-            multistate(easy, CURLM_STATE_WAITRESOLVE);
-          else {
-            /* after the connect has been sent off, go WAITCONNECT unless the
-               protocol connect is already done and we can go directly to
-               DO! */
-            result = CURLM_CALL_MULTI_PERFORM;
+    switch(easy->state) {
+    case CURLM_STATE_INIT:
+      /* init this transfer. */
+      easy->result=Curl_pretransfer(easy->easy_handle);
+
+      if(CURLE_OK == easy->result) {
+        /* after init, go CONNECT */
+        multistate(easy, CURLM_STATE_CONNECT);
+        result = CURLM_CALL_MULTI_PERFORM;
 
-            if(protocol_connect)
-              multistate(easy, CURLM_STATE_DO);
-            else
-              multistate(easy, CURLM_STATE_WAITCONNECT);
-          }
-        }
-        break;
+        easy->easy_handle->state.used_interface = Curl_if_multi;
+      }
+      break;
 
-      case CURLM_STATE_WAITRESOLVE:
-        /* awaiting an asynch name resolve to complete */
-      {
-        struct Curl_dns_entry *dns = NULL;
-
-        /* check if we have the name resolved by now */
-        easy->result = Curl_is_resolved(easy->easy_conn, &dns);
-
-        if(dns) {
-          /* Perform the next step in the connection phase, and then move on
-             to the WAITCONNECT state */
-          easy->result = Curl_async_resolved(easy->easy_conn,
-                                             &protocol_connect);
+    case CURLM_STATE_CONNECT:
+      /* Connect. We get a connection identifier filled in. */
+      Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
+      easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn,
+                                  &async, &protocol_connect);
+
+      if(CURLE_OK == easy->result) {
+        if(async)
+          /* We're now waiting for an asynchronous name lookup */
+          multistate(easy, CURLM_STATE_WAITRESOLVE);
+        else {
+          /* after the connect has been sent off, go WAITCONNECT unless the
+             protocol connect is already done and we can go directly to
+             DO! */
+          result = CURLM_CALL_MULTI_PERFORM;
 
-          if(CURLE_OK != easy->result)
-            /* if Curl_async_resolved() returns failure, the connection struct
-               is already freed and gone */
-            easy->easy_conn = NULL;           /* no more connection */
-          else {
-            /* FIX: what if protocol_connect is TRUE here?! */
+          if(protocol_connect)
+            multistate(easy, CURLM_STATE_DO);
+          else
             multistate(easy, CURLM_STATE_WAITCONNECT);
-          }
-        }
-
-        if(CURLE_OK != easy->result) {
-          /* failure detected */
-          Curl_disconnect(easy->easy_conn); /* disconnect properly */
-          easy->easy_conn = NULL;           /* no more connection */
-          break;
         }
       }
       break;
 
-      case CURLM_STATE_WAITCONNECT:
-        /* awaiting a completion of an asynch connect */
-        easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET,
-                                         &connected);
-        if(connected)
-          easy->result = Curl_protocol_connect(easy->easy_conn,
-                                               &protocol_connect);
-
-        if(CURLE_OK != easy->result) {
-          /* failure detected */
-          Curl_disconnect(easy->easy_conn); /* close the connection */
+    case CURLM_STATE_WAITRESOLVE:
+      /* awaiting an asynch name resolve to complete */
+    {
+      struct Curl_dns_entry *dns = NULL;
+
+      /* check if we have the name resolved by now */
+      easy->result = Curl_is_resolved(easy->easy_conn, &dns);
+
+      if(dns) {
+        /* Perform the next step in the connection phase, and then move on
+           to the WAITCONNECT state */
+        easy->result = Curl_async_resolved(easy->easy_conn,
+                                           &protocol_connect);
+
+        if(CURLE_OK != easy->result)
+          /* if Curl_async_resolved() returns failure, the connection struct
+             is already freed and gone */
           easy->easy_conn = NULL;           /* no more connection */
-          break;
+        else {
+          /* FIX: what if protocol_connect is TRUE here?! */
+          multistate(easy, CURLM_STATE_WAITCONNECT);
         }
+      }
 
-        if(connected) {
-          if(!protocol_connect) {
-            /* We have a TCP connection, but 'protocol_connect' may be false
-               and then we continue to 'STATE_PROTOCONNECT'. If protocol
-               connect is TRUE, we move on to STATE_DO. */
-            multistate(easy, CURLM_STATE_PROTOCONNECT);
-          }
-          else {
-            /* after the connect has completed, go DO */
-            multistate(easy, CURLM_STATE_DO);
-            result = CURLM_CALL_MULTI_PERFORM;
-          }
-        }
+      if(CURLE_OK != easy->result) {
+        /* failure detected */
+        Curl_disconnect(easy->easy_conn); /* disconnect properly */
+        easy->easy_conn = NULL;           /* no more connection */
+        break;
+      }
+    }
+    break;
+
+    case CURLM_STATE_WAITCONNECT:
+      /* awaiting a completion of an asynch connect */
+      easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET,
+                                       &connected);
+      if(connected)
+        easy->result = Curl_protocol_connect(easy->easy_conn,
+                                             &protocol_connect);
+
+      if(CURLE_OK != easy->result) {
+        /* failure detected */
+        Curl_disconnect(easy->easy_conn); /* close the connection */
+        easy->easy_conn = NULL;           /* no more connection */
         break;
+      }
 
-      case CURLM_STATE_PROTOCONNECT:
-        /* protocol-specific connect phase */
-        easy->result = Curl_protocol_connecting(easy->easy_conn,
-                                                &protocol_connect);
-        if(protocol_connect) {
+      if(connected) {
+        if(!protocol_connect) {
+          /* We have a TCP connection, but 'protocol_connect' may be false
+             and then we continue to 'STATE_PROTOCONNECT'. If protocol
+             connect is TRUE, we move on to STATE_DO. */
+          multistate(easy, CURLM_STATE_PROTOCONNECT);
+        }
+        else {
           /* after the connect has completed, go DO */
           multistate(easy, CURLM_STATE_DO);
           result = CURLM_CALL_MULTI_PERFORM;
         }
-        else if(easy->result) {
-          /* failure detected */
-          Curl_posttransfer(easy->easy_handle);
-          Curl_done(&easy->easy_conn, easy->result);
-          Curl_disconnect(easy->easy_conn); /* close the connection */
-          easy->easy_conn = NULL;           /* no more connection */
-        }
-        break;
-
-      case CURLM_STATE_DO:
-        /* Perform the protocol's DO action */
-        easy->result = Curl_do(&easy->easy_conn, &dophase_done);
+      }
+      break;
 
-        if(CURLE_OK == easy->result) {
+    case CURLM_STATE_PROTOCONNECT:
+      /* protocol-specific connect phase */
+      easy->result = Curl_protocol_connecting(easy->easy_conn,
+                                              &protocol_connect);
+      if(protocol_connect) {
+        /* after the connect has completed, go DO */
+        multistate(easy, CURLM_STATE_DO);
+        result = CURLM_CALL_MULTI_PERFORM;
+      }
+      else if(easy->result) {
+        /* failure detected */
+        Curl_posttransfer(easy->easy_handle);
+        Curl_done(&easy->easy_conn, easy->result);
+        Curl_disconnect(easy->easy_conn); /* close the connection */
+        easy->easy_conn = NULL;           /* no more connection */
+      }
+      break;
 
-          if(!dophase_done) {
-            /* DO was not completed in one function call, we must continue
-               DOING... */
-            multistate(easy, CURLM_STATE_DOING);
-            result = CURLM_OK;
+    case CURLM_STATE_DO:
+      /* Perform the protocol's DO action */
+      easy->result = Curl_do(&easy->easy_conn, &dophase_done);
+
+      if(CURLE_OK == easy->result) {
+
+        if(!dophase_done) {
+          /* DO was not completed in one function call, we must continue
+             DOING... */
+          multistate(easy, CURLM_STATE_DOING);
+          result = CURLM_OK;
+        }
+
+        /* after DO, go PERFORM... or DO_MORE */
+        else if(easy->easy_conn->bits.do_more) {
+          /* we're supposed to do more, but we need to sit down, relax
+             and wait a little while first */
+          multistate(easy, CURLM_STATE_DO_MORE);
+          result = CURLM_OK;
+        }
+        else {
+          /* we're done with the DO, now PERFORM */
+          easy->result = Curl_readwrite_init(easy->easy_conn);
+          if(CURLE_OK == easy->result) {
+            multistate(easy, CURLM_STATE_PERFORM);
+            result = CURLM_CALL_MULTI_PERFORM;
           }
+        }
+      }
+      else {
+        /* failure detected */
+        Curl_posttransfer(easy->easy_handle);
+        Curl_done(&easy->easy_conn, easy->result);
+        Curl_disconnect(easy->easy_conn); /* close the connection */
+        easy->easy_conn = NULL;           /* no more connection */
+      }
+      break;
 
+    case CURLM_STATE_DOING:
+      /* we continue DOING until the DO phase is complete */
+      easy->result = Curl_protocol_doing(easy->easy_conn, &dophase_done);
+      if(CURLE_OK == easy->result) {
+        if(dophase_done) {
           /* after DO, go PERFORM... or DO_MORE */
-          else if(easy->easy_conn->bits.do_more) {
+          if(easy->easy_conn->bits.do_more) {
             /* we're supposed to do more, but we need to sit down, relax
                and wait a little while first */
             multistate(easy, CURLM_STATE_DO_MORE);
@@ -549,178 +616,170 @@
               result = CURLM_CALL_MULTI_PERFORM;
             }
           }
-        }
-        else {
-          /* failure detected */
-          Curl_posttransfer(easy->easy_handle);
-          Curl_done(&easy->easy_conn, easy->result);
-          Curl_disconnect(easy->easy_conn); /* close the connection */
-          easy->easy_conn = NULL;           /* no more connection */
-        }
-        break;
+        } /* dophase_done */
+      }
+      else {
+        /* failure detected */
+        Curl_posttransfer(easy->easy_handle);
+        Curl_done(&easy->easy_conn, easy->result);
+        Curl_disconnect(easy->easy_conn); /* close the connection */
+        easy->easy_conn = NULL;           /* no more connection */
+      }
+      break;
+
+    case CURLM_STATE_DO_MORE:
+      /* Ready to do more? */
+      easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET,
+                                       &connected);
+      if(connected) {
+        /*
+         * When we are connected, DO MORE and then go PERFORM
+         */
+        easy->result = Curl_do_more(easy->easy_conn);
+
+        if(CURLE_OK == easy->result)
+          easy->result = Curl_readwrite_init(easy->easy_conn);
 
-      case CURLM_STATE_DOING:
-        /* we continue DOING until the DO phase is complete */
-        easy->result = Curl_protocol_doing(easy->easy_conn, &dophase_done);
         if(CURLE_OK == easy->result) {
-          if(dophase_done) {
-            /* after DO, go PERFORM... or DO_MORE */
-            if(easy->easy_conn->bits.do_more) {
-              /* we're supposed to do more, but we need to sit down, relax
-                 and wait a little while first */
-              multistate(easy, CURLM_STATE_DO_MORE);
-              result = CURLM_OK;
-            }
-            else {
-              /* we're done with the DO, now PERFORM */
-              easy->result = Curl_readwrite_init(easy->easy_conn);
-              if(CURLE_OK == easy->result) {
-                multistate(easy, CURLM_STATE_PERFORM);
-                result = CURLM_CALL_MULTI_PERFORM;
-              }
-            }
-          } /* dophase_done */
+          multistate(easy, CURLM_STATE_PERFORM);
+          result = CURLM_CALL_MULTI_PERFORM;
         }
-        else {
-          /* failure detected */
-          Curl_posttransfer(easy->easy_handle);
-          Curl_done(&easy->easy_conn, easy->result);
-          Curl_disconnect(easy->easy_conn); /* close the connection */
-          easy->easy_conn = NULL;           /* no more connection */
+      }
+      break;
+
+    case CURLM_STATE_PERFORM:
+      /* read/write data if it is ready to do so */
+      easy->result = Curl_readwrite(easy->easy_conn, &done);
+
+      if(easy->result)  {
+        /* The transfer phase returned error, we mark the connection to get
+         * closed to prevent being re-used. This is becasue we can't
+         * possibly know if the connection is in a good shape or not now. */
+        easy->easy_conn->bits.close = TRUE;
+
+        if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
+          /* if we failed anywhere, we must clean up the secondary socket if
+             it was used */
+          sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
+          easy->easy_conn->sock[SECONDARYSOCKET]=-1;
         }
-        break;
+        Curl_posttransfer(easy->easy_handle);
+        Curl_done(&easy->easy_conn, easy->result);
+      }
 
-      case CURLM_STATE_DO_MORE:
-        /* Ready to do more? */
-        easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET,
-                                         &connected);
-        if(connected) {
-          /*
-           * When we are connected, DO MORE and then go PERFORM
-           */
-          easy->result = Curl_do_more(easy->easy_conn);
+      else if(TRUE == done) {
+        char *newurl;
+        bool retry = Curl_retry_request(easy->easy_conn, &newurl);
 
-          if(CURLE_OK == easy->result)
-            easy->result = Curl_readwrite_init(easy->easy_conn);
+        /* call this even if the readwrite function returned error */
+        Curl_posttransfer(easy->easy_handle);
 
+        /* When we follow redirects, must to go back to the CONNECT state */
+        if(easy->easy_conn->newurl || retry) {
+          if(!retry) {
+            /* if the URL is a follow-location and not just a retried request
+               then figure out the URL here */
+            newurl = easy->easy_conn->newurl;
+            easy->easy_conn->newurl = NULL;
+          }
+          easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
+          if(easy->result == CURLE_OK)
+            easy->result = Curl_follow(easy->easy_handle, newurl, retry);
           if(CURLE_OK == easy->result) {
-            multistate(easy, CURLM_STATE_PERFORM);
+            multistate(easy, CURLM_STATE_CONNECT);
             result = CURLM_CALL_MULTI_PERFORM;
           }
+          else
+            /* Since we "took it", we are in charge of freeing this on
+               failure */
+            free(newurl);
         }
-        break;
-
-      case CURLM_STATE_PERFORM:
-        /* read/write data if it is ready to do so */
-        easy->result = Curl_readwrite(easy->easy_conn, &done);
-
-        if(easy->result)  {
-          /* The transfer phase returned error, we mark the connection to get
-           * closed to prevent being re-used. This is becasue we can't
-           * possibly know if the connection is in a good shape or not now. */
-          easy->easy_conn->bits.close = TRUE;
-
-          if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
-            /* if we failed anywhere, we must clean up the secondary socket if
-               it was used */
-            sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
-            easy->easy_conn->sock[SECONDARYSOCKET]=-1;
-          }
-          Curl_posttransfer(easy->easy_handle);
-          Curl_done(&easy->easy_conn, easy->result);
+        else {
+          /* after the transfer is done, go DONE */
+          multistate(easy, CURLM_STATE_DONE);
+          result = CURLM_CALL_MULTI_PERFORM;
         }
+      }
+      break;
+    case CURLM_STATE_DONE:
+      /* post-transfer command */
+      easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
+
+      /* after we have DONE what we're supposed to do, go COMPLETED, and
+         it doesn't matter what the Curl_done() returned! */
+      multistate(easy, CURLM_STATE_COMPLETED);
+      break;
 
-        else if(TRUE == done) {
-          char *newurl;
-          bool retry = Curl_retry_request(easy->easy_conn, &newurl);
-
-          /* call this even if the readwrite function returned error */
-          Curl_posttransfer(easy->easy_handle);
-
-          /* When we follow redirects, must to go back to the CONNECT state */
-          if(easy->easy_conn->newurl || retry) {
-            if(!retry) {
-              /* if the URL is a follow-location and not just a retried request
-                 then figure out the URL here */
-              newurl = easy->easy_conn->newurl;
-              easy->easy_conn->newurl = NULL;
-            }
-            easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
-            if(easy->result == CURLE_OK)
-              easy->result = Curl_follow(easy->easy_handle, newurl, retry);
-            if(CURLE_OK == easy->result) {
-              multistate(easy, CURLM_STATE_CONNECT);
-              result = CURLM_CALL_MULTI_PERFORM;
-            }
-            else
-              /* Since we "took it", we are in charge of freeing this on
-                 failure */
-              free(newurl);
-          }
-          else {
-            /* after the transfer is done, go DONE */
-            multistate(easy, CURLM_STATE_DONE);
-            result = CURLM_CALL_MULTI_PERFORM;
-          }
-        }
-        break;
-      case CURLM_STATE_DONE:
-        /* post-transfer command */
-        easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
+    case CURLM_STATE_COMPLETED:
+      /* this is a completed transfer, it is likely to still be connected */
+
+      /* This node should be delinked from the list now and we should post
+         an information message that we are complete. */
+      break;
+    default:
+      return CURLM_INTERNAL_ERROR;
+    }
 
-        /* after we have DONE what we're supposed to do, go COMPLETED, and
-           it doesn't matter what the Curl_done() returned! */
+    if(CURLM_STATE_COMPLETED != easy->state) {
+      if(CURLE_OK != easy->result) {
+        /*
+         * If an error was returned, and we aren't in completed state now,
+         * then we go to completed and consider this transfer aborted.  */
         multistate(easy, CURLM_STATE_COMPLETED);
-        break;
+      }
+      else
+        /* this one still lives! */
+        (*running_handles)++;
+    }
 
-      case CURLM_STATE_COMPLETED:
-        /* this is a completed transfer, it is likely to still be connected */
+  } while (easy->easy_handle->change.url_changed);
 
-        /* This node should be delinked from the list now and we should post
-           an information message that we are complete. */
-        break;
-      default:
-        return CURLM_INTERNAL_ERROR;
-      }
+  if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
+    /* clear out the usage of the shared DNS cache */
+    easy->easy_handle->hostcache = NULL;
 
-      if(CURLM_STATE_COMPLETED != easy->state) {
-        if(CURLE_OK != easy->result) {
-          /*
-           * If an error was returned, and we aren't in completed state now,
-           * then we go to completed and consider this transfer aborted.  */
-          multistate(easy, CURLM_STATE_COMPLETED);
-        }
-        else
-          /* this one still lives! */
-          (*running_handles)++;
-      }
+    /* now add a node to the Curl_message linked list with this info */
+    msg = (struct Curl_message *)malloc(sizeof(struct Curl_message));
 
-    } while (easy->easy_handle->change.url_changed);
+    if(!msg)
+      return CURLM_OUT_OF_MEMORY;
 
-    if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
-      /* clear out the usage of the shared DNS cache */
-      easy->easy_handle->hostcache = NULL;
+    msg->extmsg.msg = CURLMSG_DONE;
+    msg->extmsg.easy_handle = easy->easy_handle;
+    msg->extmsg.data.result = easy->result;
+    msg->next=NULL;
 
-      /* now add a node to the Curl_message linked list with this info */
-      msg = (struct Curl_message *)malloc(sizeof(struct Curl_message));
+    easy->msg = msg;
+    easy->msg_num = 1; /* there is one unread message here */
 
-      if(!msg)
-        return CURLM_OUT_OF_MEMORY;
+    multi->num_msgs++; /* increase message counter */
+  }
 
-      msg->extmsg.msg = CURLMSG_DONE;
-      msg->extmsg.easy_handle = easy->easy_handle;
-      msg->extmsg.data.result = easy->result;
-      msg->next=NULL;
+  return result;
+}
 
-      easy->msg = msg;
-      easy->msg_num = 1; /* there is one unread message here */
 
-      multi->num_msgs++; /* increase message counter */
-    }
+CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
+{
+  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
+  struct Curl_one_easy *easy;
+  CURLMcode returncode=CURLM_OK;
+
+  *running_handles = 0; /* bump this once for every living handle */
+
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  easy=multi->easy.next;
+  while(easy) {
+    CURLMcode result = multi_runsingle(multi, easy, running_handles);
+    if(result)
+      returncode = result;
+
     easy = easy->next; /* operate on next handle */
   }
 
-  return result;
+  return returncode;
 }
 
 /* This is called when an easy handle is cleanup'ed that is part of a multi
@@ -793,3 +852,176 @@
   else
     return NULL;
 }
+
+#ifdef HAVE_CURL_MULTI_SOCKET
+static CURL *multi_sock2easy(struct Curl_multi *multi,
+                             curl_socket_t s)
+{
+  /* Use the hash table and lookup what easy handle the given socket
+     belongs to */
+  (void)multi;
+  (void)s;
+  return NULL; /* TODO */
+}
+
+/*
+ * Check what sockets we deal with and their "action state" and if we have a
+ * difference from last time we call the callback accordingly.
+ */
+
+static void singlesocket(struct Curl_multi *multi,
+                         struct Curl_one_easy *easy)
+{
+  struct socketstate current;
+  int i;
+
+  memset(&current, 0, sizeof(current));
+  for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
+    current.socks[i] = CURL_SOCKET_BAD;
+
+  /* first fill in the 'current' struct with the state as it is now */
+  current.action = multi_getsock(easy, current.socks, MAX_SOCKSPEREASYHANDLE);
+
+  /* TODO: take timeouts into consideration! */
+
+  /* I think I'll just make sure that libcurl keeps a
+   * I-want-to-get-called-no-later-than-this time ("last time") for each easy
+   * handle, and then the callback will be called with a timeout set to ("last
+   * time" - now) for all sockets in the easy handle's care.
+   */
+
+  /* when filled in, we compare with the previous round's state */
+
+  if(memcmp(&current, &easy->sockstate, sizeof(struct socketstate))) {
+    /* difference, call the callback once for every socket change ! */
+    for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) {
+      int action;
+      curl_socket_t s = current.socks[i];
+
+      /* Ok, this approach is probably too naive and simple-minded but
+         it might work for a start */
+
+      if((easy->sockstate.socks[i] == CURL_SOCKET_BAD) &&
+         (s == CURL_SOCKET_BAD)) {
+        /* no socket now and there was no socket before */
+        break;
+      }
+
+      if(s == CURL_SOCKET_BAD) {
+        /* socket is removed */
+        action = CURL_POLL_REMOVE;
+        s = easy->sockstate.socks[i]; /* this is the removed socket */
+      }
+      else {
+        if(easy->sockstate.socks[i] == s) {
+          /* still the same socket, but are we waiting for the same actions? */
+          unsigned int curr;
+          unsigned int prev;
+
+          /* the current read/write bits for this particular socket */
+          curr = current.action & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i));
+
+          /* the previous read/write bits for this particular socket */
+          prev = easy->sockstate.action &
+            (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i));
+
+          if(curr == prev)
+            continue;
+        }
+
+        action = (current.action & GETSOCK_READSOCK(i)?CURL_POLL_IN:0) |
+          (current.action & GETSOCK_WRITESOCK(i)?CURL_POLL_OUT:0);
+      }
+
+      /* call the callback with this new info */
+      if(multi->socket_cb) {
+        multi->socket_cb(easy->easy_handle,
+                         s,
+                         action,
+                         100, /* TODO: time-out */
+                         multi->socket_userp);
+      }
+    }
+    /* copy the current state to the storage area */
+    memcpy(&easy->sockstate, &current, sizeof(struct socketstate));
+  }
+  else {
+    /* identical, nothing new happened so we don't do any callbacks */
+  }
+
+}
+
+static CURLMcode multi_socket(struct Curl_multi *multi,
+                              bool checkall,
+                              curl_socket_t s,
+                              CURL *easy,
+                              curl_socket_callback callback,
+                              void *userp)
+{
+  CURLMcode result = CURLM_OK;
+  int running_handles;
+  struct SessionHandle *data = easy;
+
+  multi->socket_cb = callback;
+  multi->socket_userp = userp;
+
+  if(checkall) {
+    struct Curl_one_easy *easyp;
+    result = curl_multi_perform(multi, &running_handles);
+
+    /* walk through each easy handle and do the socket state change magic
+       and callbacks */
+    easyp=multi->easy.next;
+    while(easyp) {
+      singlesocket(multi, easyp);
+      easyp = easyp->next;
+    }
+  }
+  else {
+    /* a single easy handle in a multi stack */
+    if(easy == CURL_EASY_NONE) {
+      /* oh darn, no easy handle so we only have the socket to go on */
+      data = multi_sock2easy(multi, s);
+      if(!data)
+        /* unmatched socket, major problemo! */
+        return CURLM_BAD_HANDLE; /* better return code? */
+    }
+
+    result = multi_runsingle(multi, data->set.one_easy, &running_handles);
+
+    if(result == CURLM_OK)
+      /* get the socket(s) and check if the state has been changed since
+         last */
+      singlesocket(multi, data->set.one_easy);
+  }
+
+  return result;
+}
+
+CURLMcode curl_multi_socket(CURLM *multi_handle,
+                            curl_socket_t s,
+                            CURL *easy,
+                            curl_socket_callback callback,
+                            void *userp)
+{
+  return multi_socket((struct Curl_multi *)multi_handle,
+                      FALSE, s, easy, callback, userp);
+}
+
+CURLMcode curl_multi_socket_all(CURLM *multi_handle,
+                                curl_socket_callback callback,
+                                void *userp)
+{
+  return multi_socket((struct Curl_multi *)multi_handle,
+                      TRUE, CURL_SOCKET_BAD, NULL, callback, userp);
+}
+
+CURLMcode curl_multi_timeout(CURLM *multi_handle,
+                             long *timeout_ms)
+{
+  (void)multi_handle;
+  *timeout_ms = 1000; /* silly for now */
+  return CURLM_OK;
+}
+
+#endif
Index: lib/multiif.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/multiif.h,v
retrieving revision 1.3
diff -u -r1.3 multiif.h
--- lib/multiif.h	31 Mar 2005 07:02:03 -0000	1.3
+++ lib/multiif.h	22 Dec 2005 15:36:58 -0000
@@ -27,4 +27,16 @@
  * Prototypes for library-wide functions provided by multi.c
  */
 void Curl_multi_rmeasy(void *multi, CURL *data);
+
+/* the write bits start at bit 16 for the *getsock() bitmap */
+#define GETSOCK_WRITEBITSTART 16
+
+#define GETSOCK_BLANK 0 /* no bits set */
+
+/* set the bit for the given sock number to make the bitmap for writable */
+#define GETSOCK_WRITESOCK(x) (1 << (GETSOCK_WRITEBITSTART + (x)))
+
+/* set the bit for the given sock number to make the bitmap for readable */
+#define GETSOCK_READSOCK(x) (1 << (x))
+
 #endif /* __MULTIIF_H */
Index: lib/setup.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/setup.h,v
retrieving revision 1.103
diff -u -r1.103 setup.h
--- lib/setup.h	20 Dec 2005 22:20:04 -0000	1.103
+++ lib/setup.h	22 Dec 2005 15:36:58 -0000
@@ -279,6 +279,7 @@
 
 #endif /* WIN32 */
 
+#ifndef curl_socket_typedef
 /* now typedef our socket type */
 #ifdef WIN32
 typedef SOCKET curl_socket_t;
@@ -287,6 +288,9 @@
 typedef int curl_socket_t;
 #define CURL_SOCKET_BAD -1
 #endif
+#define curl_socket_typedef
+#endif /* curl_socket_typedef */
+
 
 #if defined(ENABLE_IPV6) && defined(USE_ARES)
 #error "ares does not yet support IPv6. Disable IPv6 or ares and rebuild"
Index: lib/transfer.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/transfer.c,v
retrieving revision 1.290
diff -u -r1.290 transfer.c
--- lib/transfer.c	24 Nov 2005 10:22:47 -0000	1.290
+++ lib/transfer.c	22 Dec 2005 15:36:58 -0000
@@ -101,6 +101,7 @@
 #include "share.h"
 #include "memory.h"
 #include "select.h"
+#include "multiif.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -306,7 +307,7 @@
           data->set.buffer_size:BUFSIZE;
 
         /* receive data from the network! */
-        int readrc = Curl_read(conn, conn->sockfd, k->buf, buffersize, &nread);
+        int readrc = Curl_read(conn, conn->sockfd, k->buf, 1/*buffersize*/, &nread);
 
         /* subzero, this would've blocked */
         if(0>readrc)
@@ -1508,34 +1509,42 @@
 }
 
 /*
- * Curl_single_fdset() gets called by the multi interface code when the app
- * has requested to get the fd_sets for the current connection. This function
+ * Curl_single_getsock() gets called by the multi interface code when the app
+ * has requested to get the sockets for the current connection. This function
  * will then be called once for every connection that the multi interface
  * keeps track of. This function will only be called for connections that are
  * in the proper state to have this information available.
  */
-void Curl_single_fdset(struct connectdata *conn,
-                       fd_set *read_fd_set,
-                       fd_set *write_fd_set,
-                       fd_set *exc_fd_set,
-                       int *max_fd)
+int Curl_single_getsock(struct connectdata *conn,
+                        curl_socket_t *sock, /* points to numsocks number
+                                                of sockets */
+                        int numsocks)
 {
-  *max_fd = -1; /* init */
+  int bitmap = GETSOCK_BLANK;
+  int index = 0;
+
+  if(numsocks < 2)
+    /* simple check but we might need two slots */
+    return GETSOCK_BLANK;
+
   if(conn->keep.keepon & KEEP_READ) {
-    FD_SET(conn->sockfd, read_fd_set);
-    *max_fd = (int)conn->sockfd;
+    bitmap |= GETSOCK_READSOCK(index);
+    sock[index] = conn->sockfd;
   }
   if(conn->keep.keepon & KEEP_WRITE) {
-    FD_SET(conn->writesockfd, write_fd_set);
 
-    /* since sockets are curl_socket_t nowadays, we typecast it to int here
-       to compare it nicely */
-    if((int)conn->writesockfd > *max_fd)
-      *max_fd = (int)conn->writesockfd;
-  }
-  /* we don't use exceptions, only touch that one to prevent compiler
-     warnings! */
-  *exc_fd_set = *exc_fd_set;
+    if((conn->sockfd != conn->writesockfd) &&
+       (conn->keep.keepon & KEEP_READ)) {
+      /* only if they are not the same socket and we had a readable one,
+         we increase index */
+      index++;
+      sock[index] = conn->writesockfd;
+    }
+
+    bitmap |= GETSOCK_WRITESOCK(index);
+  }
+
+  return bitmap;
 }
 
 
Index: lib/transfer.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/transfer.h,v
retrieving revision 1.24
diff -u -r1.24 transfer.h
--- lib/transfer.h	31 Mar 2005 07:02:03 -0000	1.24
+++ lib/transfer.h	22 Dec 2005 15:36:58 -0000
@@ -28,11 +28,9 @@
 CURLcode Curl_posttransfer(struct SessionHandle *data);
 CURLcode Curl_follow(struct SessionHandle *data, char *newurl, bool retry);
 CURLcode Curl_readwrite(struct connectdata *conn, bool *done);
-void Curl_single_fdset(struct connectdata *conn,
-                       fd_set *read_fd_set,
-                       fd_set *write_fd_set,
-                       fd_set *exc_fd_set,
-                       int *max_fd);
+int Curl_single_getsock(struct connectdata *conn,
+                        curl_socket_t *socks,
+                        int numsocks);
 CURLcode Curl_readwrite_init(struct connectdata *conn);
 CURLcode Curl_readrewind(struct connectdata *conn);
 CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp);
Index: lib/url.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/url.c,v
retrieving revision 1.486
diff -u -r1.486 url.c
--- lib/url.c	20 Dec 2005 22:46:12 -0000	1.486
+++ lib/url.c	22 Dec 2005 15:37:00 -0000
@@ -2054,26 +2054,22 @@
         conn->ip_addr_str, conn->port);
 }
 
-CURLcode Curl_protocol_fdset(struct connectdata *conn,
-                             fd_set *read_fd_set,
-                             fd_set *write_fd_set,
-                             int *max_fdp)
+int Curl_protocol_getsock(struct connectdata *conn,
+                          curl_socket_t *socks,
+                          int numsocks)
 {
-  CURLcode res = CURLE_OK;
-  if(conn->curl_proto_fdset)
-    res = conn->curl_proto_fdset(conn, read_fd_set, write_fd_set, max_fdp);
-  return res;
+  if(conn->curl_proto_getsock)
+    return conn->curl_proto_getsock(conn, socks, numsocks);
+  return GETSOCK_BLANK;
 }
 
-CURLcode Curl_doing_fdset(struct connectdata *conn,
-                          fd_set *read_fd_set,
-                          fd_set *write_fd_set,
-                          int *max_fdp)
+int Curl_doing_getsock(struct connectdata *conn,
+                       curl_socket_t *socks,
+                       int numsocks)
 {
-  CURLcode res = CURLE_OK;
-  if(conn && conn->curl_doing_fdset)
-    res = conn->curl_doing_fdset(conn, read_fd_set, write_fd_set, max_fdp);
-  return res;
+  if(conn && conn->curl_doing_getsock)
+    return conn->curl_doing_getsock(conn, socks, numsocks);
+  return GETSOCK_BLANK;
 }
 
 /*
@@ -2842,8 +2838,8 @@
       conn->curl_connect = Curl_ftp_connect;
       conn->curl_connecting = Curl_ftp_multi_statemach;
       conn->curl_doing = Curl_ftp_doing;
-      conn->curl_proto_fdset = Curl_ftp_fdset;
-      conn->curl_doing_fdset = Curl_ftp_fdset;
+      conn->curl_proto_getsock = Curl_ftp_getsock;
+      conn->curl_doing_getsock = Curl_ftp_getsock;
       conn->curl_disconnect = Curl_ftp_disconnect;
     }
 
Index: lib/url.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/url.h,v
retrieving revision 1.23
diff -u -r1.23 url.h
--- lib/url.h	17 Jul 2005 12:44:11 -0000	1.23
+++ lib/url.h	22 Dec 2005 15:37:00 -0000
@@ -45,6 +45,16 @@
 CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done);
 CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done);
 void Curl_safefree(void *ptr);
+
+
+int Curl_protocol_getsock(struct connectdata *conn,
+                          curl_socket_t *socks,
+                          int numsocks);
+int Curl_doing_getsock(struct connectdata *conn,
+                       curl_socket_t *socks,
+                       int numsocks);
+
+#if 0
 CURLcode Curl_protocol_fdset(struct connectdata *conn,
                              fd_set *read_fd_set,
                              fd_set *write_fd_set,
@@ -54,3 +64,5 @@
                           fd_set *write_fd_set,
                           int *max_fdp);
 #endif
+
+#endif
Index: lib/urldata.h
===================================================================
RCS file: /cvsroot/curl/curl/lib/urldata.h,v
retrieving revision 1.275
diff -u -r1.275 urldata.h
--- lib/urldata.h	28 Nov 2005 23:06:00 -0000	1.275
+++ lib/urldata.h	22 Dec 2005 15:37:00 -0000
@@ -634,17 +634,15 @@
 
   /* Called from the multi interface during the PROTOCONNECT phase, and it
      should then return a proper fd set */
-  CURLcode (*curl_proto_fdset)(struct connectdata *conn,
-                               fd_set *read_fd_set,
-                               fd_set *write_fd_set,
-                               int *max_fdp);
+  int (*curl_proto_getsock)(struct connectdata *conn,
+                            curl_socket_t *socks,
+                            int numsocks);
 
   /* Called from the multi interface during the DOING phase, and it should
      then return a proper fd set */
-  CURLcode (*curl_doing_fdset)(struct connectdata *conn,
-                               fd_set *read_fd_set,
-                               fd_set *write_fd_set,
-                               int *max_fdp);
+  int (*curl_doing_getsock)(struct connectdata *conn,
+                            curl_socket_t *socks,
+                            int numsocks);
 
   /* This function *MAY* be set to a protocol-dependent function that is run
    * by the curl_disconnect(), as a step in the disconnection.
@@ -941,6 +939,7 @@
  * 'struct urlstate' instead. The only exceptions MUST note the changes in
  * the 'DynamicStatic' struct.
  */
+struct Curl_one_easy; /* declared and used only in multi.c */
 
 struct UserDefined {
   FILE *err;         /* the stderr user data goes here */
@@ -1032,6 +1031,12 @@
 
   char *private_data; /* Private data */
 
+  struct Curl_one_easy *one_easy; /* When adding an easy handle to a multi
+                                     handle, an internal 'Curl_one_easy'
+                                     struct is created and this is a pointer
+                                     to the particular struct associated with
+                                     this SessionHandle */
+
   struct curl_slist *http200aliases; /* linked list of aliases for http200 */
 
   long ip_version;
Index: tests/server/sws.c
===================================================================
RCS file: /cvsroot/curl/curl/tests/server/sws.c,v
retrieving revision 1.75
diff -u -r1.75 sws.c
--- tests/server/sws.c	15 Sep 2005 20:22:43 -0000	1.75
+++ tests/server/sws.c	22 Dec 2005 15:37:01 -0000
@@ -66,6 +66,13 @@
 /* include memdebug.h last */
 #include "memdebug.h"
 
+/*
+ * The normal sws build for the plain standard curl test suite has no use for
+ * fork(), but if you feel wild and crazy and want to setup some more exotic
+ * tests. Define this and run...
+ */
+#define FORK_ENABLE 1
+
 #define REQBUFSIZ 150000
 #define REQBUFSIZ_TXT "149999"
 
@@ -75,6 +82,10 @@
                        a test in case the identical testno+partno request
                        shows up again */
 
+#define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
+#define RCMD_IDLE      1 /* told to sit idle */
+#define RCMD_STREAM    2 /* told to stream */
+
 struct httprequest {
   char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
   int offset;     /* size of the incoming request */
@@ -87,6 +98,8 @@
   size_t cl;      /* Content-Length of the incoming request */
   bool digest;    /* Authorization digest header found */
   bool ntlm;      /* Authorization ntlm header found */
+
+  int rcmd;       /* doing a special command, see defines above */
 };
 
 int ProcessRequest(struct httprequest *req);
@@ -113,6 +126,13 @@
 
 #define CMD_AUTH_REQUIRED "auth_required"
 
+/* 'idle' means that it will accept the request fine but never respond
+   any data. Just keep the connection alive. */
+#define CMD_IDLE "idle"
+
+/* 'stream' means to send a never-ending stream of data */
+#define CMD_STREAM "stream"
+
 #define END_OF_HEADERS "\r\n\r\n"
 
 enum {
@@ -256,6 +276,15 @@
             logmsg("instructed to require authorization header");
             req->auth_req = TRUE;
           }
+          else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
+            logmsg("instructed to idle");
+            req->rcmd = RCMD_IDLE;
+            req->open = TRUE;
+          }
+          else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
+            logmsg("instructed to stream");
+            req->rcmd = RCMD_STREAM;
+          }
           free(cmd);
         }
       }
@@ -478,10 +507,30 @@
 
   char partbuf[80]="data";
 
-  req->open = FALSE;
-
   logmsg("Send response number %d part %d", req->testno, req->partno);
 
+  switch(req->rcmd) {
+  default:
+  case RCMD_NORMALREQ:
+    break; /* continue with business as usual */
+  case RCMD_STREAM:
+#define STREAMTHIS "a string to stream 01234567890\n"
+    count = strlen(STREAMTHIS);
+    while(1) {
+      written = swrite(sock, STREAMTHIS, count);
+      if(written != (int)count) {
+        logmsg("Stopped streaming");
+        return -1;
+      }
+    }
+    break;
+  case RCMD_IDLE:
+    /* Do nothing. Sit idle. Pretend it rains. */
+    return 0;
+  }
+
+  req->open = FALSE;
+
   if(req->testno < 0) {
     switch(req->testno) {
     case DOCNUMBER_QUIT:
@@ -752,9 +801,24 @@
   while (1) {
     msgsock = accept(sock, NULL, NULL);
 
-    if (msgsock == -1)
-      continue;
+    if (msgsock == -1) {
+      printf("MAJOR ERROR: accept() failed!\n");
+      break;
+    }
 
+#ifdef FORK_ENABLE
+    /* The fork enabled version just forks off the child and don't care
+       about it anymore, so don't assume otherwise. Beware and don't do
+       this at home. */
+    rc = fork();
+    if(-1 == rc) {
+      printf("MAJOR ERROR: fork() failed!\n");
+      break;
+    }
+
+    /* 0 is returned to the child */
+    if(0 == rc) {
+#endif
     logmsg("====> Client connect");
 
     do {
@@ -792,6 +856,9 @@
 
     if (req.testno == DOCNUMBER_QUIT)
       break;
+#ifdef FORK_ENABLE
+    }
+#endif
   }
 
   sclose(sock);
