connectionpool: some tweaks
No binary (build failed)
—
(log yok)
| @@ -276,7 +276,7 @@ static bool cf_hc_may_h3(struct Curl_cfilter *cf, | |
| struct cf_hc_ctx *ctx = cf->ctx; | |
| if(!ctx->checked_h3) { | |
| ctx->check_h3_result = | |
| - | Curl_conn_may_http3(data, cf->conn, ctx->def_transport); |
| + | Curl_conn_may_http3(data, cf->conn); |
| ctx->checked_h3 = TRUE; | |
| } | |
| return !ctx->check_h3_result; | |
| @@ -449,111 +449,109 @@ static void cpool_evict_conn(struct cpool *cpool, | |
| cpool_conn_close(cpool, admin, conn, FALSE); | |
| } | |
| - | int Curl_cpool_check_limits(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | const struct curltime *pnow) |
| + | #define CPOOL_LIMIT_OK 0 |
| + | #define CPOOL_LIMIT_DEST 1 |
| + | #define CPOOL_LIMIT_TOTAL 2 |
| + | |
| + | static int cpool_check_limits(struct Curl_easy *data, |
| + | uint32_t max_total, |
| + | struct connectdata *to_add, |
| + | uint32_t max_host, |
| + | struct cpool_bundle **pbundle, |
| + | const struct curltime *pnow) |
| { | |
| struct cpool *cpool = cpool_get_instance(data); | |
| - | struct Curl_multi *multi = data->multi; |
| - | struct Curl_easy *admin; |
| - | struct cpool_bundle *bundle; |
| - | size_t dest_limit = 0; |
| - | size_t total_limit = 0; |
| - | size_t shutdowns; |
| - | int res = CPOOL_LIMIT_OK; |
| + | struct cshutdn *cshutdn = data->multi ? &data->multi->cshutdn : NULL; |
| + | struct Curl_easy *admin = NULL; |
| if(!cpool) | |
| return CPOOL_LIMIT_OK; | |
| - | /* multi determines the limits, no matter who owns the pool */ |
| - | if(multi) { |
| - | dest_limit = multi->max_host_connections; |
| - | total_limit = multi->max_total_connections; |
| - | } |
| - | |
| - | if(!dest_limit && !total_limit) |
| + | if(!max_total && !max_host) |
| return CPOOL_LIMIT_OK; | |
| - | admin = Curl_get_admin(data); |
| - | CPOOL_LOCK(cpool, admin); |
| - | if(dest_limit) { |
| - | size_t live; |
| - | |
| - | bundle = cpool_find_bundle(cpool, conn->destination); |
| - | live = bundle ? Curl_llist_count(&bundle->conns) : 0; |
| - | shutdowns = |
| - | multi ? Curl_cshutdn_dest_count(&multi->cshutdn, conn->destination) : 0; |
| - | while((live + shutdowns) >= dest_limit) { |
| - | if(shutdowns) { |
| - | /* close one connection in shutdown right away, if we can */ |
| - | if(!Curl_cshutdn_close_oldest(&multi->cshutdn, multi->admin, |
| - | conn->destination)) |
| - | break; |
| - | } |
| - | else if(!bundle) |
| - | break; |
| - | else { |
| - | struct connectdata *oldest_idle = NULL; |
| - | /* The bundle is full. Extract the oldest connection that may |
| - | * be removed now, if there is one. */ |
| - | oldest_idle = cpool_bundle_get_oldest_idle(bundle, pnow); |
| - | if(!oldest_idle) |
| - | break; |
| - | /* disconnect the old conn and continue */ |
| - | CURL_TRC_M(admin, "Discarding connection #%" FMT_OFF_T |
| - | " from %zu to reach destination limit of %zu", |
| - | oldest_idle->connection_id, |
| - | Curl_llist_count(&bundle->conns), dest_limit); |
| - | cpool_evict_conn(cpool, admin, oldest_idle); |
| - | |
| - | /* in case the bundle was destroyed in disconnect, look it up again */ |
| - | bundle = cpool_find_bundle(cpool, conn->destination); |
| - | live = bundle ? Curl_llist_count(&bundle->conns) : 0; |
| + | if(max_host) { |
| + | size_t live = 0, shutdowns = 0; |
| + | /* if we are at or above `dest_limit`, try to get rid of connections |
| + | * in shutdown and, if that does not lower it, evict idle connections |
| + | * from the pool. */ |
| + | admin = Curl_get_admin(data); |
| + | if(*pbundle) { |
| + | live = Curl_llist_count(&(*pbundle)->conns); |
| + | if(live >= max_host) { |
| + | size_t over = live - max_host + 1; |
| + | for(; over && *pbundle; --over) { |
| + | struct connectdata *oldest_idle = |
| + | cpool_bundle_get_oldest_idle(*pbundle, pnow); |
| + | if(!oldest_idle) |
| + | break; |
| + | /* disconnect the old conn and continue */ |
| + | CURL_TRC_M(admin, "Shutting down connection #%" FMT_OFF_T |
| + | " to '%s' due to destination limit of %u", |
| + | oldest_idle->connection_id, oldest_idle->destination, |
| + | max_host); |
| + | cpool_evict_conn(cpool, admin, oldest_idle); |
| + | /* bundle may get destroyed in disconnect, look it up again */ |
| + | *pbundle = cpool_find_bundle(cpool, to_add->destination); |
| + | } |
| + | live = *pbundle ? Curl_llist_count(&(*pbundle)->conns) : 0; |
| } | |
| - | shutdowns = multi ? |
| - | Curl_cshutdn_dest_count(&multi->cshutdn, conn->destination) : 0; |
| } | |
| - | if((live + shutdowns) >= dest_limit) { |
| - | res = CPOOL_LIMIT_DEST; |
| - | goto out; |
| + | if(cshutdn) { |
| + | shutdowns = Curl_cshutdn_dest_count(cshutdn, to_add->destination); |
| + | if(shutdowns && ((live + shutdowns) >= max_host)) { |
| + | size_t over = live + shutdowns - max_host + 1; |
| + | shutdowns -= Curl_cshutdn_close_oldest(cshutdn, admin, |
| + | to_add->destination, over); |
| + | } |
| } | |
| + | /* We may not have gotten rid of as many connections as we wanted to */ |
| + | if((live + shutdowns) >= max_host) |
| + | return CPOOL_LIMIT_DEST; |
| } | |
| - | if(total_limit) { |
| - | shutdowns = multi ? Curl_cshutdn_count(&multi->cshutdn) : 0; |
| - | while((cpool->num_conn + shutdowns) >= total_limit) { |
| - | if(shutdowns) { |
| - | /* close one connection in shutdown right away, if we can */ |
| - | if(!Curl_cshutdn_close_oldest(&multi->cshutdn, multi->admin, NULL)) |
| - | break; |
| - | } |
| - | else { |
| + | /* Internal transfers are outside the total limit */ |
| + | if(max_total && !data->state.internal) { |
| + | size_t shutdowns = 0; |
| + | |
| + | if(!admin) |
| + | admin = Curl_get_admin(data); |
| + | if(cpool->num_conn >= max_total) { |
| + | size_t over = cpool->num_conn - max_total + 1; |
| + | for(; over; --over) { |
| struct connectdata *oldest_idle = | |
| cpool_get_oldest_idle(cpool, pnow, 0); | |
| if(!oldest_idle) | |
| break; | |
| /* disconnect the old conn and continue */ | |
| - | CURL_TRC_M(admin, "Discarding connection #%" |
| - | FMT_OFF_T " from %zu to reach total " |
| - | "limit of %zu", |
| - | oldest_idle->connection_id, cpool->num_conn, total_limit); |
| + | CURL_TRC_M(admin, "Shutting down idle connection #%" |
| + | FMT_OFF_T " to '%s' due to total limit of %u", |
| + | oldest_idle->connection_id, oldest_idle->destination, |
| + | max_total); |
| cpool_evict_conn(cpool, admin, oldest_idle); | |
| } | |
| - | shutdowns = multi ? Curl_cshutdn_count(&multi->cshutdn) : 0; |
| + | /* bundle might be gone due to evictions */ |
| + | if(*pbundle) |
| + | *pbundle = cpool_find_bundle(cpool, to_add->destination); |
| } | |
| - | if((cpool->num_conn + shutdowns) >= total_limit) { |
| - | res = CPOOL_LIMIT_TOTAL; |
| - | goto out; |
| + | if(cshutdn) { |
| + | shutdowns = Curl_cshutdn_count(cshutdn); |
| + | if(shutdowns && (cpool->num_conn + shutdowns) >= max_total) { |
| + | size_t over = cpool->num_conn + shutdowns - max_total + 1; |
| + | shutdowns -= Curl_cshutdn_close_oldest(cshutdn, admin, NULL, over); |
| + | } |
| } | |
| + | if((cpool->num_conn + shutdowns) >= max_total) |
| + | return CPOOL_LIMIT_TOTAL; |
| } | |
| - | out: |
| - | CPOOL_UNLOCK(cpool, admin); |
| - | return res; |
| + | return CPOOL_LIMIT_OK; |
| } | |
| CURLcode Curl_cpool_add(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| + | uint32_t max_total, |
| + | uint32_t max_host, |
| const struct curltime *pnow) | |
| { | |
| CURLcode result = CURLE_OK; | |
| @@ -570,7 +568,24 @@ CURLcode Curl_cpool_add(struct Curl_easy *data, | |
| conn->shutdown.start_ms[SECONDARYSOCKET] = -1; | |
| CPOOL_LOCK(cpool, data); | |
| + | |
| + | /* Find the bundle, should it exist, and check the limits */ |
| bundle = cpool_find_bundle(cpool, conn->destination); | |
| + | |
| + | switch(cpool_check_limits(data, max_total, conn, max_host, &bundle, pnow)) { |
| + | case CPOOL_LIMIT_DEST: |
| + | infof(data, "No more connections allowed to host"); |
| + | result = CURLE_NO_CONNECTION_AVAILABLE; |
| + | goto out; |
| + | case CPOOL_LIMIT_TOTAL: |
| + | infof(data, "No connections available, total of %u reached.", |
| + | data->multi->max_total_connections); |
| + | result = CURLE_NO_CONNECTION_AVAILABLE; |
| + | goto out; |
| + | default: |
| + | break; |
| + | } |
| + | |
| if(!bundle) { | |
| bundle = cpool_add_bundle(cpool, conn->destination); | |
| if(!bundle) { | |
| @@ -645,11 +660,11 @@ static bool cpool_foreach(struct Curl_easy *data, | |
| * | |
| * Return TRUE if idle connection kept in pool, FALSE if closed. | |
| */ | |
| - | bool Curl_cpool_conn_now_idle(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | const struct curltime *pnow) |
| + | static bool cpool_conn_now_idle(struct cpool *cpool, |
| + | struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | const struct curltime *pnow) |
| { | |
| - | struct cpool *cpool = cpool_get_instance(data); |
| struct connectdata *oldest_idle = NULL; | |
| struct Curl_easy *admin; | |
| unsigned int maxconnects; | |
| @@ -671,16 +686,8 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, | |
| maxconnects = data->multi->maxconnects; | |
| } | |
| - | /* remember times, connection had been used just before */ |
| - | conn->lastchecked_ms = conn->lastupkeep_ms = conn->lastused_ms = |
| - | curlx_ptimediff_ms(pnow, &conn->created); |
| if(cpool && maxconnects) { | |
| - | /* may be called form a callback already under lock */ |
| - | bool do_lock = !CPOOL_IS_LOCKED(cpool); |
| - | |
| admin = Curl_get_admin(data); | |
| - | if(do_lock) |
| - | CPOOL_LOCK(cpool, admin); |
| if(cpool->num_conn > maxconnects) { | |
| infof(data, "Connection pool is full, closing the oldest of %zu/%u", | |
| cpool->num_conn, maxconnects); | |
| @@ -691,15 +698,47 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, | |
| cpool_evict_conn(cpool, admin, oldest_idle); | |
| } | |
| } | |
| - | if(do_lock) |
| - | CPOOL_UNLOCK(cpool, admin); |
| } | |
| return kept; | |
| } | |
| + | static void cpool_prune_dead(struct cpool *cpool, |
| + | struct Curl_easy *data, |
| + | const struct curltime *pnow); |
| + | |
| + | static bool cpool_find_act(struct cpool *cpool, |
| + | struct Curl_easy *data, |
| + | struct cpool_bundle *bundle, |
| + | struct connectdata *conn, |
| + | cpool_match_result match) |
| + | { |
| + | switch(match) { |
| + | case CPOOL_MATCH_FOUND: |
| + | return TRUE; |
| + | case CPOOL_MATCH_TOO_OLD: |
| + | if(CONN_INUSE(conn)) |
| + | return FALSE; |
| + | FALLTHROUGH(); |
| + | case CPOOL_MATCH_CLOSE: |
| + | DEBUGASSERT(conn->bits.in_cpool); |
| + | if(conn->bits.in_cpool) { |
| + | cpool_bundle_remove(bundle, conn); |
| + | conn->bits.in_cpool = FALSE; |
| + | cpool->num_conn--; |
| + | } |
| + | cpool_conn_close(cpool, data, conn, FALSE); |
| + | break; |
| + | default: |
| + | break; |
| + | } |
| + | return FALSE; |
| + | } |
| + | |
| bool Curl_cpool_find(struct Curl_easy *data, | |
| const char *destination, | |
| + | bool prune_dead, |
| + | const struct curltime *pnow, |
| Curl_cpool_conn_match_cb *conn_cb, | |
| Curl_cpool_done_match_cb *done_cb, | |
| void *userdata) | |
| @@ -716,6 +755,10 @@ bool Curl_cpool_find(struct Curl_easy *data, | |
| return FALSE; | |
| CPOOL_LOCK(cpool, data); | |
| + | |
| + | if(prune_dead) |
| + | cpool_prune_dead(cpool, data, pnow); |
| + | |
| bundle = Curl_hash_pick(&cpool->dest2bundle, | |
| destination, strlen(destination) + 1); | |
| if(bundle) { | |
| @@ -725,31 +768,32 @@ bool Curl_cpool_find(struct Curl_easy *data, | |
| * an authentication involving several requests is using | |
| * the same connection again. */ | |
| curr = Curl_llist_head(&bundle->conns); | |
| - | while(curr) { |
| + | while(!found && curr) { |
| conn = Curl_node_elem(curr); | |
| curr = Curl_node_next(curr); | |
| - | if(data->state.lastconnect_id == conn->connection_id) { |
| - | if(conn_cb(conn, userdata)) |
| - | found = TRUE; |
| - | break; |
| - | } |
| + | if(data->state.lastconnect_id != conn->connection_id) |
| + | continue; |
| + | found = cpool_find_act(cpool, data, bundle, conn, |
| + | conn_cb(conn, userdata)); |
| + | break; |
| } | |
| } | |
| if(!found) { | |
| curr = Curl_llist_head(&bundle->conns); | |
| - | while(curr) { |
| + | while(!found && curr) { |
| conn = Curl_node_elem(curr); | |
| curr = Curl_node_next(curr); | |
| - | /* Already tried a matching connection above. No need to |
| - | * invoke callback again for this one. */ |
| - | if((data->state.lastconnect_id != conn->connection_id) && |
| - | conn_cb(conn, userdata)) { |
| - | found = TRUE; |
| - | break; |
| - | } |
| + | /* Already tried a matching connection above. */ |
| + | if(data->state.lastconnect_id == conn->connection_id) |
| + | continue; |
| + | found = cpool_find_act(cpool, data, bundle, conn, |
| + | conn_cb(conn, userdata)); |
| } | |
| } | |
| + | |
| + | if(!Curl_llist_count(&bundle->conns)) |
| + | cpool_remove_bundle(cpool, destination); |
| } | |
| if(done_cb) { | |
| @@ -761,7 +805,7 @@ bool Curl_cpool_find(struct Curl_easy *data, | |
| struct cpool_reaper_ctx { | |
| size_t reaped; | |
| - | struct curltime now; |
| + | const struct curltime *pnow; |
| }; | |
| static int cpool_reap_dead_cb(struct cpool *cpool, | |
| @@ -772,7 +816,7 @@ static int cpool_reap_dead_cb(struct cpool *cpool, | |
| if(!CONN_INUSE(conn)) { | |
| if(conn->bits.no_reuse || conn->bits.close || | |
| - | !Curl_cpool_conn_seems_healthy(conn, admin, &reaper->now)) { |
| + | !Curl_cpool_conn_seems_healthy(conn, admin, reaper->pnow)) { |
| /* terminate conn and stop the iteration */ | |
| reaper->reaped++; | |
| cpool_conn_close(cpool, admin, conn, FALSE); | |
| @@ -786,32 +830,23 @@ static int cpool_reap_dead_cb(struct cpool *cpool, | |
| * This function scans the data's connection pool for half-open/dead | |
| * connections, closes and removes them. | |
| * The cleanup is done at most once per second. | |
| - | * |
| - | * When called, this transfer has no connection attached. |
| */ | |
| - | void Curl_cpool_prune_dead(struct cpool *cpool, |
| - | struct Curl_easy *data) |
| + | static void cpool_prune_dead(struct cpool *cpool, |
| + | struct Curl_easy *data, |
| + | const struct curltime *pnow) |
| { | |
| - | struct Curl_easy *admin; |
| - | timediff_t elapsed; |
| + | timediff_t elapsed_ms = curlx_ptimediff_ms(pnow, &cpool->last_cleanup); |
| - | if(!cpool) |
| - | return; |
| - | |
| - | admin = Curl_get_admin(data); |
| - | CPOOL_LOCK(cpool, admin); |
| - | elapsed = curlx_ptimediff_ms(Curl_pgrs_now(admin), &cpool->last_cleanup); |
| - | |
| - | if(elapsed >= 1000L) { |
| + | if(elapsed_ms >= 1000L) { |
| + | struct Curl_easy *admin = Curl_get_admin(data); |
| struct cpool_reaper_ctx reaper; | |
| memset(&reaper, 0, sizeof(reaper)); | |
| - | reaper.now = *Curl_pgrs_now(admin); |
| + | reaper.pnow = pnow; |
| while(cpool_foreach(admin, cpool, &reaper, cpool_reap_dead_cb)) | |
| ; | |
| - | cpool->last_cleanup = *Curl_pgrs_now(admin); |
| + | cpool->last_cleanup = *pnow; |
| } | |
| - | CPOOL_UNLOCK(cpool, admin); |
| } | |
| static int conn_upkeep(struct cpool *cpool, | |
| @@ -890,18 +925,42 @@ struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data, | |
| return fctx.conn; | |
| } | |
| - | void Curl_cpool_do_locked(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | Curl_cpool_conn_do_cb *cb, void *cbdata) |
| + | void Curl_cpool_return(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | Curl_cpool_return_cb *cb, void *cbdata, |
| + | const struct curltime *pnow) |
| { | |
| struct cpool *cpool = cpool_get_instance(data); | |
| - | if(cpool) { |
| - | CPOOL_LOCK(cpool, data); |
| - | cb(conn, data, cbdata); |
| - | CPOOL_UNLOCK(cpool, data); |
| + | |
| + | CPOOL_LOCK(cpool, data); |
| + | |
| + | /* remember times, connection had been used just before */ |
| + | conn->lastchecked_ms = conn->lastupkeep_ms = conn->lastused_ms = |
| + | curlx_ptimediff_ms(pnow, &conn->created); |
| + | |
| + | switch(cb(data, conn, cbdata, pnow)) { |
| + | case CPOOL_DO_KEEP: |
| + | break; |
| + | case CPOOL_DO_IDLE: |
| + | /* the connection is no longer in use by any transfer */ |
| + | if(cpool_conn_now_idle(cpool, data, conn, pnow)) { |
| + | /* connection kept in the cpool */ |
| + | infof(data, "Connection #%" FMT_OFF_T " to host %s:%u left intact", |
| + | conn->connection_id, conn->origin->user_hostname, |
| + | conn->origin->port); |
| + | } |
| + | else /* connection was removed from the cpool and destroyed. */ |
| + | data->state.lastconnect_id = -1; |
| + | break; |
| + | case CPOOL_DO_CLOSE: |
| + | Curl_conn_close(data, conn, FALSE); |
| + | break; |
| + | case CPOOL_DO_TERMINATE: |
| + | Curl_conn_close(data, conn, TRUE); |
| + | break; |
| } | |
| - | else |
| - | cb(conn, data, cbdata); |
| + | |
| + | CPOOL_UNLOCK(cpool, data); |
| } | |
| static int cpool_mark_stale(struct cpool *cpool, | |
| @@ -941,9 +1000,9 @@ void Curl_cpool_nw_changed(struct cpool *cpool, struct Curl_easy *admin) | |
| /* A connection has to have been idle for less than 'conn_max_idle_ms' | |
| (the success rate is too low after this), or created less than | |
| 'conn_max_age_ms' ago, to be subject for reuse. */ | |
| - | static bool cpool_conn_maxage(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | const struct curltime *pnow) |
| + | static bool cpool_conn_too_old(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | const struct curltime *pnow) |
| { | |
| timediff_t age_ms; | |
| @@ -979,7 +1038,7 @@ bool Curl_cpool_conn_seems_healthy(struct connectdata *conn, | |
| bool healthy = TRUE; | |
| DEBUGASSERT(!data->conn); | |
| - | if(!CONN_INUSE(conn) && cpool_conn_maxage(data, conn, pnow)) /* too old? */ |
| + | if(!CONN_INUSE(conn) && cpool_conn_too_old(data, conn, pnow)) |
| return FALSE; | |
| if((curlx_ptimediff_ms(pnow, &conn->created) - conn->lastchecked_ms) < 1000) | |
| return TRUE; | |
| @@ -83,6 +83,8 @@ struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data, | |
| /* Add the connection to the pool. */ | |
| CURLcode Curl_cpool_add(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| + | uint32_t max_total, |
| + | uint32_t max_host, |
| const struct curltime *pnow) WARN_UNUSED_RESULT; | |
| /* Connection was used at the given time. */ | |
| @@ -95,21 +97,16 @@ timediff_t Curl_cpool_conn_age_ms(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| const struct curltime *pnow); | |
| - | /** |
| - | * Return if the pool has reached its configured limits for adding |
| - | * the given connection. Try to discard the oldest, idle connections |
| - | * to make space. |
| - | */ |
| - | #define CPOOL_LIMIT_OK 0 |
| - | #define CPOOL_LIMIT_DEST 1 |
| - | #define CPOOL_LIMIT_TOTAL 2 |
| - | int Curl_cpool_check_limits(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | const struct curltime *pnow); |
| + | typedef enum { |
| + | CPOOL_MATCH_FOUND, /* The passed `conn` matches, stop looking further */ |
| + | CPOOL_MATCH_CONT, /* No match, continue looking */ |
| + | CPOOL_MATCH_CLOSE, /* No match, close `conn`, continue looking */ |
| + | CPOOL_MATCH_TOO_OLD /* No match, `conn` is too old, close when idle */ |
| + | } cpool_match_result; |
| /* Return of conn is suitable. If so, stops iteration. */ | |
| - | typedef bool Curl_cpool_conn_match_cb(struct connectdata *conn, |
| - | void *userdata); |
| + | typedef cpool_match_result |
| + | Curl_cpool_conn_match_cb(struct connectdata *conn, void *userdata); |
| /* Act on the result of the find, may override it. */ | |
| typedef bool Curl_cpool_done_match_cb(void *userdata); | |
| @@ -119,6 +116,8 @@ typedef bool Curl_cpool_done_match_cb(void *userdata); | |
| * All callbacks are invoked while the pool's lock is held. | |
| * @param data current transfer | |
| * @param destination match against `conn->destination` in pool | |
| + | * @param prune_dead perform reaping of dead connections at start |
| + | * @param pnow timestamp of operation |
| * @param conn_cb must be present, called for each connection in the | |
| * bundle until it returns TRUE | |
| * @return combined result of last conn_db and result_cb or FALSE if no | |
| @@ -126,36 +125,28 @@ typedef bool Curl_cpool_done_match_cb(void *userdata); | |
| */ | |
| bool Curl_cpool_find(struct Curl_easy *data, | |
| const char *destination, | |
| + | bool prune_dead, |
| + | const struct curltime *pnow, |
| Curl_cpool_conn_match_cb *conn_cb, | |
| Curl_cpool_done_match_cb *done_cb, | |
| void *userdata); | |
| - | /* |
| - | * A connection (already in the pool) is now idle. Do any |
| - | * cleanups in regard to the pool's limits. |
| - | * |
| - | * Return TRUE if idle connection kept in pool, FALSE if closed. |
| - | */ |
| - | bool Curl_cpool_conn_now_idle(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | const struct curltime *pnow); |
| - | |
| - | /** |
| - | * Scans the connection pool for half-open/dead |
| - | * connections, closes and removes them. |
| - | * The cleanup is done at most once per second. |
| - | */ |
| - | void Curl_cpool_prune_dead(struct cpool *cpool, |
| - | struct Curl_easy *data); |
| - | |
| /** | |
| * Perform upkeep actions on connections in the transfer's pool. | |
| */ | |
| CURLcode Curl_cpool_upkeep(struct Curl_easy *data); | |
| - | typedef void Curl_cpool_conn_do_cb(struct connectdata *conn, |
| - | struct Curl_easy *data, |
| - | void *cbdata); |
| + | typedef enum { |
| + | CPOOL_DO_KEEP, /* Keep the connection, still in use */ |
| + | CPOOL_DO_IDLE, /* Connection is idle, may get closed now */ |
| + | CPOOL_DO_CLOSE, /* Close the connection (clean) */ |
| + | CPOOL_DO_TERMINATE /* Terminate the connection (unclean) */ |
| + | } cpool_do_result; |
| + | |
| + | typedef cpool_do_result Curl_cpool_return_cb(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | void *cbdata, |
| + | const struct curltime *pnow); |
| /** | |
| * Invoked the callback for the given data + connection under the | |
| @@ -163,9 +154,10 @@ typedef void Curl_cpool_conn_do_cb(struct connectdata *conn, | |
| * The callback is always invoked, even if the transfer has no connection | |
| * pool associated. | |
| */ | |
| - | void Curl_cpool_do_locked(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | Curl_cpool_conn_do_cb *cb, void *cbdata); |
| + | void Curl_cpool_return(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | Curl_cpool_return_cb *cb, void *cbdata, |
| + | const struct curltime *pnow); |
| /* Close all unused connections, prevent reuse of existing ones. */ | |
| void Curl_cpool_nw_changed(struct cpool *cpool, struct Curl_easy *admin); | |
| @@ -277,42 +277,40 @@ void Curl_cshutdn_terminate(struct Curl_easy *admin, | |
| } | |
| } | |
| - | static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn, |
| - | struct Curl_easy *admin, |
| - | const char *destination) |
| + | static size_t cshutdn_destroy_oldest(struct cshutdn *cshutdn, |
| + | struct Curl_easy *admin, |
| + | const char *destination, |
| + | size_t count) |
| { | |
| - | struct Curl_llist_node *e; |
| - | struct connectdata *conn; |
| - | |
| - | e = Curl_llist_head(&cshutdn->list); |
| - | while(e) { |
| - | conn = Curl_node_elem(e); |
| - | if(!destination || !strcmp(destination, conn->destination)) |
| - | break; |
| - | e = Curl_node_next(e); |
| - | } |
| + | struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list), *next; |
| + | struct Curl_sigpipe_ctx sigpipe_ctx; |
| + | size_t n = 0; |
| - | if(e) { |
| - | struct Curl_sigpipe_ctx sigpipe_ctx; |
| - | conn = Curl_node_elem(e); |
| - | Curl_node_remove(e); |
| - | sigpipe_init(&sigpipe_ctx); |
| - | sigpipe_apply(admin, &sigpipe_ctx); |
| - | Curl_cshutdn_terminate(admin, conn, FALSE); |
| - | sigpipe_restore(&sigpipe_ctx); |
| - | return TRUE; |
| + | sigpipe_init(&sigpipe_ctx); |
| + | while(e && (n < count)) { |
| + | struct connectdata *conn = Curl_node_elem(e); |
| + | next = Curl_node_next(e); |
| + | if(!destination || !strcmp(destination, conn->destination)) { |
| + | Curl_node_remove(e); |
| + | sigpipe_apply(admin, &sigpipe_ctx); |
| + | Curl_cshutdn_terminate(admin, conn, FALSE); |
| + | ++n; |
| + | } |
| + | e = next; |
| } | |
| - | return FALSE; |
| + | sigpipe_restore(&sigpipe_ctx); |
| + | return n; |
| } | |
| - | bool Curl_cshutdn_close_oldest(struct cshutdn *cshutdn, |
| + | size_t Curl_cshutdn_close_oldest(struct cshutdn *cshutdn, |
| struct Curl_easy *admin, | |
| - | const char *destination) |
| + | const char *destination, |
| + | size_t count) |
| { | |
| if(cshutdn) { | |
| - | return cshutdn_destroy_oldest(cshutdn, admin, destination); |
| + | return cshutdn_destroy_oldest(cshutdn, admin, destination, count); |
| } | |
| - | return FALSE; |
| + | return 0; |
| } | |
| #define NUM_POLLS_ON_STACK 10 | |
| @@ -502,7 +500,7 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn, | |
| if(max_shutdowns <= Curl_llist_count(&cshutdn->list)) { | |
| CURL_TRC_M(admin, "[SHUTDOWN] discarding oldest shutdown connection " | |
| "due to shutdown limit of %zu", max_shutdowns); | |
| - | cshutdn_destroy_oldest(cshutdn, admin, NULL); |
| + | cshutdn_destroy_oldest(cshutdn, admin, NULL, 1); |
| } | |
| if(multi->socket_cb && cshutdn_update_ev(multi, conn)) { | |
| @@ -92,12 +92,13 @@ size_t Curl_cshutdn_count(struct cshutdn *cshutdn); | |
| size_t Curl_cshutdn_dest_count(struct cshutdn *cshutdn, | |
| const char *destination); | |
| - | /* Close the oldest connection in shutdown to destination or, |
| + | /* Close the oldest `count` connections in shutdown to destination or, |
| * when destination is NULL for any destination. | |
| - | * Return TRUE if a connection has been closed. */ |
| - | bool Curl_cshutdn_close_oldest(struct cshutdn *cshutdn, |
| - | struct Curl_easy *admin, |
| - | const char *destination); |
| + | * Return the number of connections closed. */ |
| + | size_t Curl_cshutdn_close_oldest(struct cshutdn *cshutdn, |
| + | struct Curl_easy *admin, |
| + | const char *destination, |
| + | size_t count); |
| /* Add a connection to have it shut down. Terminate the oldest | |
| * connection when shutdowns exceed max_shutdowns. */ | |
| @@ -130,7 +130,7 @@ CURLcode Curl_http_setup_conn(struct Curl_easy *data, | |
| during this request */ | |
| if(data->state.http_neg.wanted == CURL_HTTP_V3x) { | |
| /* only HTTP/3, needs to work */ | |
| - | CURLcode result = Curl_conn_may_http3(data, conn, conn->transport_wanted); |
| + | CURLcode result = Curl_conn_may_http3(data, conn); |
| if(result) | |
| return result; | |
| } | |
| @@ -635,27 +635,22 @@ static bool multi_conn_should_close(struct connectdata *conn, | |
| return FALSE; | |
| } | |
| - | static void multi_done_locked(struct connectdata *conn, |
| - | struct Curl_easy *data, |
| - | void *userdata) |
| + | static cpool_do_result multi_done_locked(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | void *userdata, |
| + | const struct curltime *pnow) |
| { | |
| struct multi_done_ctx *mdctx = userdata; | |
| - | const struct curltime *pnow = Curl_pgrs_now(data); |
| - | |
| - | Curl_detach_connection(data); |
| + | (void)pnow; |
| CURL_TRC_M(data, "multi_done_locked, in use=%u", conn->attached_xfers); | |
| if(CONN_INUSE(conn)) { | |
| /* Stop if still used. */ | |
| CURL_TRC_M(data, "Connection still in use %u, no more multi_done now!", | |
| conn->attached_xfers); | |
| - | return; |
| + | return CPOOL_DO_KEEP; |
| } | |
| - | data->state.done = TRUE; /* called now! */ |
| - | |
| - | Curl_dnscache_prune(data, pnow); |
| - | |
| if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) { | |
| CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s:%u, " | |
| "forbid=%d, close=%d, premature=%d, conn_multiplex=%d", | |
| @@ -664,27 +659,17 @@ static void multi_done_locked(struct connectdata *conn, | |
| data->set.reuse_forbid, conn->bits.close, mdctx->premature, | |
| Curl_conn_is_multiplex(conn, FIRSTSOCKET)); | |
| connclose(conn); | |
| - | Curl_conn_close(data, conn, (bool)mdctx->premature); |
| + | return mdctx->premature ? CPOOL_DO_TERMINATE : CPOOL_DO_CLOSE; |
| } | |
| else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) { | |
| CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s:%u was shutdown" | |
| " by server, not reusing", conn->connection_id, | |
| conn->origin->user_hostname, conn->origin->port); | |
| connclose(conn); | |
| - | Curl_conn_close(data, conn, (bool)mdctx->premature); |
| + | return mdctx->premature ? CPOOL_DO_TERMINATE : CPOOL_DO_CLOSE; |
| } | |
| else { | |
| - | /* the connection is no longer in use by any transfer */ |
| - | if(Curl_cpool_conn_now_idle(data, conn, pnow)) { |
| - | /* connection kept in the cpool */ |
| - | infof(data, "Connection #%" FMT_OFF_T " to host %s:%u left intact", |
| - | conn->connection_id, conn->origin->user_hostname, |
| - | conn->origin->port); |
| - | } |
| - | else { |
| - | /* connection was removed from the cpool and destroyed. */ |
| - | data->state.lastconnect_id = -1; |
| - | } |
| + | return CPOOL_DO_IDLE; |
| } | |
| } | |
| @@ -695,13 +680,16 @@ static CURLcode multi_done(struct Curl_easy *data, | |
| { | |
| CURLcode result; | |
| struct connectdata *conn = data->conn; | |
| + | const struct curltime *pnow = Curl_pgrs_now(data); |
| CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d", | |
| (int)status, (int)premature, data->state.done); | |
| - | if(data->state.done) |
| + | if(data->state.done) { |
| /* Stop if multi_done() has already been called */ | |
| return CURLE_OK; | |
| + | } |
| + | data->state.done = TRUE; /* called now! */ |
| /* Shut down any ongoing async resolver operation. */ | |
| Curl_resolv_shutdown_all(data); | |
| @@ -736,7 +724,7 @@ static CURLcode multi_done(struct Curl_easy *data, | |
| * - the transfer has not connected | |
| * - we already aborted by callback to avoid this calling another callback | |
| */ | |
| - | int rc = Curl_pgrsDone(data); |
| + | int rc = Curl_pgrsDone(data, pnow); |
| if(!result && rc) | |
| result = CURLE_ABORTED_BY_CALLBACK; | |
| } | |
| @@ -754,13 +742,15 @@ static CURLcode multi_done(struct Curl_easy *data, | |
| result = Curl_req_done(&data->req, data, premature); | |
| if(conn) { | |
| - | /* Under the potential connection pool's share lock, decide what to |
| - | * do with the transfer's connection. */ |
| + | /* Detach connection and decide what to do with it. */ |
| struct multi_done_ctx mdctx; | |
| + | Curl_detach_connection(data); |
| + | Curl_dnscache_prune(data, pnow); |
| + | |
| memset(&mdctx, 0, sizeof(mdctx)); | |
| mdctx.premature = premature; | |
| - | Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); |
| + | Curl_cpool_return(data, conn, multi_done_locked, &mdctx, pnow); |
| } | |
| /* flush the netrc cache */ | |
| @@ -185,11 +185,11 @@ const struct curltime *Curl_pgrs_now(struct Curl_easy *data) | |
| pgrsDone() - transfer complete | |
| */ | |
| - | int Curl_pgrsDone(struct Curl_easy *data) |
| + | int Curl_pgrsDone(struct Curl_easy *data, const struct curltime *pnow) |
| { | |
| int rc; | |
| data->progress.delta.lastshow_us = -1; | |
| - | rc = Curl_pgrsUpdate(data); /* the final (forced) update */ |
| + | rc = Curl_pgrsUpdateX(data, pnow); /* the final (forced) update */ |
| if(rc) | |
| return rc; | |
| @@ -45,7 +45,7 @@ typedef enum { | |
| /* Get the current timestamp of the transfer */ | |
| const struct curltime *Curl_pgrs_now(struct Curl_easy *data); | |
| - | int Curl_pgrsDone(struct Curl_easy *data); |
| + | int Curl_pgrsDone(struct Curl_easy *data, const struct curltime *pnow); |
| void Curl_pgrsStart(struct Curl_easy *data, const struct curltime *pnow); | |
| void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size); | |
| void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size); | |
| @@ -1017,7 +1017,7 @@ static CURLcode tftp_done(struct Curl_easy *data, CURLcode status, | |
| (void)status; | |
| (void)premature; | |
| - | if(Curl_pgrsDone(data)) |
| + | if(Curl_pgrsDone(data, Curl_pgrs_now(data))) |
| return CURLE_ABORTED_BY_CALLBACK; | |
| /* If we have encountered an error */ | |
| @@ -983,50 +983,51 @@ static bool url_match_auth_nego(struct connectdata *conn, | |
| #define url_match_auth_nego(c, m) ((void)(c), (void)(m), TRUE) | |
| #endif | |
| - | static bool url_match_conn(struct connectdata *conn, void *userdata) |
| + | static cpool_match_result url_match_conn(struct connectdata *conn, |
| + | void *userdata) |
| { | |
| struct url_conn_match *m = userdata; | |
| bool wait_pipe = FALSE; | |
| /* general connect config setting match? */ | |
| if(!url_match_connect_config(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| /* match for destination and protocol? */ | |
| if(!url_match_destination(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_fully_connected(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_multiplex_needs(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_ssl_use(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_proxy_use(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_ssl_config(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_http_multiplex(conn, m, &wait_pipe)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_auth(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_proto_config(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_auth_ntlm(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_auth_nego(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| if(!url_match_multiplex_limits(conn, m)) | |
| - | return FALSE; |
| + | return CPOOL_MATCH_CONT; |
| /* The connection matches all conditions, but do we want to use it? */ | |
| if(wait_pipe) { | |
| @@ -1034,16 +1035,14 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) | |
| * yet. Put the transfer into PENDING and wait for conn state change. */ | |
| DEBUGASSERT(!m->found); | |
| m->wait_pipe = TRUE; | |
| - | return TRUE; |
| + | return CPOOL_MATCH_FOUND; |
| } | |
| if(m->data->set.conn_max_age_ms > 0) { | |
| timediff_t age_ms = Curl_cpool_conn_age_ms(m->data, conn, &m->now); | |
| if(age_ms > m->data->set.conn_max_age_ms) { | |
| /* Transfer is looking for a younger connection. */ | |
| - | if(!CONN_INUSE(conn)) |
| - | Curl_conn_close(m->data, conn, FALSE); |
| - | return FALSE; |
| + | return CPOOL_MATCH_TOO_OLD; |
| } | |
| } | |
| @@ -1053,13 +1052,12 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) | |
| !Curl_cpool_conn_seems_healthy(conn, m->data, &m->now)) { | |
| infof(m->data, "Connection %" FMT_OFF_T " seems to be dead, terminating", | |
| conn->connection_id); | |
| - | Curl_conn_close(m->data, conn, FALSE); |
| - | return FALSE; |
| + | return CPOOL_MATCH_CLOSE; |
| } | |
| /* conn matches our needs. */ | |
| m->found = conn; | |
| - | return TRUE; |
| + | return CPOOL_MATCH_FOUND; |
| } | |
| static bool url_match_result(void *userdata) | |
| @@ -1096,21 +1094,13 @@ static bool url_match_result(void *userdata) | |
| */ | |
| static bool url_attach_existing(struct Curl_easy *data, | |
| struct connectdata *needle, | |
| - | struct url_conn_match *m) |
| + | struct url_conn_match *m, |
| + | const struct curltime *pnow) |
| { | |
| - | struct cpool *cpool = Curl_cpool_get_instance(data); |
| - | bool success; |
| - | |
| - | DEBUGASSERT(!data->conn); |
| - | |
| - | Curl_cpool_prune_dead(cpool, data); |
| - | |
| /* Find a connection in the pool that matches what "data + needle" | |
| * requires. If a suitable candidate is found, it is attached to "data". */ | |
| - | success = Curl_cpool_find(data, needle->destination, |
| - | url_match_conn, url_match_result, m); |
| - | |
| - | return success; |
| + | return Curl_cpool_find(data, needle->destination, TRUE, pnow, |
| + | url_match_conn, url_match_result, m); |
| } | |
| /* | |
| @@ -1522,10 +1512,9 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, | |
| struct connectdata *conn) | |
| { | |
| struct Curl_peer *peer = NULL; | |
| - | CURLcode result; |
| if(conn->scheme->run->setup_connection) { | |
| - | result = conn->scheme->run->setup_connection(data, conn); |
| + | CURLcode result = conn->scheme->run->setup_connection(data, conn); |
| if(result) | |
| return result; | |
| } | |
| @@ -1556,10 +1545,10 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, | |
| if(data->set.scope_id) | |
| conn->scope_id = data->set.scope_id; | |
| else { | |
| - | struct Curl_peer *first = Curl_conn_get_first_peer(conn, FIRSTSOCKET); |
| - | if(!first) |
| + | peer = Curl_conn_get_first_peer(conn, FIRSTSOCKET); |
| + | if(!peer) |
| return CURLE_FAILED_INIT; | |
| - | conn->scope_id = first->scopeid; |
| + | conn->scope_id = peer->scopeid; |
| } | |
| #endif | |
| @@ -2269,7 +2258,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data, | |
| goto out; | |
| /* Setup a "faked" transfer that will do nothing */ | |
| - | result = Curl_cpool_add(data, needle, pnow); |
| + | result = Curl_cpool_add(data, needle, 0, 0, pnow); |
| Curl_attach_connection(data, needle, TRUE); | |
| needle = NULL; | |
| if(!result) { | |
| @@ -2299,40 +2288,42 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data, | |
| if((!data->set.reuse_fresh || data->state.followlocation) && | |
| !data->set.connect_only) { | |
| /* Ok, try to find and attach an existing one */ | |
| - | url_attach_existing(data, needle, &match); |
| - | } |
| - | |
| - | if(data->conn) { |
| - | /* We attached an existing connection for this transfer. Copy |
| - | * over transfer specific properties over from needle. */ |
| - | struct connectdata *conn = data->conn; |
| - | VERBOSE(bool tls_upgraded = |
| - | (!(needle->origin->scheme->flags & PROTOPT_SSL) && |
| - | Curl_conn_is_ssl(conn, FIRSTSOCKET))); |
| - | |
| - | conn->bits.reuse = TRUE; |
| - | url_conn_reuse_adjust(data, needle); |
| + | if(url_attach_existing(data, needle, &match, pnow)) { |
| + | /* We attached an existing connection for this transfer. Copy |
| + | * over transfer specific properties over from needle. */ |
| + | struct connectdata *conn = data->conn; |
| + | VERBOSE(bool tls_upgraded = |
| + | (!(needle->origin->scheme->flags & PROTOPT_SSL) && |
| + | Curl_conn_is_ssl(conn, FIRSTSOCKET))); |
| + | |
| + | DEBUGASSERT(conn); |
| + | conn->bits.reuse = TRUE; |
| + | url_conn_reuse_adjust(data, needle); |
| #ifndef CURL_DISABLE_PROXY | |
| - | infof(data, "Reusing existing %s: connection%s with %s %s", |
| - | conn->origin->scheme->name, |
| - | tls_upgraded ? " (upgraded to SSL)" : "", |
| - | (conn->socks_proxy.peer || conn->http_proxy.peer) ? "proxy" : "host", |
| - | conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname : |
| - | conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname : |
| - | conn->origin->hostname); |
| + | infof(data, "Reusing existing %s: connection%s with %s %s", |
| + | conn->origin->scheme->name, |
| + | tls_upgraded ? " (upgraded to SSL)" : "", |
| + | (conn->socks_proxy.peer || conn->http_proxy.peer) ? |
| + | "proxy" : "host", |
| + | conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname : |
| + | conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname : |
| + | conn->origin->hostname); |
| #else | |
| - | infof(data, "Reusing existing %s: connection%s with host %s", |
| - | conn->origin->scheme->name, |
| - | tls_upgraded ? " (upgraded to SSL)" : "", |
| - | conn->origin->hostname); |
| + | infof(data, "Reusing existing %s: connection%s with host %s", |
| + | conn->origin->scheme->name, |
| + | tls_upgraded ? " (upgraded to SSL)" : "", |
| + | conn->origin->hostname); |
| #endif | |
| + | } |
| } | |
| - | else { |
| - | /* We have decided that we want a new connection. We may not be able to do |
| - | that if we have reached the limit of how many connections we are |
| - | allowed to open. */ |
| + | if(!data->conn) { |
| + | /* We have not attached an existing connection. `needle` may become |
| + | * the connection to use, if |
| + | * - we are not waiting on an existing connection result |
| + | * - the total/host limits on active connections allow it |
| + | */ |
| if(match.wait_pipe) { | |
| /* There is a connection that *might* become usable for multiplexing | |
| "soon", and we wait for that */ | |
| @@ -2340,50 +2331,34 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data, | |
| result = CURLE_NO_CONNECTION_AVAILABLE; | |
| goto out; | |
| } | |
| - | else { |
| - | switch(Curl_cpool_check_limits(data, needle, pnow)) { |
| - | case CPOOL_LIMIT_DEST: |
| - | infof(data, "No more connections allowed to host"); |
| - | result = CURLE_NO_CONNECTION_AVAILABLE; |
| - | goto out; |
| - | case CPOOL_LIMIT_TOTAL: |
| - | if(data->master_mid != UINT32_MAX) |
| - | CURL_TRC_M(data, "Allowing sub-requests (like DoH) to override " |
| - | "max connection limit"); |
| - | else { |
| - | infof(data, "No connections available, total of %u reached.", |
| - | data->multi->max_total_connections); |
| - | result = CURLE_NO_CONNECTION_AVAILABLE; |
| - | goto out; |
| - | } |
| - | break; |
| - | default: |
| - | break; |
| - | } |
| - | } |
| - | /* Convert needle into a full connection by cloning the |
| - | * ssl filter config used in matching into the connection. */ |
| + | /* Add needle to conn pool, observing the limits. |
| + | * Initializes connection_id. |
| + | * May fail due to limits with CURLE_NO_CONNECTION_AVAILABLE. */ |
| + | result = Curl_cpool_add(data, needle, |
| + | data->multi->max_total_connections, |
| + | data->multi->max_host_connections, |
| + | pnow); |
| + | if(result) |
| + | goto out; |
| + | |
| + | /* All fine, attach it and forget needle */ |
| + | Curl_attach_connection(data, needle, TRUE); |
| + | needle = NULL; |
| + | |
| + | /* Clone the SSL filter configs into the new connection. */ |
| result = Curl_ssl_conn_config_clone(&match.ssl_config, | |
| #ifndef CURL_DISABLE_PROXY | |
| &match.proxy_ssl_config, | |
| #else | |
| NULL, | |
| #endif | |
| - | needle); |
| + | data->conn); |
| if(result) { | |
| DEBUGF(infof(data, "Error: clone connection SSL config")); | |
| goto out; | |
| } | |
| - | /* Add needle to conn pool, which assigns the connection id. |
| - | * Attach regardless of result, for correct handling. */ |
| - | result = Curl_cpool_add(data, needle, pnow); |
| - | Curl_attach_connection(data, needle, TRUE); |
| - | needle = NULL; |
| - | if(result) |
| - | goto out; |
| - | |
| #ifdef USE_NTLM | |
| /* If NTLM is requested in a part of this connection, make sure we do not | |
| assume the state is fine as this is a fresh connection and NTLM is | |
| @@ -2404,6 +2379,8 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data, | |
| #endif | |
| } | |
| + | /* COMMON setup code for reused and new connections from here on. */ |
| + | |
| /* Setup and init stuff before DO starts, in preparing for the transfer. */ | |
| result = Curl_init_transfer(data, data->conn); | |
| if(result) | |
| @@ -51,6 +51,7 @@ | |
| #include "bufq.h" | |
| #include "curlx/dynbuf.h" | |
| #include "curlx/win32-fopen.h" | |
| + | #include "connect.h" |
| #include "cfilters.h" | |
| #include "vdns/cf-dns.h" | |
| #include "vquic/cf-ngtcp2.h" | |
| @@ -1192,10 +1193,9 @@ CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, | |
| #endif /* !CURL_DISABLE_PROXY && USE_PROXY_HTTP3 */ | |
| CURLcode Curl_conn_may_http3(struct Curl_easy *data, | |
| - | const struct connectdata *conn, |
| - | unsigned char transport) |
| + | struct connectdata *conn) |
| { | |
| - | if(transport == TRNSPRT_UNIX) { |
| + | if(Curl_conn_get_first_peer(conn, FIRSTSOCKET)->unix_socket) { |
| failf(data, "HTTP/3 cannot be used over UNIX domain sockets"); | |
| return CURLE_QUIC_CONNECT_ERROR; | |
| } | |
| @@ -1324,12 +1324,10 @@ struct nghttp3_mem *Curl_nghttp3_mem(void) | |
| #else /* CURL_DISABLE_HTTP || !USE_HTTP3 */ | |
| CURLcode Curl_conn_may_http3(struct Curl_easy *data, | |
| - | const struct connectdata *conn, |
| - | unsigned char transport) |
| + | struct connectdata *conn) |
| { | |
| (void)data; | |
| (void)conn; | |
| - | (void)transport; |
| DEBUGF(infof(data, "QUIC is not supported in this build")); | |
| return CURLE_NOT_BUILT_IN; | |
| } | |
| @@ -86,7 +86,6 @@ extern struct Curl_cftype Curl_cft_h3_proxy; | |
| #endif /* !CURL_DISABLE_HTTP && USE_HTTP3 */ | |
| CURLcode Curl_conn_may_http3(struct Curl_easy *data, | |
| - | const struct connectdata *conn, |
| - | unsigned char transport); |
| + | struct connectdata *conn); |
| #endif /* HEADER_CURL_VQUIC_QUIC_H */ | |
| @@ -2760,7 +2760,7 @@ static CURLcode myssh_done(struct Curl_easy *data, | |
| else | |
| result = status; | |
| - | if(Curl_pgrsDone(data)) |
| + | if(Curl_pgrsDone(data, Curl_pgrs_now(data))) |
| return CURLE_ABORTED_BY_CALLBACK; | |
| CURL_REQ_CLEAR_IO(data); | |
| @@ -3620,7 +3620,7 @@ static CURLcode ssh_done(struct Curl_easy *data, CURLcode status) | |
| else | |
| result = status; | |
| - | if(Curl_pgrsDone(data)) |
| + | if(Curl_pgrsDone(data, Curl_pgrs_now(data))) |
| return CURLE_ABORTED_BY_CALLBACK; | |
| CURL_REQ_CLEAR_IO(data); | |
| @@ -28,8 +28,6 @@ run 1: foobar and so on fun! | |
| [- Mutex unlock CONNECT | |
| -] Mutex lock CONNECT | |
| [- Mutex unlock CONNECT | |
| - | -] Mutex lock CONNECT |
| - | [- Mutex unlock CONNECT |
| run 1: foobar and so on fun! | |
| -] Mutex lock CONNECT | |
| [- Mutex unlock CONNECT | |
| @@ -41,8 +39,6 @@ run 1: foobar and so on fun! | |
| [- Mutex unlock CONNECT | |
| -] Mutex lock CONNECT | |
| [- Mutex unlock CONNECT | |
| - | -] Mutex lock CONNECT |
| - | [- Mutex unlock CONNECT |
| run 1: foobar and so on fun! | |
| -] Mutex lock CONNECT | |
| [- Mutex unlock CONNECT | |
| @@ -54,8 +50,6 @@ run 1: foobar and so on fun! | |
| [- Mutex unlock CONNECT | |
| -] Mutex lock CONNECT | |
| [- Mutex unlock CONNECT | |
| - | -] Mutex lock CONNECT |
| - | [- Mutex unlock CONNECT |
| run 1: foobar and so on fun! | |
| -] Mutex lock CONNECT | |
| [- Mutex unlock CONNECT | |