Revert "16508: Split shutdown handling from connection pool"

This reverts commit 7ea4911075c1595d32049bfbd94dd38e2c312c50.
This commit is contained in:
Viktor Szakats 2025-03-01 01:52:36 +01:00
parent efa9f302d4
commit ad956e8b51
No known key found for this signature in database
GPG Key ID: B5ABD165E2AEF201
23 changed files with 955 additions and 1144 deletions

View File

@ -123,7 +123,6 @@ LIB_CFILES = \
cf-socket.c \
cfilters.c \
conncache.c \
cshutdn.c \
connect.c \
content_encoding.c \
cookie.c \
@ -260,7 +259,6 @@ LIB_HFILES = \
cf-socket.h \
cfilters.h \
conncache.h \
cshutdn.h \
connect.h \
content_encoding.h \
cookie.h \

View File

@ -202,7 +202,7 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
if(!Curl_shutdown_started(data, sockindex)) {
CURL_TRC_M(data, "shutdown start on%s connection",
sockindex ? " secondary" : "");
Curl_shutdown_start(data, sockindex, 0, &now);
Curl_shutdown_start(data, sockindex, &now);
}
else {
timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now);

File diff suppressed because it is too large Load Diff

View File

@ -35,19 +35,6 @@ struct Curl_waitfds;
struct Curl_multi;
struct Curl_share;
/**
* Terminate the connection, e.g. close and destroy.
* If the connection is in a cpool, remove it.
* If a `cshutdn` is available (e.g. data has a multi handle),
* pass the connection to that for controlled shutdown.
* Otherwise terminate it right away.
* Takes ownership of `conn`.
* `data` should not be attached to a connection.
*/
void Curl_conn_terminate(struct Curl_easy *data,
struct connectdata *conn,
bool aborted);
/**
* Callback invoked when disconnecting connections.
* @param data transfer last handling the connection, not attached
@ -67,11 +54,12 @@ struct cpool {
curl_off_t next_connection_id;
curl_off_t next_easy_id;
struct curltime last_cleanup;
struct Curl_easy *idata; /* internal handle for maintenance */
struct Curl_llist shutdowns; /* The connections being shut down */
struct Curl_easy *idata; /* internal handle used for discard */
struct Curl_multi *multi; /* != NULL iff pool belongs to multi */
struct Curl_share *share; /* != NULL iff pool belongs to share */
Curl_cpool_disconnect_cb *disconnect_cb;
BIT(locked);
BIT(initialised);
};
/* Init the pool, pass multi only if pool is owned by it.
@ -79,7 +67,7 @@ struct cpool {
*/
int Curl_cpool_init(struct cpool *cpool,
Curl_cpool_disconnect_cb *disconnect_cb,
struct Curl_easy *idata,
struct Curl_multi *multi,
struct Curl_share *share,
size_t size);
@ -90,13 +78,14 @@ void Curl_cpool_destroy(struct cpool *connc);
* Assigns `data->id`. */
void Curl_cpool_xfer_init(struct Curl_easy *data);
/* Get the connection with the given id from `data`'s conn pool. */
/**
* Get the connection with the given id from the transfer's pool.
*/
struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
curl_off_t conn_id);
/* Add the connection to the pool. */
CURLcode Curl_cpool_add(struct Curl_easy *data,
struct connectdata *conn) WARN_UNUSED_RESULT;
CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
struct connectdata *conn) WARN_UNUSED_RESULT;
/**
* Return if the pool has reached its configured limits for adding
@ -142,6 +131,17 @@ bool Curl_cpool_find(struct Curl_easy *data,
bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
struct connectdata *conn);
/**
* Remove the connection from the pool and tear it down.
* If `aborted` is FALSE, the connection will be shut down first
* before closing and destroying it.
* If the shutdown is not immediately complete, the connection
* will be placed into the pool's shutdown queue.
*/
void Curl_cpool_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool aborted);
/**
* This function scans the data's connection pool for half-open/dead
* connections, closes and removes them.
@ -178,4 +178,22 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
struct connectdata *conn,
Curl_cpool_conn_do_cb *cb, void *cbdata);
/**
* Add sockets and POLLIN/OUT flags for connections handled by the pool.
*/
CURLcode Curl_cpool_add_pollfds(struct cpool *connc,
struct curl_pollfds *cpfds);
unsigned int Curl_cpool_add_waitfds(struct cpool *connc,
struct Curl_waitfds *cwfds);
void Curl_cpool_setfds(struct cpool *cpool,
fd_set *read_fd_set, fd_set *write_fd_set,
int *maxfd);
/**
* Run connections on socket. If socket is CURL_SOCKET_TIMEOUT, run
* maintenance on all connections.
*/
void Curl_cpool_multi_perform(struct Curl_multi *multi, curl_socket_t s);
#endif /* HEADER_CURL_CONNCACHE_H */

View File

