diff options
Diffstat (limited to 'libs/libcurl/src/multi.c')
-rw-r--r-- | libs/libcurl/src/multi.c | 226 |
1 files changed, 130 insertions, 96 deletions
diff --git a/libs/libcurl/src/multi.c b/libs/libcurl/src/multi.c index 9728e5a2f2..f852846952 100644 --- a/libs/libcurl/src/multi.c +++ b/libs/libcurl/src/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -77,6 +77,7 @@ static CURLMcode add_next_timeout(struct curltime now, struct Curl_easy *d); static CURLMcode multi_timeout(struct Curl_multi *multi, long *timeout_ms); +static void process_pending_handles(struct Curl_multi *multi); #ifdef DEBUGBUILD static const char * const statename[]={ @@ -366,6 +367,9 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, if(data->multi) return CURLM_ADDED_ALREADY; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + /* Initialize timeout list for this handle */ Curl_llist_init(&data->state.timeoutlist, NULL); @@ -375,6 +379,8 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, * potential multi's connection cache growing which won't be undone in this * function no matter what. */ + if(data->set.errorbuffer) + data->set.errorbuffer[0] = 0; /* set the easy handle */ multistate(data, CURLM_STATE_INIT); @@ -479,38 +485,6 @@ static void debug_print_sock_hash(void *p) } #endif -/* Mark the connection as 'idle', or close it if the cache is full. - Returns TRUE if the connection is kept, or FALSE if it was closed. */ -static bool -ConnectionDone(struct Curl_easy *data, struct connectdata *conn) -{ - /* data->multi->maxconnects can be negative, deal with it. */ - size_t maxconnects = - (data->multi->maxconnects < 0) ? data->multi->num_easy * 4: - data->multi->maxconnects; - struct connectdata *conn_candidate = NULL; - - /* Mark the current connection as 'unused' */ - conn->inuse = FALSE; - - if(maxconnects > 0 && - data->state.conn_cache->num_connections > maxconnects) { - infof(data, "Connection cache is full, closing the oldest one.\n"); - - conn_candidate = Curl_conncache_oldest_idle(data); - - if(conn_candidate) { - /* Set the connection's owner correctly */ - conn_candidate->data = data; - - /* the winner gets the honour of being disconnected */ - (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); - } - } - - return (conn_candidate == conn) ? FALSE : TRUE; -} - static CURLcode multi_done(struct connectdata **connp, CURLcode status, /* an error if this is called after an error was detected */ @@ -567,13 +541,14 @@ static CURLcode multi_done(struct connectdata **connp, result = CURLE_ABORTED_BY_CALLBACK; } - if(conn->send_pipe.size + conn->recv_pipe.size != 0 && - !data->set.reuse_forbid && - !conn->bits.close) { - /* Stop if pipeline is not empty and we do not have to close - connection. */ + process_pending_handles(data->multi); /* connection / multiplex */ + + if(conn->send_pipe.size || conn->recv_pipe.size) { + /* Stop if pipeline is not empty . */ data->easy_conn = NULL; - DEBUGF(infof(data, "Connection still in use, no more multi_done now!\n")); + DEBUGF(infof(data, "Connection still in use %d/%d, " + "no more multi_done now!\n", + conn->send_pipe.size, conn->recv_pipe.size)); return CURLE_OK; } @@ -584,6 +559,7 @@ static CURLcode multi_done(struct connectdata **connp, Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ conn->dns_entry = NULL; } + Curl_hostcache_prune(data); /* if the transfer was completed in a paused state there can be buffered data left to free */ @@ -612,7 +588,8 @@ static CURLcode multi_done(struct connectdata **connp, && !(conn->ntlm.state == NTLMSTATE_TYPE2 || conn->proxyntlm.state == NTLMSTATE_TYPE2) #endif - ) || conn->bits.close || premature) { + ) || conn->bits.close + || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { CURLcode res2 = Curl_disconnect(conn, premature); /* close connection */ /* If we had an error already, make sure we return that one. But @@ -621,17 +598,21 @@ static CURLcode multi_done(struct connectdata **connp, result = res2; } else { + char buffer[256]; + /* create string before returning the connection */ + snprintf(buffer, sizeof(buffer), + "Connection #%ld to host %s left intact", + conn->connection_id, + conn->bits.socksproxy ? conn->socks_proxy.host.dispname : + conn->bits.httpproxy ? conn->http_proxy.host.dispname : + conn->bits.conn_to_host ? conn->conn_to_host.dispname : + conn->host.dispname); + /* the connection is no longer in use */ - if(ConnectionDone(data, conn)) { + if(Curl_conncache_return_conn(conn)) { /* remember the most recently used connection */ data->state.lastconnect = conn; - - infof(data, "Connection #%ld to host %s left intact\n", - conn->connection_id, - conn->bits.socksproxy ? conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname); + infof(data, "%s\n", buffer); } else data->state.lastconnect = NULL; @@ -666,6 +647,9 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, if(!data->multi) return CURLM_OK; /* it is already removed so let's say it is fine! */ + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE; easy_owns_conn = (data->easy_conn && (data->easy_conn->data == easy)) ? TRUE : FALSE; @@ -676,10 +660,6 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, /* this handle is "alive" so we need to count down the total number of alive connections when this is removed */ multi->num_alive--; - - /* When this handle gets removed, other handles may be able to get the - connection */ - Curl_multi_process_pending_handles(multi); } if(data->easy_conn && @@ -929,6 +909,9 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + data = multi->easyp; while(data) { bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE); @@ -982,6 +965,9 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi, if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + /* If the internally desired timeout is actually shorter than requested from the outside, then use the shorter time! But only if the internal timer is actually larger than -1! */ @@ -1147,6 +1133,9 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, { CURLMcode rc; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + rc = curl_multi_add_handle(multi, data); if(!rc) { struct SingleRequest *k = &data->req; @@ -1353,20 +1342,20 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(multi_ischanged(multi, TRUE)) { DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n")); - Curl_multi_process_pending_handles(multi); + process_pending_handles(multi); /* pipelined/multiplexed */ } if(data->easy_conn && data->mstate > CURLM_STATE_CONNECT && - data->mstate < CURLM_STATE_COMPLETED) + data->mstate < CURLM_STATE_COMPLETED) { /* Make sure we set the connection's current owner */ data->easy_conn->data = data; + } if(data->easy_conn && (data->mstate >= CURLM_STATE_CONNECT) && (data->mstate < CURLM_STATE_COMPLETED)) { /* we need to wait for the connect state as only then is the start time stored, but we must not check already completed handles */ - timeout_ms = Curl_timeleft(data, &now, (data->mstate <= CURLM_STATE_WAITDO)? TRUE:FALSE); @@ -1799,16 +1788,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case CURLM_STATE_DO_DONE: /* Move ourselves from the send to recv pipeline */ Curl_move_handle_from_send_to_recv_pipe(data, data->easy_conn); - /* Check if we can move pending requests to send pipe */ - Curl_multi_process_pending_handles(multi); + + if(data->easy_conn->bits.multiplex || data->easy_conn->send_pipe.size) + /* Check if we can move pending requests to send pipe */ + process_pending_handles(multi); /* pipelined/multiplexed */ /* 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 - { + else { if(data->state.wildcardmatch && ((data->easy_conn->handler->flags & PROTOPT_WILDCARD) == 0)) { data->wildcard.state = CURLWC_DONE; @@ -1837,22 +1827,26 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!result) { send_timeout_ms = 0; if(data->set.max_send_speed > 0) - send_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded, - data->progress.ul_limit_size, - data->set.max_send_speed, - data->progress.ul_limit_start, - now); + send_timeout_ms = + Curl_pgrsLimitWaitTime(data->progress.uploaded, + data->progress.ul_limit_size, + data->set.max_send_speed, + data->progress.ul_limit_start, + now); recv_timeout_ms = 0; if(data->set.max_recv_speed > 0) - recv_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded, - data->progress.dl_limit_size, - data->set.max_recv_speed, - data->progress.dl_limit_start, - now); - - if(send_timeout_ms <= 0 && recv_timeout_ms <= 0) + recv_timeout_ms = + Curl_pgrsLimitWaitTime(data->progress.downloaded, + data->progress.dl_limit_size, + data->set.max_recv_speed, + data->progress.dl_limit_start, + now); + + if(!send_timeout_ms && !recv_timeout_ms) { multistate(data, CURLM_STATE_PERFORM); + Curl_ratelimit(data, now); + } else if(send_timeout_ms >= recv_timeout_ms) Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); else @@ -1884,7 +1878,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->progress.dl_limit_start, now); - if(send_timeout_ms > 0 || recv_timeout_ms > 0) { + if(send_timeout_ms || recv_timeout_ms) { + Curl_ratelimit(data, now); multistate(data, CURLM_STATE_TOOFAST); if(send_timeout_ms >= recv_timeout_ms) Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); @@ -1952,9 +1947,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(data->easy_conn->recv_pipe.head) Curl_expire(data->easy_conn->recv_pipe.head->ptr, 0, EXPIRE_RUN_NOW); - /* Check if we can move pending requests to send pipe */ - Curl_multi_process_pending_handles(multi); - /* 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) { @@ -2011,8 +2003,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* 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); + + if(data->easy_conn->bits.multiplex || data->easy_conn->send_pipe.size) + /* Check if we can move pending requests to connection */ + process_pending_handles(multi); /* pipelined/multiplexing */ /* post-transfer command */ res = multi_done(&data->easy_conn, result, FALSE); @@ -2080,7 +2074,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->state.pipe_broke = FALSE; /* Check if we can move pending requests to send pipe */ - Curl_multi_process_pending_handles(multi); + process_pending_handles(multi); /* connection */ if(data->easy_conn) { /* if this has a connection, unsubscribe from the pipelines */ @@ -2153,6 +2147,9 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + data = multi->easyp; while(data) { CURLMcode result; @@ -2200,6 +2197,9 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) struct Curl_easy *nextdata; if(GOOD_MULTI_HANDLE(multi)) { + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + multi->type = 0; /* not good anymore */ /* Firsrt remove all remaining easy handles */ @@ -2260,7 +2260,9 @@ CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue) *msgs_in_queue = 0; /* default to none */ - if(GOOD_MULTI_HANDLE(multi) && Curl_llist_count(&multi->msglist)) { + if(GOOD_MULTI_HANDLE(multi) && + !multi->in_callback && + Curl_llist_count(&multi->msglist)) { /* there is one or more messages in the list */ struct curl_llist_element *e; @@ -2422,6 +2424,12 @@ static void singlesocket(struct Curl_multi *multi, data->numsocks = num; } +void Curl_updatesocket(struct Curl_easy *data) +{ + singlesocket(data->multi, data); +} + + /* * Curl_multi_closed() * @@ -2650,6 +2658,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + va_start(param, option); switch(option) { @@ -2714,7 +2725,10 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, int *running_handles) { - CURLMcode result = multi_socket(multi, FALSE, s, 0, running_handles); + CURLMcode result; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + result = multi_socket(multi, FALSE, s, 0, running_handles); if(CURLM_OK >= result) update_timer(multi); return result; @@ -2723,8 +2737,10 @@ CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, int ev_bitmask, int *running_handles) { - CURLMcode result = multi_socket(multi, FALSE, s, - ev_bitmask, running_handles); + CURLMcode result; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles); if(CURLM_OK >= result) update_timer(multi); return result; @@ -2733,8 +2749,10 @@ CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles) { - CURLMcode result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, - running_handles); + CURLMcode result; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); if(CURLM_OK >= result) update_timer(multi); return result; @@ -2786,6 +2804,9 @@ CURLMcode curl_multi_timeout(struct Curl_multi *multi, if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + return multi_timeout(multi, timeout_ms); } @@ -3018,6 +3039,9 @@ CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s, { struct Curl_sh_entry *there = NULL; + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + there = sh_getentry(&multi->sockhash, s); if(!there) @@ -3058,28 +3082,38 @@ struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi) return &multi->pipelining_server_bl; } -void Curl_multi_process_pending_handles(struct Curl_multi *multi) +static void process_pending_handles(struct Curl_multi *multi) { struct curl_llist_element *e = multi->pending.head; - - while(e) { + if(e) { struct Curl_easy *data = e->ptr; - struct curl_llist_element *next = e->next; - if(data->mstate == CURLM_STATE_CONNECT_PEND) { - multistate(data, CURLM_STATE_CONNECT); + DEBUGASSERT(data->mstate == CURLM_STATE_CONNECT_PEND); - /* Remove this node from the list */ - Curl_llist_remove(&multi->pending, e, NULL); + multistate(data, CURLM_STATE_CONNECT); - /* Make sure that the handle will be processed soonish. */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } + /* Remove this node from the list */ + Curl_llist_remove(&multi->pending, e, NULL); - e = next; /* operate on next handle */ + /* Make sure that the handle will be processed soonish. */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); } } +void Curl_set_in_callback(struct Curl_easy *easy, bool value) +{ + if(easy->multi_easy) + easy->multi_easy->in_callback = value; + else if(easy->multi) + easy->multi->in_callback = value; +} + +bool Curl_is_in_callback(struct Curl_easy *easy) +{ + return ((easy->multi && easy->multi->in_callback) || + (easy->multi_easy && easy->multi_easy->in_callback)); +} + #ifdef DEBUGBUILD void Curl_multi_dump(struct Curl_multi *multi) { |