Index: lib/connect.c =================================================================== RCS file: /cvsroot/curl/curl/lib/connect.c,v retrieving revision 1.141 diff -u -r1.141 connect.c --- lib/connect.c 18 Dec 2005 15:36:14 -0000 1.141 +++ lib/connect.c 2 Jan 2006 23:39:24 -0000 @@ -98,6 +98,7 @@ #include "memory.h" #include "select.h" #include "url.h" /* for Curl_safefree() */ +#include "multiif.h" /* The last #include file should be: */ #include "memdebug.h" @@ -506,6 +507,7 @@ CURLcode code = CURLE_OK; curl_socket_t sockfd = conn->sock[sockindex]; long allow = DEFAULT_CONNECT_TIMEOUT; + long allow_total = 0; long has_passed; curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); @@ -518,12 +520,12 @@ /* subtract the most strict timeout of the ones */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) - allow = data->set.timeout*1000; + allow_total = allow = data->set.timeout*1000; else allow = data->set.connecttimeout*1000; } else if(data->set.timeout) { - allow = data->set.timeout*1000; + allow_total = allow = data->set.timeout*1000; } else if(data->set.connecttimeout) { allow = data->set.connecttimeout*1000; @@ -536,10 +538,13 @@ } if(conn->bits.tcpconnect) { /* we are connected already! */ + Curl_actbefore(data, allow_total); *connected = TRUE; return CURLE_OK; } + Curl_actbefore(data, allow); + /* check for connect without timeout as we want to return immediately */ rc = waitconnect(sockfd, 0); 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 2 Jan 2006 23:39:25 -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 2 Jan 2006 23:39:25 -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 2 Jan 2006 23:39:25 -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 2 Jan 2006 23:39:25 -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 2 Jan 2006 23:39:25 -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 2 Jan 2006 23:39:25 -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; - - 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; - - 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; + bitmap = multi_getsock(easy, sockbunch, MAX_SOCKSPEREASYHANDLE); - 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); - } + for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++) { + curl_socket_t s = CURL_SOCKET_BAD; - 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,203 @@ 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(¤t, 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(¤t, &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, + multi->socket_userp); + } + } + /* copy the current state to the storage area */ + memcpy(&easy->sockstate, ¤t, 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; +} + +/* given a number of milliseconds from now to use to set the 'act before + this'-time for the transfer, to be extracted by curl_multi_timeout() */ +void Curl_actbefore(struct SessionHandle *data, long milli) +{ + if(!milli) { + /* no timeout, clear the time data */ + data->state.actbefore.tv_sec = + data->state.actbefore.tv_usec = 0; + } + else { + struct timeval *nowp = &data->state.actbefore; + int rest; + + *nowp = Curl_tvnow(); + + nowp->tv_sec += milli/1000; + + nowp->tv_usec += (milli%1000)*1000; + + rest = (int)(nowp->tv_usec - 1000000); + if(rest > 0) { + /* bigger than a full microsec */ + nowp->tv_sec++; + nowp->tv_usec -= 1000000; + } + } +} + +#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 2 Jan 2006 23:39:25 -0000 @@ -26,5 +26,18 @@ /* * Prototypes for library-wide functions provided by multi.c */ +void Curl_actbefore(struct SessionHandle *data, long milli); 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.104 diff -u -r1.104 setup.h --- lib/setup.h 2 Jan 2006 18:35:58 -0000 1.104 +++ lib/setup.h 2 Jan 2006 23:39:25 -0000 @@ -280,6 +280,7 @@ #endif /* WIN32 */ +#ifndef curl_socket_typedef /* now typedef our socket type */ #ifdef WIN32 typedef SOCKET curl_socket_t; @@ -288,6 +289,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 2 Jan 2006 23:39:26 -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 @@ -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 2 Jan 2006 23:39:26 -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 2 Jan 2006 23:39:27 -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 2 Jan 2006 23:39:27 -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 2 Jan 2006 23:39:28 -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. @@ -909,6 +907,7 @@ #if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) ENGINE *engine; #endif /* USE_SSLEAY */ + struct timeval actbefore; /* set this with Curl_actbefore() only */ }; @@ -941,6 +940,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 +1032,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;