Newer
Older
/* Force the connection closed because the server could continue to
send us stuff at any time. (The disconnect_conn logic used below
doesn't work at this point). */
data->easy_conn->bits.close = TRUE;
data->result = CURLE_OPERATION_TIMEDOUT;
multistate(data, CURLM_STATE_COMPLETED);
Daniel Stenberg
committed
case CURLM_STATE_INIT:
/* init this transfer. */
Daniel Stenberg
committed
/* after init, go CONNECT */
Daniel Stenberg
committed
result = CURLM_CALL_MULTI_PERFORM;
}
break;
Daniel Stenberg
committed
case CURLM_STATE_CONNECT_PEND:
/* We will stay here until there is a connection available. Then
we try again in the CURLM_STATE_CONNECT state. */
break;
Daniel Stenberg
committed
case CURLM_STATE_CONNECT:
/* Connect. We want to get a connection identifier filled in. */
Curl_pgrsTime(data, TIMER_STARTSINGLE);
data->result = Curl_connect(data, &data->easy_conn,
Daniel Stenberg
committed
&async, &protocol_connect);
if(CURLE_NO_CONNECTION_AVAILABLE == data->result) {
/* There was no connection available. We will go to the pending
state and wait for an available connection. */
multistate(data, CURLM_STATE_CONNECT_PEND);
data->result = CURLE_OK;
break;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
/* Add this handle to the send or pend pipeline */
data->result = Curl_add_handle_to_pipeline(data, data->easy_conn);
if(CURLE_OK != data->result)
disconnect_conn = TRUE;
else {
Daniel Stenberg
committed
if(async)
/* We're now waiting for an asynchronous name lookup */
else {
Daniel Stenberg
committed
/* after the connect has been sent off, go WAITCONNECT unless the
protocol connect is already done and we can go directly to
Daniel Stenberg
committed
WAITDO or DO! */
Daniel Stenberg
committed
result = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect)
Daniel Stenberg
committed
CURLM_STATE_WAITDO:CURLM_STATE_DO);
else {
#ifndef CURL_DISABLE_HTTP
if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
multistate(data, CURLM_STATE_WAITPROXYCONNECT);
Daniel Stenberg
committed
else
}
}
}
Daniel Stenberg
committed
}
break;
Daniel Stenberg
committed
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 */
data->result = Curl_resolver_is_resolved(data->easy_conn, &dns);
Daniel Stenberg
committed
/* Update sockets here, because the socket(s) may have been
closed and the application thus needs to be told, even if it
is likely that the same socket(s) will again be used further
down. If the name has not yet been resolved, it is likely
that new sockets have been opened in an attempt to contact
another resolver. */
Daniel Stenberg
committed
if(dns) {
Daniel Stenberg
committed
/* Perform the next step in the connection phase, and then move on
to the WAITCONNECT state */
data->result = Curl_async_resolved(data->easy_conn,
Daniel Stenberg
committed
&protocol_connect);
Daniel Stenberg
committed
/* if Curl_async_resolved() returns failure, the connection struct
is already freed and gone */
Daniel Stenberg
committed
else {
/* call again please so that we get the next socket setup */
result = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect)
Daniel Stenberg
committed
CURLM_STATE_WAITDO:CURLM_STATE_DO);
Daniel Stenberg
committed
else {
#ifndef CURL_DISABLE_HTTP
if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
multistate(data, CURLM_STATE_WAITPROXYCONNECT);
Daniel Stenberg
committed
else
Daniel Stenberg
committed
}
Daniel Stenberg
committed
}
Daniel Stenberg
committed
/* failure detected */
disconnect_conn = TRUE;
Daniel Stenberg
committed
break;
}
}
break;
#ifndef CURL_DISABLE_HTTP
Daniel Stenberg
committed
case CURLM_STATE_WAITPROXYCONNECT:
/* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
data->result = Curl_http_connect(data->easy_conn, &protocol_connect);
Daniel Stenberg
committed
Daniel Stenberg
committed
/* reset the error buffer */
if(data->set.errorbuffer)
data->set.errorbuffer[0] = '\0';
data->state.errorbuf = FALSE;
Daniel Stenberg
committed
Daniel Stenberg
committed
result = CURLM_CALL_MULTI_PERFORM;
Daniel Stenberg
committed
}
else if(CURLE_OK == data->result) {
if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE)
multistate(data, CURLM_STATE_WAITCONNECT);
Daniel Stenberg
committed
}
break;
Daniel Stenberg
committed
Daniel Stenberg
committed
case CURLM_STATE_WAITCONNECT:
/* awaiting a completion of an asynch connect */
FIRSTSOCKET,
Daniel Stenberg
committed
&connected);
if(connected) {
/* if everything is still fine we do the protocol-specific connect
setup */
data->result = Curl_protocol_connect(data->easy_conn,
&protocol_connect);
}
Daniel Stenberg
committed
/* failure detected */
/* Just break, the cleaning up is handled all in one place */
disconnect_conn = TRUE;
break;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
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
Daniel Stenberg
committed
connect is TRUE, we move on to STATE_DO.
BUT if we are using a proxy we must change to WAITPROXYCONNECT
Daniel Stenberg
committed
#ifndef CURL_DISABLE_HTTP
if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
multistate(data, CURLM_STATE_WAITPROXYCONNECT);
Daniel Stenberg
committed
else
#endif
Daniel Stenberg
committed
Daniel Stenberg
committed
}
Daniel Stenberg
committed
else
Daniel Stenberg
committed
/* after the connect has completed, go WAITDO or DO */
Daniel Stenberg
committed
CURLM_STATE_WAITDO:CURLM_STATE_DO);
Daniel Stenberg
committed
result = CURLM_CALL_MULTI_PERFORM;
Daniel Stenberg
committed
}
break;
Daniel Stenberg
committed
Daniel Stenberg
committed
case CURLM_STATE_PROTOCONNECT:
/* protocol-specific connect phase */
data->result = Curl_protocol_connecting(data->easy_conn,
Daniel Stenberg
committed
&protocol_connect);
if((data->result == CURLE_OK) && protocol_connect) {
Daniel Stenberg
committed
/* after the connect has completed, go WAITDO or DO */
Daniel Stenberg
committed
CURLM_STATE_WAITDO:CURLM_STATE_DO);
Daniel Stenberg
committed
result = CURLM_CALL_MULTI_PERFORM;
}
Daniel Stenberg
committed
/* failure detected */
Curl_posttransfer(data);
disconnect_conn = TRUE;
Daniel Stenberg
committed
}
break;
case CURLM_STATE_WAITDO:
/* Wait for our turn to DO when we're pipelining requests */
infof(data, "WAITDO: Conn %ld send pipe %zu inuse %s athead %s\n",
data->easy_conn->connection_id,
data->easy_conn->send_pipe->size,
data->easy_conn->writechannel_inuse?"TRUE":"FALSE",
isHandleAtHead(data,
Daniel Stenberg
committed
#endif
isHandleAtHead(data,
/* Grab the channel */
data->easy_conn->writechannel_inuse = TRUE;
multistate(data, CURLM_STATE_DO);
result = CURLM_CALL_MULTI_PERFORM;
}
break;
Daniel Stenberg
committed
case CURLM_STATE_DO:
if(data->set.connect_only) {
Daniel Stenberg
committed
/* keep connection open for application to use the socket */
data->easy_conn->bits.close = FALSE;
multistate(data, CURLM_STATE_DONE);
data->result = CURLE_OK;
result = CURLM_CALL_MULTI_PERFORM;
Daniel Stenberg
committed
}
else {
/* Perform the protocol's DO action */
data->result = Curl_do(&data->easy_conn, &dophase_done);
/* When Curl_do() returns failure, data->easy_conn might be NULL! */
Daniel Stenberg
committed
Daniel Stenberg
committed
if(!dophase_done) {
/* some steps needed for wildcard matching */
if(data->set.wildcardmatch) {
struct WildcardData *wc = &data->wildcard;
if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
/* skip some states if it is important */
Curl_done(&data->easy_conn, CURLE_OK, FALSE);
multistate(data, CURLM_STATE_DONE);
result = CURLM_CALL_MULTI_PERFORM;
break;
}
}
Daniel Stenberg
committed
/* DO was not completed in one function call, we must continue
DOING... */
Daniel Stenberg
committed
result = CURLM_OK;
}
/* after DO, go DO_DONE... or DO_MORE */
Daniel Stenberg
committed
/* we're supposed to do more, but we need to sit down, relax
and wait a little while first */
Daniel Stenberg
committed
result = CURLM_OK;
}
else {
/* we're done with the DO, now DO_DONE */
Daniel Stenberg
committed
result = CURLM_CALL_MULTI_PERFORM;
Daniel Stenberg
committed
}
}
else if((CURLE_SEND_ERROR == data->result) &&
data->easy_conn->bits.reuse) {
/*
* In this situation, a connection that we were trying to use
* may have unexpectedly died. If possible, send the connection
* back to the CONNECT phase so we can try again.
*/
followtype follow=FOLLOW_NONE;
CURLcode drc;
bool retry = FALSE;
drc = Curl_retry_request(data->easy_conn, &newurl);
if(drc) {
/* a failure here pretty much implies an out of memory */
disconnect_conn = TRUE;
}
else
Curl_posttransfer(data);
drc = Curl_done(&data->easy_conn, data->result, FALSE);
/* When set to retry the connection, we must to go back to
* the CONNECT state */
if(retry) {
if((drc == CURLE_OK) || (drc == CURLE_SEND_ERROR)) {
follow = FOLLOW_RETRY;
drc = Curl_follow(data, newurl, follow);
if(drc == CURLE_OK) {
result = CURLM_CALL_MULTI_PERFORM;
}
else {
/* Follow failed */
free(newurl);
}
}
else {
/* done didn't return OK or SEND_ERROR */
free(newurl);
}
}
else {
/* Have error handler disconnect conn if we can't retry */
disconnect_conn = TRUE;
}
}
else {
/* failure detected */
Curl_posttransfer(data);
if(data->easy_conn)
Curl_done(&data->easy_conn, data->result, FALSE);
disconnect_conn = TRUE;
}
Daniel Stenberg
committed
}
break;
Daniel Stenberg
committed
Daniel Stenberg
committed
case CURLM_STATE_DOING:
/* we continue DOING until the DO phase is complete */
data->result = Curl_protocol_doing(data->easy_conn,
&dophase_done);
Daniel Stenberg
committed
if(dophase_done) {
/* after DO, go DO_DONE or DO_MORE */
CURLM_STATE_DO_MORE:
CURLM_STATE_DO_DONE);
result = CURLM_CALL_MULTI_PERFORM;
Daniel Stenberg
committed
} /* dophase_done */
}
else {
/* failure detected */
Curl_posttransfer(data);
disconnect_conn = TRUE;
Daniel Stenberg
committed
}
break;
case CURLM_STATE_DO_MORE:
/*
* When we are connected, DO MORE and then go DO_DONE
*/
data->result = Curl_do_more(data->easy_conn, &control);
Daniel Stenberg
committed
/* No need to remove this handle from the send pipeline here since that
is done in Curl_done() */
if(control) {
/* if positive, advance to DO_DONE
if negative, go back to DOING */
CURLM_STATE_DO_DONE:
CURLM_STATE_DOING);
Daniel Stenberg
committed
result = CURLM_CALL_MULTI_PERFORM;
else
/* stay in DO_MORE */
result = CURLM_OK;
}
else {
/* failure detected */
Curl_posttransfer(data);
disconnect_conn = TRUE;
Daniel Stenberg
committed
}
break;
case CURLM_STATE_DO_DONE:
Daniel Stenberg
committed
/* Move ourselves from the send to recv pipeline */
Curl_move_handle_from_send_to_recv_pipe(data, data->easy_conn);
Daniel Stenberg
committed
/* Check if we can move pending requests to send pipe */
Curl_multi_process_pending_handles(multi);
/* Only perform the transfer if there's a good socket to work with.
Having both BAD is a signal to skip immediately to DONE */
if((data->easy_conn->sockfd != CURL_SOCKET_BAD) ||
(data->easy_conn->writesockfd != CURL_SOCKET_BAD))
multistate(data, CURLM_STATE_WAITPERFORM);
else
multistate(data, CURLM_STATE_DONE);
result = CURLM_CALL_MULTI_PERFORM;
break;
case CURLM_STATE_WAITPERFORM:
/* Wait for our turn to PERFORM */
isHandleAtHead(data,
/* Grab the channel */
data->easy_conn->readchannel_inuse = TRUE;
multistate(data, CURLM_STATE_PERFORM);
result = CURLM_CALL_MULTI_PERFORM;
}
Daniel Stenberg
committed
else {
infof(data, "WAITPERFORM: Conn %ld recv pipe %zu inuse %s athead %s\n",
data->easy_conn->connection_id,
data->easy_conn->recv_pipe->size,
data->easy_conn->readchannel_inuse?"TRUE":"FALSE",
isHandleAtHead(data,
Daniel Stenberg
committed
}
#endif
break;
case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
/* if both rates are within spec, resume transfer */
if(Curl_pgrsUpdate(data->easy_conn))
data->result = CURLE_ABORTED_BY_CALLBACK;
if(( (data->set.max_send_speed == 0) ||
(data->progress.ulspeed < data->set.max_send_speed )) &&
( (data->set.max_recv_speed == 0) ||
(data->progress.dlspeed < data->set.max_recv_speed)))
Daniel Stenberg
committed
case CURLM_STATE_PERFORM:
{
char *newurl = NULL;
bool retry = FALSE;
/* check if over send speed */
if((data->set.max_send_speed > 0) &&
(data->progress.ulspeed > data->set.max_send_speed)) {
int buffersize;
/* calculate upload rate-limitation timeout. */
buffersize = (int)(data->set.buffer_size ?
data->set.buffer_size : BUFSIZE);
timeout_ms = Curl_sleep_time(data->set.max_send_speed,
data->progress.ulspeed, buffersize);
Curl_expire(data, timeout_ms);
break;
}
/* check if over recv speed */
if((data->set.max_recv_speed > 0) &&
(data->progress.dlspeed > data->set.max_recv_speed)) {
int buffersize;
/* Calculate download rate-limitation timeout. */
buffersize = (int)(data->set.buffer_size ?
data->set.buffer_size : BUFSIZE);
timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
data->progress.dlspeed, buffersize);
Curl_expire(data, timeout_ms);
break;
}
Daniel Stenberg
committed
/* read/write data if it is ready to do so */
data->result = Curl_readwrite(data->easy_conn, &done);
Daniel Stenberg
committed
if(!(k->keepon & KEEP_RECV)) {
/* We're done receiving */
}
if(!(k->keepon & KEEP_SEND)) {
/* We're done sending */
}
/* If CURLE_RECV_ERROR happens early enough, we assume it was a race
* condition and the server closed the re-used connection exactly when
* we wanted to use it, so figure out if that is indeed the case.
*/
CURLcode ret = Curl_retry_request(data->easy_conn, &newurl);
if(!ret)
retry = (newurl)?TRUE:FALSE;
if(retry) {
/* if we are to retry, set the result to OK and consider the
request as done */
/*
* The transfer phase returned error, we mark the connection to get
Daniel Stenberg
committed
* closed to prevent being re-used. This is because we can't possibly
* know if the connection is in a good shape or not now. Unless it is
* a protocol which uses two "channels" like FTP, as then the error
* happened in the data connection.
*/
if(!(data->easy_conn->handler->flags & PROTOPT_DUAL))
data->easy_conn->bits.close = TRUE;
Daniel Stenberg
committed
Curl_posttransfer(data);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
followtype follow=FOLLOW_NONE;
Daniel Stenberg
committed
Daniel Stenberg
committed
/* call this even if the readwrite function returned error */
Curl_posttransfer(data);
Daniel Stenberg
committed
Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe);
Daniel Stenberg
committed
/* expire the new receiving pipeline head */
if(data->easy_conn->recv_pipe->head)
Curl_expire(data->easy_conn->recv_pipe->head->ptr, 1);
Daniel Stenberg
committed
/* Check if we can move pending requests to send pipe */
Curl_multi_process_pending_handles(multi);
Daniel Stenberg
committed
/* When we follow redirects or is set to retry the connection, we must
to go back to the CONNECT state */
if(data->req.newurl || retry) {
Daniel Stenberg
committed
if(!retry) {
/* if the URL is a follow-location and not just a retried request
then figure out the URL here */
newurl = data->req.newurl;
data->req.newurl = NULL;
Daniel Stenberg
committed
follow = FOLLOW_REDIR;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
else
follow = FOLLOW_RETRY;
data->result = Curl_done(&data->easy_conn, CURLE_OK, FALSE);
if(CURLE_OK == data->result) {
data->result = Curl_follow(data, newurl, follow);
if(CURLE_OK == data->result) {
multistate(data, CURLM_STATE_CONNECT);
result = CURLM_CALL_MULTI_PERFORM;
newurl = NULL; /* handed over the memory ownership to
Curl_follow(), make sure we don't free() it
here */
}
Daniel Stenberg
committed
else {
/* after the transfer is done, go DONE */
/* but first check to see if we got a location info even though we're
not following redirects */
if(data->req.location) {
if(newurl)
free(newurl);
newurl = data->req.location;
data->req.location = NULL;
data->result = Curl_follow(data, newurl, FOLLOW_FAKE);
if(CURLE_OK == data->result)
newurl = NULL; /* allocation was handed over Curl_follow() */
else
disconnect_conn = TRUE;
}
Daniel Stenberg
committed
result = CURLM_CALL_MULTI_PERFORM;
}
}
if(newurl)
free(newurl);
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case CURLM_STATE_DONE:
/* this state is highly transient, so run another loop after this */
result = CURLM_CALL_MULTI_PERFORM;
CURLcode res;
/* Remove ourselves from the receive pipeline, if we are there. */
Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe);
/* Check if we can move pending requests to send pipe */
Curl_multi_process_pending_handles(multi);
/* post-transfer command */
res = Curl_done(&data->easy_conn, CURLE_OK, FALSE);
/* allow a previously set error code take precedence */
if(!data->result)
data->result = res;
/*
* If there are other handles on the pipeline, Curl_done won't set
* easy_conn to NULL. In such a case, curl_multi_remove_handle() can
* access free'd data, if the connection is free'd and the handle
* removed before we perform the processing in CURLM_STATE_COMPLETED
*/
}
if(data->set.wildcardmatch) {
if(data->wildcard.state != CURLWC_DONE) {
/* if a wildcard is set and we are not ending -> lets start again
/* after we have DONE what we're supposed to do, go COMPLETED, and
it doesn't matter what the Curl_done() returned! */
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case CURLM_STATE_COMPLETED:
/* this is a completed transfer, it is likely to still be connected */
Daniel Stenberg
committed
/* This node should be delinked from the list now and we should post
an information message that we are complete. */
/* Important: reset the conn pointer so that we don't point to memory
that could be freed anytime */
Curl_expire(data, 0); /* stop all timers */
Daniel Stenberg
committed
break;
case CURLM_STATE_MSGSENT:
return CURLM_OK; /* do nothing */
Daniel Stenberg
committed
default:
return CURLM_INTERNAL_ERROR;
}
if(data->mstate < CURLM_STATE_COMPLETED) {
if(CURLE_OK != data->result) {
Daniel Stenberg
committed
/*
* If an error was returned, and we aren't in completed state now,
* then we go to completed and consider this transfer aborted.
*/
/* NOTE: no attempt to disconnect connections must be made
in the case blocks above - cleanup happens only here */
data->state.pipe_broke = FALSE;
Daniel Stenberg
committed
Daniel Stenberg
committed
/* if this has a connection, unsubscribe from the pipelines */
data->easy_conn->writechannel_inuse = FALSE;
data->easy_conn->readchannel_inuse = FALSE;
Curl_removeHandleFromPipeline(data,
Curl_removeHandleFromPipeline(data,
Daniel Stenberg
committed
/* Check if we can move pending requests to send pipe */
Curl_multi_process_pending_handles(multi);
if(disconnect_conn) {
/* disconnect properly */
Curl_disconnect(data->easy_conn, /* dead_connection */ FALSE);
/* This is where we make sure that the easy_conn pointer is reset.
We don't have to do this in every case block above where a
failure is detected */
/* Curl_connect() failed */
(void)Curl_posttransfer(data);
Daniel Stenberg
committed
}
/* if there's still a connection to use, call the progress function */
else if(data->easy_conn && Curl_pgrsUpdate(data->easy_conn)) {
/* aborted due to progress callback return code must close the
connection */
data->result = CURLE_ABORTED_BY_CALLBACK;
/* if not yet in DONE state, go there, otherwise COMPLETED */
multistate(data, (data->mstate < CURLM_STATE_DONE)?
CURLM_STATE_DONE: CURLM_STATE_COMPLETED);
result = CURLM_CALL_MULTI_PERFORM;
Daniel Stenberg
committed
}
} WHILE_FALSE; /* just to break out from! */
/* now fill in the Curl_message with this info */
Daniel Stenberg
committed
msg->extmsg.msg = CURLMSG_DONE;
msg->extmsg.easy_handle = data;
result = multi_addmsg(multi, msg);
}
}
Daniel Stenberg
committed
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
Daniel Stenberg
committed
CURLMcode returncode=CURLM_OK;
struct Curl_tree *t;
struct timeval now = Curl_tvnow();
Daniel Stenberg
committed
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
CURLMcode result;
if(!wc->filelist) {
CURLcode ret = Curl_wildcard_init(wc); /* init wildcard structures */
if(ret)
return CURLM_OUT_OF_MEMORY;
}
}
Daniel Stenberg
committed
do
while(CURLM_CALL_MULTI_PERFORM == result);
Daniel Stenberg
committed
/* destruct wildcard structures if it is needed */
if(wc->state == CURLWC_DONE || result)
Curl_wildcard_dtor(wc);
}
Daniel Stenberg
committed
if(result)
returncode = result;
Daniel Stenberg
committed
}
/*
* Simply remove all expired timers from the splay since handles are dealt
* with unconditionally by this function and curl_multi_timeout() requires
* that already passed/handled expire times are removed from the splay.
*
* It is important that the 'now' value is set at the entry of this function
* and not for the current time as it may have ticked a little while since
* then and then we risk this loop to remove timers that actually have not
* been handled!
Daniel Stenberg
committed
*/
do {
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
if(t)
/* the removed may have another timeout in queue */
(void)add_next_timeout(now, multi, t->payload);
Daniel Stenberg
committed
Daniel Stenberg
committed
} while(t);
Daniel Stenberg
committed
*running_handles = multi->num_alive;
if(CURLM_OK >= returncode)
update_timer(multi);
Daniel Stenberg
committed
Daniel Stenberg
committed
return returncode;
}
static void close_all_connections(struct Curl_multi *multi)
{
struct connectdata *conn;
conn = Curl_conncache_find_first_connection(multi->conn_cache);
while(conn) {
conn->data = multi->closure_handle;
/* This will remove the connection from the cache */
(void)Curl_disconnect(conn, FALSE);
conn = Curl_conncache_find_first_connection(multi->conn_cache);
}
}
CURLMcode curl_multi_cleanup(CURLM *multi_handle)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct SessionHandle *data;
struct SessionHandle *nextdata;
/* Close all the connections in the connection cache */
close_all_connections(multi);
if(multi->closure_handle) {
sigpipe_ignore(multi->closure_handle, &pipe_st);
multi->closure_handle->dns.hostcache = multi->hostcache;
Curl_hostcache_clean(multi->closure_handle,
multi->closure_handle->dns.hostcache);
Curl_close(multi->closure_handle);
multi->closure_handle = NULL;
Curl_hash_destroy(multi->sockhash);
multi->sockhash = NULL;
Curl_conncache_destroy(multi->conn_cache);
multi->conn_cache = NULL;
/* remove the pending list of messages */
Curl_llist_destroy(multi->msglist, NULL);
data = multi->easyp;
while(data) {
nextdata=data->next;
if(data->dns.hostcachetype == HCACHE_MULTI) {
Daniel Stenberg
committed
/* clear out the usage of the shared DNS cache */
Curl_hostcache_clean(data, data->dns.hostcache);
data->dns.hostcache = NULL;
data->dns.hostcachetype = HCACHE_NONE;
Daniel Stenberg
committed
}
/* Clear the pointer to the connection cache */
data->multi = NULL; /* clear the association */
}
Curl_hash_destroy(multi->hostcache);
multi->hostcache = NULL;
/* Free the blacklists by setting them to NULL */
Curl_pipeline_set_site_blacklist(NULL, &multi->pipelining_site_bl);
Curl_pipeline_set_server_blacklist(NULL, &multi->pipelining_server_bl);
return CURLM_OK;
}
else
return CURLM_BAD_HANDLE;
}
/*
* curl_multi_info_read()
*
* This function is the primary way for a multi/multi_socket application to
* figure out if a transfer has ended. We MUST make this function as fast as
* possible as it will be polled frequently and we MUST NOT scan any lists in
* here to figure out things. We must scale fine to thousands of handles and
* beyond. The current design is fully O(1).
*/
CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
{
struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
struct Curl_message *msg;
*msgs_in_queue = 0; /* default to none */
if(GOOD_MULTI_HANDLE(multi) && Curl_llist_count(multi->msglist)) {
/* there is one or more messages in the list */
struct curl_llist_element *e;
/* extract the head of the list to return */
e = multi->msglist->head;
msg = e->ptr;
/* remove the extracted entry */
Curl_llist_remove(multi->msglist, e, NULL);
*msgs_in_queue = curlx_uztosi(Curl_llist_count(multi->msglist));
}
else
Daniel Stenberg
committed
/*
Daniel Stenberg
committed
* singlesocket() checks what sockets we deal with and their "action state"
* and if we have a different state in any of those sockets from last time we
* call the callback accordingly.
Daniel Stenberg
committed
*/
static void singlesocket(struct Curl_multi *multi,
Daniel Stenberg
committed
{
Daniel Stenberg
committed
curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
Daniel Stenberg
committed
int i;
struct Curl_sh_entry *entry;
curl_socket_t s;
int num;
Daniel Stenberg
committed
unsigned int curraction;
Daniel Stenberg
committed
bool remove_sock_from_hash;
Daniel Stenberg
committed
for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
Daniel Stenberg
committed
socks[i] = CURL_SOCKET_BAD;
Daniel Stenberg
committed
/* Fill in the 'current' struct with the state as it is now: what sockets to
supervise and for what actions */
curraction = multi_getsock(data, socks, MAX_SOCKSPEREASYHANDLE);
Daniel Stenberg
committed
/* We have 0 .. N sockets already and we get to know about the 0 .. M
sockets we should have from now on. Detect the differences, remove no
longer supervised ones and add new ones */
Daniel Stenberg
committed
/* walk over the sockets we got right now */
for(i=0; (i< MAX_SOCKSPEREASYHANDLE) &&
Daniel Stenberg
committed
(curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)));
i++) {
int action = CURL_POLL_NONE;
Daniel Stenberg
committed
Daniel Stenberg
committed
s = socks[i];
Daniel Stenberg
committed
/* get it from the hash */
entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
Daniel Stenberg
committed
Daniel Stenberg
committed
if(curraction & GETSOCK_READSOCK(i))
action |= CURL_POLL_IN;
Daniel Stenberg
committed
if(curraction & GETSOCK_WRITESOCK(i))
action |= CURL_POLL_OUT;
Daniel Stenberg
committed
if(entry) {
/* yeps, already present so check if it has the same action set */
if(entry->action == action)
/* same, continue */
continue;
}
else {
/* this is a socket we didn't have before, add it! */
if(!entry)
/* fatal */
return;
}
Daniel Stenberg
committed
/* we know (entry != NULL) at this point, see the logic above */
if(multi->socket_cb)
s,
action,
multi->socket_userp,
entry->socketp);
Daniel Stenberg
committed
entry->action = action; /* store the current action state */
}
Daniel Stenberg
committed
num = i; /* number of sockets */
/* when we've walked over all the sockets we should have right now, we must
make sure to detect sockets that are removed */
int j;
for(j=0; j<num; j++) {
Daniel Stenberg
committed
if(s == socks[j]) {
/* this is still supervised */
s = CURL_SOCKET_BAD;
break;
Daniel Stenberg
committed
}
}
if(s != CURL_SOCKET_BAD) {
Daniel Stenberg
committed
/* this socket has been removed. Tell the app to remove it */