@ -161,7 +161,7 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
}
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
int timeout_ms, struct curltime *nowp)
struct curltime *nowp)
{
struct curltime now;
@ -171,13 +171,8 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
nowp = &now;
}
data->conn->shutdown.start[sockindex] = *nowp;
data->conn->shutdown.timeout_ms = (timeout_ms >= 0) ?
(unsigned int)timeout_ms :
((data->set.shutdowntimeout > 0) ?
data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
if(data->conn->shutdown.timeout_ms)
Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms,
EXPIRE_SHUTDOWN);
data->conn->shutdown.timeout_ms = (data->set.shutdowntimeout > 0) ?
data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS;
}
timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,

View File

@ -45,7 +45,7 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
#define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000)
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
int timeout_ms, struct curltime *nowp);
struct curltime *nowp);
/* return how much time there is left to shutdown the connection at
* sockindex. Returns 0 if there is no limit or shutdown has not started. */

View File

@ -1,566 +0,0 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
* Copyright (C) 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
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#include "urldata.h"
#include "url.h"
#include "cfilters.h"
#include "progress.h"
#include "multiif.h"
#include "multi_ev.h"
#include "sendf.h"
#include "cshutdn.h"
#include "http_negotiate.h"
#include "http_ntlm.h"
#include "sigpipe.h"
#include "connect.h"
#include "select.h"
#include "strcase.h"
#include "strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
static void cshutdn_run_conn_handler(struct Curl_easy *data,
struct connectdata *conn)
{
if(!conn->bits.shutdown_handler) {
if(conn->dns_entry)
Curl_resolv_unlink(data, &conn->dns_entry);
/* Cleanup NTLM connection-related data */
Curl_http_auth_cleanup_ntlm(conn);
/* Cleanup NEGOTIATE connection-related data */
Curl_http_auth_cleanup_negotiate(conn);
if(conn->handler && conn->handler->disconnect) {
/* Some disconnect handlers do a blocking wait on server responses.
* FTP/IMAP/SMTP and SFTP are among them. When using the internal
* handle, set an overall short timeout so we do not hang for the
* default 120 seconds. */
if(data->state.internal) {
data->set.timeout = DEFAULT_SHUTDOWN_TIMEOUT_MS;
(void)Curl_pgrsTime(data, TIMER_STARTOP);
}
/* This is set if protocol-specific cleanups should be made */
DEBUGF(infof(data, "connection #%" FMT_OFF_T
", shutdown protocol handler (aborted=%d)",
conn->connection_id, conn->bits.aborted));
/* There are protocol handlers that block on retrieving
* server responses here (FTP). Set a short timeout. */
conn->handler->disconnect(data, conn, conn->bits.aborted);
}
/* possible left-overs from the async name resolvers */
Curl_resolver_cancel(data);
conn->bits.shutdown_handler = TRUE;
}
}
static void cshutdn_run_once(struct Curl_easy *data,
struct connectdata *conn,
bool *done)
{
CURLcode r1, r2;
bool done1, done2;
/* We expect to be attached when called */
DEBUGASSERT(data->conn == conn);
cshutdn_run_conn_handler(data, conn);
if(conn->bits.shutdown_filters) {
*done = TRUE;
return;
}
if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
else {
r1 = CURLE_OK;
done1 = TRUE;
}
if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
else {
r2 = CURLE_OK;
done2 = TRUE;
}
/* we are done when any failed or both report success */
*done = (r1 || r2 || (done1 && done2));
if(*done)
conn->bits.shutdown_filters = TRUE;
}
void Curl_cshutdn_run_once(struct Curl_easy *data,
struct connectdata *conn,
bool *done)
{
DEBUGASSERT(!data->conn);
Curl_attach_connection(data, conn);
cshutdn_run_once(data, conn, done);
CURL_TRC_M(data, "[SHUTDOWN] shutdown, done=%d", *done);
Curl_detach_connection(data);
}
void Curl_cshutdn_terminate(struct Curl_easy *data,
struct connectdata *conn,
bool do_shutdown)
{
struct Curl_easy *admin = data;
bool done;
/* there must be a connection to close */
DEBUGASSERT(conn);
/* it must be removed from the connection pool */
DEBUGASSERT(!conn->bits.in_cpool);
/* the transfer must be detached from the connection */
DEBUGASSERT(data && !data->conn);
/* If we can obtain an internal admin handle, use that to attach
* and terminate the connection. Some protocol will try to mess with
* `data` during shutdown and we do not want that with a `data` from
* the application. */
if(data->multi && data->multi->admin)
admin = data->multi->admin;
Curl_attach_connection(admin, conn);
cshutdn_run_conn_handler(admin, conn);
if(do_shutdown) {
/* Make a last attempt to shutdown handlers and filters, if
* not done so already. */
cshutdn_run_once(admin, conn, &done);
}
CURL_TRC_M(admin, "[SHUTDOWN] closing connection");
Curl_conn_close(admin, SECONDARYSOCKET);
Curl_conn_close(admin, FIRSTSOCKET);
Curl_detach_connection(admin);
if(data->multi)
Curl_multi_ev_conn_done(data->multi, data, conn);
Curl_conn_free(admin, conn);
if(data->multi) {
CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged");
Curl_multi_connchanged(data->multi);
}
}
static void cshutdn_destroy_oldest(struct cshutdn *cshutdn,
struct Curl_easy *data)
{
struct Curl_llist_node *e;
struct connectdata *conn;
e = Curl_llist_head(&cshutdn->list);
if(e) {
SIGPIPE_VARIABLE(pipe_st);
conn = Curl_node_elem(e);
Curl_node_remove(e);
sigpipe_init(&pipe_st);
sigpipe_apply(data, &pipe_st);
Curl_cshutdn_terminate(data, conn, FALSE);
sigpipe_restore(&pipe_st);
}
}
#define NUM_POLLS_ON_STACK 10
static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
struct Curl_easy *data,
int timeout_ms)
{
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
struct curl_pollfds cpfds;
CURLcode result;
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
result = Curl_cshutdn_add_pollfds(cshutdn, data, &cpfds);
if(result)
goto out;
Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
out:
Curl_pollfds_cleanup(&cpfds);
return result;
}
static void cshutdn_perform(struct cshutdn *cshutdn,
struct Curl_easy *data)
{
struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
struct Curl_llist_node *enext;
struct connectdata *conn;
struct curltime *nowp = NULL;
struct curltime now;
timediff_t next_expire_ms = 0, ms;
bool done;
if(!e)
return;
CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections",
Curl_llist_count(&cshutdn->list));
while(e) {
enext = Curl_node_next(e);
conn = Curl_node_elem(e);
Curl_cshutdn_run_once(data, conn, &done);
if(done) {
Curl_node_remove(e);
Curl_cshutdn_terminate(data, conn, FALSE);
}
else {
/* idata has one timer list, but maybe more than one connection.
* Set EXPIRE_SHUTDOWN to the smallest time left for all. */
if(!nowp) {
now = Curl_now();
nowp = &now;
}
ms = Curl_conn_shutdown_timeleft(conn, nowp);
if(ms && ms < next_expire_ms)
next_expire_ms = ms;
}
e = enext;
}
if(next_expire_ms)
Curl_expire_ex(data, nowp, next_expire_ms, EXPIRE_SHUTDOWN);
}
static void cshutdn_terminate_all(struct cshutdn *cshutdn,
struct Curl_easy *data,
int timeout_ms)
{
struct curltime started = Curl_now();
struct Curl_llist_node *e;
SIGPIPE_VARIABLE(pipe_st);
DEBUGASSERT(cshutdn);
DEBUGASSERT(data);
CURL_TRC_M(data, "[SHUTDOWN] shutdown all");
sigpipe_init(&pipe_st);
sigpipe_apply(data, &pipe_st);
while(Curl_llist_head(&cshutdn->list)) {
timediff_t timespent;
int remain_ms;
cshutdn_perform(cshutdn, data);
if(!Curl_llist_head(&cshutdn->list)) {
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly");
break;
}
/* wait for activity, timeout or "nothing" */
timespent = Curl_timediff(Curl_now(), started);
if(timespent >= (timediff_t)timeout_ms) {
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s",
(timeout_ms > 0) ? "timeout" : "best effort done");
break;
}
remain_ms = timeout_ms - (int)timespent;
if(cshutdn_wait(cshutdn, data, remain_ms)) {
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, aborted");
break;
}
}
/* Terminate any remaining. */
e = Curl_llist_head(&cshutdn->list);
while(e) {
struct connectdata *conn = Curl_node_elem(e);
Curl_node_remove(e);
Curl_cshutdn_terminate(data, conn, FALSE);
e = Curl_llist_head(&cshutdn->list);
}
DEBUGASSERT(!Curl_llist_count(&cshutdn->list));
Curl_hostcache_clean(data, data->dns.hostcache);
sigpipe_restore(&pipe_st);
}
int Curl_cshutdn_init(struct cshutdn *cshutdn,
struct Curl_multi *multi)
{
DEBUGASSERT(multi);
cshutdn->multi = multi;
Curl_llist_init(&cshutdn->list, NULL);
cshutdn->initialised = TRUE;
return 0; /* good */
}
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
struct Curl_easy *data)
{
if(cshutdn->initialised && data) {
int timeout_ms = 0;
/* Just for testing, run graceful shutdown */
#ifdef DEBUGBUILD
{
const char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
if(p) {
curl_off_t l;
if(!Curl_str_number(&p, &l, INT_MAX))
timeout_ms = (int)l;
}
}
#endif
CURL_TRC_M(data, "[SHUTDOWN] destroy, %zu connections, timeout=%dms",
Curl_llist_count(&cshutdn->list), timeout_ms);
cshutdn_terminate_all(cshutdn, data, timeout_ms);
}
cshutdn->multi = NULL;
}
size_t Curl_cshutdn_count(struct Curl_easy *data)
{
if(data && data->multi) {
struct cshutdn *csd = &data->multi->cshutdn;
return Curl_llist_count(&csd->list);
}
return 0;
}
size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
const char *destination)
{
if(data && data->multi) {
struct cshutdn *csd = &data->multi->cshutdn;
size_t n = 0;
struct Curl_llist_node *e = Curl_llist_head(&csd->list);
while(e) {
struct connectdata *conn = Curl_node_elem(e);
if(!strcmp(destination, conn->destination))
++n;
e = Curl_node_next(e);
}
return n;
}
return 0;
}
static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
struct Curl_easy *data,
struct connectdata *conn)
{
CURLMcode mresult;
DEBUGASSERT(cshutdn);
DEBUGASSERT(cshutdn->multi->socket_cb);
Curl_attach_connection(data, conn);
mresult = Curl_multi_ev_assess_conn(cshutdn->multi, data, conn);
Curl_detach_connection(data);
return mresult;
}
void Curl_cshutdn_add(struct cshutdn *cshutdn,
struct connectdata *conn,
size_t conns_in_pool)
{
struct Curl_easy *data = cshutdn->multi->admin;
size_t max_total = (cshutdn->multi->max_total_connections > 0) ?
(size_t)cshutdn->multi->max_total_connections : 0;
/* Add the connection to our shutdown list for non-blocking shutdown
* during multi processing. */
if(max_total > 0 && (max_total <=
(conns_in_pool + Curl_llist_count(&cshutdn->list)))) {
CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection "
"due to connection limit of %zu", max_total);
cshutdn_destroy_oldest(cshutdn, data);
}
if(cshutdn->multi->socket_cb) {
if(cshutdn_update_ev(cshutdn, data, conn)) {
CURL_TRC_M(data, "[SHUTDOWN] update events failed, discarding #%"
FMT_OFF_T, conn->connection_id);
Curl_cshutdn_terminate(data, conn, FALSE);
return;
}
}
Curl_llist_append(&cshutdn->list, conn, &conn->cshutdn_node);
CURL_TRC_M(data, "[SHUTDOWN] added #%" FMT_OFF_T
" to shutdowns, now %zu conns in shutdown",
conn->connection_id, Curl_llist_count(&cshutdn->list));
}
static void cshutdn_multi_socket(struct cshutdn *cshutdn,
struct Curl_easy *data,
curl_socket_t s)
{
struct Curl_llist_node *e;
struct connectdata *conn;
bool done;
DEBUGASSERT(cshutdn->multi->socket_cb);
e = Curl_llist_head(&cshutdn->list);
while(e) {
conn = Curl_node_elem(e);
if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
Curl_cshutdn_run_once(data, conn, &done);
if(done || cshutdn_update_ev(cshutdn, data, conn)) {
Curl_node_remove(e);
Curl_cshutdn_terminate(data, conn, FALSE);
}
break;
}
e = Curl_node_next(e);
}
}
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
struct Curl_easy *data,
curl_socket_t s)
{
if((s == CURL_SOCKET_TIMEOUT) || (!cshutdn->multi->socket_cb))
cshutdn_perform(cshutdn, data);
else
cshutdn_multi_socket(cshutdn, data, s);
}
/* return fd_set info about the shutdown connections */
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
fd_set *read_fd_set, fd_set *write_fd_set,
int *maxfd)
{
if(Curl_llist_head(&cshutdn->list)) {
struct Curl_llist_node *e;
for(e = Curl_llist_head(&cshutdn->list); e;
e = Curl_node_next(e)) {
struct easy_pollset ps;
unsigned int i;
struct connectdata *conn = Curl_node_elem(e);
memset(&ps, 0, sizeof(ps));
Curl_attach_connection(data, conn);
Curl_conn_adjust_pollset(data, conn, &ps);
Curl_detach_connection(data);
for(i = 0; i < ps.num; i++) {
#if defined(__DJGPP__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warith-conversion"
#endif
if(ps.actions[i] & CURL_POLL_IN)
FD_SET(ps.sockets[i], read_fd_set);
if(ps.actions[i] & CURL_POLL_OUT)
FD_SET(ps.sockets[i], write_fd_set);
#if defined(__DJGPP__)
#pragma GCC diagnostic pop
#endif
if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
((int)ps.sockets[i] > *maxfd))
*maxfd = (int)ps.sockets[i];
}
}
}
}
/* return information about the shutdown connections */
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
struct Curl_waitfds *cwfds)
{
unsigned int need = 0;
if(Curl_llist_head(&cshutdn->list)) {
struct Curl_llist_node *e;
struct easy_pollset ps;
struct connectdata *conn;
for(e = Curl_llist_head(&cshutdn->list); e;
e = Curl_node_next(e)) {
conn = Curl_node_elem(e);
memset(&ps, 0, sizeof(ps));
Curl_attach_connection(data, conn);
Curl_conn_adjust_pollset(data, conn, &ps);
Curl_detach_connection(data);
need += Curl_waitfds_add_ps(cwfds, &ps);
}
}
return need;
}
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
struct curl_pollfds *cpfds)
{
CURLcode result = CURLE_OK;
if(Curl_llist_head(&cshutdn->list)) {
struct Curl_llist_node *e;
struct easy_pollset ps;
struct connectdata *conn;
for(e = Curl_llist_head(&cshutdn->list); e;
e = Curl_node_next(e)) {
conn = Curl_node_elem(e);
memset(&ps, 0, sizeof(ps));
Curl_attach_connection(data, conn);
Curl_conn_adjust_pollset(data, conn, &ps);
Curl_detach_connection(data);
result = Curl_pollfds_add_ps(cpfds, &ps);
if(result) {
Curl_pollfds_cleanup(cpfds);
goto out;
}
}
}
out:
return result;
}

