mirror of
https://github.com/curl/curl.git
synced 2026-08-25 13:53:37 +03:00
connections: use admin handles only for maintenance
Maintenance tasks like connection upkeep, liveliness checks and cache eviction must only run on an admin handle, not the application transfer from the calling context. Then application transfers will get only attached to the connections they actually work on. This makes `data->state.recent_conn_id` meaningful for checks if a subsequent address is finding the previous connection again. One thing led to another: - add Curl_get_admin(data), to obtain an admin handle for an application handle, inheriting some properties for connection operation. - rename `Curl_easy *data` parameter to `Curl_easy *admin` where only admin handles should be passed. Add DEBUGASSERTs in the called function. - rename `Curl_conn_terminate()` to `Curl_conn_close()` because this makes connections enter the shutdown close where possible. - rename `Curl_cshutdn_terminate()` to `Curl_conn_terminate()` since this definitely kills the connection and does not involve a `cshutdn` instance. - add Curl_share_lock_share(), Curl_share_unlock_share() so it possible to operate on a share without the passed easy handle having the share set. This catches a case where a shared needed to be locked but was not before. Adjust test1554 results. Fixes #22567 Reported-by: cybertron10 on github Closes #22572
This commit is contained in:
parent
6e9880366f
commit
7103a93b05
15 changed files with 399 additions and 312 deletions
315
lib/conncache.c
315
lib/conncache.c
|
|
@ -42,7 +42,7 @@
|
||||||
do { \
|
do { \
|
||||||
if(c) { \
|
if(c) { \
|
||||||
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
|
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
|
||||||
Curl_share_lock((d), CURL_LOCK_DATA_CONNECT, \
|
Curl_share_lock_share((c)->share, (d), CURL_LOCK_DATA_CONNECT, \
|
||||||
CURL_LOCK_ACCESS_SINGLE); \
|
CURL_LOCK_ACCESS_SINGLE); \
|
||||||
DEBUGASSERT(!(c)->locked); \
|
DEBUGASSERT(!(c)->locked); \
|
||||||
(c)->locked = TRUE; \
|
(c)->locked = TRUE; \
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
DEBUGASSERT((c)->locked); \
|
DEBUGASSERT((c)->locked); \
|
||||||
(c)->locked = FALSE; \
|
(c)->locked = FALSE; \
|
||||||
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
|
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
|
||||||
Curl_share_unlock((d), CURL_LOCK_DATA_CONNECT); \
|
Curl_share_unlock_share((c)->share, (d), CURL_LOCK_DATA_CONNECT); \
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
|
@ -111,16 +111,12 @@ static void cpool_bundle_free_entry(void *freethis)
|
||||||
}
|
}
|
||||||
|
|
||||||
void Curl_cpool_init(struct cpool *cpool,
|
void Curl_cpool_init(struct cpool *cpool,
|
||||||
struct Curl_easy *idata,
|
|
||||||
struct Curl_share *share,
|
struct Curl_share *share,
|
||||||
size_t size)
|
size_t size)
|
||||||
{
|
{
|
||||||
Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
|
Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
|
||||||
curlx_str_key_compare, cpool_bundle_free_entry);
|
curlx_str_key_compare, cpool_bundle_free_entry);
|
||||||
|
|
||||||
DEBUGASSERT(idata);
|
|
||||||
|
|
||||||
cpool->idata = idata;
|
|
||||||
cpool->share = share;
|
cpool->share = share;
|
||||||
cpool->initialized = TRUE;
|
cpool->initialized = TRUE;
|
||||||
}
|
}
|
||||||
|
|
@ -186,6 +182,8 @@ static void cpool_discard_conn(struct cpool *cpool,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
bool aborted)
|
bool aborted)
|
||||||
{
|
{
|
||||||
|
struct cshutdn *cshutdn;
|
||||||
|
struct Curl_easy *admin;
|
||||||
bool done = FALSE;
|
bool done = FALSE;
|
||||||
|
|
||||||
DEBUGASSERT(data);
|
DEBUGASSERT(data);
|
||||||
|
|
@ -193,12 +191,13 @@ static void cpool_discard_conn(struct cpool *cpool,
|
||||||
DEBUGASSERT(cpool);
|
DEBUGASSERT(cpool);
|
||||||
DEBUGASSERT(!conn->bits.in_cpool);
|
DEBUGASSERT(!conn->bits.in_cpool);
|
||||||
|
|
||||||
|
admin = Curl_get_admin(data);
|
||||||
/*
|
/*
|
||||||
* If this connection is not marked to force-close, leave it open if there
|
* If this connection is not marked to force-close, leave it open if there
|
||||||
* are other users of it
|
* are other users of it
|
||||||
*/
|
*/
|
||||||
if(CONN_INUSE(conn) && !aborted) {
|
if(CONN_INUSE(conn) && !aborted) {
|
||||||
CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
|
CURL_TRC_M(admin, "[CPOOL] not discarding #%" FMT_OFF_T
|
||||||
" still in use by %u transfers", conn->connection_id,
|
" still in use by %u transfers", conn->connection_id,
|
||||||
conn->attached_xfers);
|
conn->attached_xfers);
|
||||||
return;
|
return;
|
||||||
|
|
@ -219,35 +218,36 @@ static void cpool_discard_conn(struct cpool *cpool,
|
||||||
done = TRUE;
|
done = TRUE;
|
||||||
if(!done) {
|
if(!done) {
|
||||||
/* Attempt to shutdown the connection right away. */
|
/* Attempt to shutdown the connection right away. */
|
||||||
Curl_cshutdn_run_once(cpool->idata, conn, &done);
|
Curl_conn_shutdown_once(admin, conn, &done);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(done || !data->multi)
|
cshutdn = Curl_cshutdn_get(data);
|
||||||
Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
|
if(done || !cshutdn)
|
||||||
|
Curl_conn_terminate(admin, conn, FALSE);
|
||||||
else
|
else
|
||||||
Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
|
Curl_cshutdn_add(cshutdn, conn, cpool->num_conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Curl_cpool_destroy(struct cpool *cpool)
|
void Curl_cpool_destroy(struct cpool *cpool, struct Curl_easy *admin)
|
||||||
{
|
{
|
||||||
if(cpool && cpool->initialized && cpool->idata) {
|
if(cpool && cpool->initialized && admin) {
|
||||||
struct connectdata *conn;
|
struct connectdata *conn;
|
||||||
struct Curl_sigpipe_ctx pipe_ctx;
|
struct Curl_sigpipe_ctx pipe_ctx;
|
||||||
|
|
||||||
CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections",
|
CURL_TRC_M(admin, "%s[CPOOL] destroy, %zu connections",
|
||||||
cpool->share ? "[SHARE] " : "", cpool->num_conn);
|
cpool->share ? "[SHARE] " : "", cpool->num_conn);
|
||||||
/* Move all connections to the shutdown list */
|
/* Move all connections to the shutdown list */
|
||||||
sigpipe_init(&pipe_ctx);
|
sigpipe_init(&pipe_ctx);
|
||||||
CPOOL_LOCK(cpool, cpool->idata);
|
CPOOL_LOCK(cpool, admin);
|
||||||
conn = cpool_get_first(cpool);
|
conn = cpool_get_first(cpool);
|
||||||
if(conn)
|
if(conn)
|
||||||
sigpipe_apply(cpool->idata, &pipe_ctx);
|
sigpipe_apply(admin, &pipe_ctx);
|
||||||
while(conn) {
|
while(conn) {
|
||||||
cpool_remove_conn(cpool, conn);
|
cpool_remove_conn(cpool, conn);
|
||||||
cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
|
cpool_discard_conn(cpool, admin, conn, FALSE);
|
||||||
conn = cpool_get_first(cpool);
|
conn = cpool_get_first(cpool);
|
||||||
}
|
}
|
||||||
CPOOL_UNLOCK(cpool, cpool->idata);
|
CPOOL_UNLOCK(cpool, admin);
|
||||||
sigpipe_restore(&pipe_ctx);
|
sigpipe_restore(&pipe_ctx);
|
||||||
Curl_hash_destroy(&cpool->dest2bundle);
|
Curl_hash_destroy(&cpool->dest2bundle);
|
||||||
}
|
}
|
||||||
|
|
@ -255,17 +255,22 @@ void Curl_cpool_destroy(struct cpool *cpool)
|
||||||
|
|
||||||
static struct cpool *cpool_get_instance(struct Curl_easy *data)
|
static struct cpool *cpool_get_instance(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
if(data) {
|
/* admin handles do not necessarily find the correct pool */
|
||||||
if(CURL_SHARE_KEEP_CONNECT(data->share))
|
DEBUGASSERT(data->mid);
|
||||||
return &data->share->cpool;
|
if(CURL_SHARE_KEEP_CONNECT(data->share))
|
||||||
else if(data->multi_easy)
|
return &data->share->cpool;
|
||||||
return &data->multi_easy->cpool;
|
else if(data->multi_easy)
|
||||||
else if(data->multi)
|
return &data->multi_easy->cpool;
|
||||||
return &data->multi->cpool;
|
else if(data->multi)
|
||||||
}
|
return &data->multi->cpool;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct cpool *Curl_cpool_get_instance(struct Curl_easy *data)
|
||||||
|
{
|
||||||
|
return cpool_get_instance(data);
|
||||||
|
}
|
||||||
|
|
||||||
void Curl_cpool_xfer_init(struct Curl_easy *data)
|
void Curl_cpool_xfer_init(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct cpool *cpool = cpool_get_instance(data);
|
struct cpool *cpool = cpool_get_instance(data);
|
||||||
|
|
@ -367,26 +372,90 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool,
|
||||||
return oldest_idle;
|
return oldest_idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cpool_conn_close(struct cpool *cpool,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
struct connectdata *conn,
|
||||||
|
bool aborted)
|
||||||
|
{
|
||||||
|
struct Curl_easy *admin;
|
||||||
|
bool do_lock;
|
||||||
|
|
||||||
|
DEBUGASSERT(cpool);
|
||||||
|
DEBUGASSERT(data && !data->conn);
|
||||||
|
if(!cpool)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If this connection is not marked to force-close, leave it open if there
|
||||||
|
* are other users of it */
|
||||||
|
if(CONN_INUSE(conn) && !aborted) {
|
||||||
|
DEBUGASSERT(0); /* does this ever happen? */
|
||||||
|
DEBUGF(infof(data, "conn terminate when inuse: %u", conn->attached_xfers));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This method may be called while we are under lock, e.g. from a
|
||||||
|
* user callback in find. */
|
||||||
|
admin = Curl_get_admin(data);
|
||||||
|
do_lock = !CPOOL_IS_LOCKED(cpool);
|
||||||
|
if(do_lock)
|
||||||
|
CPOOL_LOCK(cpool, admin);
|
||||||
|
|
||||||
|
if(conn->bits.in_cpool) {
|
||||||
|
cpool_remove_conn(cpool, conn);
|
||||||
|
DEBUGASSERT(!conn->bits.in_cpool);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* treat the connection as aborted in CONNECT_ONLY situations,
|
||||||
|
* so no graceful shutdown is attempted. */
|
||||||
|
if(conn->bits.connect_only)
|
||||||
|
aborted = TRUE;
|
||||||
|
|
||||||
|
if(data->multi) {
|
||||||
|
/* Add it to the multi's cpool for shutdown handling */
|
||||||
|
infof(data, "%s connection #%" FMT_OFF_T,
|
||||||
|
aborted ? "closing" : "shutting down", conn->connection_id);
|
||||||
|
cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* No multi available, terminate */
|
||||||
|
infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
|
||||||
|
Curl_conn_terminate(admin, conn, !aborted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(do_lock)
|
||||||
|
CPOOL_UNLOCK(cpool, admin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Curl_conn_close(struct Curl_easy *data,
|
||||||
|
struct connectdata *conn,
|
||||||
|
bool aborted)
|
||||||
|
{
|
||||||
|
struct cpool *cpool = cpool_get_instance(data);
|
||||||
|
cpool_conn_close(cpool, data, conn, aborted);
|
||||||
|
}
|
||||||
|
|
||||||
/* Evict an idle connection to make room in the pool. A pool owned by
|
/* Evict an idle connection to make room in the pool. A pool owned by
|
||||||
* a share has no multi that could perform a controlled shutdown of the
|
* a share has no multi that could perform a controlled shutdown of the
|
||||||
* connection; terminate it right away. Otherwise, hand it to the
|
* connection; terminate it right away. Otherwise, hand it to the
|
||||||
* transfer's multi for shutdown. Expects the pool to be locked. */
|
* transfer's multi for shutdown. Expects the pool to be locked. */
|
||||||
static void cpool_evict_conn(struct cpool *cpool,
|
static void cpool_evict_conn(struct cpool *cpool,
|
||||||
struct Curl_easy *data,
|
struct Curl_easy *admin,
|
||||||
struct connectdata *conn)
|
struct connectdata *conn)
|
||||||
{
|
{
|
||||||
if(cpool->share) {
|
if(cpool->share) {
|
||||||
cpool_remove_conn(cpool, conn);
|
cpool_remove_conn(cpool, conn);
|
||||||
Curl_cshutdn_terminate(cpool->idata, conn, TRUE);
|
Curl_conn_terminate(admin, conn, TRUE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Curl_conn_terminate(data, conn, FALSE);
|
cpool_conn_close(cpool, admin, conn, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Curl_cpool_check_limits(struct Curl_easy *data,
|
int Curl_cpool_check_limits(struct Curl_easy *data,
|
||||||
struct connectdata *conn)
|
struct connectdata *conn)
|
||||||
{
|
{
|
||||||
struct cpool *cpool = cpool_get_instance(data);
|
struct cpool *cpool = cpool_get_instance(data);
|
||||||
|
struct cshutdn *cshutdn = Curl_cshutdn_get(data);
|
||||||
|
struct Curl_easy *admin;
|
||||||
struct cpool_bundle *bundle;
|
struct cpool_bundle *bundle;
|
||||||
size_t dest_limit = 0;
|
size_t dest_limit = 0;
|
||||||
size_t total_limit = 0;
|
size_t total_limit = 0;
|
||||||
|
|
@ -405,17 +474,18 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
|
||||||
if(!dest_limit && !total_limit)
|
if(!dest_limit && !total_limit)
|
||||||
return CPOOL_LIMIT_OK;
|
return CPOOL_LIMIT_OK;
|
||||||
|
|
||||||
CPOOL_LOCK(cpool, cpool->idata);
|
admin = Curl_get_admin(data);
|
||||||
|
CPOOL_LOCK(cpool, admin);
|
||||||
if(dest_limit) {
|
if(dest_limit) {
|
||||||
size_t live;
|
size_t live;
|
||||||
|
|
||||||
bundle = cpool_find_bundle(cpool, conn);
|
bundle = cpool_find_bundle(cpool, conn);
|
||||||
live = bundle ? Curl_llist_count(&bundle->conns) : 0;
|
live = bundle ? Curl_llist_count(&bundle->conns) : 0;
|
||||||
shutdowns = Curl_cshutdn_dest_count(data, conn->destination);
|
shutdowns = Curl_cshutdn_dest_count(cshutdn, conn->destination);
|
||||||
while((live + shutdowns) >= dest_limit) {
|
while((live + shutdowns) >= dest_limit) {
|
||||||
if(shutdowns) {
|
if(shutdowns) {
|
||||||
/* close one connection in shutdown right away, if we can */
|
/* close one connection in shutdown right away, if we can */
|
||||||
if(!Curl_cshutdn_close_oldest(data, conn->destination))
|
if(!Curl_cshutdn_close_oldest(cshutdn, conn->destination))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(!bundle)
|
else if(!bundle)
|
||||||
|
|
@ -429,17 +499,17 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
|
||||||
if(!oldest_idle)
|
if(!oldest_idle)
|
||||||
break;
|
break;
|
||||||
/* disconnect the old conn and continue */
|
/* disconnect the old conn and continue */
|
||||||
CURL_TRC_M(data, "Discarding connection #%" FMT_OFF_T
|
CURL_TRC_M(admin, "Discarding connection #%" FMT_OFF_T
|
||||||
" from %zu to reach destination limit of %zu",
|
" from %zu to reach destination limit of %zu",
|
||||||
oldest_idle->connection_id,
|
oldest_idle->connection_id,
|
||||||
Curl_llist_count(&bundle->conns), dest_limit);
|
Curl_llist_count(&bundle->conns), dest_limit);
|
||||||
cpool_evict_conn(cpool, data, oldest_idle);
|
cpool_evict_conn(cpool, admin, oldest_idle);
|
||||||
|
|
||||||
/* in case the bundle was destroyed in disconnect, look it up again */
|
/* in case the bundle was destroyed in disconnect, look it up again */
|
||||||
bundle = cpool_find_bundle(cpool, conn);
|
bundle = cpool_find_bundle(cpool, conn);
|
||||||
live = bundle ? Curl_llist_count(&bundle->conns) : 0;
|
live = bundle ? Curl_llist_count(&bundle->conns) : 0;
|
||||||
}
|
}
|
||||||
shutdowns = Curl_cshutdn_dest_count(data, conn->destination);
|
shutdowns = Curl_cshutdn_dest_count(cshutdn, conn->destination);
|
||||||
}
|
}
|
||||||
if((live + shutdowns) >= dest_limit) {
|
if((live + shutdowns) >= dest_limit) {
|
||||||
res = CPOOL_LIMIT_DEST;
|
res = CPOOL_LIMIT_DEST;
|
||||||
|
|
@ -448,11 +518,11 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
if(total_limit) {
|
if(total_limit) {
|
||||||
shutdowns = Curl_cshutdn_count(data);
|
shutdowns = Curl_cshutdn_count(cshutdn);
|
||||||
while((cpool->num_conn + shutdowns) >= total_limit) {
|
while((cpool->num_conn + shutdowns) >= total_limit) {
|
||||||
if(shutdowns) {
|
if(shutdowns) {
|
||||||
/* close one connection in shutdown right away, if we can */
|
/* close one connection in shutdown right away, if we can */
|
||||||
if(!Curl_cshutdn_close_oldest(data, NULL))
|
if(!Curl_cshutdn_close_oldest(cshutdn, NULL))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -461,13 +531,13 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
|
||||||
if(!oldest_idle)
|
if(!oldest_idle)
|
||||||
break;
|
break;
|
||||||
/* disconnect the old conn and continue */
|
/* disconnect the old conn and continue */
|
||||||
CURL_TRC_M(data, "Discarding connection #%"
|
CURL_TRC_M(admin, "Discarding connection #%"
|
||||||
FMT_OFF_T " from %zu to reach total "
|
FMT_OFF_T " from %zu to reach total "
|
||||||
"limit of %zu",
|
"limit of %zu",
|
||||||
oldest_idle->connection_id, cpool->num_conn, total_limit);
|
oldest_idle->connection_id, cpool->num_conn, total_limit);
|
||||||
cpool_evict_conn(cpool, data, oldest_idle);
|
cpool_evict_conn(cpool, admin, oldest_idle);
|
||||||
}
|
}
|
||||||
shutdowns = Curl_cshutdn_count(data);
|
shutdowns = Curl_cshutdn_count(cshutdn);
|
||||||
}
|
}
|
||||||
if((cpool->num_conn + shutdowns) >= total_limit) {
|
if((cpool->num_conn + shutdowns) >= total_limit) {
|
||||||
res = CPOOL_LIMIT_TOTAL;
|
res = CPOOL_LIMIT_TOTAL;
|
||||||
|
|
@ -476,7 +546,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
CPOOL_UNLOCK(cpool, cpool->idata);
|
CPOOL_UNLOCK(cpool, admin);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -529,7 +599,8 @@ out:
|
||||||
static bool cpool_foreach(struct Curl_easy *data,
|
static bool cpool_foreach(struct Curl_easy *data,
|
||||||
struct cpool *cpool,
|
struct cpool *cpool,
|
||||||
void *param,
|
void *param,
|
||||||
int (*func)(struct Curl_easy *data,
|
int (*func)(struct cpool *cpool,
|
||||||
|
struct Curl_easy *data,
|
||||||
struct connectdata *conn, void *param))
|
struct connectdata *conn, void *param))
|
||||||
{
|
{
|
||||||
struct Curl_hash_iterator iter;
|
struct Curl_hash_iterator iter;
|
||||||
|
|
@ -553,7 +624,7 @@ static bool cpool_foreach(struct Curl_easy *data,
|
||||||
struct connectdata *conn = Curl_node_elem(curr);
|
struct connectdata *conn = Curl_node_elem(curr);
|
||||||
curr = Curl_node_next(curr);
|
curr = Curl_node_next(curr);
|
||||||
|
|
||||||
if(func(data, conn, param) == 1) {
|
if(func(cpool, data, conn, param) == 1) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -573,6 +644,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
|
||||||
unsigned int maxconnects;
|
unsigned int maxconnects;
|
||||||
struct connectdata *oldest_idle = NULL;
|
struct connectdata *oldest_idle = NULL;
|
||||||
struct cpool *cpool = cpool_get_instance(data);
|
struct cpool *cpool = cpool_get_instance(data);
|
||||||
|
struct Curl_easy *admin;
|
||||||
bool kept = TRUE;
|
bool kept = TRUE;
|
||||||
|
|
||||||
if(!data || !data->multi)
|
if(!data || !data->multi)
|
||||||
|
|
@ -590,8 +662,10 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
|
||||||
if(cpool && maxconnects) {
|
if(cpool && maxconnects) {
|
||||||
/* may be called form a callback already under lock */
|
/* may be called form a callback already under lock */
|
||||||
bool do_lock = !CPOOL_IS_LOCKED(cpool);
|
bool do_lock = !CPOOL_IS_LOCKED(cpool);
|
||||||
|
|
||||||
|
admin = Curl_get_admin(data);
|
||||||
if(do_lock)
|
if(do_lock)
|
||||||
CPOOL_LOCK(cpool, data);
|
CPOOL_LOCK(cpool, admin);
|
||||||
if(cpool->num_conn > maxconnects) {
|
if(cpool->num_conn > maxconnects) {
|
||||||
infof(data, "Connection pool is full, closing the oldest of %zu/%u",
|
infof(data, "Connection pool is full, closing the oldest of %zu/%u",
|
||||||
cpool->num_conn, maxconnects);
|
cpool->num_conn, maxconnects);
|
||||||
|
|
@ -599,11 +673,11 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
|
||||||
oldest_idle = cpool_get_oldest_idle(cpool, Curl_pgrs_now(data));
|
oldest_idle = cpool_get_oldest_idle(cpool, Curl_pgrs_now(data));
|
||||||
kept = (oldest_idle != conn);
|
kept = (oldest_idle != conn);
|
||||||
if(oldest_idle) {
|
if(oldest_idle) {
|
||||||
cpool_evict_conn(cpool, data, oldest_idle);
|
cpool_evict_conn(cpool, admin, oldest_idle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(do_lock)
|
if(do_lock)
|
||||||
CPOOL_UNLOCK(cpool, data);
|
CPOOL_UNLOCK(cpool, admin);
|
||||||
}
|
}
|
||||||
|
|
||||||
return kept;
|
return kept;
|
||||||
|
|
@ -649,74 +723,23 @@ bool Curl_cpool_find(struct Curl_easy *data,
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Curl_conn_terminate(struct Curl_easy *data,
|
|
||||||
struct connectdata *conn,
|
|
||||||
bool aborted)
|
|
||||||
{
|
|
||||||
struct cpool *cpool = cpool_get_instance(data);
|
|
||||||
bool do_lock;
|
|
||||||
|
|
||||||
DEBUGASSERT(cpool);
|
|
||||||
DEBUGASSERT(data && !data->conn);
|
|
||||||
if(!cpool)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* If this connection is not marked to force-close, leave it open if there
|
|
||||||
* are other users of it */
|
|
||||||
if(CONN_INUSE(conn) && !aborted) {
|
|
||||||
DEBUGASSERT(0); /* does this ever happen? */
|
|
||||||
DEBUGF(infof(data, "conn terminate when inuse: %u", conn->attached_xfers));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This method may be called while we are under lock, e.g. from a
|
|
||||||
* user callback in find. */
|
|
||||||
do_lock = !CPOOL_IS_LOCKED(cpool);
|
|
||||||
if(do_lock)
|
|
||||||
CPOOL_LOCK(cpool, data);
|
|
||||||
|
|
||||||
if(conn->bits.in_cpool) {
|
|
||||||
cpool_remove_conn(cpool, conn);
|
|
||||||
DEBUGASSERT(!conn->bits.in_cpool);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* treat the connection as aborted in CONNECT_ONLY situations,
|
|
||||||
* so no graceful shutdown is attempted. */
|
|
||||||
if(conn->bits.connect_only)
|
|
||||||
aborted = TRUE;
|
|
||||||
|
|
||||||
if(data->multi) {
|
|
||||||
/* Add it to the multi's cpool for shutdown handling */
|
|
||||||
infof(data, "%s connection #%" FMT_OFF_T,
|
|
||||||
aborted ? "closing" : "shutting down", conn->connection_id);
|
|
||||||
cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* No multi available, terminate */
|
|
||||||
infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
|
|
||||||
Curl_cshutdn_terminate(cpool->idata, conn, !aborted);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(do_lock)
|
|
||||||
CPOOL_UNLOCK(cpool, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cpool_reaper_ctx {
|
struct cpool_reaper_ctx {
|
||||||
size_t reaped;
|
size_t reaped;
|
||||||
struct curltime now;
|
struct curltime now;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int cpool_reap_dead_cb(struct Curl_easy *data,
|
static int cpool_reap_dead_cb(struct cpool *cpool,
|
||||||
|
struct Curl_easy *admin,
|
||||||
struct connectdata *conn, void *param)
|
struct connectdata *conn, void *param)
|
||||||
{
|
{
|
||||||
struct cpool_reaper_ctx *reaper = param;
|
struct cpool_reaper_ctx *reaper = param;
|
||||||
|
|
||||||
if(!CONN_INUSE(conn)) {
|
if(!CONN_INUSE(conn)) {
|
||||||
if(conn->bits.no_reuse || conn->bits.close ||
|
if(conn->bits.no_reuse || conn->bits.close ||
|
||||||
!Curl_cpool_conn_seems_healthy(conn, data, &reaper->now)) {
|
!Curl_cpool_conn_seems_healthy(conn, admin, &reaper->now)) {
|
||||||
/* terminate conn and stop the iteration */
|
/* terminate conn and stop the iteration */
|
||||||
reaper->reaped++;
|
reaper->reaped++;
|
||||||
Curl_conn_terminate(data, conn, FALSE);
|
cpool_conn_close(cpool, admin, conn, FALSE);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -730,46 +753,49 @@ static int cpool_reap_dead_cb(struct Curl_easy *data,
|
||||||
*
|
*
|
||||||
* When called, this transfer has no connection attached.
|
* When called, this transfer has no connection attached.
|
||||||
*/
|
*/
|
||||||
void Curl_cpool_prune_dead(struct Curl_easy *data)
|
void Curl_cpool_prune_dead(struct cpool *cpool,
|
||||||
|
struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct cpool *cpool = cpool_get_instance(data);
|
struct Curl_easy *admin;
|
||||||
timediff_t elapsed;
|
timediff_t elapsed;
|
||||||
|
|
||||||
if(!cpool)
|
if(!cpool)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CPOOL_LOCK(cpool, data);
|
admin = Curl_get_admin(data);
|
||||||
elapsed = curlx_ptimediff_ms(Curl_pgrs_now(data), &cpool->last_cleanup);
|
CPOOL_LOCK(cpool, admin);
|
||||||
|
elapsed = curlx_ptimediff_ms(Curl_pgrs_now(admin), &cpool->last_cleanup);
|
||||||
|
|
||||||
if(elapsed >= 1000L) {
|
if(elapsed >= 1000L) {
|
||||||
struct cpool_reaper_ctx reaper;
|
struct cpool_reaper_ctx reaper;
|
||||||
|
|
||||||
memset(&reaper, 0, sizeof(reaper));
|
memset(&reaper, 0, sizeof(reaper));
|
||||||
reaper.now = *Curl_pgrs_now(data);
|
reaper.now = *Curl_pgrs_now(admin);
|
||||||
while(cpool_foreach(data, cpool, &reaper, cpool_reap_dead_cb))
|
while(cpool_foreach(admin, cpool, &reaper, cpool_reap_dead_cb))
|
||||||
;
|
;
|
||||||
cpool->last_cleanup = *Curl_pgrs_now(data);
|
cpool->last_cleanup = *Curl_pgrs_now(admin);
|
||||||
}
|
}
|
||||||
CPOOL_UNLOCK(cpool, data);
|
CPOOL_UNLOCK(cpool, admin);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int conn_upkeep(struct Curl_easy *data,
|
static int conn_upkeep(struct cpool *cpool,
|
||||||
|
struct Curl_easy *admin,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
void *param)
|
void *param)
|
||||||
{
|
{
|
||||||
(void)param;
|
(void)param;
|
||||||
if(curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->keepalive) >=
|
if(curlx_ptimediff_ms(Curl_pgrs_now(admin), &conn->keepalive) >=
|
||||||
data->set.upkeep_interval_ms) {
|
admin->set.upkeep_interval_ms) {
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
|
|
||||||
/* briefly attach for action */
|
/* briefly attach for action */
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(admin, conn, FALSE);
|
||||||
result = Curl_conn_keep_alive(data, conn);
|
result = Curl_conn_keep_alive(admin, conn);
|
||||||
conn->keepalive = *Curl_pgrs_now(data);
|
conn->keepalive = *Curl_pgrs_now(admin);
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(admin);
|
||||||
|
|
||||||
if(result && !CONN_INUSE(conn)) {
|
if(result && !CONN_INUSE(conn)) {
|
||||||
Curl_conn_terminate(data, conn, FALSE);
|
cpool_conn_close(cpool, admin, conn, FALSE);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -779,14 +805,15 @@ static int conn_upkeep(struct Curl_easy *data,
|
||||||
CURLcode Curl_cpool_upkeep(struct Curl_easy *data)
|
CURLcode Curl_cpool_upkeep(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct cpool *cpool = cpool_get_instance(data);
|
struct cpool *cpool = cpool_get_instance(data);
|
||||||
|
struct Curl_easy *admin = Curl_get_admin(data);
|
||||||
|
|
||||||
if(!cpool)
|
if(!cpool)
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
|
|
||||||
CPOOL_LOCK(cpool, data);
|
CPOOL_LOCK(cpool, admin);
|
||||||
while(cpool_foreach(data, cpool, NULL, conn_upkeep))
|
while(cpool_foreach(admin, cpool, NULL, conn_upkeep))
|
||||||
;
|
;
|
||||||
CPOOL_UNLOCK(cpool, data);
|
CPOOL_UNLOCK(cpool, admin);
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -795,10 +822,12 @@ struct cpool_find_ctx {
|
||||||
struct connectdata *conn;
|
struct connectdata *conn;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int cpool_find_conn(struct Curl_easy *data,
|
static int cpool_find_conn(struct cpool *cpool,
|
||||||
|
struct Curl_easy *data,
|
||||||
struct connectdata *conn, void *param)
|
struct connectdata *conn, void *param)
|
||||||
{
|
{
|
||||||
struct cpool_find_ctx *fctx = param;
|
struct cpool_find_ctx *fctx = param;
|
||||||
|
(void)cpool;
|
||||||
(void)data;
|
(void)data;
|
||||||
if(conn->connection_id == fctx->id) {
|
if(conn->connection_id == fctx->id) {
|
||||||
fctx->conn = conn;
|
fctx->conn = conn;
|
||||||
|
|
@ -837,36 +866,37 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
|
||||||
cb(conn, data, cbdata);
|
cb(conn, data, cbdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cpool_mark_stale(struct Curl_easy *data,
|
static int cpool_mark_stale(struct cpool *cpool,
|
||||||
|
struct Curl_easy *admin,
|
||||||
struct connectdata *conn, void *param)
|
struct connectdata *conn, void *param)
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)cpool;
|
||||||
|
(void)admin;
|
||||||
(void)param;
|
(void)param;
|
||||||
conn->bits.no_reuse = TRUE;
|
conn->bits.no_reuse = TRUE;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cpool_reap_no_reuse(struct Curl_easy *data,
|
static int cpool_reap_no_reuse(struct cpool *cpool,
|
||||||
|
struct Curl_easy *admin,
|
||||||
struct connectdata *conn, void *param)
|
struct connectdata *conn, void *param)
|
||||||
{
|
{
|
||||||
(void)param;
|
(void)param;
|
||||||
if(!CONN_INUSE(conn) && conn->bits.no_reuse) {
|
if(!CONN_INUSE(conn) && conn->bits.no_reuse) {
|
||||||
Curl_conn_terminate(data, conn, FALSE);
|
cpool_conn_close(cpool, admin, conn, FALSE);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0; /* continue iteration */
|
return 0; /* continue iteration */
|
||||||
}
|
}
|
||||||
|
|
||||||
void Curl_cpool_nw_changed(struct Curl_easy *data)
|
void Curl_cpool_nw_changed(struct cpool *cpool, struct Curl_easy *admin)
|
||||||
{
|
{
|
||||||
struct cpool *cpool = cpool_get_instance(data);
|
if(cpool && admin) {
|
||||||
|
CPOOL_LOCK(cpool, admin);
|
||||||
if(cpool) {
|
cpool_foreach(admin, cpool, NULL, cpool_mark_stale);
|
||||||
CPOOL_LOCK(cpool, data);
|
while(cpool_foreach(admin, cpool, NULL, cpool_reap_no_reuse))
|
||||||
cpool_foreach(data, cpool, NULL, cpool_mark_stale);
|
|
||||||
while(cpool_foreach(data, cpool, NULL, cpool_reap_no_reuse))
|
|
||||||
;
|
;
|
||||||
CPOOL_UNLOCK(cpool, data);
|
CPOOL_UNLOCK(cpool, admin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -907,24 +937,27 @@ bool Curl_cpool_conn_seems_healthy(struct connectdata *conn,
|
||||||
struct Curl_easy *data,
|
struct Curl_easy *data,
|
||||||
const struct curltime *pnow)
|
const struct curltime *pnow)
|
||||||
{
|
{
|
||||||
|
struct Curl_easy *admin;
|
||||||
bool healthy = TRUE;
|
bool healthy = TRUE;
|
||||||
|
|
||||||
DEBUGASSERT(!data->conn);
|
DEBUGASSERT(!data->conn);
|
||||||
if(!CONN_INUSE(conn) && cpool_conn_maxage(data, conn, pnow)) /* too old? */
|
if(!CONN_INUSE(conn) && cpool_conn_maxage(data, conn, pnow)) /* too old? */
|
||||||
return FALSE;
|
return FALSE;
|
||||||
else if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000)
|
if(curlx_ptimediff_ms(pnow, &conn->lastchecked) < 1000)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
else if(conn->scheme->run->connection_is_dead) {
|
|
||||||
Curl_attach_connection(data, conn);
|
admin = Curl_get_admin(data);
|
||||||
healthy = !conn->scheme->run->connection_is_dead(data, conn);
|
if(conn->scheme->run->connection_is_dead) {
|
||||||
Curl_detach_connection(data);
|
Curl_attach_connection(admin, conn, FALSE);
|
||||||
|
healthy = !conn->scheme->run->connection_is_dead(admin, conn);
|
||||||
|
Curl_detach_connection(admin);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bool input_pending = FALSE;
|
bool input_pending = FALSE;
|
||||||
|
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(admin, conn, FALSE);
|
||||||
healthy = Curl_conn_is_alive(data, conn, &input_pending);
|
healthy = Curl_conn_is_alive(admin, conn, &input_pending);
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(admin);
|
||||||
if(healthy && input_pending &&
|
if(healthy && input_pending &&
|
||||||
!CONN_INUSE(conn) && !Curl_conn_is_multiplex(conn, FIRSTSOCKET)) {
|
!CONN_INUSE(conn) && !Curl_conn_is_multiplex(conn, FIRSTSOCKET)) {
|
||||||
/* Non-multiplexed connections without attached transfers should
|
/* Non-multiplexed connections without attached transfers should
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ struct Curl_multi;
|
||||||
struct Curl_share;
|
struct Curl_share;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminate the connection, e.g. close and destroy.
|
* Close and destroy the connection.
|
||||||
* If the connection is in a cpool, remove it.
|
* If the connection is in a cpool, remove it.
|
||||||
* If a `cshutdn` is available (e.g. data has a multi handle),
|
* If a `cshutdn` is available (e.g. data has a multi handle),
|
||||||
* pass the connection to that for controlled shutdown.
|
* pass the connection to that for controlled shutdown.
|
||||||
|
|
@ -42,9 +42,9 @@ struct Curl_share;
|
||||||
* Takes ownership of `conn`.
|
* Takes ownership of `conn`.
|
||||||
* `data` should not be attached to a connection.
|
* `data` should not be attached to a connection.
|
||||||
*/
|
*/
|
||||||
void Curl_conn_terminate(struct Curl_easy *data,
|
void Curl_conn_close(struct Curl_easy *data,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
bool aborted);
|
bool aborted);
|
||||||
|
|
||||||
struct cpool {
|
struct cpool {
|
||||||
/* the pooled connections, bundled per destination */
|
/* the pooled connections, bundled per destination */
|
||||||
|
|
@ -53,22 +53,24 @@ struct cpool {
|
||||||
curl_off_t next_connection_id;
|
curl_off_t next_connection_id;
|
||||||
curl_off_t next_easy_id;
|
curl_off_t next_easy_id;
|
||||||
struct curltime last_cleanup;
|
struct curltime last_cleanup;
|
||||||
struct Curl_easy *idata; /* internal handle for maintenance */
|
|
||||||
struct Curl_share *share; /* != NULL if pool belongs to share */
|
struct Curl_share *share; /* != NULL if pool belongs to share */
|
||||||
BIT(locked);
|
BIT(locked);
|
||||||
BIT(initialized);
|
BIT(initialized);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Get connection pool instance for data or NULL if none exists */
|
||||||
|
struct cpool *Curl_cpool_get_instance(struct Curl_easy *data);
|
||||||
|
|
||||||
/* Init the pool, pass multi only if pool is owned by it.
|
/* Init the pool, pass multi only if pool is owned by it.
|
||||||
* Cannot fail.
|
* Cannot fail.
|
||||||
*/
|
*/
|
||||||
void Curl_cpool_init(struct cpool *cpool,
|
void Curl_cpool_init(struct cpool *cpool,
|
||||||
struct Curl_easy *idata,
|
|
||||||
struct Curl_share *share,
|
struct Curl_share *share,
|
||||||
size_t size);
|
size_t size);
|
||||||
|
|
||||||
/* Destroy all connections and free all members */
|
/* Destroy all connections and free all members */
|
||||||
void Curl_cpool_destroy(struct cpool *cpool);
|
void Curl_cpool_destroy(struct cpool *cpool,
|
||||||
|
struct Curl_easy *admin);
|
||||||
|
|
||||||
/* Init the transfer to be used within its connection pool.
|
/* Init the transfer to be used within its connection pool.
|
||||||
* Assigns `data->id`. */
|
* Assigns `data->id`. */
|
||||||
|
|
@ -126,13 +128,12 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
|
||||||
struct connectdata *conn);
|
struct connectdata *conn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function scans the data's connection pool for half-open/dead
|
* Scans the connection pool for half-open/dead
|
||||||
* connections, closes and removes them.
|
* connections, closes and removes them.
|
||||||
* The cleanup is done at most once per second.
|
* The cleanup is done at most once per second.
|
||||||
*
|
|
||||||
* When called, this transfer has no connection attached.
|
|
||||||
*/
|
*/
|
||||||
void Curl_cpool_prune_dead(struct Curl_easy *data);
|
void Curl_cpool_prune_dead(struct cpool *cpool,
|
||||||
|
struct Curl_easy *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform upkeep actions on connections in the transfer's pool.
|
* Perform upkeep actions on connections in the transfer's pool.
|
||||||
|
|
@ -154,7 +155,7 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
|
||||||
Curl_cpool_conn_do_cb *cb, void *cbdata);
|
Curl_cpool_conn_do_cb *cb, void *cbdata);
|
||||||
|
|
||||||
/* Close all unused connections, prevent reuse of existing ones. */
|
/* Close all unused connections, prevent reuse of existing ones. */
|
||||||
void Curl_cpool_nw_changed(struct Curl_easy *data);
|
void Curl_cpool_nw_changed(struct cpool *cpool, struct Curl_easy *admin);
|
||||||
|
|
||||||
/* Return TRUE iff the given connection is considered healthy, e.g.
|
/* Return TRUE iff the given connection is considered healthy, e.g.
|
||||||
* usable for more transfers. */
|
* usable for more transfers. */
|
||||||
|
|
|
||||||
174
lib/cshutdn.c
174
lib/cshutdn.c
|
|
@ -38,6 +38,13 @@
|
||||||
#include "curlx/strparse.h"
|
#include "curlx/strparse.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct cshutdn *Curl_cshutdn_get(struct Curl_easy *data)
|
||||||
|
{
|
||||||
|
if(data && data->multi)
|
||||||
|
return &data->multi->cshutdn;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void cshutdn_run_conn_handler(struct Curl_easy *data,
|
static void cshutdn_run_conn_handler(struct Curl_easy *data,
|
||||||
struct connectdata *conn)
|
struct connectdata *conn)
|
||||||
{
|
{
|
||||||
|
|
@ -107,22 +114,22 @@ static void cshutdn_run_once(struct Curl_easy *data,
|
||||||
conn->bits.shutdown_filters = TRUE;
|
conn->bits.shutdown_filters = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Curl_cshutdn_run_once(struct Curl_easy *data,
|
void Curl_conn_shutdown_once(struct Curl_easy *admin,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
bool *done)
|
bool *done)
|
||||||
{
|
{
|
||||||
DEBUGASSERT(!data->conn);
|
DEBUGASSERT(!admin->conn);
|
||||||
Curl_attach_connection(data, conn);
|
DEBUGASSERT(!admin->mid);
|
||||||
cshutdn_run_once(data, conn, done);
|
Curl_attach_connection(admin, conn, FALSE);
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] shutdown, done=%d", *done);
|
cshutdn_run_once(admin, conn, done);
|
||||||
Curl_detach_connection(data);
|
CURL_TRC_M(admin, "[SHUTDOWN] shutdown, done=%d", *done);
|
||||||
|
Curl_detach_connection(admin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Curl_cshutdn_terminate(struct Curl_easy *data,
|
void Curl_conn_terminate(struct Curl_easy *admin,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
bool do_shutdown)
|
bool do_shutdown)
|
||||||
{
|
{
|
||||||
struct Curl_easy *admin = data;
|
|
||||||
bool done;
|
bool done;
|
||||||
|
|
||||||
/* there must be a connection to close */
|
/* there must be a connection to close */
|
||||||
|
|
@ -130,16 +137,10 @@ void Curl_cshutdn_terminate(struct Curl_easy *data,
|
||||||
/* it must be removed from the connection pool */
|
/* it must be removed from the connection pool */
|
||||||
DEBUGASSERT(!conn->bits.in_cpool);
|
DEBUGASSERT(!conn->bits.in_cpool);
|
||||||
/* the transfer must be detached from the connection */
|
/* the transfer must be detached from the connection */
|
||||||
DEBUGASSERT(data && !data->conn);
|
DEBUGASSERT(admin && !admin->conn);
|
||||||
|
DEBUGASSERT(!admin->mid);
|
||||||
|
|
||||||
/* If we can obtain an internal admin handle, use that to attach
|
Curl_attach_connection(admin, conn, FALSE);
|
||||||
* 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);
|
cshutdn_run_conn_handler(admin, conn);
|
||||||
if(do_shutdown) {
|
if(do_shutdown) {
|
||||||
|
|
@ -154,18 +155,17 @@ void Curl_cshutdn_terminate(struct Curl_easy *data,
|
||||||
Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET);
|
Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET);
|
||||||
Curl_detach_connection(admin);
|
Curl_detach_connection(admin);
|
||||||
|
|
||||||
if(data->multi)
|
if(admin->multi)
|
||||||
Curl_multi_ev_conn_done(data->multi, data, conn);
|
Curl_multi_ev_conn_done(admin->multi, admin, conn);
|
||||||
Curl_conn_free(admin, conn);
|
Curl_conn_free(admin, conn);
|
||||||
|
|
||||||
if(data->multi) {
|
if(admin->multi) {
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged");
|
CURL_TRC_M(admin, "[SHUTDOWN] trigger multi connchanged");
|
||||||
Curl_multi_connchanged(data->multi);
|
Curl_multi_connchanged(admin->multi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn,
|
static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
const char *destination)
|
const char *destination)
|
||||||
{
|
{
|
||||||
struct Curl_llist_node *e;
|
struct Curl_llist_node *e;
|
||||||
|
|
@ -184,20 +184,19 @@ static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn,
|
||||||
conn = Curl_node_elem(e);
|
conn = Curl_node_elem(e);
|
||||||
Curl_node_remove(e);
|
Curl_node_remove(e);
|
||||||
sigpipe_init(&sigpipe_ctx);
|
sigpipe_init(&sigpipe_ctx);
|
||||||
sigpipe_apply(data, &sigpipe_ctx);
|
sigpipe_apply(cshutdn->multi->admin, &sigpipe_ctx);
|
||||||
Curl_cshutdn_terminate(data, conn, FALSE);
|
Curl_conn_terminate(cshutdn->multi->admin, conn, FALSE);
|
||||||
sigpipe_restore(&sigpipe_ctx);
|
sigpipe_restore(&sigpipe_ctx);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
|
bool Curl_cshutdn_close_oldest(struct cshutdn *cshutdn,
|
||||||
const char *destination)
|
const char *destination)
|
||||||
{
|
{
|
||||||
if(data && data->multi) {
|
if(cshutdn) {
|
||||||
struct cshutdn *csd = &data->multi->cshutdn;
|
return cshutdn_destroy_oldest(cshutdn, destination);
|
||||||
return cshutdn_destroy_oldest(csd, data, destination);
|
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +204,6 @@ bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
|
||||||
#define NUM_POLLS_ON_STACK 10
|
#define NUM_POLLS_ON_STACK 10
|
||||||
|
|
||||||
static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
|
static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
int timeout_ms)
|
int timeout_ms)
|
||||||
{
|
{
|
||||||
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
|
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
|
||||||
|
|
@ -214,7 +212,7 @@ static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
|
||||||
|
|
||||||
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
|
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
|
||||||
|
|
||||||
result = Curl_cshutdn_add_pollfds(cshutdn, data, &cpfds);
|
result = Curl_cshutdn_add_pollfds(cshutdn, &cpfds);
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
@ -226,11 +224,11 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cshutdn_perform(struct cshutdn *cshutdn,
|
static void cshutdn_perform(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
struct Curl_sigpipe_ctx *sigpipe_ctx)
|
struct Curl_sigpipe_ctx *sigpipe_ctx)
|
||||||
{
|
{
|
||||||
struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
|
struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
|
||||||
struct Curl_llist_node *enext;
|
struct Curl_llist_node *enext;
|
||||||
|
struct Curl_easy *admin = cshutdn->multi->admin;
|
||||||
struct connectdata *conn;
|
struct connectdata *conn;
|
||||||
timediff_t next_expire_ms = 0, ms;
|
timediff_t next_expire_ms = 0, ms;
|
||||||
bool done;
|
bool done;
|
||||||
|
|
@ -238,21 +236,21 @@ static void cshutdn_perform(struct cshutdn *cshutdn,
|
||||||
if(!e)
|
if(!e)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections",
|
CURL_TRC_M(admin, "[SHUTDOWN] perform on %zu connections",
|
||||||
Curl_llist_count(&cshutdn->list));
|
Curl_llist_count(&cshutdn->list));
|
||||||
sigpipe_apply(data, sigpipe_ctx);
|
sigpipe_apply(admin, sigpipe_ctx);
|
||||||
while(e) {
|
while(e) {
|
||||||
enext = Curl_node_next(e);
|
enext = Curl_node_next(e);
|
||||||
conn = Curl_node_elem(e);
|
conn = Curl_node_elem(e);
|
||||||
Curl_cshutdn_run_once(data, conn, &done);
|
Curl_conn_shutdown_once(admin, conn, &done);
|
||||||
if(done) {
|
if(done) {
|
||||||
Curl_node_remove(e);
|
Curl_node_remove(e);
|
||||||
Curl_cshutdn_terminate(data, conn, FALSE);
|
Curl_conn_terminate(admin, conn, FALSE);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* idata has one timer list, but maybe more than one connection.
|
/* idata has one timer list, but maybe more than one connection.
|
||||||
* Set EXPIRE_SHUTDOWN to the smallest time left for all. */
|
* Set EXPIRE_SHUTDOWN to the smallest time left for all. */
|
||||||
ms = Curl_conn_shutdown_timeleft(data, conn);
|
ms = Curl_conn_shutdown_timeleft(admin, conn);
|
||||||
if(ms && (!next_expire_ms || (ms < next_expire_ms)))
|
if(ms && (!next_expire_ms || (ms < next_expire_ms)))
|
||||||
next_expire_ms = ms;
|
next_expire_ms = ms;
|
||||||
}
|
}
|
||||||
|
|
@ -260,45 +258,42 @@ static void cshutdn_perform(struct cshutdn *cshutdn,
|
||||||
}
|
}
|
||||||
|
|
||||||
if(next_expire_ms)
|
if(next_expire_ms)
|
||||||
Curl_expire_ex(data, next_expire_ms, EXPIRE_SHUTDOWN);
|
Curl_expire_ex(admin, next_expire_ms, EXPIRE_SHUTDOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cshutdn_terminate_all(struct cshutdn *cshutdn,
|
static void cshutdn_terminate_all(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
int timeout_ms)
|
int timeout_ms)
|
||||||
{
|
{
|
||||||
struct curltime started = *Curl_pgrs_now(data);
|
struct Curl_easy *admin = cshutdn->multi->admin;
|
||||||
|
struct curltime started = *Curl_pgrs_now(admin);
|
||||||
struct Curl_llist_node *e;
|
struct Curl_llist_node *e;
|
||||||
struct Curl_sigpipe_ctx sigpipe_ctx;
|
struct Curl_sigpipe_ctx sigpipe_ctx;
|
||||||
|
|
||||||
DEBUGASSERT(cshutdn);
|
CURL_TRC_M(admin, "[SHUTDOWN] shutdown all");
|
||||||
DEBUGASSERT(data);
|
|
||||||
|
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] shutdown all");
|
|
||||||
sigpipe_init(&sigpipe_ctx);
|
sigpipe_init(&sigpipe_ctx);
|
||||||
|
|
||||||
while(Curl_llist_head(&cshutdn->list)) {
|
while(Curl_llist_head(&cshutdn->list)) {
|
||||||
timediff_t spent_ms;
|
timediff_t spent_ms;
|
||||||
int remain_ms;
|
int remain_ms;
|
||||||
|
|
||||||
cshutdn_perform(cshutdn, data, &sigpipe_ctx);
|
cshutdn_perform(cshutdn, &sigpipe_ctx);
|
||||||
|
|
||||||
if(!Curl_llist_head(&cshutdn->list)) {
|
if(!Curl_llist_head(&cshutdn->list)) {
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly");
|
CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished cleanly");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait for activity, timeout or "nothing" */
|
/* wait for activity, timeout or "nothing" */
|
||||||
spent_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &started);
|
spent_ms = curlx_ptimediff_ms(Curl_pgrs_now(admin), &started);
|
||||||
if(spent_ms >= (timediff_t)timeout_ms) {
|
if(spent_ms >= (timediff_t)timeout_ms) {
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s",
|
CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished, %s",
|
||||||
(timeout_ms > 0) ? "timeout" : "best effort done");
|
(timeout_ms > 0) ? "timeout" : "best effort done");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
remain_ms = timeout_ms - (int)spent_ms;
|
remain_ms = timeout_ms - (int)spent_ms;
|
||||||
if(cshutdn_wait(cshutdn, data, remain_ms)) {
|
if(cshutdn_wait(cshutdn, remain_ms)) {
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, aborted");
|
CURL_TRC_M(admin, "[SHUTDOWN] shutdown finished, aborted");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -308,7 +303,7 @@ static void cshutdn_terminate_all(struct cshutdn *cshutdn,
|
||||||
while(e) {
|
while(e) {
|
||||||
struct connectdata *conn = Curl_node_elem(e);
|
struct connectdata *conn = Curl_node_elem(e);
|
||||||
Curl_node_remove(e);
|
Curl_node_remove(e);
|
||||||
Curl_cshutdn_terminate(data, conn, FALSE);
|
Curl_conn_terminate(admin, conn, FALSE);
|
||||||
e = Curl_llist_head(&cshutdn->list);
|
e = Curl_llist_head(&cshutdn->list);
|
||||||
}
|
}
|
||||||
DEBUGASSERT(!Curl_llist_count(&cshutdn->list));
|
DEBUGASSERT(!Curl_llist_count(&cshutdn->list));
|
||||||
|
|
@ -327,12 +322,13 @@ int Curl_cshutdn_init(struct cshutdn *cshutdn,
|
||||||
}
|
}
|
||||||
|
|
||||||
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
|
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data)
|
struct Curl_easy *admin)
|
||||||
{
|
{
|
||||||
if(cshutdn->initialized && data) {
|
if(cshutdn->initialized && admin) {
|
||||||
int timeout_ms = 0;
|
int timeout_ms = 0;
|
||||||
/* for testing, run graceful shutdown */
|
/* for testing, run graceful shutdown */
|
||||||
#ifdef DEBUGBUILD
|
#ifdef DEBUGBUILD
|
||||||
|
DEBUGASSERT(!admin->mid);
|
||||||
{
|
{
|
||||||
const char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
|
const char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
|
||||||
if(p) {
|
if(p) {
|
||||||
|
|
@ -343,29 +339,28 @@ void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] destroy, %zu connections, timeout=%dms",
|
CURL_TRC_M(admin, "[SHUTDOWN] destroy, %zu connections, timeout=%dms",
|
||||||
Curl_llist_count(&cshutdn->list), timeout_ms);
|
Curl_llist_count(&cshutdn->list), timeout_ms);
|
||||||
cshutdn_terminate_all(cshutdn, data, timeout_ms);
|
cshutdn_terminate_all(cshutdn, timeout_ms);
|
||||||
}
|
}
|
||||||
cshutdn->multi = NULL;
|
cshutdn->multi = NULL;
|
||||||
|
cshutdn->initialized = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Curl_cshutdn_count(struct Curl_easy *data)
|
size_t Curl_cshutdn_count(struct cshutdn *cshutdn)
|
||||||
{
|
{
|
||||||
if(data && data->multi) {
|
if(cshutdn) {
|
||||||
struct cshutdn *csd = &data->multi->cshutdn;
|
return Curl_llist_count(&cshutdn->list);
|
||||||
return Curl_llist_count(&csd->list);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
|
size_t Curl_cshutdn_dest_count(struct cshutdn *cshutdn,
|
||||||
const char *destination)
|
const char *destination)
|
||||||
{
|
{
|
||||||
if(data && data->multi) {
|
if(cshutdn) {
|
||||||
struct cshutdn *csd = &data->multi->cshutdn;
|
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
struct Curl_llist_node *e = Curl_llist_head(&csd->list);
|
struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
|
||||||
while(e) {
|
while(e) {
|
||||||
struct connectdata *conn = Curl_node_elem(e);
|
struct connectdata *conn = Curl_node_elem(e);
|
||||||
if(!strcmp(destination, conn->destination))
|
if(!strcmp(destination, conn->destination))
|
||||||
|
|
@ -378,17 +373,17 @@ size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
|
static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
struct connectdata *conn)
|
struct connectdata *conn)
|
||||||
{
|
{
|
||||||
|
struct Curl_easy *admin = cshutdn->multi->admin;
|
||||||
CURLMcode mresult;
|
CURLMcode mresult;
|
||||||
|
|
||||||
DEBUGASSERT(cshutdn);
|
DEBUGASSERT(cshutdn);
|
||||||
DEBUGASSERT(cshutdn->multi->socket_cb);
|
DEBUGASSERT(cshutdn->multi->socket_cb);
|
||||||
|
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(admin, conn, FALSE);
|
||||||
mresult = Curl_multi_ev_assess_conn(cshutdn->multi, data, conn);
|
mresult = Curl_multi_ev_assess_conn(cshutdn->multi, admin, conn);
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(admin);
|
||||||
return mresult;
|
return mresult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -396,47 +391,46 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
size_t conns_in_pool)
|
size_t conns_in_pool)
|
||||||
{
|
{
|
||||||
struct Curl_easy *data = cshutdn->multi->admin;
|
struct Curl_easy *admin = cshutdn->multi->admin;
|
||||||
size_t max_total = cshutdn->multi->max_total_connections;
|
size_t max_total = cshutdn->multi->max_total_connections;
|
||||||
|
|
||||||
/* Add the connection to our shutdown list for non-blocking shutdown
|
/* Add the connection to our shutdown list for non-blocking shutdown
|
||||||
* during multi processing. */
|
* during multi processing. */
|
||||||
if(max_total > 0 &&
|
if(max_total > 0 &&
|
||||||
(max_total <= (conns_in_pool + Curl_llist_count(&cshutdn->list)))) {
|
(max_total <= (conns_in_pool + Curl_llist_count(&cshutdn->list)))) {
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection "
|
CURL_TRC_M(admin, "[SHUTDOWN] discarding oldest shutdown connection "
|
||||||
"due to connection limit of %zu", max_total);
|
"due to connection limit of %zu", max_total);
|
||||||
cshutdn_destroy_oldest(cshutdn, data, NULL);
|
cshutdn_destroy_oldest(cshutdn, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cshutdn->multi->socket_cb) {
|
if(cshutdn->multi->socket_cb) {
|
||||||
if(cshutdn_update_ev(cshutdn, data, conn)) {
|
if(cshutdn_update_ev(cshutdn, conn)) {
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] update events failed, discarding #%"
|
CURL_TRC_M(admin, "[SHUTDOWN] update events failed, discarding #%"
|
||||||
FMT_OFF_T, conn->connection_id);
|
FMT_OFF_T, conn->connection_id);
|
||||||
Curl_cshutdn_terminate(data, conn, FALSE);
|
Curl_conn_terminate(admin, conn, FALSE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Curl_llist_append(&cshutdn->list, conn, &conn->cshutdn_node);
|
Curl_llist_append(&cshutdn->list, conn, &conn->cshutdn_node);
|
||||||
CURL_TRC_M(data, "[SHUTDOWN] added #%" FMT_OFF_T
|
CURL_TRC_M(admin, "[SHUTDOWN] added #%" FMT_OFF_T
|
||||||
" to shutdowns, now %zu conns in shutdown",
|
" to shutdowns, now %zu conns in shutdown",
|
||||||
conn->connection_id, Curl_llist_count(&cshutdn->list));
|
conn->connection_id, Curl_llist_count(&cshutdn->list));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
|
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
struct Curl_sigpipe_ctx *sigpipe_ctx)
|
struct Curl_sigpipe_ctx *sigpipe_ctx)
|
||||||
{
|
{
|
||||||
cshutdn_perform(cshutdn, data, sigpipe_ctx);
|
cshutdn_perform(cshutdn, sigpipe_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return fd_set info about the shutdown connections */
|
/* return fd_set info about the shutdown connections */
|
||||||
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
|
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
fd_set *read_fd_set, fd_set *write_fd_set,
|
fd_set *read_fd_set, fd_set *write_fd_set,
|
||||||
int *maxfd)
|
int *maxfd)
|
||||||
{
|
{
|
||||||
if(Curl_llist_head(&cshutdn->list)) {
|
if(Curl_llist_head(&cshutdn->list)) {
|
||||||
|
struct Curl_easy *admin = cshutdn->multi->admin;
|
||||||
struct Curl_llist_node *e;
|
struct Curl_llist_node *e;
|
||||||
struct easy_pollset ps;
|
struct easy_pollset ps;
|
||||||
|
|
||||||
|
|
@ -447,9 +441,9 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
|
|
||||||
Curl_pollset_reset(&ps);
|
Curl_pollset_reset(&ps);
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(admin, conn, FALSE);
|
||||||
result = Curl_conn_adjust_pollset(data, conn, &ps);
|
result = Curl_conn_adjust_pollset(admin, conn, &ps);
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(admin);
|
||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -473,13 +467,13 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
|
||||||
|
|
||||||
/* return information about the shutdown connections */
|
/* return information about the shutdown connections */
|
||||||
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
|
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
struct Curl_waitfds *cwfds)
|
struct Curl_waitfds *cwfds)
|
||||||
{
|
{
|
||||||
unsigned int need = 0;
|
unsigned int need = 0;
|
||||||
|
|
||||||
if(Curl_llist_head(&cshutdn->list)) {
|
if(Curl_llist_head(&cshutdn->list)) {
|
||||||
struct Curl_llist_node *e;
|
struct Curl_llist_node *e;
|
||||||
|
struct Curl_easy *admin = cshutdn->multi->admin;
|
||||||
struct easy_pollset ps;
|
struct easy_pollset ps;
|
||||||
struct connectdata *conn;
|
struct connectdata *conn;
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
|
|
@ -488,9 +482,9 @@ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
|
||||||
for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) {
|
for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) {
|
||||||
conn = Curl_node_elem(e);
|
conn = Curl_node_elem(e);
|
||||||
Curl_pollset_reset(&ps);
|
Curl_pollset_reset(&ps);
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(admin, conn, FALSE);
|
||||||
result = Curl_conn_adjust_pollset(data, conn, &ps);
|
result = Curl_conn_adjust_pollset(admin, conn, &ps);
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(admin);
|
||||||
|
|
||||||
if(!result)
|
if(!result)
|
||||||
need += Curl_waitfds_add_ps(cwfds, &ps);
|
need += Curl_waitfds_add_ps(cwfds, &ps);
|
||||||
|
|
@ -501,7 +495,6 @@ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
|
||||||
}
|
}
|
||||||
|
|
||||||
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
|
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
struct curl_pollfds *cpfds)
|
struct curl_pollfds *cpfds)
|
||||||
{
|
{
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
@ -509,15 +502,16 @@ CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
|
||||||
if(Curl_llist_head(&cshutdn->list)) {
|
if(Curl_llist_head(&cshutdn->list)) {
|
||||||
struct Curl_llist_node *e;
|
struct Curl_llist_node *e;
|
||||||
struct easy_pollset ps;
|
struct easy_pollset ps;
|
||||||
|
struct Curl_easy *admin = cshutdn->multi->admin;
|
||||||
struct connectdata *conn;
|
struct connectdata *conn;
|
||||||
|
|
||||||
Curl_pollset_init(&ps);
|
Curl_pollset_init(&ps);
|
||||||
for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) {
|
for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) {
|
||||||
conn = Curl_node_elem(e);
|
conn = Curl_node_elem(e);
|
||||||
Curl_pollset_reset(&ps);
|
Curl_pollset_reset(&ps);
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(admin, conn, FALSE);
|
||||||
result = Curl_conn_adjust_pollset(data, conn, &ps);
|
result = Curl_conn_adjust_pollset(admin, conn, &ps);
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(admin);
|
||||||
|
|
||||||
if(!result)
|
if(!result)
|
||||||
result = Curl_pollfds_add_ps(cpfds, &ps);
|
result = Curl_pollfds_add_ps(cpfds, &ps);
|
||||||
|
|
|
||||||
|
|
@ -33,20 +33,20 @@ struct Curl_share;
|
||||||
struct Curl_sigpipe_ctx;
|
struct Curl_sigpipe_ctx;
|
||||||
|
|
||||||
/* Run the shutdown of the connection once.
|
/* Run the shutdown of the connection once.
|
||||||
* Shortly attach/detach `data` to `conn` while doing so.
|
* Shortly attach/detach the admin handle to `conn` while doing so.
|
||||||
* `done` will be set TRUE if any error was encountered or if
|
* `done` will be set TRUE if any error was encountered or if
|
||||||
* the connection was shut down completely. */
|
* the connection was shut down completely. */
|
||||||
void Curl_cshutdn_run_once(struct Curl_easy *data,
|
void Curl_conn_shutdown_once(struct Curl_easy *admin,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
bool *done);
|
bool *done);
|
||||||
|
|
||||||
/* Terminates the connection, e.g. closes and destroys it.
|
/* Terminates the connection, e.g. closes and destroys it.
|
||||||
* If `do_shutdown` is TRUE, the shutdown will be run once before
|
* If `do_shutdown` is TRUE, the shutdown will be run once before
|
||||||
* terminating it.
|
* terminating it.
|
||||||
* Takes ownership of `conn`. */
|
* Takes ownership of `conn`. */
|
||||||
void Curl_cshutdn_terminate(struct Curl_easy *data,
|
void Curl_conn_terminate(struct Curl_easy *admin,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
bool do_shutdown);
|
bool do_shutdown);
|
||||||
|
|
||||||
/* A `cshutdown` is always owned by a multi handle to maintain
|
/* A `cshutdown` is always owned by a multi handle to maintain
|
||||||
* the connections to be shut down. It registers timers and
|
* the connections to be shut down. It registers timers and
|
||||||
|
|
@ -57,25 +57,28 @@ struct cshutdn {
|
||||||
BIT(initialized);
|
BIT(initialized);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Get the cshutdn instance relevant for `data` or NULL if there is none */
|
||||||
|
struct cshutdn *Curl_cshutdn_get(struct Curl_easy *data);
|
||||||
|
|
||||||
/* Init as part of the given multi handle. */
|
/* Init as part of the given multi handle. */
|
||||||
int Curl_cshutdn_init(struct cshutdn *cshutdn,
|
int Curl_cshutdn_init(struct cshutdn *cshutdn,
|
||||||
struct Curl_multi *multi);
|
struct Curl_multi *multi);
|
||||||
|
|
||||||
/* Terminate all remaining connections and free resources. */
|
/* Terminate all remaining connections and free resources. */
|
||||||
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
|
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data);
|
struct Curl_easy *admin);
|
||||||
|
|
||||||
/* Number of connections being shut down. */
|
/* Number of connections being shut down. */
|
||||||
size_t Curl_cshutdn_count(struct Curl_easy *data);
|
size_t Curl_cshutdn_count(struct cshutdn *cshutdn);
|
||||||
|
|
||||||
/* Number of connections to the destination being shut down. */
|
/* Number of connections to the destination being shut down. */
|
||||||
size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
|
size_t Curl_cshutdn_dest_count(struct cshutdn *cshutdn,
|
||||||
const char *destination);
|
const char *destination);
|
||||||
|
|
||||||
/* Close the oldest connection in shutdown to destination or,
|
/* Close the oldest connection in shutdown to destination or,
|
||||||
* when destination is NULL for any destination.
|
* when destination is NULL for any destination.
|
||||||
* Return TRUE if a connection has been closed. */
|
* Return TRUE if a connection has been closed. */
|
||||||
bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
|
bool Curl_cshutdn_close_oldest(struct cshutdn *cshutdn,
|
||||||
const char *destination);
|
const char *destination);
|
||||||
|
|
||||||
/* Add a connection to have it shut down. Terminate the oldest
|
/* Add a connection to have it shut down. Terminate the oldest
|
||||||
|
|
@ -86,21 +89,17 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn,
|
||||||
|
|
||||||
/* Add sockets and POLLIN/OUT flags for connections being shut down. */
|
/* Add sockets and POLLIN/OUT flags for connections being shut down. */
|
||||||
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
|
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
struct curl_pollfds *cpfds);
|
struct curl_pollfds *cpfds);
|
||||||
|
|
||||||
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
|
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
struct Curl_waitfds *cwfds);
|
struct Curl_waitfds *cwfds);
|
||||||
|
|
||||||
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
|
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
fd_set *read_fd_set, fd_set *write_fd_set,
|
fd_set *read_fd_set, fd_set *write_fd_set,
|
||||||
int *maxfd);
|
int *maxfd);
|
||||||
|
|
||||||
/* Run maintenance on all connections. */
|
/* Run maintenance on all connections. */
|
||||||
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
|
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
|
||||||
struct Curl_easy *data,
|
|
||||||
struct Curl_sigpipe_ctx *sigpipe_ctx);
|
struct Curl_sigpipe_ctx *sigpipe_ctx);
|
||||||
|
|
||||||
#endif /* HEADER_CURL_CSHUTDN_H */
|
#endif /* HEADER_CURL_CSHUTDN_H */
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ static void share_destroy(struct Curl_share *share)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) {
|
if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) {
|
||||||
Curl_cpool_destroy(&share->cpool);
|
Curl_cpool_destroy(&share->cpool, share->admin);
|
||||||
}
|
}
|
||||||
|
|
||||||
Curl_dnscache_destroy(&share->dnscache);
|
Curl_dnscache_destroy(&share->dnscache);
|
||||||
|
|
@ -264,7 +264,7 @@ CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
|
||||||
case CURL_LOCK_DATA_CONNECT:
|
case CURL_LOCK_DATA_CONNECT:
|
||||||
/* It is safe to set this option several times on a share. */
|
/* It is safe to set this option several times on a share. */
|
||||||
if(!share->cpool.initialized) {
|
if(!share->cpool.initialized) {
|
||||||
Curl_cpool_init(&share->cpool, share->admin, share, 103);
|
Curl_cpool_init(&share->cpool, share, 103);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -371,11 +371,11 @@ CURLSHcode curl_share_cleanup(CURLSH *sh)
|
||||||
return CURLSHE_OK;
|
return CURLSHE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type,
|
CURLSHcode Curl_share_lock_share(struct Curl_share *share,
|
||||||
curl_lock_access accesstype)
|
struct Curl_easy *data,
|
||||||
|
curl_lock_data type,
|
||||||
|
curl_lock_access accesstype)
|
||||||
{
|
{
|
||||||
struct Curl_share *share = data->share;
|
|
||||||
|
|
||||||
if(!share)
|
if(!share)
|
||||||
return CURLSHE_INVALID;
|
return CURLSHE_INVALID;
|
||||||
|
|
||||||
|
|
@ -388,10 +388,16 @@ CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type,
|
||||||
return CURLSHE_OK;
|
return CURLSHE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type)
|
CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type,
|
||||||
|
curl_lock_access accesstype)
|
||||||
{
|
{
|
||||||
struct Curl_share *share = data->share;
|
return Curl_share_lock_share(data->share, data, type, accesstype);
|
||||||
|
}
|
||||||
|
|
||||||
|
CURLSHcode Curl_share_unlock_share(struct Curl_share *share,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
curl_lock_data type)
|
||||||
|
{
|
||||||
if(!share)
|
if(!share)
|
||||||
return CURLSHE_INVALID;
|
return CURLSHE_INVALID;
|
||||||
|
|
||||||
|
|
@ -403,6 +409,11 @@ CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type)
|
||||||
return CURLSHE_OK;
|
return CURLSHE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type)
|
||||||
|
{
|
||||||
|
return Curl_share_unlock_share(data->share, data, type);
|
||||||
|
}
|
||||||
|
|
||||||
CURLcode Curl_share_easy_unlink(struct Curl_easy *data)
|
CURLcode Curl_share_easy_unlink(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct Curl_share *share = data->share;
|
struct Curl_share *share = data->share;
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,14 @@ CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type,
|
||||||
curl_lock_access accesstype);
|
curl_lock_access accesstype);
|
||||||
CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type);
|
CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type);
|
||||||
|
|
||||||
|
CURLSHcode Curl_share_lock_share(struct Curl_share *share,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
curl_lock_data type,
|
||||||
|
curl_lock_access accesstype);
|
||||||
|
CURLSHcode Curl_share_unlock_share(struct Curl_share *share,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
curl_lock_data type);
|
||||||
|
|
||||||
/* convenience macro to check if this handle is using a shared SSL spool */
|
/* convenience macro to check if this handle is using a shared SSL spool */
|
||||||
#define CURL_SHARE_ssl_scache(data) \
|
#define CURL_SHARE_ssl_scache(data) \
|
||||||
((data)->share && \
|
((data)->share && \
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ static struct curl_trc_feat Curl_trc_feat_ids = {
|
||||||
|
|
||||||
static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen)
|
static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen)
|
||||||
{
|
{
|
||||||
curl_off_t cid = data->state.recent_conn_id;
|
curl_off_t cid = data->state.lastconnect_id;
|
||||||
if(data->id >= 0) {
|
if(data->id >= 0) {
|
||||||
if(cid >= 0)
|
if(cid >= 0)
|
||||||
return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid);
|
return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid);
|
||||||
|
|
|
||||||
11
lib/easy.c
11
lib/easy.c
|
|
@ -760,7 +760,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
|
||||||
if(data->conn) {
|
if(data->conn) {
|
||||||
struct connectdata *conn = data->conn;
|
struct connectdata *conn = data->conn;
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(data);
|
||||||
Curl_conn_terminate(data, conn, TRUE);
|
Curl_conn_close(data, conn, TRUE);
|
||||||
DEBUGASSERT(!data->conn);
|
DEBUGASSERT(!data->conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -996,7 +996,6 @@ CURL *curl_easy_duphandle(CURL *curl)
|
||||||
|
|
||||||
/* the connection pool is setup on demand */
|
/* the connection pool is setup on demand */
|
||||||
outcurl->state.lastconnect_id = -1;
|
outcurl->state.lastconnect_id = -1;
|
||||||
outcurl->state.recent_conn_id = -1;
|
|
||||||
outcurl->id = -1;
|
outcurl->id = -1;
|
||||||
outcurl->mid = UINT32_MAX;
|
outcurl->mid = UINT32_MAX;
|
||||||
outcurl->master_mid = UINT32_MAX;
|
outcurl->master_mid = UINT32_MAX;
|
||||||
|
|
@ -1130,7 +1129,7 @@ void curl_easy_reset(CURL *curl)
|
||||||
|
|
||||||
data->progress.hide = TRUE;
|
data->progress.hide = TRUE;
|
||||||
data->state.current_speed = -1; /* init to negative == impossible */
|
data->state.current_speed = -1; /* init to negative == impossible */
|
||||||
data->state.recent_conn_id = -1; /* clear remembered connection id */
|
data->state.lastconnect_id = -1; /* clear remembered connection id */
|
||||||
|
|
||||||
/* zero out authentication data: */
|
/* zero out authentication data: */
|
||||||
memset(&data->state.authhost, 0, sizeof(struct auth));
|
memset(&data->state.authhost, 0, sizeof(struct auth));
|
||||||
|
|
@ -1259,7 +1258,7 @@ CURLcode Curl_easy_recv(struct Curl_easy *data,
|
||||||
if(!data->conn)
|
if(!data->conn)
|
||||||
/* on first invoke, the transfer has been detached from the connection and
|
/* on first invoke, the transfer has been detached from the connection and
|
||||||
needs to be reattached */
|
needs to be reattached */
|
||||||
Curl_attach_connection(data, c);
|
Curl_attach_connection(data, c, TRUE);
|
||||||
|
|
||||||
*n = 0;
|
*n = 0;
|
||||||
return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n);
|
return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n);
|
||||||
|
|
@ -1290,7 +1289,7 @@ CURLcode Curl_connect_only_attach(struct Curl_easy *data)
|
||||||
if(!data->conn)
|
if(!data->conn)
|
||||||
/* on first invoke, the transfer has been detached from the connection and
|
/* on first invoke, the transfer has been detached from the connection and
|
||||||
needs to be reattached */
|
needs to be reattached */
|
||||||
Curl_attach_connection(data, c);
|
Curl_attach_connection(data, c, TRUE);
|
||||||
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
@ -1316,7 +1315,7 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
|
||||||
if(!data->conn)
|
if(!data->conn)
|
||||||
/* on first invoke, the transfer has been detached from the connection and
|
/* on first invoke, the transfer has been detached from the connection and
|
||||||
needs to be reattached */
|
needs to be reattached */
|
||||||
Curl_attach_connection(data, c);
|
Curl_attach_connection(data, c, TRUE);
|
||||||
|
|
||||||
sigpipe_ignore(data, &sigpipe_ctx);
|
sigpipe_ignore(data, &sigpipe_ctx);
|
||||||
result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, FALSE, n);
|
result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, FALSE, n);
|
||||||
|
|
|
||||||
|
|
@ -463,8 +463,7 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
|
||||||
*param_offt = data->id;
|
*param_offt = data->id;
|
||||||
break;
|
break;
|
||||||
case CURLINFO_CONN_ID:
|
case CURLINFO_CONN_ID:
|
||||||
*param_offt = data->conn ?
|
*param_offt = data->state.lastconnect_id;
|
||||||
data->conn->connection_id : data->state.recent_conn_id;
|
|
||||||
break;
|
break;
|
||||||
case CURLINFO_EARLYDATA_SENT_T:
|
case CURLINFO_EARLYDATA_SENT_T:
|
||||||
*param_offt = data->progress.earlydata_sent;
|
*param_offt = data->progress.earlydata_sent;
|
||||||
|
|
|
||||||
58
lib/multi.c
58
lib/multi.c
|
|
@ -285,7 +285,7 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size,
|
||||||
if(Curl_cshutdn_init(&multi->cshutdn, multi))
|
if(Curl_cshutdn_init(&multi->cshutdn, multi))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
Curl_cpool_init(&multi->cpool, multi->admin, NULL, chashsize);
|
Curl_cpool_init(&multi->cpool, NULL, chashsize);
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache))
|
if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache))
|
||||||
|
|
@ -334,7 +334,7 @@ error:
|
||||||
Curl_multi_ev_cleanup(multi);
|
Curl_multi_ev_cleanup(multi);
|
||||||
Curl_hash_destroy(&multi->proto_hash);
|
Curl_hash_destroy(&multi->proto_hash);
|
||||||
Curl_dnscache_destroy(&multi->dnscache);
|
Curl_dnscache_destroy(&multi->dnscache);
|
||||||
Curl_cpool_destroy(&multi->cpool);
|
Curl_cpool_destroy(&multi->cpool, multi->admin);
|
||||||
Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
|
Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
Curl_ssl_scache_destroy(multi->ssl_scache);
|
Curl_ssl_scache_destroy(multi->ssl_scache);
|
||||||
|
|
@ -654,20 +654,19 @@ static void multi_done_locked(struct connectdata *conn,
|
||||||
data->set.reuse_forbid, conn->bits.close, mdctx->premature,
|
data->set.reuse_forbid, conn->bits.close, mdctx->premature,
|
||||||
Curl_conn_is_multiplex(conn, FIRSTSOCKET));
|
Curl_conn_is_multiplex(conn, FIRSTSOCKET));
|
||||||
connclose(conn);
|
connclose(conn);
|
||||||
Curl_conn_terminate(data, conn, (bool)mdctx->premature);
|
Curl_conn_close(data, conn, (bool)mdctx->premature);
|
||||||
}
|
}
|
||||||
else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
|
else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
|
||||||
CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s was shutdown"
|
CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s was shutdown"
|
||||||
" by server, not reusing", conn->connection_id,
|
" by server, not reusing", conn->connection_id,
|
||||||
conn->destination);
|
conn->destination);
|
||||||
connclose(conn);
|
connclose(conn);
|
||||||
Curl_conn_terminate(data, conn, (bool)mdctx->premature);
|
Curl_conn_close(data, conn, (bool)mdctx->premature);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* the connection is no longer in use by any transfer */
|
/* the connection is no longer in use by any transfer */
|
||||||
if(Curl_cpool_conn_now_idle(data, conn)) {
|
if(Curl_cpool_conn_now_idle(data, conn)) {
|
||||||
/* connection kept in the cpool */
|
/* connection kept in the cpool */
|
||||||
data->state.lastconnect_id = conn->connection_id;
|
|
||||||
infof(data, "Connection #%" FMT_OFF_T " to host %s left intact",
|
infof(data, "Connection #%" FMT_OFF_T " to host %s left intact",
|
||||||
conn->connection_id, conn->destination);
|
conn->connection_id, conn->destination);
|
||||||
}
|
}
|
||||||
|
|
@ -845,7 +844,7 @@ CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi,
|
||||||
struct connectdata *conn;
|
struct connectdata *conn;
|
||||||
(void)Curl_getconnectinfo(data, &conn);
|
(void)Curl_getconnectinfo(data, &conn);
|
||||||
if(conn)
|
if(conn)
|
||||||
Curl_conn_terminate(data, conn, TRUE);
|
Curl_conn_close(data, conn, TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -943,17 +942,23 @@ void Curl_detach_connection(struct Curl_easy *data)
|
||||||
/*
|
/*
|
||||||
* Curl_attach_connection() attaches this transfer to this connection.
|
* Curl_attach_connection() attaches this transfer to this connection.
|
||||||
*
|
*
|
||||||
* This is the only function that should assign data->conn
|
* This is the only function that should assign data->conn.
|
||||||
|
* `matched == TRUE` means the transfer's properties match this
|
||||||
|
* connection and it is not a temporary attach for maintenance.
|
||||||
*/
|
*/
|
||||||
void Curl_attach_connection(struct Curl_easy *data,
|
void Curl_attach_connection(struct Curl_easy *data,
|
||||||
struct connectdata *conn)
|
struct connectdata *conn,
|
||||||
|
bool matched)
|
||||||
{
|
{
|
||||||
DEBUGASSERT(data);
|
DEBUGASSERT(data);
|
||||||
DEBUGASSERT(!data->conn);
|
DEBUGASSERT(!data->conn);
|
||||||
DEBUGASSERT(conn);
|
DEBUGASSERT(conn);
|
||||||
DEBUGASSERT(conn->attached_xfers < UINT32_MAX);
|
DEBUGASSERT(conn->attached_xfers < UINT32_MAX);
|
||||||
data->conn = conn;
|
data->conn = conn;
|
||||||
data->state.recent_conn_id = conn->connection_id;
|
if(matched)
|
||||||
|
data->state.lastconnect_id = conn->connection_id;
|
||||||
|
else
|
||||||
|
DEBUGASSERT(!data->mid); /* admin handle */
|
||||||
conn->attached_xfers++;
|
conn->attached_xfers++;
|
||||||
/* all attached transfers must be from the same multi */
|
/* all attached transfers must be from the same multi */
|
||||||
if(!conn->attached_multi)
|
if(!conn->attached_multi)
|
||||||
|
|
@ -1288,8 +1293,8 @@ CURLMcode curl_multi_fdset(CURLM *m,
|
||||||
} while(Curl_uint32_bset_next(&multi->process, mid, &mid));
|
} while(Curl_uint32_bset_next(&multi->process, mid, &mid));
|
||||||
}
|
}
|
||||||
|
|
||||||
Curl_cshutdn_setfds(&multi->cshutdn, multi->admin,
|
Curl_cshutdn_setfds(&multi->cshutdn, read_fd_set, write_fd_set,
|
||||||
read_fd_set, write_fd_set, &this_max_fd);
|
&this_max_fd);
|
||||||
|
|
||||||
*max_fd = this_max_fd;
|
*max_fd = this_max_fd;
|
||||||
Curl_pollset_cleanup(&ps);
|
Curl_pollset_cleanup(&ps);
|
||||||
|
|
@ -1337,7 +1342,7 @@ CURLMcode curl_multi_waitfds(CURLM *m,
|
||||||
} while(Curl_uint32_bset_next(&multi->process, mid, &mid));
|
} while(Curl_uint32_bset_next(&multi->process, mid, &mid));
|
||||||
}
|
}
|
||||||
|
|
||||||
need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds);
|
need += Curl_cshutdn_add_waitfds(&multi->cshutdn, &cwfds);
|
||||||
|
|
||||||
if(need != cwfds.n && ufds)
|
if(need != cwfds.n && ufds)
|
||||||
mresult = CURLM_OUT_OF_MEMORY;
|
mresult = CURLM_OUT_OF_MEMORY;
|
||||||
|
|
@ -1575,7 +1580,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
|
||||||
} while(Curl_uint32_bset_next(&multi->process, mid, &mid));
|
} while(Curl_uint32_bset_next(&multi->process, mid, &mid));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Curl_cshutdn_add_pollfds(&multi->cshutdn, multi->admin, &cpfds)) {
|
if(Curl_cshutdn_add_pollfds(&multi->cshutdn, &cpfds)) {
|
||||||
mresult = CURLM_OUT_OF_MEMORY;
|
mresult = CURLM_OUT_OF_MEMORY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -1774,7 +1779,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
|
||||||
|
|
||||||
/* take this handle to the perform state right away */
|
/* take this handle to the perform state right away */
|
||||||
multistate(data, MSTATE_PERFORMING);
|
multistate(data, MSTATE_PERFORMING);
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(data, conn, TRUE);
|
||||||
CURL_REQ_SET_RECV(data);
|
CURL_REQ_SET_RECV(data);
|
||||||
}
|
}
|
||||||
return mresult;
|
return mresult;
|
||||||
|
|
@ -2391,7 +2396,7 @@ static CURLcode is_finished(struct Curl_multi *multi,
|
||||||
We do not have to do this in every case block above where a
|
We do not have to do this in every case block above where a
|
||||||
failure is detected */
|
failure is detected */
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(data);
|
||||||
Curl_conn_terminate(data, conn, dead_connection);
|
Curl_conn_close(data, conn, dead_connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(data->mstate == MSTATE_CONNECT) {
|
else if(data->mstate == MSTATE_CONNECT) {
|
||||||
|
|
@ -2711,7 +2716,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||||
struct Curl_easy *data,
|
struct Curl_easy *data,
|
||||||
struct Curl_sigpipe_ctx *sigpipe_ctx)
|
struct Curl_sigpipe_ctx *sigpipe_ctx)
|
||||||
{
|
{
|
||||||
CURLMcode mresult;
|
CURLMcode mresult = CURLM_OK;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
if(multi->dead) {
|
if(multi->dead) {
|
||||||
|
|
@ -2738,8 +2743,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||||
#ifdef USE_RESOLV_THREADED
|
#ifdef USE_RESOLV_THREADED
|
||||||
Curl_async_thrdd_multi_process(multi);
|
Curl_async_thrdd_multi_process(multi);
|
||||||
#endif
|
#endif
|
||||||
Curl_cshutdn_perform(&multi->cshutdn, multi->admin, sigpipe_ctx);
|
Curl_cshutdn_perform(&multi->cshutdn, sigpipe_ctx);
|
||||||
return CURLM_OK;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
sigpipe_apply(data, sigpipe_ctx);
|
sigpipe_apply(data, sigpipe_ctx);
|
||||||
|
|
@ -2758,8 +2763,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||||
data->mstate < MSTATE_COMPLETED) {
|
data->mstate < MSTATE_COMPLETED) {
|
||||||
/* Make sure we set the connection's current owner */
|
/* Make sure we set the connection's current owner */
|
||||||
DEBUGASSERT(data->conn);
|
DEBUGASSERT(data->conn);
|
||||||
if(!data->conn)
|
if(!data->conn) {
|
||||||
return CURLM_INTERNAL_ERROR;
|
mresult = CURLM_INTERNAL_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for the connect state as only then is the start time stored, but
|
/* Wait for the connect state as only then is the start time stored, but
|
||||||
|
|
@ -2841,7 +2848,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return CURLM_INTERNAL_ERROR;
|
mresult = CURLM_INTERNAL_ERROR;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data->mstate >= MSTATE_CONNECT &&
|
if(data->mstate >= MSTATE_CONNECT &&
|
||||||
|
|
@ -2865,11 +2873,13 @@ statemachine_end:
|
||||||
|
|
||||||
if(MSTATE_COMPLETED == data->mstate) {
|
if(MSTATE_COMPLETED == data->mstate) {
|
||||||
handle_completed(multi, data, result);
|
handle_completed(multi, data, result);
|
||||||
return CURLM_OK;
|
mresult = CURLM_OK;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
} while((mresult == CURLM_CALL_MULTI_PERFORM) ||
|
} while((mresult == CURLM_CALL_MULTI_PERFORM) ||
|
||||||
multi_ischanged(multi, FALSE));
|
multi_ischanged(multi, FALSE));
|
||||||
|
|
||||||
|
out:
|
||||||
data->result = result;
|
data->result = result;
|
||||||
return mresult;
|
return mresult;
|
||||||
}
|
}
|
||||||
|
|
@ -3014,7 +3024,7 @@ CURLMcode curl_multi_cleanup(CURLM *m)
|
||||||
#ifdef USE_RESOLV_THREADED
|
#ifdef USE_RESOLV_THREADED
|
||||||
Curl_async_thrdd_multi_destroy(multi, !multi->quick_exit);
|
Curl_async_thrdd_multi_destroy(multi, !multi->quick_exit);
|
||||||
#endif
|
#endif
|
||||||
Curl_cpool_destroy(&multi->cpool);
|
Curl_cpool_destroy(&multi->cpool, multi->admin);
|
||||||
Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
|
Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
|
||||||
if(multi->admin) {
|
if(multi->admin) {
|
||||||
CURL_TRC_M(multi->admin, "multi_cleanup, closing admin handle, done");
|
CURL_TRC_M(multi->admin, "multi_cleanup, closing admin handle, done");
|
||||||
|
|
@ -3393,7 +3403,7 @@ CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...)
|
||||||
Curl_dnscache_clear(multi->admin);
|
Curl_dnscache_clear(multi->admin);
|
||||||
}
|
}
|
||||||
if(val & CURLMNWC_CLEAR_CONNS) {
|
if(val & CURLMNWC_CLEAR_CONNS) {
|
||||||
Curl_cpool_nw_changed(multi->admin);
|
Curl_cpool_nw_changed(&multi->cpool, multi->admin);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ void Curl_expire_clear(struct Curl_easy *data);
|
||||||
void Curl_expire_done(struct Curl_easy *data, expire_id id);
|
void Curl_expire_done(struct Curl_easy *data, expire_id id);
|
||||||
CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT;
|
CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT;
|
||||||
void Curl_attach_connection(struct Curl_easy *data,
|
void Curl_attach_connection(struct Curl_easy *data,
|
||||||
struct connectdata *conn);
|
struct connectdata *conn,
|
||||||
|
bool matched);
|
||||||
void Curl_detach_connection(struct Curl_easy *data);
|
void Curl_detach_connection(struct Curl_easy *data);
|
||||||
bool Curl_multiplex_wanted(const struct Curl_multi *multi);
|
bool Curl_multiplex_wanted(const struct Curl_multi *multi);
|
||||||
CURLcode Curl_preconnect(struct Curl_easy *data);
|
CURLcode Curl_preconnect(struct Curl_easy *data);
|
||||||
|
|
|
||||||
44
lib/url.c
44
lib/url.c
|
|
@ -471,7 +471,6 @@ CURLcode Curl_open(struct Curl_easy **curl)
|
||||||
data->magic = CURLEASY_MAGIC_NUMBER;
|
data->magic = CURLEASY_MAGIC_NUMBER;
|
||||||
/* most recent connection is not yet defined */
|
/* most recent connection is not yet defined */
|
||||||
data->state.lastconnect_id = -1;
|
data->state.lastconnect_id = -1;
|
||||||
data->state.recent_conn_id = -1;
|
|
||||||
/* and not assigned an id yet */
|
/* and not assigned an id yet */
|
||||||
data->id = -1;
|
data->id = -1;
|
||||||
data->mid = UINT32_MAX;
|
data->mid = UINT32_MAX;
|
||||||
|
|
@ -914,7 +913,7 @@ static bool url_allow_sspi_empty_creds(struct Curl_creds *conn_creds,
|
||||||
* To avoid TOCTOU attacks, do not reuse on empty credentials
|
* To avoid TOCTOU attacks, do not reuse on empty credentials
|
||||||
* UNLESS this connection is the one used by this transfer before. */
|
* UNLESS this connection is the one used by this transfer before. */
|
||||||
if(!Curl_creds_has_user(conn_creds) &&
|
if(!Curl_creds_has_user(conn_creds) &&
|
||||||
(data->state.recent_conn_id != conn->connection_id))
|
(data->state.lastconnect_id != conn->connection_id))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
#else
|
#else
|
||||||
(void)conn_creds;
|
(void)conn_creds;
|
||||||
|
|
@ -1113,7 +1112,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
|
||||||
!Curl_cpool_conn_seems_healthy(conn, m->data, &m->now)) {
|
!Curl_cpool_conn_seems_healthy(conn, m->data, &m->now)) {
|
||||||
infof(m->data, "Connection %" FMT_OFF_T " seems to be dead, terminating",
|
infof(m->data, "Connection %" FMT_OFF_T " seems to be dead, terminating",
|
||||||
conn->connection_id);
|
conn->connection_id);
|
||||||
Curl_conn_terminate(m->data, conn, FALSE);
|
Curl_conn_close(m->data, conn, FALSE);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1128,7 +1127,7 @@ static bool url_match_result(void *userdata)
|
||||||
if(match->found) {
|
if(match->found) {
|
||||||
/* Attach it now while still under lock, so the connection does
|
/* Attach it now while still under lock, so the connection does
|
||||||
* no longer appear idle and can be reaped. */
|
* no longer appear idle and can be reaped. */
|
||||||
Curl_attach_connection(match->data, match->found);
|
Curl_attach_connection(match->data, match->found, TRUE);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
else if(match->seen_single_use_conn && !match->seen_multiplex_conn) {
|
else if(match->seen_single_use_conn && !match->seen_multiplex_conn) {
|
||||||
|
|
@ -1159,11 +1158,11 @@ static bool url_attach_existing(struct Curl_easy *data,
|
||||||
struct connectdata *needle,
|
struct connectdata *needle,
|
||||||
bool *waitpipe)
|
bool *waitpipe)
|
||||||
{
|
{
|
||||||
|
struct cpool *cpool = Curl_cpool_get_instance(data);
|
||||||
struct url_conn_match match;
|
struct url_conn_match match;
|
||||||
bool success;
|
bool success;
|
||||||
|
|
||||||
DEBUGASSERT(!data->conn);
|
DEBUGASSERT(!data->conn);
|
||||||
Curl_cpool_prune_dead(data);
|
|
||||||
|
|
||||||
memset(&match, 0, sizeof(match));
|
memset(&match, 0, sizeof(match));
|
||||||
match.data = data;
|
match.data = data;
|
||||||
|
|
@ -1171,6 +1170,8 @@ static bool url_attach_existing(struct Curl_easy *data,
|
||||||
match.now = *Curl_pgrs_now(data);
|
match.now = *Curl_pgrs_now(data);
|
||||||
match.may_multiplex = xfer_may_multiplex(data, needle);
|
match.may_multiplex = xfer_may_multiplex(data, needle);
|
||||||
|
|
||||||
|
Curl_cpool_prune_dead(cpool, data);
|
||||||
|
|
||||||
#ifdef USE_NTLM
|
#ifdef USE_NTLM
|
||||||
match.want_ntlm_http =
|
match.want_ntlm_http =
|
||||||
(data->state.authhost.want & CURLAUTH_NTLM) &&
|
(data->state.authhost.want & CURLAUTH_NTLM) &&
|
||||||
|
|
@ -2303,9 +2304,9 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Setup a "faked" transfer that will do nothing */
|
/* Setup a "faked" transfer that will do nothing */
|
||||||
Curl_attach_connection(data, needle);
|
result = Curl_cpool_add(data, needle);
|
||||||
|
Curl_attach_connection(data, needle, TRUE);
|
||||||
needle = NULL;
|
needle = NULL;
|
||||||
result = Curl_cpool_add(data, data->conn);
|
|
||||||
if(!result) {
|
if(!result) {
|
||||||
/* Setup whatever necessary for a resumed transfer */
|
/* Setup whatever necessary for a resumed transfer */
|
||||||
result = setup_range(data);
|
result = setup_range(data);
|
||||||
|
|
@ -2411,7 +2412,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data)
|
||||||
/* Add needle to conn pool, which assigns the connection id.
|
/* Add needle to conn pool, which assigns the connection id.
|
||||||
* Attach regardless of result, for correct handling. */
|
* Attach regardless of result, for correct handling. */
|
||||||
result = Curl_cpool_add(data, needle);
|
result = Curl_cpool_add(data, needle);
|
||||||
Curl_attach_connection(data, needle);
|
Curl_attach_connection(data, needle, TRUE);
|
||||||
needle = NULL;
|
needle = NULL;
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -2523,7 +2524,7 @@ out:
|
||||||
/* We are not allowed to return failure with memory left allocated in the
|
/* We are not allowed to return failure with memory left allocated in the
|
||||||
connectdata struct, free those here */
|
connectdata struct, free those here */
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(data);
|
||||||
Curl_conn_terminate(data, conn, TRUE);
|
Curl_conn_close(data, conn, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -2595,6 +2596,31 @@ void *Curl_conn_meta_get(struct connectdata *conn, const char *key)
|
||||||
return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
|
return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Curl_easy *Curl_get_admin(struct Curl_easy *data)
|
||||||
|
{
|
||||||
|
struct Curl_easy *admin;
|
||||||
|
|
||||||
|
if(!data->mid) /* already an admin handle */
|
||||||
|
admin = data;
|
||||||
|
else if(data->multi)
|
||||||
|
admin = data->multi->admin;
|
||||||
|
else if(data->multi_easy)
|
||||||
|
admin = data->multi_easy->admin;
|
||||||
|
else {
|
||||||
|
DEBUGASSERT(0); /* we do not want this. does it happen? */
|
||||||
|
admin = data;
|
||||||
|
}
|
||||||
|
if(admin != data) {
|
||||||
|
admin->set.conn_max_idle_ms = data->set.conn_max_idle_ms;
|
||||||
|
admin->set.conn_max_age_ms = data->set.conn_max_age_ms;
|
||||||
|
admin->set.upkeep_interval_ms = data->set.upkeep_interval_ms;
|
||||||
|
admin->set.timeout = data->set.timeout;
|
||||||
|
admin->set.server_response_timeout = data->set.server_response_timeout;
|
||||||
|
admin->set.no_signal = data->set.no_signal;
|
||||||
|
}
|
||||||
|
return admin;
|
||||||
|
}
|
||||||
|
|
||||||
CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2)
|
CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2)
|
||||||
{
|
{
|
||||||
if(r1 && (r1 != CURLE_AGAIN))
|
if(r1 && (r1 != CURLE_AGAIN))
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,12 @@ CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key,
|
||||||
void Curl_conn_meta_remove(struct connectdata *conn, const char *key);
|
void Curl_conn_meta_remove(struct connectdata *conn, const char *key);
|
||||||
void *Curl_conn_meta_get(struct connectdata *conn, const char *key);
|
void *Curl_conn_meta_get(struct connectdata *conn, const char *key);
|
||||||
|
|
||||||
|
/* Get an admin handle for internal operations from the given
|
||||||
|
* easy handle, if possible. The admin handle inherits certain
|
||||||
|
* properties from `data`. If no admin handle is available (not multi
|
||||||
|
* or share attached), the easy handle itself is returned. */
|
||||||
|
struct Curl_easy *Curl_get_admin(struct Curl_easy *data);
|
||||||
|
|
||||||
#define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */
|
#define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */
|
||||||
#define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless
|
#define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless
|
||||||
specified */
|
specified */
|
||||||
|
|
|
||||||
|
|
@ -540,9 +540,7 @@ struct UrlState {
|
||||||
/* buffers to store authentication data in, as parsed from input options */
|
/* buffers to store authentication data in, as parsed from input options */
|
||||||
struct curltime keeps_speed; /* for the progress meter really */
|
struct curltime keeps_speed; /* for the progress meter really */
|
||||||
|
|
||||||
curl_off_t lastconnect_id; /* The last connection, -1 if undefined */
|
curl_off_t lastconnect_id; /* The last assigned connection or -1 */
|
||||||
curl_off_t recent_conn_id; /* The most recent connection used, might no
|
|
||||||
* longer exist */
|
|
||||||
struct dynbuf headerb; /* buffer to store headers in */
|
struct dynbuf headerb; /* buffer to store headers in */
|
||||||
#ifndef CURL_DISABLE_HSTS
|
#ifndef CURL_DISABLE_HSTS
|
||||||
struct curl_slist *hstslist; /* list of HSTS files set by
|
struct curl_slist *hstslist; /* list of HSTS files set by
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,8 @@ run 1: foobar and so on fun!
|
||||||
[- Mutex unlock SHARE
|
[- Mutex unlock SHARE
|
||||||
-] Mutex lock SHARE
|
-] Mutex lock SHARE
|
||||||
[- Mutex unlock SHARE
|
[- Mutex unlock SHARE
|
||||||
|
-] Mutex lock CONNECT
|
||||||
|
[- Mutex unlock CONNECT
|
||||||
</datacheck>
|
</datacheck>
|
||||||
</reply>
|
</reply>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue