summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/multi.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/multi.c')
-rw-r--r--libs/libcurl/src/multi.c226
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)
{