View File

@ -1,104 +0,0 @@
#ifndef HEADER_CURL_CSHUTDN_H
#define HEADER_CURL_CSHUTDN_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include <curl/curl.h>
#include "timeval.h"
struct connectdata;
struct Curl_easy;
struct curl_pollfds;
struct Curl_waitfds;
struct Curl_multi;
struct Curl_share;
/* Run the shutdown of the connection once.
* Will shortly attach/detach `data` to `conn` while doing so.
* `done` will be set TRUE if any error was encountered or if
* the connection was shut down completely. */
void Curl_cshutdn_run_once(struct Curl_easy *data,
struct connectdata *conn,
bool *done);
/* Terminates the connection, e.g. closes and destroys it.
* If `run_shutdown` is TRUE, the shutdown will be run once before
* terminating it.
* Takes ownership of `conn`. */
void Curl_cshutdn_terminate(struct Curl_easy *data,
struct connectdata *conn,
bool run_shutdown);
/* A `cshutdown` is always owned by a multi handle to maintain
* the connections to be shut down. It registers timers and
* sockets to monitor via the multi handle. */
struct cshutdn {
struct Curl_llist list; /* connections being shut down */
struct Curl_multi *multi; /* the multi owning this */
BIT(initialised);
};
/* Init as part of the given multi handle. */
int Curl_cshutdn_init(struct cshutdn *cshutdn,
struct Curl_multi *multi);
/* Terminate all remaining connections and free resources. */
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
struct Curl_easy *data);
/* Number of connections being shut down. */
size_t Curl_cshutdn_count(struct Curl_easy *data);
/* Number of connections to the destination being shut down. */
size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
const char *destination);
/* Add a connection to have it shut down. Will terminate the oldest
* connection when total connection limit of multi is being reached. */
void Curl_cshutdn_add(struct cshutdn *cshutdn,
struct connectdata *conn,
size_t conns_in_pool);
/* Add sockets and POLLIN/OUT flags for connections being shut down. */
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
struct curl_pollfds *cpfds);
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
struct Curl_waitfds *cwfds);
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
fd_set *read_fd_set, fd_set *write_fd_set,
int *maxfd);
/* Run shut down connections using socket. If socket is CURL_SOCKET_TIMEOUT,
* run maintenance on all connections. */
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
struct Curl_easy *data,
curl_socket_t s);
#endif /* HEADER_CURL_CSHUTDN_H */

