]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/lib/multi.c
hello world
[icculus/iodoom3.git] / neo / curl / lib / multi.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  * 
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id: multi.c,v 1.47 2004/03/11 13:13:35 bagder Exp $
22  ***************************************************************************/
23
24 #include "setup.h"
25 #include <stdlib.h>
26 #include <string.h>
27
28 #ifdef HAVE_SYS_TYPES_H
29 #include <sys/types.h>
30 #endif
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
33 #endif
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #include <curl/curl.h>
39
40 #include "urldata.h"
41 #include "transfer.h"
42 #include "url.h"
43 #include "connect.h"
44 #include "progress.h"
45
46 /* The last #include file should be: */
47 #ifdef CURLDEBUG
48 #include "memdebug.h"
49 #endif
50
51 struct Curl_message {
52   /* the 'CURLMsg' is the part that is visible to the external user */
53   struct CURLMsg extmsg;
54   struct Curl_message *next;
55 };
56
57 typedef enum {
58   CURLM_STATE_INIT,
59   CURLM_STATE_CONNECT,     /* resolve/connect has been sent off */
60   CURLM_STATE_WAITRESOLVE, /* we're awaiting the resolve to finalize */
61   CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */
62   CURLM_STATE_DO,          /* send off the request (part 1) */
63   CURLM_STATE_DO_MORE,     /* send off the request (part 2) */
64   CURLM_STATE_PERFORM,     /* transfer data */
65   CURLM_STATE_DONE,        /* post data transfer operation */
66   CURLM_STATE_COMPLETED,   /* operation complete */
67
68   CURLM_STATE_LAST /* not a true state, never use this */
69 } CURLMstate;
70
71 struct Curl_one_easy {
72   /* first, two fields for the linked list of these */
73   struct Curl_one_easy *next;
74   struct Curl_one_easy *prev;
75   
76   struct SessionHandle *easy_handle; /* the easy handle for this unit */
77   struct connectdata *easy_conn;     /* the "unit's" connection */
78
79   CURLMstate state;  /* the handle's state */
80   CURLcode result;   /* previous result */
81
82   struct Curl_message *msg; /* A pointer to one single posted message.
83                                Cleanup should be done on this pointer NOT on
84                                the linked list in Curl_multi.  This message
85                                will be deleted when this handle is removed
86                                from the multi-handle */
87   int msg_num; /* number of messages left in 'msg' to return */
88 };
89
90
91 #define CURL_MULTI_HANDLE 0x000bab1e
92
93 #define GOOD_MULTI_HANDLE(x) ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE))
94 #define GOOD_EASY_HANDLE(x) (x)
95
96 /* This is the struct known as CURLM on the outside */
97 struct Curl_multi {
98   /* First a simple identifier to easier detect if a user mix up
99      this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */
100   long type;
101
102   /* We have a linked list with easy handles */
103   struct Curl_one_easy easy; 
104   /* This is the amount of entries in the linked list above. */
105   int num_easy;
106
107   int num_msgs; /* total amount of messages in the easy handles */
108
109   /* Hostname cache */
110   curl_hash *hostcache;
111 };
112
113
114 CURLM *curl_multi_init(void)
115 {
116   struct Curl_multi *multi;
117
118   multi = (void *)malloc(sizeof(struct Curl_multi));
119
120   if(multi) {
121     memset(multi, 0, sizeof(struct Curl_multi));
122     multi->type = CURL_MULTI_HANDLE;
123   }
124
125   multi->hostcache = Curl_hash_alloc(7, Curl_freednsinfo);
126   if(!multi->hostcache) {
127     /* failure, free mem and bail out */
128     free(multi);
129     multi = NULL;
130   }
131   return (CURLM *) multi;
132 }
133
134 CURLMcode curl_multi_add_handle(CURLM *multi_handle,
135                                 CURL *easy_handle)
136 {
137   struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
138   struct Curl_one_easy *easy;
139
140   /* First, make some basic checks that the CURLM handle is a good handle */
141   if(!GOOD_MULTI_HANDLE(multi))
142     return CURLM_BAD_HANDLE;
143   
144   /* Verify that we got a somewhat good easy handle too */
145   if(!GOOD_EASY_HANDLE(easy_handle))
146     return CURLM_BAD_EASY_HANDLE;
147
148   /* Now, time to add an easy handle to the multi stack */
149   easy = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy));
150   if(!easy)
151     return CURLM_OUT_OF_MEMORY;
152   
153   /* clean it all first (just to be sure) */
154   memset(easy, 0, sizeof(struct Curl_one_easy));
155
156   /* set the easy handle */
157   easy->easy_handle = easy_handle;
158   easy->state = CURLM_STATE_INIT;
159
160   /* for multi interface connections, we share DNS cache automaticly */
161   easy->easy_handle->hostcache = multi->hostcache;
162   
163   /* We add this new entry first in the list. We make our 'next' point to the
164      previous next and our 'prev' point back to the 'first' struct */
165   easy->next = multi->easy.next;
166   easy->prev = &multi->easy; 
167
168   /* make 'easy' the first node in the chain */
169   multi->easy.next = easy;
170
171   /* if there was a next node, make sure its 'prev' pointer links back to
172      the new node */
173   if(easy->next)
174     easy->next->prev = easy;
175
176   /* increase the node-counter */
177   multi->num_easy++;
178
179   return CURLM_CALL_MULTI_PERFORM;
180 }
181
182 CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
183                                    CURL *curl_handle)
184 {
185   struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
186   struct Curl_one_easy *easy;
187
188   /* First, make some basic checks that the CURLM handle is a good handle */
189   if(!GOOD_MULTI_HANDLE(multi))
190     return CURLM_BAD_HANDLE;
191   
192   /* Verify that we got a somewhat good easy handle too */
193   if(!GOOD_EASY_HANDLE(curl_handle))
194     return CURLM_BAD_EASY_HANDLE;
195
196   /* scan through the list and remove the 'curl_handle' */
197   easy = multi->easy.next;
198   while(easy) {
199     if(easy->easy_handle == (struct SessionHandle *)curl_handle)
200       break;
201     easy=easy->next;
202   }
203   if(easy) {
204     /* If the 'state' is not INIT or COMPLETED, we might need to do something
205        nice to put the easy_handle in a good known state when this returns. */
206
207     /* clear out the usage of the shared DNS cache */
208     easy->easy_handle->hostcache = NULL;
209     
210     /* make the previous node point to our next */
211     if(easy->prev)
212       easy->prev->next = easy->next;
213     /* make our next point to our previous node */
214     if(easy->next)
215       easy->next->prev = easy->prev;
216     
217     /* NOTE NOTE NOTE
218        We do not touch the easy handle here! */
219     if (easy->msg)
220       free(easy->msg);
221     free(easy);
222
223     multi->num_easy--; /* one less to care about now */
224
225     return CURLM_OK;
226   }
227   else
228     return CURLM_BAD_EASY_HANDLE; /* twasn't found */
229 }
230
231 CURLMcode curl_multi_fdset(CURLM *multi_handle,
232                            fd_set *read_fd_set, fd_set *write_fd_set,
233                            fd_set *exc_fd_set, int *max_fd)
234 {
235   /* Scan through all the easy handles to get the file descriptors set.
236      Some easy handles may not have connected to the remote host yet,
237      and then we must make sure that is done. */
238   struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
239   struct Curl_one_easy *easy;
240   int this_max_fd=-1;
241
242   if(!GOOD_MULTI_HANDLE(multi))
243     return CURLM_BAD_HANDLE;
244
245   *max_fd = -1; /* so far none! */
246
247   easy=multi->easy.next;
248   while(easy) {
249     switch(easy->state) {
250     default:
251       break;
252     case CURLM_STATE_WAITRESOLVE:
253       /* waiting for a resolve to complete */
254       Curl_multi_ares_fdset(easy->easy_conn, read_fd_set, write_fd_set,
255                             &this_max_fd);
256       if(this_max_fd > *max_fd)
257         *max_fd = this_max_fd;
258       break;
259
260     case CURLM_STATE_WAITCONNECT:
261     case CURLM_STATE_DO_MORE:
262       {
263         /* when we're waiting for a connect, we wait for the socket to
264            become writable */
265         struct connectdata *conn = easy->easy_conn;
266         curl_socket_t sockfd;
267
268         if(CURLM_STATE_WAITCONNECT == easy->state) {
269           sockfd = conn->sock[FIRSTSOCKET];
270           FD_SET(sockfd, write_fd_set);
271         }
272         else {
273           /* When in DO_MORE state, we could be either waiting for us
274              to connect to a remote site, or we could wait for that site
275              to connect to us. It makes a difference in the way: if we
276              connect to the site we wait for the socket to become writable, if 
277              the site connects to us we wait for it to become readable */
278           sockfd = conn->sock[SECONDARYSOCKET];
279           FD_SET(sockfd, write_fd_set);
280         }
281
282         if((int)sockfd > *max_fd)
283           *max_fd = (int)sockfd;
284       }
285       break;
286     case CURLM_STATE_PERFORM:
287       /* This should have a set of file descriptors for us to set.  */
288       /* after the transfer is done, go DONE */
289
290       Curl_single_fdset(easy->easy_conn,
291                         read_fd_set, write_fd_set,
292                         exc_fd_set, &this_max_fd);
293
294       /* remember the maximum file descriptor */
295       if(this_max_fd > *max_fd)
296         *max_fd = this_max_fd;
297
298       break;
299     }
300     easy = easy->next; /* check next handle */
301   }
302
303   return CURLM_OK;
304 }
305
306 CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
307 {
308   struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
309   struct Curl_one_easy *easy;
310   bool done;
311   CURLMcode result=CURLM_OK;
312   struct Curl_message *msg = NULL;
313   bool connected;
314   bool async;
315
316   *running_handles = 0; /* bump this once for every living handle */
317
318   if(!GOOD_MULTI_HANDLE(multi))
319     return CURLM_BAD_HANDLE;
320
321   easy=multi->easy.next;
322   while(easy) {
323 #ifdef CURLDEBUG
324     fprintf(stderr, "HANDLE %p: State: %x\n",
325             (char *)easy, easy->state);
326 #endif
327     do {
328       if (CURLM_STATE_WAITCONNECT <= easy->state &&
329           easy->state <= CURLM_STATE_DO &&
330           easy->easy_handle->change.url_changed) {
331         char *gotourl;
332         Curl_posttransfer(easy->easy_handle);
333
334         easy->result = Curl_done(easy->easy_conn);
335         if(CURLE_OK == easy->result) {
336           gotourl = strdup(easy->easy_handle->change.url);
337           easy->easy_handle->change.url_changed = FALSE;
338           easy->result = Curl_follow(easy->easy_handle, gotourl);
339           if(CURLE_OK == easy->result)
340             easy->state = CURLM_STATE_CONNECT;
341           else
342             free(gotourl);
343         }
344       }
345     
346       easy->easy_handle->change.url_changed = FALSE;
347
348       switch(easy->state) {
349       case CURLM_STATE_INIT:
350         /* init this transfer. */
351         easy->result=Curl_pretransfer(easy->easy_handle);
352
353         if(CURLE_OK == easy->result) {
354           /* after init, go CONNECT */
355           easy->state = CURLM_STATE_CONNECT;
356           result = CURLM_CALL_MULTI_PERFORM; 
357         
358           easy->easy_handle->state.used_interface = Curl_if_multi;
359         }
360         break;
361
362       case CURLM_STATE_CONNECT:
363         /* Connect. We get a connection identifier filled in. */
364         Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
365         easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn,
366                                     &async);
367
368         if(CURLE_OK == easy->result) {
369           if(async)
370             /* We're now waiting for an asynchronous name lookup */
371             easy->state = CURLM_STATE_WAITRESOLVE;
372           else {
373             /* after the connect has been sent off, go WAITCONNECT */
374             easy->state = CURLM_STATE_WAITCONNECT;
375             result = CURLM_CALL_MULTI_PERFORM;
376           }
377         }
378         break;
379
380       case CURLM_STATE_WAITRESOLVE:
381         /* awaiting an asynch name resolve to complete */
382       {
383         struct Curl_dns_entry *dns;
384
385         /* check if we have the name resolved by now */
386         easy->result = Curl_is_resolved(easy->easy_conn, &dns);
387
388         if(dns) {
389           /* Perform the next step in the connection phase, and then move on
390              to the WAITCONNECT state */
391           easy->result = Curl_async_resolved(easy->easy_conn);
392
393           if(CURLE_OK != easy->result)
394             /* if Curl_async_resolved() returns failure, the connection struct
395                is already freed and gone */
396             easy->easy_conn = NULL;           /* no more connection */
397
398           easy->state = CURLM_STATE_WAITCONNECT;
399         }
400         
401         if(CURLE_OK != easy->result) {
402           /* failure detected */
403           Curl_disconnect(easy->easy_conn); /* disconnect properly */
404           easy->easy_conn = NULL;           /* no more connection */
405           break;
406         }
407       }
408       break;
409
410       case CURLM_STATE_WAITCONNECT:
411         /* awaiting a completion of an asynch connect */
412         easy->result = Curl_is_connected(easy->easy_conn,
413                                          easy->easy_conn->sock[FIRSTSOCKET],
414                                          &connected);
415         if(connected)
416           easy->result = Curl_protocol_connect(easy->easy_conn, NULL);
417
418         if(CURLE_OK != easy->result) {
419           /* failure detected */
420           Curl_disconnect(easy->easy_conn); /* close the connection */
421           easy->easy_conn = NULL;           /* no more connection */
422           break;
423         }
424
425         if(connected) {
426           /* after the connect has completed, go DO */
427           easy->state = CURLM_STATE_DO;
428           result = CURLM_CALL_MULTI_PERFORM; 
429         }
430         break;
431
432       case CURLM_STATE_DO:
433         /* Do the fetch or put request */
434         easy->result = Curl_do(&easy->easy_conn);
435         if(CURLE_OK == easy->result) {
436
437           /* after do, go PERFORM... or DO_MORE */
438           if(easy->easy_conn->bits.do_more) {
439             /* we're supposed to do more, but we need to sit down, relax
440                and wait a little while first */
441             easy->state = CURLM_STATE_DO_MORE;
442             result = CURLM_OK;
443           }
444           else {
445             /* we're done with the DO, now PERFORM */
446             easy->result = Curl_readwrite_init(easy->easy_conn);
447             if(CURLE_OK == easy->result) {
448               easy->state = CURLM_STATE_PERFORM;
449               result = CURLM_CALL_MULTI_PERFORM; 
450             }
451           }
452         }
453         break;
454
455       case CURLM_STATE_DO_MORE:
456         /*
457          * First, check if we really are ready to do more.
458          */
459         easy->result =
460           Curl_is_connected(easy->easy_conn,
461                             easy->easy_conn->sock[SECONDARYSOCKET],
462                             &connected);
463         if(connected) {
464           /*
465            * When we are connected, DO MORE and then go PERFORM
466            */
467           easy->result = Curl_do_more(easy->easy_conn);
468
469           if(CURLE_OK == easy->result)
470             easy->result = Curl_readwrite_init(easy->easy_conn);
471
472           if(CURLE_OK == easy->result) {
473             easy->state = CURLM_STATE_PERFORM;
474             result = CURLM_CALL_MULTI_PERFORM; 
475           }
476         }
477         break;
478
479       case CURLM_STATE_PERFORM:
480         /* read/write data if it is ready to do so */
481         easy->result = Curl_readwrite(easy->easy_conn, &done);
482
483         if(easy->result)  {
484           /* The transfer phase returned error, we mark the connection to get
485            * closed to prevent being re-used. This is becasue we can't
486            * possibly know if the connection is in a good shape or not now. */
487           easy->easy_conn->bits.close = TRUE;
488
489           if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
490             /* if we failed anywhere, we must clean up the secondary socket if
491                it was used */
492             sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
493             easy->easy_conn->sock[SECONDARYSOCKET]=-1;
494           }
495           Curl_posttransfer(easy->easy_handle);
496           Curl_done(easy->easy_conn);
497         }
498
499         /* after the transfer is done, go DONE */
500         else if(TRUE == done) {
501
502           /* call this even if the readwrite function returned error */
503           Curl_posttransfer(easy->easy_handle);
504
505           /* When we follow redirects, must to go back to the CONNECT state */
506           if(easy->easy_conn->newurl) {
507             char *newurl = easy->easy_conn->newurl;
508             easy->easy_conn->newurl = NULL;
509             easy->result = Curl_done(easy->easy_conn);
510             if(easy->result == CURLE_OK)
511               easy->result = Curl_follow(easy->easy_handle, newurl);
512             if(CURLE_OK == easy->result) {
513               easy->state = CURLM_STATE_CONNECT;
514               result = CURLM_CALL_MULTI_PERFORM;
515             }
516           }
517           else {
518             easy->state = CURLM_STATE_DONE;
519             result = CURLM_CALL_MULTI_PERFORM; 
520           }
521         }
522         break;
523       case CURLM_STATE_DONE:
524         /* post-transfer command */
525         easy->result = Curl_done(easy->easy_conn);
526
527         /* after we have DONE what we're supposed to do, go COMPLETED, and
528            it doesn't matter what the Curl_done() returned! */
529         easy->state = CURLM_STATE_COMPLETED;
530         break;
531
532       case CURLM_STATE_COMPLETED:
533         /* this is a completed transfer, it is likely to still be connected */
534
535         /* This node should be delinked from the list now and we should post
536            an information message that we are complete. */
537         break;
538       default:
539         return CURLM_INTERNAL_ERROR;
540       }
541
542       if(CURLM_STATE_COMPLETED != easy->state) {
543         if(CURLE_OK != easy->result) {
544           /*
545            * If an error was returned, and we aren't in completed state now,
546            * then we go to completed and consider this transfer aborted.  */
547           easy->state = CURLM_STATE_COMPLETED;
548         }
549         else
550           /* this one still lives! */
551           (*running_handles)++;
552       }
553
554     } while (easy->easy_handle->change.url_changed);
555
556     if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
557       /* clear out the usage of the shared DNS cache */
558       easy->easy_handle->hostcache = NULL;
559
560       /* now add a node to the Curl_message linked list with this info */
561       msg = (struct Curl_message *)malloc(sizeof(struct Curl_message));
562
563       if(!msg)
564         return CURLM_OUT_OF_MEMORY;
565
566       msg->extmsg.msg = CURLMSG_DONE;
567       msg->extmsg.easy_handle = easy->easy_handle;
568       msg->extmsg.data.result = easy->result;
569       msg->next=NULL;
570
571       easy->msg = msg;
572       easy->msg_num = 1; /* there is one unread message here */
573
574       multi->num_msgs++; /* increase message counter */
575     }
576
577     easy = easy->next; /* operate on next handle */
578   }
579
580   return result;
581 }
582
583 CURLMcode curl_multi_cleanup(CURLM *multi_handle)
584 {
585   struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
586   struct Curl_one_easy *easy;
587   struct Curl_one_easy *nexteasy;
588
589   if(GOOD_MULTI_HANDLE(multi)) {
590     multi->type = 0; /* not good anymore */
591     Curl_hash_destroy(multi->hostcache);
592
593     /* remove all easy handles */
594     easy = multi->easy.next;
595     while(easy) {
596       nexteasy=easy->next;
597       /* clear out the usage of the shared DNS cache */
598       easy->easy_handle->hostcache = NULL;
599
600       if (easy->msg)
601         free(easy->msg);
602       free(easy);
603       easy = nexteasy;
604     }
605
606     free(multi);
607
608     return CURLM_OK;
609   }
610   else
611     return CURLM_BAD_HANDLE;
612 }
613
614 CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
615 {
616   struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
617
618   *msgs_in_queue = 0; /* default to none */
619
620   if(GOOD_MULTI_HANDLE(multi)) {
621     struct Curl_one_easy *easy;
622     
623     if(!multi->num_msgs)
624       return NULL; /* no messages left to return */
625
626     easy=multi->easy.next;
627     while(easy) {
628       if(easy->msg_num) {
629         easy->msg_num--;
630         break;
631       }
632       easy = easy->next;
633     }
634     if(!easy)
635       return NULL; /* this means internal count confusion really */
636
637     multi->num_msgs--;
638     *msgs_in_queue = multi->num_msgs;
639
640     return &easy->msg->extmsg;
641   }
642   else
643     return NULL;
644 }