1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: multi.c,v 1.47 2004/03/11 13:13:35 bagder Exp $
22 ***************************************************************************/
28 #ifdef HAVE_SYS_TYPES_H
29 #include <sys/types.h>
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
38 #include <curl/curl.h>
46 /* The last #include file should be: */
52 /* the 'CURLMsg' is the part that is visible to the external user */
53 struct CURLMsg extmsg;
54 struct Curl_message *next;
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 */
68 CURLM_STATE_LAST /* not a true state, never use this */
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;
76 struct SessionHandle *easy_handle; /* the easy handle for this unit */
77 struct connectdata *easy_conn; /* the "unit's" connection */
79 CURLMstate state; /* the handle's state */
80 CURLcode result; /* previous result */
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 */
91 #define CURL_MULTI_HANDLE 0x000bab1e
93 #define GOOD_MULTI_HANDLE(x) ((x)&&(((struct Curl_multi *)x)->type == CURL_MULTI_HANDLE))
94 #define GOOD_EASY_HANDLE(x) (x)
96 /* This is the struct known as CURLM on the outside */
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. */
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. */
107 int num_msgs; /* total amount of messages in the easy handles */
110 curl_hash *hostcache;
114 CURLM *curl_multi_init(void)
116 struct Curl_multi *multi;
118 multi = (void *)malloc(sizeof(struct Curl_multi));
121 memset(multi, 0, sizeof(struct Curl_multi));
122 multi->type = CURL_MULTI_HANDLE;
125 multi->hostcache = Curl_hash_alloc(7, Curl_freednsinfo);
126 if(!multi->hostcache) {
127 /* failure, free mem and bail out */
131 return (CURLM *) multi;
134 CURLMcode curl_multi_add_handle(CURLM *multi_handle,
137 struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
138 struct Curl_one_easy *easy;
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;
144 /* Verify that we got a somewhat good easy handle too */
145 if(!GOOD_EASY_HANDLE(easy_handle))
146 return CURLM_BAD_EASY_HANDLE;
148 /* Now, time to add an easy handle to the multi stack */
149 easy = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy));
151 return CURLM_OUT_OF_MEMORY;
153 /* clean it all first (just to be sure) */
154 memset(easy, 0, sizeof(struct Curl_one_easy));
156 /* set the easy handle */
157 easy->easy_handle = easy_handle;
158 easy->state = CURLM_STATE_INIT;
160 /* for multi interface connections, we share DNS cache automaticly */
161 easy->easy_handle->hostcache = multi->hostcache;
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;
168 /* make 'easy' the first node in the chain */
169 multi->easy.next = easy;
171 /* if there was a next node, make sure its 'prev' pointer links back to
174 easy->next->prev = easy;
176 /* increase the node-counter */
179 return CURLM_CALL_MULTI_PERFORM;
182 CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
185 struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
186 struct Curl_one_easy *easy;
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;
192 /* Verify that we got a somewhat good easy handle too */
193 if(!GOOD_EASY_HANDLE(curl_handle))
194 return CURLM_BAD_EASY_HANDLE;
196 /* scan through the list and remove the 'curl_handle' */
197 easy = multi->easy.next;
199 if(easy->easy_handle == (struct SessionHandle *)curl_handle)
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. */
207 /* clear out the usage of the shared DNS cache */
208 easy->easy_handle->hostcache = NULL;
210 /* make the previous node point to our next */
212 easy->prev->next = easy->next;
213 /* make our next point to our previous node */
215 easy->next->prev = easy->prev;
218 We do not touch the easy handle here! */
223 multi->num_easy--; /* one less to care about now */
228 return CURLM_BAD_EASY_HANDLE; /* twasn't found */
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)
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;
242 if(!GOOD_MULTI_HANDLE(multi))
243 return CURLM_BAD_HANDLE;
245 *max_fd = -1; /* so far none! */
247 easy=multi->easy.next;
249 switch(easy->state) {
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,
256 if(this_max_fd > *max_fd)
257 *max_fd = this_max_fd;
260 case CURLM_STATE_WAITCONNECT:
261 case CURLM_STATE_DO_MORE:
263 /* when we're waiting for a connect, we wait for the socket to
265 struct connectdata *conn = easy->easy_conn;
266 curl_socket_t sockfd;
268 if(CURLM_STATE_WAITCONNECT == easy->state) {
269 sockfd = conn->sock[FIRSTSOCKET];
270 FD_SET(sockfd, write_fd_set);
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);
282 if((int)sockfd > *max_fd)
283 *max_fd = (int)sockfd;
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 */
290 Curl_single_fdset(easy->easy_conn,
291 read_fd_set, write_fd_set,
292 exc_fd_set, &this_max_fd);
294 /* remember the maximum file descriptor */
295 if(this_max_fd > *max_fd)
296 *max_fd = this_max_fd;
300 easy = easy->next; /* check next handle */
306 CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
308 struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
309 struct Curl_one_easy *easy;
311 CURLMcode result=CURLM_OK;
312 struct Curl_message *msg = NULL;
316 *running_handles = 0; /* bump this once for every living handle */
318 if(!GOOD_MULTI_HANDLE(multi))
319 return CURLM_BAD_HANDLE;
321 easy=multi->easy.next;
324 fprintf(stderr, "HANDLE %p: State: %x\n",
325 (char *)easy, easy->state);
328 if (CURLM_STATE_WAITCONNECT <= easy->state &&
329 easy->state <= CURLM_STATE_DO &&
330 easy->easy_handle->change.url_changed) {
332 Curl_posttransfer(easy->easy_handle);
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;
346 easy->easy_handle->change.url_changed = FALSE;
348 switch(easy->state) {
349 case CURLM_STATE_INIT:
350 /* init this transfer. */
351 easy->result=Curl_pretransfer(easy->easy_handle);
353 if(CURLE_OK == easy->result) {
354 /* after init, go CONNECT */
355 easy->state = CURLM_STATE_CONNECT;
356 result = CURLM_CALL_MULTI_PERFORM;
358 easy->easy_handle->state.used_interface = Curl_if_multi;
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,
368 if(CURLE_OK == easy->result) {
370 /* We're now waiting for an asynchronous name lookup */
371 easy->state = CURLM_STATE_WAITRESOLVE;
373 /* after the connect has been sent off, go WAITCONNECT */
374 easy->state = CURLM_STATE_WAITCONNECT;
375 result = CURLM_CALL_MULTI_PERFORM;
380 case CURLM_STATE_WAITRESOLVE:
381 /* awaiting an asynch name resolve to complete */
383 struct Curl_dns_entry *dns;
385 /* check if we have the name resolved by now */
386 easy->result = Curl_is_resolved(easy->easy_conn, &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);
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 */
398 easy->state = CURLM_STATE_WAITCONNECT;
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 */
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],
416 easy->result = Curl_protocol_connect(easy->easy_conn, NULL);
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 */
426 /* after the connect has completed, go DO */
427 easy->state = CURLM_STATE_DO;
428 result = CURLM_CALL_MULTI_PERFORM;
433 /* Do the fetch or put request */
434 easy->result = Curl_do(&easy->easy_conn);
435 if(CURLE_OK == easy->result) {
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;
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;
455 case CURLM_STATE_DO_MORE:
457 * First, check if we really are ready to do more.
460 Curl_is_connected(easy->easy_conn,
461 easy->easy_conn->sock[SECONDARYSOCKET],
465 * When we are connected, DO MORE and then go PERFORM
467 easy->result = Curl_do_more(easy->easy_conn);
469 if(CURLE_OK == easy->result)
470 easy->result = Curl_readwrite_init(easy->easy_conn);
472 if(CURLE_OK == easy->result) {
473 easy->state = CURLM_STATE_PERFORM;
474 result = CURLM_CALL_MULTI_PERFORM;
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);
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;
489 if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
490 /* if we failed anywhere, we must clean up the secondary socket if
492 sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
493 easy->easy_conn->sock[SECONDARYSOCKET]=-1;
495 Curl_posttransfer(easy->easy_handle);
496 Curl_done(easy->easy_conn);
499 /* after the transfer is done, go DONE */
500 else if(TRUE == done) {
502 /* call this even if the readwrite function returned error */
503 Curl_posttransfer(easy->easy_handle);
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;
518 easy->state = CURLM_STATE_DONE;
519 result = CURLM_CALL_MULTI_PERFORM;
523 case CURLM_STATE_DONE:
524 /* post-transfer command */
525 easy->result = Curl_done(easy->easy_conn);
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;
532 case CURLM_STATE_COMPLETED:
533 /* this is a completed transfer, it is likely to still be connected */
535 /* This node should be delinked from the list now and we should post
536 an information message that we are complete. */
539 return CURLM_INTERNAL_ERROR;
542 if(CURLM_STATE_COMPLETED != easy->state) {
543 if(CURLE_OK != easy->result) {
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;
550 /* this one still lives! */
551 (*running_handles)++;
554 } while (easy->easy_handle->change.url_changed);
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;
560 /* now add a node to the Curl_message linked list with this info */
561 msg = (struct Curl_message *)malloc(sizeof(struct Curl_message));
564 return CURLM_OUT_OF_MEMORY;
566 msg->extmsg.msg = CURLMSG_DONE;
567 msg->extmsg.easy_handle = easy->easy_handle;
568 msg->extmsg.data.result = easy->result;
572 easy->msg_num = 1; /* there is one unread message here */
574 multi->num_msgs++; /* increase message counter */
577 easy = easy->next; /* operate on next handle */
583 CURLMcode curl_multi_cleanup(CURLM *multi_handle)
585 struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
586 struct Curl_one_easy *easy;
587 struct Curl_one_easy *nexteasy;
589 if(GOOD_MULTI_HANDLE(multi)) {
590 multi->type = 0; /* not good anymore */
591 Curl_hash_destroy(multi->hostcache);
593 /* remove all easy handles */
594 easy = multi->easy.next;
597 /* clear out the usage of the shared DNS cache */
598 easy->easy_handle->hostcache = NULL;
611 return CURLM_BAD_HANDLE;
614 CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
616 struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
618 *msgs_in_queue = 0; /* default to none */
620 if(GOOD_MULTI_HANDLE(multi)) {
621 struct Curl_one_easy *easy;
624 return NULL; /* no messages left to return */
626 easy=multi->easy.next;
635 return NULL; /* this means internal count confusion really */
638 *msgs_in_queue = multi->num_msgs;
640 return &easy->msg->extmsg;