summaryrefslogtreecommitdiffstats
path: root/Utilities/cmcurl/lib/multi.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/multi.c')
-rw-r--r--Utilities/cmcurl/lib/multi.c281
1 files changed, 155 insertions, 126 deletions
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c
index 8bb9366..2432b15 100644
--- a/Utilities/cmcurl/lib/multi.c
+++ b/Utilities/cmcurl/lib/multi.c
@@ -42,6 +42,7 @@
#include "multihandle.h"
#include "pipeline.h"
#include "sigpipe.h"
+#include "connect.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -442,7 +443,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
sockets that time-out or have actions will be dealt with. Since this
handle has no action yet, we make sure it times out to get things to
happen. */
- Curl_expire(data, 1);
+ Curl_expire(data, 0);
/* increase the node-counter */
multi->num_easy++;
@@ -462,6 +463,14 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
handle is added */
memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
+ /* The closure handle only ever has default timeouts set. To improve the
+ state somewhat we clone the timeouts from each added handle so that the
+ closure handle always has the same timeouts as the most recently added
+ easy handle. */
+ multi->closure_handle->set.timeout = data->set.timeout;
+ multi->closure_handle->set.server_response_timeout =
+ data->set.server_response_timeout;
+
update_timer(multi);
return CURLM_OK;
}
@@ -569,12 +578,12 @@ static CURLcode multi_done(struct connectdata **connp,
result = CURLE_ABORTED_BY_CALLBACK;
}
- if((!premature &&
- conn->send_pipe->size + conn->recv_pipe->size != 0 &&
- !data->set.reuse_forbid &&
- !conn->bits.close)) {
+ 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. */
+ data->easy_conn = NULL;
DEBUGF(infof(data, "Connection still in use, no more multi_done now!\n"));
return CURLE_OK;
}
@@ -685,7 +694,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
/* If the handle is in a pipeline and has started sending off its
request but not received its response yet, we need to close
connection. */
- connclose(data->easy_conn, "Removed with partial response");
+ streamclose(data->easy_conn, "Removed with partial response");
/* Set connection owner so that the DONE function closes it. We can
safely do this here since connection is killed. */
data->easy_conn->data = easy;
@@ -695,7 +704,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
/* The timer must be shut down before data->multi is set to NULL,
else the timenode will remain in the splay tree after
curl_easy_cleanup is called. */
- Curl_expire(data, 0);
+ Curl_expire_clear(data);
if(data->dns.hostcachetype == HCACHE_MULTI) {
/* stop using the multi handle's DNS cache */
@@ -1298,7 +1307,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
return CURLM_BAD_EASY_HANDLE;
do {
- bool disconnect_conn = FALSE;
+ /* A "stream" here is a logical stream if the protocol can handle that
+ (HTTP/2), or the full connection for older protocols */
+ bool stream_error = FALSE;
rc = CURLM_OK;
/* Handle the case when the pipe breaks, i.e., the connection
@@ -1376,8 +1387,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Force connection closed if the connection has indeed been used */
if(data->mstate > CURLM_STATE_DO) {
- connclose(data->easy_conn, "Disconnected with pending data");
- disconnect_conn = TRUE;
+ streamclose(data->easy_conn, "Disconnected with pending data");
+ stream_error = TRUE;
}
result = CURLE_OPERATION_TIMEDOUT;
(void)multi_done(&data->easy_conn, result, TRUE);
@@ -1426,7 +1437,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Add this handle to the send or pend pipeline */
result = Curl_add_handle_to_pipeline(data, data->easy_conn);
if(result)
- disconnect_conn = TRUE;
+ stream_error = TRUE;
else {
if(async)
/* We're now waiting for an asynchronous name lookup */
@@ -1518,7 +1529,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(result) {
/* failure detected */
- disconnect_conn = TRUE;
+ stream_error = TRUE;
break;
}
}
@@ -1558,7 +1569,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else if(result) {
/* failure detected */
/* Just break, the cleaning up is handled all in one place */
- disconnect_conn = TRUE;
+ stream_error = TRUE;
break;
}
break;
@@ -1578,7 +1589,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* failure detected */
Curl_posttransfer(data);
multi_done(&data->easy_conn, result, TRUE);
- disconnect_conn = TRUE;
+ stream_error = TRUE;
}
break;
@@ -1595,7 +1606,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* failure detected */
Curl_posttransfer(data);
multi_done(&data->easy_conn, result, TRUE);
- disconnect_conn = TRUE;
+ stream_error = TRUE;
}
break;
@@ -1670,7 +1681,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(drc) {
/* a failure here pretty much implies an out of memory */
result = drc;
- disconnect_conn = TRUE;
+ stream_error = TRUE;
}
else
retry = (newurl)?TRUE:FALSE;
@@ -1703,7 +1714,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
else {
/* Have error handler disconnect conn if we can't retry */
- disconnect_conn = TRUE;
+ stream_error = TRUE;
free(newurl);
}
}
@@ -1712,7 +1723,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
Curl_posttransfer(data);
if(data->easy_conn)
multi_done(&data->easy_conn, result, FALSE);
- disconnect_conn = TRUE;
+ stream_error = TRUE;
}
}
break;
@@ -1734,7 +1745,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* failure detected */
Curl_posttransfer(data);
multi_done(&data->easy_conn, result, FALSE);
- disconnect_conn = TRUE;
+ stream_error = TRUE;
}
break;
@@ -1763,7 +1774,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* failure detected */
Curl_posttransfer(data);
multi_done(&data->easy_conn, result, FALSE);
- disconnect_conn = TRUE;
+ stream_error = TRUE;
}
break;
@@ -1800,9 +1811,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
result = Curl_speedcheck(data, now);
if(( (data->set.max_send_speed == 0) ||
- (data->progress.ulspeed < data->set.max_send_speed)) &&
+ (Curl_pgrsLimitWaitTime(data->progress.uploaded,
+ data->progress.ul_limit_size,
+ data->set.max_send_speed,
+ data->progress.ul_limit_start,
+ now) <= 0)) &&
( (data->set.max_recv_speed == 0) ||
- (data->progress.dlspeed < data->set.max_recv_speed)))
+ (Curl_pgrsLimitWaitTime(data->progress.downloaded,
+ data->progress.dl_limit_size,
+ data->set.max_recv_speed,
+ data->progress.dl_limit_start,
+ now) <= 0)))
multistate(data, CURLM_STATE_PERFORM);
break;
@@ -1810,41 +1829,38 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
{
char *newurl = NULL;
bool retry = FALSE;
+ bool comeback = FALSE;
/* check if over send speed */
- if((data->set.max_send_speed > 0) &&
- (data->progress.ulspeed > data->set.max_send_speed)) {
- int buffersize;
-
- multistate(data, CURLM_STATE_TOOFAST);
-
- /* 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_latest(data, timeout_ms);
- break;
+ if(data->set.max_send_speed > 0) {
+ timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded,
+ data->progress.ul_limit_size,
+ data->set.max_send_speed,
+ data->progress.ul_limit_start,
+ now);
+ if(timeout_ms > 0) {
+ multistate(data, CURLM_STATE_TOOFAST);
+ Curl_expire_latest(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;
-
- multistate(data, CURLM_STATE_TOOFAST);
-
- /* 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_latest(data, timeout_ms);
- break;
+ if(data->set.max_recv_speed > 0) {
+ timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded,
+ data->progress.dl_limit_size,
+ data->set.max_recv_speed,
+ data->progress.dl_limit_start,
+ now);
+ if(timeout_ms > 0) {
+ multistate(data, CURLM_STATE_TOOFAST);
+ Curl_expire_latest(data, timeout_ms);
+ break;
+ }
}
/* read/write data if it is ready to do so */
- result = Curl_readwrite(data->easy_conn, data, &done);
+ result = Curl_readwrite(data->easy_conn, data, &done, &comeback);
k = &data->req;
@@ -1884,10 +1900,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(!(data->easy_conn->handler->flags & PROTOPT_DUAL) &&
result != CURLE_HTTP2_STREAM)
- connclose(data->easy_conn, "Transfer returned error");
+ streamclose(data->easy_conn, "Transfer returned error");
Curl_posttransfer(data);
- multi_done(&data->easy_conn, result, FALSE);
+ multi_done(&data->easy_conn, result, TRUE);
}
else if(done) {
followtype follow=FOLLOW_NONE;
@@ -1900,7 +1916,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* expire the new receiving pipeline head */
if(data->easy_conn->recv_pipe->head)
- Curl_expire_latest(data->easy_conn->recv_pipe->head->ptr, 1);
+ Curl_expire_latest(data->easy_conn->recv_pipe->head->ptr, 0);
/* Check if we can move pending requests to send pipe */
Curl_multi_process_pending_handles(multi);
@@ -1943,13 +1959,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(!result)
newurl = NULL; /* allocation was handed over Curl_follow() */
else
- disconnect_conn = TRUE;
+ stream_error = TRUE;
}
multistate(data, CURLM_STATE_DONE);
rc = CURLM_CALL_MULTI_PERFORM;
}
}
+ else if(comeback)
+ rc = CURLM_CALL_MULTI_PERFORM;
free(newurl);
break;
@@ -2008,7 +2026,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
that could be freed anytime */
data->easy_conn = NULL;
- Curl_expire(data, 0); /* stop all timers */
+ Curl_expire_clear(data); /* stop all timers */
break;
case CURLM_STATE_MSGSENT:
@@ -2042,7 +2060,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
Curl_removeHandleFromPipeline(data, data->easy_conn->send_pipe);
Curl_removeHandleFromPipeline(data, data->easy_conn->recv_pipe);
- if(disconnect_conn) {
+ if(stream_error) {
/* Don't attempt to send data over a connection that timed out */
bool dead_connection = result == CURLE_OPERATION_TIMEDOUT;
/* disconnect properly */
@@ -2066,7 +2084,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* aborted due to progress callback return code must close the
connection */
result = CURLE_ABORTED_BY_CALLBACK;
- connclose(data->easy_conn, "Aborted by callback");
+ streamclose(data->easy_conn, "Aborted by callback");
/* if not yet in DONE state, go there, otherwise COMPLETED */
multistate(data, (data->mstate < CURLM_STATE_DONE)?
@@ -2160,6 +2178,7 @@ static void close_all_connections(struct Curl_multi *multi)
conn->data->easy_conn = NULL; /* clear the easy handle's connection
pointer */
/* This will remove the connection from the cache */
+ connclose(conn, "kill all");
(void)Curl_disconnect(conn, FALSE);
sigpipe_restore(&pipe_st);
@@ -2876,92 +2895,59 @@ multi_addtimeout(struct curl_llist *timeoutlist,
* given a number of milliseconds from now to use to set the 'act before
* this'-time for the transfer, to be extracted by curl_multi_timeout()
*
- * Note that the timeout will be added to a queue of timeouts if it defines a
- * moment in time that is later than the current head of queue.
- *
- * Pass zero to clear all timeout values for this handle.
-*/
+ * The timeout will be added to a queue of timeouts if it defines a moment in
+ * time that is later than the current head of queue.
+ */
void Curl_expire(struct Curl_easy *data, long milli)
{
struct Curl_multi *multi = data->multi;
struct timeval *nowp = &data->state.expiretime;
int rc;
+ struct timeval set;
/* this is only interesting while there is still an associated multi struct
remaining! */
if(!multi)
return;
- if(!milli) {
- /* No timeout, clear the time data. */
- if(nowp->tv_sec || nowp->tv_usec) {
- /* Since this is an cleared time, we must remove the previous entry from
- the splay tree */
- struct curl_llist *list = data->state.timeoutlist;
-
- rc = Curl_splayremovebyaddr(multi->timetree,
- &data->state.timenode,
- &multi->timetree);
- if(rc)
- infof(data, "Internal error clearing splay node = %d\n", rc);
-
- /* flush the timeout list too */
- while(list->size > 0)
- Curl_llist_remove(list, list->tail, NULL);
+ set = Curl_tvnow();
+ set.tv_sec += milli/1000;
+ set.tv_usec += (milli%1000)*1000;
-#ifdef DEBUGBUILD
- infof(data, "Expire cleared\n");
-#endif
- nowp->tv_sec = 0;
- nowp->tv_usec = 0;
- }
+ if(set.tv_usec >= 1000000) {
+ set.tv_sec++;
+ set.tv_usec -= 1000000;
}
- else {
- struct timeval set;
-
- set = Curl_tvnow();
- set.tv_sec += milli/1000;
- set.tv_usec += (milli%1000)*1000;
-
- if(set.tv_usec >= 1000000) {
- set.tv_sec++;
- set.tv_usec -= 1000000;
- }
-
- if(nowp->tv_sec || nowp->tv_usec) {
- /* This means that the struct is added as a node in the splay tree.
- Compare if the new time is earlier, and only remove-old/add-new if it
- is. */
- long diff = curlx_tvdiff(set, *nowp);
- if(diff > 0) {
- /* the new expire time was later so just add it to the queue
- and get out */
- multi_addtimeout(data->state.timeoutlist, &set);
- return;
- }
- /* the new time is newer than the presently set one, so add the current
- to the queue and update the head */
- multi_addtimeout(data->state.timeoutlist, nowp);
-
- /* Since this is an updated time, we must remove the previous entry from
- the splay tree first and then re-add the new value */
- rc = Curl_splayremovebyaddr(multi->timetree,
- &data->state.timenode,
- &multi->timetree);
- if(rc)
- infof(data, "Internal error removing splay node = %d\n", rc);
+ if(nowp->tv_sec || nowp->tv_usec) {
+ /* This means that the struct is added as a node in the splay tree.
+ Compare if the new time is earlier, and only remove-old/add-new if it
+ is. */
+ long diff = curlx_tvdiff(set, *nowp);
+ if(diff > 0) {
+ /* the new expire time was later so just add it to the queue
+ and get out */
+ multi_addtimeout(data->state.timeoutlist, &set);
+ return;
}
- *nowp = set;
- data->state.timenode.payload = data;
- multi->timetree = Curl_splayinsert(*nowp,
- multi->timetree,
- &data->state.timenode);
+ /* the new time is newer than the presently set one, so add the current
+ to the queue and update the head */
+ multi_addtimeout(data->state.timeoutlist, nowp);
+
+ /* Since this is an updated time, we must remove the previous entry from
+ the splay tree first and then re-add the new value */
+ rc = Curl_splayremovebyaddr(multi->timetree,
+ &data->state.timenode,
+ &multi->timetree);
+ if(rc)
+ infof(data, "Internal error removing splay node = %d\n", rc);
}
-#if 0
- Curl_splayprint(multi->timetree, 0, TRUE);
-#endif
+
+ *nowp = set;
+ data->state.timenode.payload = data;
+ multi->timetree = Curl_splayinsert(*nowp, multi->timetree,
+ &data->state.timenode);
}
/*
@@ -3004,6 +2990,49 @@ void Curl_expire_latest(struct Curl_easy *data, long milli)
Curl_expire(data, milli);
}
+
+/*
+ * Curl_expire_clear()
+ *
+ * Clear ALL timeout values for this handle.
+ */
+void Curl_expire_clear(struct Curl_easy *data)
+{
+ struct Curl_multi *multi = data->multi;
+ struct timeval *nowp = &data->state.expiretime;
+ int rc;
+
+ /* this is only interesting while there is still an associated multi struct
+ remaining! */
+ if(!multi)
+ return;
+
+ if(nowp->tv_sec || nowp->tv_usec) {
+ /* Since this is an cleared time, we must remove the previous entry from
+ the splay tree */
+ struct curl_llist *list = data->state.timeoutlist;
+
+ rc = Curl_splayremovebyaddr(multi->timetree,
+ &data->state.timenode,
+ &multi->timetree);
+ if(rc)
+ infof(data, "Internal error clearing splay node = %d\n", rc);
+
+ /* flush the timeout list too */
+ while(list->size > 0)
+ Curl_llist_remove(list, list->tail, NULL);
+
+#ifdef DEBUGBUILD
+ infof(data, "Expire cleared\n");
+#endif
+ nowp->tv_sec = 0;
+ nowp->tv_usec = 0;
+ }
+}
+
+
+
+
CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
void *hashp)
{
@@ -3064,7 +3093,7 @@ void Curl_multi_process_pending_handles(struct Curl_multi *multi)
Curl_llist_remove(multi->pending, e, NULL);
/* Make sure that the handle will be processed soonish. */
- Curl_expire_latest(data, 1);
+ Curl_expire_latest(data, 0);
}
e = next; /* operate on next handle */