View File

@ -770,7 +770,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
Curl_detach_connection(data);
s = Curl_getconnectinfo(data, &c);
if((s != CURL_SOCKET_BAD) && c) {
Curl_conn_terminate(data, c, TRUE);
Curl_cpool_disconnect(data, c, TRUE);
}
DEBUGASSERT(!data->conn);
}

View File

@ -587,9 +587,8 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
}
#endif
/* store the latest code for later retrieval, except during shutdown */
if(!data->conn->proto.ftpc.shutdown)
data->info.httpcode = code;
/* store the latest code for later retrieval */
data->info.httpcode = code;
if(ftpcode)
*ftpcode = code;
@ -3132,8 +3131,6 @@ static CURLcode ftp_block_statemach(struct Curl_easy *data,
CURLcode result = CURLE_OK;
while(ftpc->state != FTP_STOP) {
if(ftpc->shutdown)
CURL_TRC_FTP(data, "in shutdown, waiting for server response");
result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
if(result)
break;
@ -4045,7 +4042,6 @@ static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
CURLcode result = CURLE_OK;
if(conn->proto.ftpc.ctl_valid) {
CURL_TRC_FTP(data, "sending QUIT to close session");
result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
if(result) {
failf(data, "Failure sending QUIT command: %s",
@ -4085,7 +4081,6 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
ftp_quit() will check the state of ftp->ctl_valid. If it is ok it
will try to send the QUIT command, otherwise it will just return.
*/
ftpc->shutdown = TRUE;
if(dead_connection)
ftpc->ctl_valid = FALSE;

View File

@ -160,7 +160,6 @@ struct ftp_conn {
BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent
caching the current directory */
BIT(wait_data_conn); /* this is set TRUE if data connection is waited */
BIT(shutdown); /* connection is being shutdown, e.g. QUIT */
};
#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */

View File

@ -1450,7 +1450,7 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
if(result) {
Curl_detach_connection(data);
Curl_conn_terminate(data, conn, TRUE);
Curl_cpool_disconnect(data, conn, TRUE);
}
return result;
}

View File

@ -227,23 +227,8 @@ struct Curl_multi *Curl_multi_handle(size_t ev_hashsize, /* event hash */
Curl_hash_init(&multi->proto_hash, 23,
Curl_hash_str, Curl_str_key_compare, ph_freeentry);
multi->admin = curl_easy_init();
if(!multi->admin)
goto error;
/* Initialize admin handle to operate inside this multi */
multi->admin->multi = multi;
multi->admin->state.internal = TRUE;
Curl_llist_init(&multi->admin->state.timeoutlist, NULL);
#ifdef DEBUGBUILD
if(getenv("CURL_DEBUG"))
multi->admin->set.verbose = TRUE;
#endif
if(Curl_cshutdn_init(&multi->cshutdn, multi))
goto error;
if(Curl_cpool_init(&multi->cpool, Curl_on_disconnect,
multi->admin, NULL, chashsize))
multi, NULL, chashsize))
goto error;
if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache))
@ -279,13 +264,7 @@ error:
Curl_hash_destroy(&multi->proto_hash);
Curl_hash_destroy(&multi->hostcache);
Curl_cpool_destroy(&multi->cpool);
Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
Curl_ssl_scache_destroy(multi->ssl_scache);
if(multi->admin) {
multi->admin->multi = NULL;
Curl_close(&multi->admin);
}
free(multi);
return NULL;
}
@ -417,15 +396,6 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
Curl_cpool_xfer_init(data);
multi_warn_debug(multi, data);
/* The admin handle only ever has default timeouts set. To improve the
state somewhat we clone the timeouts from each added handle so that the
admin handle always has the same timeouts as the most recently added
easy handle. */
multi->admin->set.timeout = data->set.timeout;
multi->admin->set.server_response_timeout =
data->set.server_response_timeout;
multi->admin->set.no_signal = data->set.no_signal;
CURL_TRC_M(data, "added, transfers=%u", multi->num_easy);
return CURLM_OK;
}
@ -505,7 +475,7 @@ static void multi_done_locked(struct connectdata *conn,
conn->bits.close, mdctx->premature,
Curl_conn_is_multiplex(conn, FIRSTSOCKET));
connclose(conn, "disconnecting");
Curl_conn_terminate(data, conn, mdctx->premature);
Curl_cpool_disconnect(data, conn, mdctx->premature);
}
else {
/* the connection is no longer in use by any transfer */
@ -714,7 +684,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
curl_socket_t s;
s = Curl_getconnectinfo(data, &c);
if((s != CURL_SOCKET_BAD) && c) {
Curl_conn_terminate(data, c, TRUE);
Curl_cpool_disconnect(data, c, TRUE);
}
}
@ -1062,8 +1032,7 @@ CURLMcode curl_multi_fdset(CURLM *m,
}
}
Curl_cshutdn_setfds(&multi->cshutdn, multi->admin,
read_fd_set, write_fd_set, &this_max_fd);
Curl_cpool_setfds(&multi->cpool, read_fd_set, write_fd_set, &this_max_fd);
*max_fd = this_max_fd;
@ -1099,7 +1068,7 @@ CURLMcode curl_multi_waitfds(CURLM *m,
need += Curl_waitfds_add_ps(&cwfds, &ps);
}
need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds);
need += Curl_cpool_add_waitfds(&multi->cpool, &cwfds);
if(need != cwfds.n && ufds) {
result = CURLM_OUT_OF_MEMORY;
@ -1177,7 +1146,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
}
}
if(Curl_cshutdn_add_pollfds(&multi->cshutdn, multi->admin, &cpfds)) {
if(Curl_cpool_add_pollfds(&multi->cpool, &cpfds)) {
result = CURLM_OUT_OF_MEMORY;
goto out;
}
@ -2523,7 +2492,7 @@ statemachine_end:
We do not have to do this in every case block above where a
failure is detected */
Curl_detach_connection(data);
Curl_conn_terminate(data, conn, dead_connection);
Curl_cpool_disconnect(data, conn, dead_connection);
}
}
else if(data->mstate == MSTATE_CONNECT) {
@ -2612,7 +2581,7 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
pointer now */
n = Curl_node_next(e);
if(data && data != multi->admin) {
if(data && data != multi->cpool.idata) {
/* connection pool handle is processed below */
sigpipe_apply(data, &pipe_st);
result = multi_runsingle(multi, &now, data);
@ -2621,8 +2590,8 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
}
}
sigpipe_apply(multi->admin, &pipe_st);
Curl_cshutdn_perform(&multi->cshutdn, multi->admin, CURL_SOCKET_TIMEOUT);
sigpipe_apply(multi->cpool.idata, &pipe_st);
Curl_cpool_multi_perform(multi, CURL_SOCKET_TIMEOUT);
sigpipe_restore(&pipe_st);
if(multi_ischanged(m, TRUE))
@ -2721,11 +2690,6 @@ CURLMcode curl_multi_cleanup(CURLM *m)
}
Curl_cpool_destroy(&multi->cpool);
Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
if(multi->admin) {
multi->admin->multi = NULL;
Curl_close(&multi->admin);
}
multi->magic = 0; /* not good anymore */
@ -2891,7 +2855,7 @@ static CURLMcode multi_run_expired(struct multi_run_ctx *mrc)
continue;
(void)add_next_timeout(mrc->now, multi, data);
if(data == multi->admin) {
if(data == multi->cpool.idata) {
mrc->run_cpool = TRUE;
continue;
}
@ -2967,8 +2931,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
out:
if(mrc.run_cpool) {
sigpipe_apply(multi->admin, &mrc.pipe_st);
Curl_cshutdn_perform(&multi->cshutdn, multi->admin, s);
sigpipe_apply(multi->cpool.idata, &mrc.pipe_st);
Curl_cpool_multi_perform(multi, s);
}
sigpipe_restore(&mrc.pipe_st);

View File

@ -27,13 +27,11 @@
#include "llist.h"
#include "hash.h"
#include "conncache.h"
#include "cshutdn.h"
#include "multi_ev.h"
#include "psl.h"
#include "socketpair.h"
struct connectdata;
struct Curl_easy;
struct Curl_message {
struct Curl_llist_node list;
@ -101,8 +99,6 @@ struct Curl_multi {
struct Curl_llist msgsent; /* in MSGSENT */
curl_off_t next_easy_mid; /* next multi-id for easy handle added */
struct Curl_easy *admin; /* internal easy handle for admin operations */
/* callback function and user data pointer for the *socket() API */
curl_socket_callback socket_cb;
void *socket_userp;
@ -144,8 +140,8 @@ struct Curl_multi {
* the multi handle is cleaned up (see Curl_hash_add2()).*/
struct Curl_hash proto_hash;
struct cshutdn cshutdn; /* connection shutdown handling */
struct cpool cpool; /* connection pool (bundles) */
/* Shared connection cache (bundles)*/
struct cpool cpool;
long max_host_connections; /* if >0, a fixed limit of the maximum number
of connections per host */

View File

@ -29,7 +29,6 @@
#include "urldata.h"
#include "cfilters.h"
#include "connect.h"
#include "sendf.h"
#include "select.h"
#include "progress.h"
@ -75,11 +74,6 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
}
if(disconnecting) {
timediff_t total_left_ms = Curl_timeleft(data, NULL, FALSE);
timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0));
}
return timeout_ms;
}
@ -102,7 +96,6 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
}
DEBUGF(infof(data, "pp_statematch, timeout=%" FMT_TIMEDIFF_T, timeout_ms));
if(block) {
interval_ms = 1000; /* use 1 second timeout intervals */
if(timeout_ms < interval_ms)
@ -142,8 +135,6 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
}
else if(rc)
result = pp->statemachine(data, data->conn);
else if(disconnecting)
return CURLE_OPERATION_TIMEDOUT;
return result;
}

