}
#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)
{
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;
}
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;
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;
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;
}
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) {
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;
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;
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;
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 */
/* 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;
}
}
else {
/* failure detected */
Curl_posttransfer(data);
- Curl_done(&data->easy_conn, result, FALSE);
+ multi_done(&data->easy_conn, result, FALSE);
disconnect_conn = TRUE;
}
break;
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
else {
/* failure detected */
Curl_posttransfer(data);
- Curl_done(&data->easy_conn, result, FALSE);
+ multi_done(&data->easy_conn, result, FALSE);
disconnect_conn = TRUE;
}
break;
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;
}
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) {
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
}
/* 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;
/* Local static prototypes */
static struct connectdata *
-find_oldest_idle_connection(struct SessionHandle *data);
-static struct connectdata *
find_oldest_idle_connection_in_bundle(struct SessionHandle *data,
struct connectbundle *bundle);
static void conn_free(struct connectdata *conn);
* Returns the pointer to the oldest idle connection, or NULL if none was
* found.
*/
-static struct connectdata *
-find_oldest_idle_connection(struct SessionHandle *data)
+struct connectdata *
+Curl_oldest_idle_connection(struct SessionHandle *data)
{
struct conncache *bc = data->state.conn_cache;
struct curl_hash_iterator iter;
return FALSE; /* no matching connecting exists */
}
-/* 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 = find_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;
-}
-
/* after a TCP connection to the proxy has been verified, this function does
the next magic step.
struct connectdata *conn_candidate;
/* The cache is full. Let's see if we can kill a connection. */
- conn_candidate = find_oldest_idle_connection(data);
+ conn_candidate = Curl_oldest_idle_connection(data);
if(conn_candidate) {
/* Set the connection's owner correctly, then kill it */
return result;
}
-CURLcode Curl_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, "Curl_done\n"));
-
- if(data->state.done)
- /* Stop if Curl_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 Curl_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;
-}
-
/*
* Curl_init_do() inits the readwrite session. This is inited each time (in
* the DO function before the protocol-specific DO functions are invoked) for
conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to
* use */
- data->state.done = FALSE; /* Curl_done() is not called yet */
+ data->state.done = FALSE; /* *_done() is not called yet */
data->state.expect100header = FALSE;
if(data->set.opt_no_body)