aboutsummaryrefslogtreecommitdiff
path: root/lib/multi.c
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2016-03-30 00:17:02 +0200
committerDaniel Stenberg <daniel@haxx.se>2016-03-30 07:52:28 +0200
commit575e885db0f3b2c50434f63269438fe21cbbb978 (patch)
tree48bd63f9b3ccd9220f0de284de6fd678311f2134 /lib/multi.c
parent93935c08c1b85482bc86bd6adcc33c1d924eed5e (diff)
multi: turn Curl_done into file local multi_done
... as it now is used by multi.c only.
Diffstat (limited to 'lib/multi.c')
-rw-r--r--lib/multi.c217
1 files changed, 188 insertions, 29 deletions
diff --git a/lib/multi.c b/lib/multi.c
index e88d7d492..522671d3e 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -484,6 +484,167 @@ 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 SessionHandle *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_oldest_idle_connection(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 */
+ bool premature)
+{
+ CURLcode result;
+ struct connectdata *conn;
+ struct SessionHandle *data;
+
+ DEBUGASSERT(*connp);
+
+ conn = *connp;
+ data = conn->data;
+
+ DEBUGF(infof(data, "multi_done\n"));
+
+ if(data->state.done)
+ /* Stop if multi_done() has already been called */
+ return CURLE_OK;
+
+ Curl_getoff_all_pipelines(data, conn);
+
+ /* Cleanup possible redirect junk */
+ free(data->req.newurl);
+ data->req.newurl = NULL;
+ free(data->req.location);
+ data->req.location = NULL;
+
+ switch(status) {
+ case CURLE_ABORTED_BY_CALLBACK:
+ case CURLE_READ_ERROR:
+ case CURLE_WRITE_ERROR:
+ /* When we're aborted due to a callback return code it basically have to
+ be counted as premature as there is trouble ahead if we don't. We have
+ many callbacks and protocols work differently, we could potentially do
+ this more fine-grained in the future. */
+ premature = TRUE;
+ default:
+ break;
+ }
+
+ /* this calls the protocol-specific function pointer previously set */
+ if(conn->handler->done)
+ result = conn->handler->done(conn, status, premature);
+ else
+ result = status;
+
+ if(CURLE_ABORTED_BY_CALLBACK != result) {
+ /* avoid this if we already aborted by callback to avoid this calling
+ another callback */
+ CURLcode rc = Curl_pgrsDone(conn);
+ if(!result && rc)
+ result = CURLE_ABORTED_BY_CALLBACK;
+ }
+
+ if((!premature &&
+ 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. */
+ DEBUGF(infof(data, "Connection still in use, no more multi_done now!\n"));
+ return CURLE_OK;
+ }
+
+ data->state.done = TRUE; /* called just now! */
+ Curl_resolver_cancel(conn);
+
+ if(conn->dns_entry) {
+ Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
+ conn->dns_entry = NULL;
+ }
+
+ /* if the transfer was completed in a paused state there can be buffered
+ data left to write and then kill */
+ free(data->state.tempwrite);
+ data->state.tempwrite = NULL;
+
+ /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
+ forced us to close this connection. This is ignored for requests taking
+ place in a NTLM authentication handshake
+
+ if conn->bits.close is TRUE, it means that the connection should be
+ closed in spite of all our efforts to be nice, due to protocol
+ restrictions in our or the server's end
+
+ if premature is TRUE, it means this connection was said to be DONE before
+ the entire request operation is complete and thus we can't know in what
+ state it is for re-using, so we're forced to close it. In a perfect world
+ we can add code that keep track of if we really must close it here or not,
+ but currently we have no such detail knowledge.
+ */
+
+ if((data->set.reuse_forbid
+#if defined(USE_NTLM)
+ && !(conn->ntlm.state == NTLMSTATE_TYPE2 ||
+ conn->proxyntlm.state == NTLMSTATE_TYPE2)
+#endif
+ ) || conn->bits.close || premature) {
+ CURLcode res2 = Curl_disconnect(conn, premature); /* close connection */
+
+ /* If we had an error already, make sure we return that one. But
+ if we got a new error, return that. */
+ if(!result && res2)
+ result = res2;
+ }
+ else {
+ /* the connection is no longer in use */
+ if(ConnectionDone(data, 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.httpproxy?conn->proxy.dispname:conn->host.dispname);
+ }
+ else
+ data->state.lastconnect = NULL;
+ }
+
+ *connp = NULL; /* to make the caller of this function better detect that
+ this was either closed or handed over to the connection
+ cache here, and therefore cannot be used from this point on
+ */
+ Curl_free_request_state(data);
+
+ return result;
+}
+
CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
CURL *curl_handle)
{
@@ -529,8 +690,8 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
request but not received its response yet, we need to close
connection. */
connclose(data->easy_conn, "Removed with partial response");
- /* Set connection owner so that Curl_done() closes it.
- We can safely do this here since connection is killed. */
+ /* 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;
easy_owns_conn = TRUE;
}
@@ -548,26 +709,26 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
if(data->easy_conn) {
- /* we must call Curl_done() here (if we still "own it") so that we don't
- leave a half-baked one around */
+ /* we must call multi_done() here (if we still own the connection) so that
+ we don't leave a half-baked one around */
if(easy_owns_conn) {
- /* Curl_done() clears the conn->data field to lose the association
+ /* multi_done() clears the conn->data field to lose the association
between the easy handle and the connection
Note that this ignores the return code simply because there's
nothing really useful to do with it anyway! */
- (void)Curl_done(&data->easy_conn, data->result, premature);
+ (void)multi_done(&data->easy_conn, data->result, premature);
}
else
- /* Clear connection pipelines, if Curl_done above was not called */
+ /* Clear connection pipelines, if multi_done above was not called */
Curl_getoff_all_pipelines(data, data->easy_conn);
}
Curl_wildcard_dtor(&data->wildcard);
/* destroy the timeout list that is held in the easy handle, do this *after*
- Curl_done() as that may actuall call Curl_expire that uses this */
+ multi_done() as that may actually call Curl_expire that uses this */
if(data->state.timeoutlist) {
Curl_llist_destroy(data->state.timeoutlist, NULL);
data->state.timeoutlist = NULL;
@@ -1005,18 +1166,16 @@ static CURLcode multi_reconnect_request(struct connectdata **connp)
infof(data, "Re-used connection seems dead, get a new one\n");
connclose(conn, "Reconnect dead connection"); /* enforce close */
- result = Curl_done(&conn, result, FALSE); /* we are so done with this */
+ result = multi_done(&conn, result, FALSE); /* we are so done with this */
/* conn may no longer be a good pointer, clear it to avoid mistakes by
parent functions */
*connp = NULL;
/*
- * According to bug report #1330310. We need to check for CURLE_SEND_ERROR
- * here as well. I figure this could happen when the request failed on a FTP
- * connection and thus Curl_done() itself tried to use the connection
- * (again). Slight Lack of feedback in the report, but I don't think this
- * extra check can do much harm.
+ * We need to check for CURLE_SEND_ERROR here as well. This could happen
+ * when the request failed on a FTP connection and thus multi_done() itself
+ * tried to use the connection (again).
*/
if(!result || (CURLE_SEND_ERROR == result)) {
bool async;
@@ -1227,7 +1386,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
disconnect_conn = TRUE;
}
result = CURLE_OPERATION_TIMEDOUT;
- (void)Curl_done(&data->easy_conn, result, TRUE);
+ (void)multi_done(&data->easy_conn, result, TRUE);
/* Skip the statemachine and go directly to error handling section. */
goto statemachine_end;
}
@@ -1372,7 +1531,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
rc = CURLM_CALL_MULTI_PERFORM;
/* connect back to proxy again */
result = CURLE_OK;
- Curl_done(&data->easy_conn, CURLE_OK, FALSE);
+ multi_done(&data->easy_conn, CURLE_OK, FALSE);
multistate(data, CURLM_STATE_CONNECT);
}
else if(!result) {
@@ -1416,7 +1575,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else if(result) {
/* failure detected */
Curl_posttransfer(data);
- Curl_done(&data->easy_conn, result, TRUE);
+ multi_done(&data->easy_conn, result, TRUE);
disconnect_conn = TRUE;
}
break;
@@ -1433,7 +1592,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else if(result) {
/* failure detected */
Curl_posttransfer(data);
- Curl_done(&data->easy_conn, result, TRUE);
+ multi_done(&data->easy_conn, result, TRUE);
disconnect_conn = TRUE;
}
break;
@@ -1468,7 +1627,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
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);
+ multi_done(&data->easy_conn, CURLE_OK, FALSE);
multistate(data, CURLM_STATE_DONE);
rc = CURLM_CALL_MULTI_PERFORM;
break;
@@ -1515,7 +1674,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
retry = (newurl)?TRUE:FALSE;
Curl_posttransfer(data);
- drc = Curl_done(&data->easy_conn, result, FALSE);
+ drc = multi_done(&data->easy_conn, result, FALSE);
/* When set to retry the connection, we must to go back to
* the CONNECT state */
@@ -1550,7 +1709,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* failure detected */
Curl_posttransfer(data);
if(data->easy_conn)
- Curl_done(&data->easy_conn, result, FALSE);
+ multi_done(&data->easy_conn, result, FALSE);
disconnect_conn = TRUE;
}
}
@@ -1572,7 +1731,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else {
/* failure detected */
Curl_posttransfer(data);
- Curl_done(&data->easy_conn, result, FALSE);
+ multi_done(&data->easy_conn, result, FALSE);
disconnect_conn = TRUE;
}
break;
@@ -1584,7 +1743,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
result = multi_do_more(data->easy_conn, &control);
/* No need to remove this handle from the send pipeline here since that
- is done in Curl_done() */
+ is done in multi_done() */
if(!result) {
if(control) {
/* if positive, advance to DO_DONE
@@ -1601,7 +1760,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else {
/* failure detected */
Curl_posttransfer(data);
- Curl_done(&data->easy_conn, result, FALSE);
+ multi_done(&data->easy_conn, result, FALSE);
disconnect_conn = TRUE;
}
break;
@@ -1725,7 +1884,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
connclose(data->easy_conn, "Transfer returned error");
Curl_posttransfer(data);
- Curl_done(&data->easy_conn, result, FALSE);
+ multi_done(&data->easy_conn, result, FALSE);
}
else if(done) {
followtype follow=FOLLOW_NONE;
@@ -1756,7 +1915,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
else
follow = FOLLOW_RETRY;
- result = Curl_done(&data->easy_conn, CURLE_OK, FALSE);
+ result = multi_done(&data->easy_conn, CURLE_OK, FALSE);
if(!result) {
result = Curl_follow(data, newurl, follow);
if(!result) {
@@ -1806,14 +1965,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
Curl_multi_process_pending_handles(multi);
/* post-transfer command */
- res = Curl_done(&data->easy_conn, result, FALSE);
+ res = multi_done(&data->easy_conn, result, FALSE);
/* allow a previously set error code take precedence */
if(!result)
result = res;
/*
- * If there are other handles on the pipeline, Curl_done won't set
+ * If there are other handles on the pipeline, multi_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
@@ -1832,7 +1991,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
/* after we have DONE what we're supposed to do, go COMPLETED, and
- it doesn't matter what the Curl_done() returned! */
+ it doesn't matter what the multi_done() returned! */
multistate(data, CURLM_STATE_COMPLETED);
break;