Index: ares/Makefile.inc =================================================================== RCS file: /cvsroot/curl/curl/ares/Makefile.inc,v retrieving revision 1.11 diff -u -r1.11 Makefile.inc --- ares/Makefile.inc 10 Aug 2005 16:54:00 -0000 1.11 +++ ares/Makefile.inc 20 Dec 2005 09:20:37 -0000 @@ -1,11 +1,11 @@ -CSOURCES = ares_fds.c ares_process.c ares_free_hostent.c ares_query.c \ -ares__close_sockets.c ares_free_string.c ares_search.c ares__get_hostent.c \ -ares_gethostbyaddr.c ares_send.c ares__read_line.c ares_gethostbyname.c \ -ares_strerror.c ares_cancel.c ares_init.c ares_timeout.c ares_destroy.c \ -ares_mkquery.c ares_version.c ares_expand_name.c ares_parse_a_reply.c \ -windows_port.c ares_expand_string.c ares_parse_ptr_reply.c \ -ares_parse_aaaa_reply.c ares_getnameinfo.c inet_net_pton.c bitncmp.c \ -inet_ntop.c +CSOURCES = ares_fds.c ares_getsock.c ares_process.c ares_free_hostent.c \ +ares_query.c ares__close_sockets.c ares_free_string.c ares_search.c \ +ares__get_hostent.c ares_gethostbyaddr.c ares_send.c ares__read_line.c \ +ares_gethostbyname.c ares_strerror.c ares_cancel.c ares_init.c \ +ares_timeout.c ares_destroy.c ares_mkquery.c ares_version.c \ +ares_expand_name.c ares_parse_a_reply.c windows_port.c \ +ares_expand_string.c ares_parse_ptr_reply.c ares_parse_aaaa_reply.c \ +ares_getnameinfo.c inet_net_pton.c bitncmp.c inet_ntop.c HHEADERS = ares.h ares_private.h setup.h ares_dns.h ares_version.h \ nameser.h inet_net_pton.h inet_ntop.h ares_ipv6.h bitncmp.h Index: ares/ares.h =================================================================== RCS file: /cvsroot/curl/curl/ares/ares.h,v retrieving revision 1.21 diff -u -r1.21 ares.h --- ares/ares.h 19 Dec 2005 00:15:04 -0000 1.21 +++ ares/ares.h 20 Dec 2005 09:20:38 -0000 @@ -70,8 +70,8 @@ #define ARES_EBADFLAGS 18 /* ares_getaddrinfo error codes */ -#define ARES_ENONAME 19 -#define ARES_EBADHINTS 20 +#define ARES_ENONAME 19 +#define ARES_EBADHINTS 20 /* Flag values */ #define ARES_FLAG_USEVC (1 << 0) @@ -108,27 +108,32 @@ #define ARES_NI_LOOKUPHOST (1 << 8) #define ARES_NI_LOOKUPSERVICE (1 << 9) /* Reserved for future use */ -#define ARES_NI_IDN (1 << 10) -#define ARES_NI_IDN_ALLOW_UNASSIGNED (1 << 11) +#define ARES_NI_IDN (1 << 10) +#define ARES_NI_IDN_ALLOW_UNASSIGNED (1 << 11) #define ARES_NI_IDN_USE_STD3_ASCII_RULES (1 << 12) /* Addrinfo flag values */ -#define ARES_AI_CANONNAME (1 << 0) -#define ARES_AI_NUMERICHOST (1 << 1) -#define ARES_AI_PASSIVE (1 << 2) -#define ARES_AI_NUMERICSERV (1 << 3) -#define ARES_AI_V4MAPPED (1 << 4) -#define ARES_AI_ALL (1 << 5) -#define ARES_AI_ADDRCONFIG (1 << 6) +#define ARES_AI_CANONNAME (1 << 0) +#define ARES_AI_NUMERICHOST (1 << 1) +#define ARES_AI_PASSIVE (1 << 2) +#define ARES_AI_NUMERICSERV (1 << 3) +#define ARES_AI_V4MAPPED (1 << 4) +#define ARES_AI_ALL (1 << 5) +#define ARES_AI_ADDRCONFIG (1 << 6) /* Reserved for future use */ -#define ARES_AI_IDN (1 << 10) -#define ARES_AI_IDN_ALLOW_UNASSIGNED (1 << 11) +#define ARES_AI_IDN (1 << 10) +#define ARES_AI_IDN_ALLOW_UNASSIGNED (1 << 11) #define ARES_AI_IDN_USE_STD3_ASCII_RULES (1 << 12) -#define ARES_AI_CANONIDN (1 << 13) +#define ARES_AI_CANONIDN (1 << 13) -#define ARES_AI_MASK (ARES_AI_CANONNAME|ARES_AI_NUMERICHOST|ARES_AI_PASSIVE| \ - ARES_AI_NUMERICSERV|ARES_AI_V4MAPPED|ARES_AI_ALL| \ - ARES_AI_ADDRCONFIG) +#define ARES_AI_MASK (ARES_AI_CANONNAME|ARES_AI_NUMERICHOST|ARES_AI_PASSIVE| \ + ARES_AI_NUMERICSERV|ARES_AI_V4MAPPED|ARES_AI_ALL| \ + ARES_AI_ADDRCONFIG) +#define ARES_GETSOCK_MAXNUM 16 /* ares_getsock() can return info about this + many sockets */ +#define ARES_GETSOCK_READABLE(bits,num) (bits & (1<< (num))) +#define ARES_GETSOCK_WRITABLE(bits,num) (bits & (1 << ((num) + \ + ARES_GETSOCK_MAXNUM))) struct ares_options { int flags; @@ -172,9 +177,11 @@ void ares_gethostbyaddr(ares_channel channel, const void *addr, int addrlen, int family, ares_host_callback callback, void *arg); void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa, - socklen_t salen, int flags, ares_nameinfo_callback callback, + socklen_t salen, int flags, + ares_nameinfo_callback callback, void *arg); int ares_fds(ares_channel channel, fd_set *read_fds, fd_set *write_fds); +int ares_getsock(ares_channel channel, int *socks, int numsocks); struct timeval *ares_timeout(ares_channel channel, struct timeval *maxtv, struct timeval *tv); void ares_process(ares_channel channel, fd_set *read_fds, fd_set *write_fds); Index: ares/ares_getsock.c =================================================================== RCS file: ares/ares_getsock.c diff -N ares/ares_getsock.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ares/ares_getsock.c 20 Dec 2005 09:20:38 -0000 @@ -0,0 +1,71 @@ +/* $Id: hiper-1.patch,v 1.1 2005/12/20 10:06:35 bagder Exp $ */ + +/* Copyright 2005 by Daniel Stenberg. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. M.I.T. makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#include "setup.h" +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include "ares.h" +#include "ares_private.h" + +int ares_getsock(ares_channel channel, + int *s, + int numsocks) /* size of the 'socks' array */ +{ + struct server_state *server; + ares_socket_t nfds; + int i; + int sockindex=0; + int bitmap = 0; + unsigned int setbits = 0xffffffff; + + ares_socket_t *socks = (ares_socket_t *)s; + + /* No queries, no file descriptors. */ + if (!channel->queries) + return 0; + + nfds = 0; + for (i = 0; i < channel->nservers; i++) + { + server = &channel->servers[i]; + if (server->udp_socket != ARES_SOCKET_BAD) + { + if(sockindex >= numsocks) + break; + socks[sockindex] = server->udp_socket; + bitmap |= ARES_GETSOCK_READABLE(setbits, sockindex); + sockindex++; + } + if (server->tcp_socket != ARES_SOCKET_BAD) + { + if(sockindex >= numsocks) + break; + socks[sockindex] = server->tcp_socket; + bitmap |= ARES_GETSOCK_READABLE(setbits, sockindex); + sockindex++; + + if (server->qhead) { + /* then the tcp socket is also writable! */ + bitmap |= ARES_GETSOCK_WRITABLE(setbits, sockindex-1); + } + + } + } + return (int)nfds; +} 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 20 Dec 2005 09:20:39 -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 20 Dec 2005 09:20:39 -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 20 Dec 2005 09:20:39 -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 20 Dec 2005 09:20:39 -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 20 Dec 2005 09:20:39 -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 20 Dec 2005 09:20:39 -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; @@ -266,6 +285,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 +354,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 +611,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 */ - /* after we have DONE what we're supposed to do, go COMPLETED, and - it doesn't matter what the Curl_done() returned! */ + /* 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) { + 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 +847,169 @@ 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! */ + + /* 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, + 100, /* TODO: time-out */ + 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; + + 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 */ + easy = multi_sock2easy(multi, s); + if(!easy) + /* unmatched socket, major problemo! */ + return CURLM_BAD_HANDLE; /* better return code? */ + } + + result = multi_runsingle(multi, easy, &running_handles); + + if(result == CURLM_OK) + /* get the socket(s) and check if the state has been changed since + last */ + singlesocket(multi, 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 20 Dec 2005 09:20:39 -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.102 diff -u -r1.102 setup.h --- lib/setup.h 17 Dec 2005 20:37:54 -0000 1.102 +++ lib/setup.h 20 Dec 2005 09:20:40 -0000 @@ -263,6 +263,7 @@ #endif /* WIN32 */ +#ifndef curl_socket_typedef /* now typedef our socket type */ #ifdef WIN32 typedef SOCKET curl_socket_t; @@ -271,6 +272,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 20 Dec 2005 09:20:40 -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 @@ -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 20 Dec 2005 09:20:40 -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.485 diff -u -r1.485 url.c --- lib/url.c 16 Dec 2005 14:52:17 -0000 1.485 +++ lib/url.c 20 Dec 2005 09:20:42 -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; } /* @@ -2840,8 +2836,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 20 Dec 2005 09:20:42 -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 20 Dec 2005 09:20:42 -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. 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 20 Dec 2005 09:20:42 -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);