View File

@ -47,16 +47,6 @@ curl_share_init(void)
share->magic = CURL_GOOD_SHARE;
share->specifier |= (1 << CURL_LOCK_DATA_SHARE);
Curl_init_dnscache(&share->hostcache, 23);
share->admin = curl_easy_init();
if(!share->admin) {
free(share);
return NULL;
}
share->admin->state.internal = TRUE;
#ifdef DEBUGBUILD
if(getenv("CURL_DEBUG"))
share->admin->set.verbose = TRUE;
#endif
}
return share;
@ -135,9 +125,9 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
case CURL_LOCK_DATA_CONNECT:
/* It is safe to set this option several times on a share. */
if(!share->cpool.initialised) {
if(!share->cpool.idata) {
if(Curl_cpool_init(&share->cpool, Curl_on_disconnect,
share->admin, share, 103))
NULL, share, 103))
res = CURLSHE_NOMEM;
}
break;
@ -267,7 +257,6 @@ curl_share_cleanup(CURLSH *sh)
#endif
Curl_psl_destroy(&share->psl);
Curl_close(&share->admin);
if(share->unlockfunc)
share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);

View File

@ -31,7 +31,6 @@
#include "urldata.h"
#include "conncache.h"
struct Curl_easy;
struct Curl_ssl_scache;
#define CURL_GOOD_SHARE 0x7e117a1e
@ -49,7 +48,6 @@ struct Curl_share {
curl_lock_function lockfunc;
curl_unlock_function unlockfunc;
void *clientdata;
struct Curl_easy *admin;
struct cpool cpool;
struct Curl_hash hostcache;
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)

