diff --git a/lib/cf-dns.c b/lib/cf-dns.c index 30ba1cb94d..437a3491c3 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -509,6 +509,30 @@ cf_dns_get_nth_ai(const struct Curl_addrinfo *ai, return NULL; } +bool Curl_conn_dns_has_any_ai(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->resolv_result) + return FALSE; + else if(ctx->dns) + return !!ctx->dns->addr; + else +#ifdef USE_IPV6 + return Curl_resolv_get_ai(data, ctx->resolv_id, AF_INET, 0) || + Curl_resolv_get_ai(data, ctx->resolv_id, AF_INET6, 0); +#else + return !!Curl_resolv_get_ai(data, ctx->resolv_id, AF_INET, 0); +#endif + } + } + return FALSE; +} + /* Return the addrinfo at `index` for the given `family` from the * first "resolve" filter underneath `cf`. If the DNS resolving is * not done yet or if no address for the family exists, returns NULL. diff --git a/lib/cf-dns.h b/lib/cf-dns.h index 909672e26a..24e266f1dd 100644 --- a/lib/cf-dns.h +++ b/lib/cf-dns.h @@ -47,6 +47,9 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex); CURLcode Curl_cf_dns_result(struct Curl_cfilter *cf); +/* Returns TRUE if any addressinfo is available via + * `Curl_conn_dns_get_ai()`. */ +bool Curl_conn_dns_has_any_ai(struct Curl_easy *data, int sockindex); const struct Curl_addrinfo * Curl_conn_dns_get_ai(struct Curl_easy *data, diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index 197b1c73c0..b07631ac40 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -58,19 +58,17 @@ struct cf_hc_baller { BIT(shutdown); }; -static void cf_hc_baller_reset(struct cf_hc_baller *b, - struct Curl_easy *data) +static void cf_hc_baller_discard(struct cf_hc_baller *b, + struct Curl_easy *data) { if(b->cf) { Curl_conn_cf_close(b->cf, data); Curl_conn_cf_discard_chain(&b->cf, data); b->cf = NULL; } - b->result = CURLE_OK; - b->reply_ms = -1; } -static bool cf_hc_baller_is_active(struct cf_hc_baller *b) +static bool cf_hc_baller_is_connecting(struct cf_hc_baller *b) { return b->cf && !b->result; } @@ -114,24 +112,24 @@ struct cf_hc_ctx { cf_hc_state state; struct curltime started; /* when connect started */ CURLcode result; /* overall result */ + CURLcode check_h3_result; struct cf_hc_baller ballers[2]; size_t baller_count; timediff_t soft_eyeballs_timeout_ms; timediff_t hard_eyeballs_timeout_ms; uint8_t def_transport; + BIT(httpsrr_resolved); + BIT(checked_h3); + BIT(ballers_complete); }; -static void cf_hc_ctx_reset(struct Curl_easy *data, +static void cf_hc_ctx_close(struct Curl_easy *data, struct cf_hc_ctx *ctx) { if(ctx) { size_t i; for(i = 0; i < ctx->baller_count; ++i) - cf_hc_baller_reset(&ctx->ballers[i], data); - ctx->state = CF_HC_RESOLV; - ctx->result = CURLE_OK; - ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; - ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4; + cf_hc_baller_discard(&ctx->ballers[i], data); } } @@ -139,7 +137,7 @@ static void cf_hc_ctx_destroy(struct Curl_easy *data, struct cf_hc_ctx *ctx) { if(ctx) { - cf_hc_ctx_reset(data, ctx); + cf_hc_ctx_close(data, ctx); curlx_free(ctx); } } @@ -150,6 +148,10 @@ static void cf_hc_baller_assign(struct cf_hc_baller *b, { b->alpn_id = alpn_id; b->transport = def_transport; + b->cf = NULL; + b->result = CURLE_OK; + b->reply_ms = -1; + b->shutdown = FALSE; switch(b->alpn_id) { case ALPN_h3: b->name = "h3"; @@ -172,24 +174,14 @@ static void cf_hc_baller_assign(struct cf_hc_baller *b, static void cf_hc_baller_init(struct cf_hc_baller *b, struct Curl_cfilter *cf, - struct Curl_easy *data, - uint8_t transport) + struct Curl_easy *data) { struct Curl_cfilter *save = cf->next; cf->next = NULL; b->started = *Curl_pgrs_now(data); - switch(b->alpn_id) { - case ALPN_h3: - transport = TRNSPRT_QUIC; - break; - default: - break; - } - - if(!b->result) - b->result = Curl_cf_setup_insert_after(cf, data, transport, - CURL_CF_SSL_ENABLE); + b->result = Curl_cf_setup_insert_after(cf, data, b->transport, + CURL_CF_SSL_ENABLE); b->cf = cf->next; cf->next = save; } @@ -213,39 +205,34 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, struct cf_hc_baller *winner) { struct cf_hc_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - int reply_ms; - size_t i; - DEBUGASSERT(winner->cf); - for(i = 0; i < ctx->baller_count; ++i) - if(winner != &ctx->ballers[i]) - cf_hc_baller_reset(&ctx->ballers[i], data); + /* Make the winner's connection filter out own sub-filter, check, move, + * close all remaining. */ + if(cf->next) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + if(!winner->cf) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } - reply_ms = cf_hc_baller_reply_ms(winner, data); - if(reply_ms >= 0) - CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", - winner->name, - (int)curlx_ptimediff_ms(Curl_pgrs_now(data), - &winner->started), reply_ms); - else - CURL_TRC_CF(data, cf, "deferred handshake %s: %dms", - winner->name, (int)curlx_ptimediff_ms(Curl_pgrs_now(data), - &winner->started)); - - /* install the winning filter below this one. */ cf->next = winner->cf; winner->cf = NULL; - /* whatever errors where reported by ballers, clear our errorbuf */ + ctx->state = CF_HC_SUCCESS; + cf->connected = TRUE; + + cf_hc_ctx_close(data, ctx); + /* ballers may have failf()'d, the winner resets it, so our + * errorbuf is clean again. */ Curl_reset_fail(data); #ifdef USE_NGHTTP2 { - /* Using nghttp2, we add the filter "below" us, so when the conn - * closes, we tear it down for a fresh reconnect */ + /* For a negotiated HTTP/2 connection insert the h2 filter. */ const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); if(alpn && !strcmp("h2", alpn)) { - result = Curl_http2_switch_at(cf, data); + CURLcode result = Curl_http2_switch_at(cf, data); if(result) { ctx->state = CF_HC_FAILURE; ctx->result = result; @@ -254,198 +241,235 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, } } #endif - - ctx->state = CF_HC_SUCCESS; - cf->connected = TRUE; - return result; + return CURLE_OK; } -static bool time_to_start_next(struct Curl_cfilter *cf, - struct Curl_easy *data, - size_t idx, struct curltime now) +static bool time_to_start_baller2(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; timediff_t elapsed_ms; - size_t i; - if(idx >= ctx->baller_count) + if(ctx->baller_count < 2) return FALSE; - if(cf_hc_baller_has_started(&ctx->ballers[idx])) + else if(cf_hc_baller_has_started(&ctx->ballers[1])) return FALSE; - for(i = 0; i < idx; i++) { - if(!ctx->ballers[i].result) - break; - } - if(i == idx) { - CURL_TRC_CF(data, cf, "all previous attempts failed, starting %s", - ctx->ballers[idx].name); + else if(ctx->ballers[0].result) { + CURL_TRC_CF(data, cf, "%s baller failed, starting %s", + ctx->ballers[0].name, ctx->ballers[1].name); return TRUE; } - elapsed_ms = curlx_ptimediff_ms(&now, &ctx->started); + + elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started); if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { - CURL_TRC_CF(data, cf, "hard timeout of %" FMT_TIMEDIFF_T "ms reached, " - "starting %s", - ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name); + CURL_TRC_CF(data, cf, "%s inconclusive after %" FMT_TIMEDIFF_T ", " + "starting %s", ctx->ballers[0].name, + ctx->hard_eyeballs_timeout_ms, ctx->ballers[1].name); return TRUE; } - - if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) { - if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) { - CURL_TRC_CF(data, cf, "soft timeout of %" FMT_TIMEDIFF_T "ms reached, " - "%s has not seen any data, starting %s", - ctx->soft_eyeballs_timeout_ms, - ctx->ballers[idx - 1].name, ctx->ballers[idx].name); + else if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) { + if(cf_hc_baller_reply_ms(&ctx->ballers[0], data) < 0) { + CURL_TRC_CF(data, cf, "%s has not seen any data after %" + FMT_TIMEDIFF_T "ms, starting %s", + ctx->ballers[0].name, ctx->soft_eyeballs_timeout_ms, + ctx->ballers[1].name); return TRUE; } - /* set the effective hard timeout again */ - Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms, - EXPIRE_ALPN_EYEBALLS); } return FALSE; } -static bool cf_https_alpns_contain(enum alpnid id, - enum alpnid *list, size_t len) -{ - size_t i; - for(i = 0; i < len; ++i) { - if(id == list[i]) - return TRUE; - } - return FALSE; -} - -static CURLcode cf_hc_resolv(struct Curl_cfilter *cf, - struct Curl_easy *data) +static bool cf_hc_may_h3(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; - enum alpnid alpn_ids[2]; - size_t alpn_count = 0, i; - CURLcode result = CURLE_OK; + if(!ctx->checked_h3) { + ctx->check_h3_result = + Curl_conn_may_http3(data, cf->conn, ctx->def_transport); + ctx->checked_h3 = TRUE; + } + return !ctx->check_h3_result; +} - if(cf->conn->bits.tls_enable_alpn) { +static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + enum alpnid not_this_one) +{ #ifdef USE_HTTPSRR - /* Is there an HTTPSRR use its ALPNs here. - * We are here after having selected a connection to a host+port and - * can no longer change that. Any HTTPSRR advice for other hosts and ports - * we need to ignore. */ - const struct Curl_https_rrinfo *rr; + /* Is there an HTTPSRR use its ALPNs here. + * We are here after having selected a connection to a host+port and + * can no longer change that. Any HTTPSRR advice for other hosts and ports + * we need to ignore. */ + const struct Curl_https_rrinfo *rr; + size_t i; - /* Do we have HTTPS-RR information? */ - rr = Curl_conn_dns_get_https(data, cf->sockindex); + /* Do we have HTTPS-RR information? */ + rr = Curl_conn_dns_get_https(data, cf->sockindex); - if(rr && !rr->no_def_alpn && /* ALPNs are defaults */ - (!rr->target || /* for same host */ - !rr->target[0] || - (rr->target[0] == '.' && - !rr->target[1])) && - (!rr->port_set || /* for same port */ - rr->port == cf->conn->remote_port)) { - for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) && - alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) { - enum alpnid alpn = (enum alpnid)rr->alpns[i]; - if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count)) - continue; - switch(alpn) { - case ALPN_h3: - if(Curl_conn_may_http3(data, cf->conn, ctx->def_transport)) - break; /* not possible */ - if(data->state.http_neg.allowed & CURL_HTTP_V3x) { - CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - case ALPN_h2: - if(data->state.http_neg.allowed & CURL_HTTP_V2x) { - CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - case ALPN_h1: - if(data->state.http_neg.allowed & CURL_HTTP_V1x) { - CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - default: /* ignore */ - break; + if(rr && !rr->no_def_alpn && /* ALPNs are defaults */ + (!rr->target || /* for same host */ + !rr->target[0] || + (rr->target[0] == '.' && + !rr->target[1])) && + (!rr->port_set || /* for same port */ + rr->port == cf->conn->remote_port)) { + for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) { + enum alpnid alpn_rr = (enum alpnid)rr->alpns[i]; + if(alpn_rr == not_this_one) /* don't want this one */ + continue; + switch(alpn_rr) { + case ALPN_h3: + if((data->state.http_neg.allowed & CURL_HTTP_V3x) && + cf_hc_may_h3(cf, data)) { + return alpn_rr; } + break; + case ALPN_h2: + if(data->state.http_neg.allowed & CURL_HTTP_V2x) { + return alpn_rr; + } + break; + case ALPN_h1: + if(data->state.http_neg.allowed & CURL_HTTP_V1x) { + return alpn_rr; + } + break; + default: /* ignore */ + break; } } + } +#else + (void)cf; + (void)data; + (void)not_this_one; #endif + return ALPN_none; +} - /* Add preferred HTTP version ALPN first */ - if(data->state.http_neg.preferred && - (alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.preferred & data->state.http_neg.allowed)) { - enum alpnid alpn_pref = ALPN_none; - switch(data->state.http_neg.preferred) { - case CURL_HTTP_V3x: - if(!Curl_conn_may_http3(data, cf->conn, ctx->def_transport)) - alpn_pref = ALPN_h3; - break; - case CURL_HTTP_V2x: - alpn_pref = ALPN_h2; - break; - case CURL_HTTP_V1x: - alpn_pref = ALPN_h1; - break; - default: - break; - } - if(alpn_pref && - !cf_https_alpns_contain(alpn_pref, alpn_ids, alpn_count)) { - alpn_ids[alpn_count++] = alpn_pref; - } - } - - if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V3x) && - !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) { - result = Curl_conn_may_http3(data, cf->conn, ctx->def_transport); - if(!result) { - CURL_TRC_CF(data, cf, "adding wanted h3"); - alpn_ids[alpn_count++] = ALPN_h3; - } - else if(data->state.http_neg.wanted == CURL_HTTP_V3x) - goto out; /* only h3 allowed, not possible, error out */ - } - if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V2x) && - !cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) { - CURL_TRC_CF(data, cf, "adding wanted h2"); - alpn_ids[alpn_count++] = ALPN_h2; - } - else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V1x) && - !cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) { - CURL_TRC_CF(data, cf, "adding wanted h1"); - alpn_ids[alpn_count++] = ALPN_h1; +static enum alpnid cf_hc_get_pref_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + enum alpnid not_this_one) +{ + if((data->state.http_neg.preferred & data->state.http_neg.allowed)) { + switch(data->state.http_neg.preferred) { + case CURL_HTTP_V3x: + if(cf_hc_may_h3(cf, data) && (ALPN_h3 != not_this_one)) + return ALPN_h3; + break; + case CURL_HTTP_V2x: + if(ALPN_h2 != not_this_one) + return ALPN_h2; + break; + case CURL_HTTP_V1x: + /* If we are trying h2 already, h1 is already used as fallback */ + if((ALPN_h1 != not_this_one) && (ALPN_h2 != not_this_one)) + return ALPN_h1; + break; + default: + break; } } + return ALPN_none; +} - if(!alpn_count) { - alpn_ids[0] = ALPN_none; - alpn_count = 1; +static enum alpnid cf_hc_get_first_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + http_majors choices, + enum alpnid not_this_one) +{ + if((ALPN_h3 != not_this_one) && (choices & CURL_HTTP_V3x) && + cf_hc_may_h3(cf, data)) { + return ALPN_h3; + } + if((ALPN_h2 != not_this_one) && (choices & CURL_HTTP_V2x)) { + return ALPN_h2; + } + /* If we are trying h2 already, h1 is already used as fallback */ + if((ALPN_h1 != not_this_one) && (ALPN_h2 != not_this_one) && + (choices & CURL_HTTP_V1x)) { + return ALPN_h1; + } + return ALPN_none; +} + +static CURLcode cf_hc_set_baller1(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + enum alpnid alpn1 = ALPN_none; + VERBOSE(const char *source = "HTTPS-RR"); + + DEBUGASSERT(cf->conn->bits.tls_enable_alpn); + + alpn1 = cf_hc_get_httpsrr_alpn(cf, data, ALPN_none); + if(alpn1 == ALPN_none) { + /* preference is configured and allowed, can we use it? */ + VERBOSE(source = "preferred version"); + alpn1 = cf_hc_get_pref_alpn(cf, data, ALPN_none); + } + if(alpn1 == ALPN_none) { + VERBOSE(source = "wanted versions"); + alpn1 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.wanted, + ALPN_none); + } + if(alpn1 == ALPN_none) { + VERBOSE(source = "allowed versions"); + alpn1 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.allowed, + ALPN_none); } - /* Install ballers for the ALPNs we selected. */ - DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers)); - if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) { - failf(data, "https-connect filter create with unsupported %zu ALPN ids", - alpn_count); - result = CURLE_FAILED_INIT; - goto out; + if(alpn1 == ALPN_none) { + /* None of the wanted/allowed HTTP versions could be chosen */ + if(ctx->check_h3_result) { + CURL_TRC_CF(data, cf, "unable to use HTTP/3"); + return ctx->check_h3_result; + } + CURL_TRC_CF(data, cf, "unable to select HTTP version"); + return CURLE_FAILED_INIT; } - for(i = 0; i < alpn_count; ++i) - cf_hc_baller_assign(&ctx->ballers[i], alpn_ids[i], ctx->def_transport); - for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i) - ctx->ballers[i].alpn_id = ALPN_none; - ctx->baller_count = alpn_count; + cf_hc_baller_assign(&ctx->ballers[0], alpn1, ctx->def_transport); + ctx->baller_count = 1; + CURL_TRC_CF(data, cf, "1st attempt uses %s from %s", + ctx->ballers[0].name, source); + return CURLE_OK; +} -out: - return result; +static void cf_hc_set_baller2(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + enum alpnid alpn2 = ALPN_none, alpn1 = ctx->ballers[0].alpn_id; + VERBOSE(const char *source = "HTTPS-RR"); + + if(ctx->ballers_complete) + return; /* already done */ + if(!ctx->httpsrr_resolved) + return; /* HTTPS-RR pending */ + + alpn2 = cf_hc_get_httpsrr_alpn(cf, data, alpn1); + if(alpn2 == ALPN_none) { + /* preference is configured and allowed, can we use it? */ + VERBOSE(source = "preferred version"); + alpn2 = cf_hc_get_pref_alpn(cf, data, alpn1); + } + if(alpn2 == ALPN_none) { + VERBOSE(source = "wanted versions"); + alpn2 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.wanted, + alpn1); + } + + if(alpn2 != ALPN_none) { + cf_hc_baller_assign(&ctx->ballers[1], alpn2, ctx->def_transport); + ctx->baller_count = 2; + CURL_TRC_CF(data, cf, "2nd attempt uses %s from %s", + ctx->ballers[1].name, source); + } + ctx->ballers_complete = TRUE; } static CURLcode cf_hc_connect(struct Curl_cfilter *cf, @@ -454,7 +478,6 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, { struct cf_hc_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - size_t i, failed_ballers; if(cf->connected) { *done = TRUE; @@ -462,35 +485,41 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } *done = FALSE; + + if(!ctx->httpsrr_resolved) + ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(data, cf->sockindex); + switch(ctx->state) { case CF_HC_RESOLV: - result = cf_hc_resolv(cf, data); - if(result) { - if(result == CURLE_AGAIN) - result = CURLE_OK; - goto out; - } - DEBUGASSERT(ctx->baller_count); + /* Without any addressinfo, delay the start of balling. */ + if(!Curl_conn_dns_has_any_ai(data, cf->sockindex)) + return CURLE_OK; ctx->state = CF_HC_INIT; FALLTHROUGH(); case CF_HC_INIT: DEBUGASSERT(!cf->next); - for(i = 0; i < ctx->baller_count; i++) - DEBUGASSERT(!ctx->ballers[i].cf); CURL_TRC_CF(data, cf, "connect, init"); + result = cf_hc_set_baller1(cf, data); + if(result) { + ctx->result = result; + ctx->state = CF_HC_FAILURE; + goto out; + } + cf_hc_set_baller2(cf, data); ctx->started = *Curl_pgrs_now(data); - cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport); - if(ctx->baller_count > 1) { + cf_hc_baller_init(&ctx->ballers[0], cf, data); + if((ctx->baller_count > 1) || !ctx->ballers_complete) { Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); - CURL_TRC_CF(data, cf, "set next attempt to start in %" FMT_TIMEDIFF_T - "ms", ctx->soft_eyeballs_timeout_ms); } ctx->state = CF_HC_CONNECT; FALLTHROUGH(); case CF_HC_CONNECT: - if(cf_hc_baller_is_active(&ctx->ballers[0])) { + if(!ctx->ballers_complete) + cf_hc_set_baller2(cf, data); + + if(cf_hc_baller_is_connecting(&ctx->ballers[0])) { result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done); if(!result && *done) { result = baller_connected(cf, data, &ctx->ballers[0]); @@ -498,12 +527,11 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } } - if(time_to_start_next(cf, data, 1, *Curl_pgrs_now(data))) { - cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport); + if(time_to_start_baller2(cf, data)) { + cf_hc_baller_init(&ctx->ballers[1], cf, data); } - if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) { - CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name); + if(cf_hc_baller_is_connecting(&ctx->ballers[1])) { result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done); if(!result && *done) { result = baller_connected(cf, data, &ctx->ballers[1]); @@ -511,21 +539,12 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } } - failed_ballers = 0; - for(i = 0; i < ctx->baller_count; i++) { - if(ctx->ballers[i].result) - ++failed_ballers; - } - - if(failed_ballers == ctx->baller_count) { + if(ctx->ballers[0].result && + (ctx->ballers[1].result || + (ctx->ballers_complete && (ctx->baller_count < 2)))) { /* all have failed. we give up */ CURL_TRC_CF(data, cf, "connect, all attempts failed"); - for(i = 0; i < ctx->baller_count; i++) { - if(ctx->ballers[i].result) { - result = ctx->ballers[i].result; - break; - } - } + ctx->result = result = ctx->ballers[0].result; ctx->state = CF_HC_FAILURE; goto out; } @@ -569,7 +588,7 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, for(i = 0; i < ctx->baller_count; i++) { struct cf_hc_baller *b = &ctx->ballers[i]; bool bdone = FALSE; - if(!cf_hc_baller_is_active(b) || b->shutdown) + if(!cf_hc_baller_is_connecting(b) || b->shutdown) continue; b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone); if(b->result || bdone) @@ -602,7 +621,7 @@ static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf, for(i = 0; (i < ctx->baller_count) && !result; i++) { struct cf_hc_baller *b = &ctx->ballers[i]; - if(!cf_hc_baller_is_active(b)) + if(!cf_hc_baller_is_connecting(b)) continue; result = Curl_conn_cf_adjust_pollset(b->cf, data, ps); } @@ -705,7 +724,7 @@ out: static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) { CURL_TRC_CF(data, cf, "close"); - cf_hc_ctx_reset(data, cf->ctx); + cf_hc_ctx_close(data, cf->ctx); cf->connected = FALSE; if(cf->next) { diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 9713c9d506..9042626ff7 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -725,7 +725,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, unsigned char transport) { if(transport == TRNSPRT_UNIX) { - /* cannot do QUIC over a Unix domain socket */ + failf(data, "HTTP/3 cannot be used over UNIX domain sockets"); return CURLE_QUIC_CONNECT_ERROR; } if(!(conn->scheme->flags & PROTOPT_SSL)) {