CURLMcode curl_multi_perform(CURLM *multi_handle, 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; bool connected; bool async; *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) { #if 0 fprintf(stderr, "HANDLE %p: State: %x\n", (char *)easy, easy->state); #endif 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); if(CURLE_OK == easy->result) easy->state = CURLM_STATE_CONNECT; else free(gotourl); } else { easy->result = CURLE_OUT_OF_MEMORY; easy->state = CURLM_STATE_COMPLETED; break; } } } 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 */ easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; easy->easy_handle->state.used_interface = Curl_if_multi; } 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); if(CURLE_OK == easy->result) { if(async) /* We're now waiting for an asynchronous name lookup */ easy->state = CURLM_STATE_WAITRESOLVE; else { /* after the connect has been sent off, go WAITCONNECT */ easy->state = CURLM_STATE_WAITCONNECT; result = CURLM_CALL_MULTI_PERFORM; } } 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); 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 */ easy->state = 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); if(CURLE_OK != easy->result) { /* failure detected */ Curl_disconnect(easy->easy_conn); /* close the connection */ easy->easy_conn = NULL; /* no more connection */ break; } if(connected) { /* after the connect has completed, go DO */ easy->state = CURLM_STATE_DO; result = CURLM_CALL_MULTI_PERFORM; } break; case CURLM_STATE_DO: /* Do the fetch or put request */ easy->result = Curl_do(&easy->easy_conn); if(CURLE_OK == easy->result) { /* 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 */ easy->state = 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) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } } break; case CURLM_STATE_DO_MORE: /* * First, check if we really are 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); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } 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); } /* after the transfer is done, go DONE */ else if(TRUE == done) { /* 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) { char *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); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; } } else { easy->state = 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! */ easy->state = CURLM_STATE_COMPLETED; break; case CURLM_STATE_COMPLETED: /* this is a completed transfer, it is likely to still be connected */ /* This node should be delinked from the list now and we should post an information message that we are complete. */ break; default: return CURLM_INTERNAL_ERROR; } 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. */ easy->state = CURLM_STATE_COMPLETED; } else /* this one still lives! */ (*running_handles)++; } } while (easy->easy_handle->change.url_changed); if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { /* clear out the usage of the shared DNS cache */ easy->easy_handle->hostcache = NULL; /* now add a node to the Curl_message linked list with this info */ msg = (struct Curl_message *)malloc(sizeof(struct Curl_message)); if(!msg) return CURLM_OUT_OF_MEMORY; msg->extmsg.msg = CURLMSG_DONE; msg->extmsg.easy_handle = easy->easy_handle; msg->extmsg.data.result = easy->result; msg->next=NULL; easy->msg = msg; easy->msg_num = 1; /* there is one unread message here */ multi->num_msgs++; /* increase message counter */ } easy = easy->next; /* operate on next handle */ } return result; }
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; 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) { 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 */ easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; } break; case CURLM_STATE_CONNECT: if (Curl_global_host_cache_use(easy->easy_handle)) { easy->easy_handle->hostcache = Curl_global_host_cache_get(); } else { if (multi->hostcache == NULL) { multi->hostcache = curl_hash_alloc(7, Curl_freeaddrinfo); } easy->easy_handle->hostcache = multi->hostcache; } /* Connect. We get a connection identifier filled in. */ easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); /* after connect, go DO */ if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_DO; result = CURLM_CALL_MULTI_PERFORM; } break; case CURLM_STATE_DO: /* Do the fetch or put request */ easy->result = Curl_do(&easy->easy_conn); /* after do, go PERFORM */ if(CURLE_OK == easy->result) { if(CURLE_OK == Curl_readwrite_init(easy->easy_conn)) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } break; case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); /* hm, when we follow redirects, we may need to go back to the CONNECT state */ /* after the transfer is done, go DONE */ if(TRUE == done) { /* call this even if the readwrite function returned error */ easy->result = Curl_posttransfer(easy->easy_handle); easy->state = CURLM_STATE_DONE; result = CURLM_CALL_MULTI_PERFORM; } break; case CURLM_STATE_DONE: /* post-transfer command */ easy->result = Curl_done(easy->easy_conn); /* after we have DONE what we're supposed to do, go COMPLETED */ if(CURLE_OK == easy->result) easy->state = CURLM_STATE_COMPLETED; break; case CURLM_STATE_COMPLETED: /* this is a completed transfer, it is likely to still be connected */ /* This node should be delinked from the list now and we should post an information message that we are complete. */ break; default: return CURLM_INTERNAL_ERROR; } if((CURLM_STATE_COMPLETED != easy->state) && (CURLE_OK != easy->result)) { /* * If an error was returned, and we aren't in completed now, * then we go to completed and consider this transfer aborted. */ easy->state = CURLM_STATE_COMPLETED; } else if(CURLM_STATE_COMPLETED != easy->state) /* this one still lives! */ (*running_handles)++; easy = easy->next; /* operate on next handle */ } return result; }