View File

@ -1209,7 +1209,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
}
else if(Curl_conn_seems_dead(conn, data, NULL)) {
/* removed and disconnect. Do not treat as aborted. */
Curl_conn_terminate(data, conn, FALSE);
Curl_cpool_disconnect(data, conn, FALSE);
return FALSE;
}
@ -3540,12 +3540,14 @@ static CURLcode create_conn(struct Curl_easy *data,
/* Setup a "faked" transfer that will do nothing */
if(!result) {
Curl_attach_connection(data, conn);
result = Curl_cpool_add(data, conn);
if(!result) {
/* Setup whatever necessary for a resumed transfer */
result = setup_range(data);
}
result = Curl_cpool_add_conn(data, conn);
if(result)
goto out;
/*
* Setup whatever necessary for a resumed transfer
*/
result = setup_range(data);
if(result) {
DEBUGASSERT(conn->handler->done);
/* we ignore the return code for the protocol-specific DONE */
@ -3682,7 +3684,7 @@ static CURLcode create_conn(struct Curl_easy *data,
}
Curl_attach_connection(data, conn);
result = Curl_cpool_add(data, conn);
result = Curl_cpool_add_conn(data, conn);
if(result)
goto out;
}
@ -3821,7 +3823,7 @@ CURLcode Curl_connect(struct Curl_easy *data,
/* We are not allowed to return failure with memory left allocated in the
connectdata struct, free those here */
Curl_detach_connection(data);
Curl_conn_terminate(data, conn, TRUE);
Curl_cpool_disconnect(data, conn, TRUE);
}
return result;

View File

@ -753,7 +753,6 @@ struct ldapconninfo;
*/
struct connectdata {
struct Curl_llist_node cpool_node; /* conncache lists */
struct Curl_llist_node cshutdn_node; /* cshutdn list */
curl_closesocket_callback fclosesocket; /* function closing the socket(s) */
void *closesocket_client;
@ -1134,7 +1133,6 @@ typedef enum {
EXPIRE_QUIC,
EXPIRE_FTP_ACCEPT,
EXPIRE_ALPN_EYEBALLS,
EXPIRE_SHUTDOWN,
EXPIRE_LAST /* not an actual timer, used as a marker only */
} expire_id;

View File

@ -1853,7 +1853,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
if(cf->cft == &Curl_cft_ssl) {
bool done;
CURL_TRC_CF(data, cf, "shutdown and remove SSL, start");
Curl_shutdown_start(data, sockindex, 0, NULL);
Curl_shutdown_start(data, sockindex, NULL);
result = vtls_shutdown_blocking(cf, data, send_shutdown, &done);
Curl_shutdown_clear(data, sockindex);
if(!result && !done) /* blocking failed? */

View File

@ -2589,10 +2589,6 @@ static int cb_socket(CURL *easy, curl_socket_t s, int action,
int events = 0;
(void)easy;
#if DEBUG_UV
fprintf(tool_stderr, "parallel_event: cb_socket, fd=%d, action=%x, p=%p\n",
(int)s, action, socketp);
#endif
switch(action) {
case CURL_POLL_IN:
case CURL_POLL_OUT:
@ -2682,26 +2678,12 @@ static CURLcode parallel_event(struct parastate *s)
}
}
result = s->result;
/* Make sure to return some kind of error if there was a multi problem */
if(s->mcode) {
result = (s->mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY :
/* The other multi errors should never happen, so return
something suitably generic */
CURLE_BAD_FUNCTION_ARGUMENT;
}
/* We need to cleanup the multi here, since the uv context lives on the
* stack and will be gone. multi_cleanup can triggere events! */
curl_multi_cleanup(s->multi);
#if DEBUG_UV
fprintf(tool_stderr, "DONE parallel_event -> %d, mcode=%d, %d running, "
"%d more\n",
result, s->mcode, uv.s->still_running, s->more_transfers);
s->result, s->mcode, uv.s->still_running, s->more_transfers);
#endif
return result;
return s->result;
}
#endif
@ -2805,7 +2787,7 @@ static CURLcode parallel_transfers(struct GlobalConfig *global,
#ifdef DEBUGBUILD
if(global->test_event_based)
#ifdef USE_LIBUV
return parallel_event(s);
result = parallel_event(s);
#else
errorf(global, "Testing --parallel event-based requires libuv");
#endif
@ -3246,9 +3228,7 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[])
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
curl_share_setopt(share, CURLSHOPT_SHARE,
CURL_LOCK_DATA_SSL_SESSION);
/* Running parallel, use the multi connection cache */
if(!global->parallel)
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);

View File

@ -67,6 +67,8 @@ run 1: foobar and so on fun!
-> Mutex lock SHARE
<- Mutex unlock SHARE
-> Mutex lock SHARE
-> Mutex lock CONNECT
<- Mutex unlock CONNECT
<- Mutex unlock SHARE
</datacheck>
</reply>

View File

@ -105,7 +105,7 @@ class TestShutdown:
r = curl.http_download(urls=[url], alpn_proto=proto)
r.check_response(http_status=200, count=count)
shutdowns = [line for line in r.trace_lines
if re.match(r'.*\[SHUTDOWN\] shutdown, done=1', line)]
if re.match(r'.*\[CPOOL\] shutdown, done=1', line)]
assert len(shutdowns) == count, f'{shutdowns}'
# run downloads with CURLOPT_FORBID_REUSE set, meaning *we* close
@ -128,7 +128,7 @@ class TestShutdown:
])
r.check_exit_code(0)
shutdowns = [line for line in r.trace_lines
if re.match(r'.*SHUTDOWN\] shutdown, done=1', line)]
if re.match(r'.*CPOOL\] shutdown, done=1', line)]
assert len(shutdowns) == count, f'{shutdowns}'
# run event-based downloads with CURLOPT_FORBID_REUSE set, meaning *we* close
@ -153,7 +153,7 @@ class TestShutdown:
r.check_response(http_status=200, count=count)
# check that we closed all connections
closings = [line for line in r.trace_lines
if re.match(r'.*SHUTDOWN\] closing', line)]
if re.match(r'.*CPOOL\] closing', line)]
assert len(closings) == count, f'{closings}'
# check that all connection sockets were removed from event
removes = [line for line in r.trace_lines
@ -178,5 +178,5 @@ class TestShutdown:
r.check_response(http_status=200, count=2)
# check connection cache closings
shutdowns = [line for line in r.trace_lines
if re.match(r'.*SHUTDOWN\] shutdown, done=1', line)]
if re.match(r'.*CPOOL\] shutdown, done=1', line)]
assert len(shutdowns) == 1, f'{shutdowns}'