cf-https-connect.c (21653B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25 #include "curl_setup.h" 26 27 #if !defined(CURL_DISABLE_HTTP) 28 29 #include "urldata.h" 30 #include <curl/curl.h> 31 #include "curl_trc.h" 32 #include "cfilters.h" 33 #include "connect.h" 34 #include "hostip.h" 35 #include "multiif.h" 36 #include "cf-https-connect.h" 37 #include "http2.h" 38 #include "vquic/vquic.h" 39 40 /* The last 3 #include files should be in this order */ 41 #include "curl_printf.h" 42 #include "curl_memory.h" 43 #include "memdebug.h" 44 45 typedef enum { 46 CF_HC_INIT, 47 CF_HC_CONNECT, 48 CF_HC_SUCCESS, 49 CF_HC_FAILURE 50 } cf_hc_state; 51 52 struct cf_hc_baller { 53 const char *name; 54 struct Curl_cfilter *cf; 55 CURLcode result; 56 struct curltime started; 57 int reply_ms; 58 unsigned char transport; 59 enum alpnid alpn_id; 60 BIT(shutdown); 61 }; 62 63 static void cf_hc_baller_reset(struct cf_hc_baller *b, 64 struct Curl_easy *data) 65 { 66 if(b->cf) { 67 Curl_conn_cf_close(b->cf, data); 68 Curl_conn_cf_discard_chain(&b->cf, data); 69 b->cf = NULL; 70 } 71 b->result = CURLE_OK; 72 b->reply_ms = -1; 73 } 74 75 static bool cf_hc_baller_is_active(struct cf_hc_baller *b) 76 { 77 return b->cf && !b->result; 78 } 79 80 static bool cf_hc_baller_has_started(struct cf_hc_baller *b) 81 { 82 return !!b->cf; 83 } 84 85 static int cf_hc_baller_reply_ms(struct cf_hc_baller *b, 86 struct Curl_easy *data) 87 { 88 if(b->cf && (b->reply_ms < 0)) 89 b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS, 90 &b->reply_ms, NULL); 91 return b->reply_ms; 92 } 93 94 static bool cf_hc_baller_data_pending(struct cf_hc_baller *b, 95 const struct Curl_easy *data) 96 { 97 return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data); 98 } 99 100 static bool cf_hc_baller_needs_flush(struct cf_hc_baller *b, 101 struct Curl_easy *data) 102 { 103 return b->cf && !b->result && Curl_conn_cf_needs_flush(b->cf, data); 104 } 105 106 static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b, 107 struct Curl_easy *data, 108 int event, int arg1, void *arg2) 109 { 110 if(b->cf && !b->result) 111 return Curl_conn_cf_cntrl(b->cf, data, FALSE, event, arg1, arg2); 112 return CURLE_OK; 113 } 114 115 struct cf_hc_ctx { 116 cf_hc_state state; 117 struct curltime started; /* when connect started */ 118 CURLcode result; /* overall result */ 119 struct cf_hc_baller ballers[2]; 120 size_t baller_count; 121 timediff_t soft_eyeballs_timeout_ms; 122 timediff_t hard_eyeballs_timeout_ms; 123 }; 124 125 static void cf_hc_baller_assign(struct cf_hc_baller *b, 126 enum alpnid alpn_id, 127 unsigned char def_transport) 128 { 129 b->alpn_id = alpn_id; 130 b->transport = def_transport; 131 switch(b->alpn_id) { 132 case ALPN_h3: 133 b->name = "h3"; 134 b->transport = TRNSPRT_QUIC; 135 break; 136 case ALPN_h2: 137 b->name = "h2"; 138 break; 139 case ALPN_h1: 140 b->name = "h1"; 141 break; 142 default: 143 b->result = CURLE_FAILED_INIT; 144 break; 145 } 146 } 147 148 static void cf_hc_baller_init(struct cf_hc_baller *b, 149 struct Curl_cfilter *cf, 150 struct Curl_easy *data, 151 int transport) 152 { 153 struct Curl_cfilter *save = cf->next; 154 155 cf->next = NULL; 156 b->started = curlx_now(); 157 switch(b->alpn_id) { 158 case ALPN_h3: 159 transport = TRNSPRT_QUIC; 160 break; 161 default: 162 break; 163 } 164 165 if(!b->result) 166 b->result = Curl_cf_setup_insert_after(cf, data, transport, 167 CURL_CF_SSL_ENABLE); 168 b->cf = cf->next; 169 cf->next = save; 170 } 171 172 static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b, 173 struct Curl_cfilter *cf, 174 struct Curl_easy *data, 175 bool *done) 176 { 177 struct Curl_cfilter *save = cf->next; 178 179 cf->next = b->cf; 180 b->result = Curl_conn_cf_connect(cf->next, data, done); 181 b->cf = cf->next; /* it might mutate */ 182 cf->next = save; 183 return b->result; 184 } 185 186 static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data) 187 { 188 struct cf_hc_ctx *ctx = cf->ctx; 189 size_t i; 190 191 if(ctx) { 192 for(i = 0; i < ctx->baller_count; ++i) 193 cf_hc_baller_reset(&ctx->ballers[i], data); 194 ctx->state = CF_HC_INIT; 195 ctx->result = CURLE_OK; 196 ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; 197 ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4; 198 } 199 } 200 201 static CURLcode baller_connected(struct Curl_cfilter *cf, 202 struct Curl_easy *data, 203 struct cf_hc_baller *winner) 204 { 205 struct cf_hc_ctx *ctx = cf->ctx; 206 CURLcode result = CURLE_OK; 207 int reply_ms; 208 size_t i; 209 210 DEBUGASSERT(winner->cf); 211 for(i = 0; i < ctx->baller_count; ++i) 212 if(winner != &ctx->ballers[i]) 213 cf_hc_baller_reset(&ctx->ballers[i], data); 214 215 reply_ms = cf_hc_baller_reply_ms(winner, data); 216 if(reply_ms >= 0) 217 CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", 218 winner->name, (int)curlx_timediff(curlx_now(), 219 winner->started), reply_ms); 220 else 221 CURL_TRC_CF(data, cf, "deferred handshake %s: %dms", 222 winner->name, (int)curlx_timediff(curlx_now(), 223 winner->started)); 224 225 /* install the winning filter below this one. */ 226 cf->next = winner->cf; 227 winner->cf = NULL; 228 229 switch(cf->conn->alpn) { 230 case CURL_HTTP_VERSION_3: 231 break; 232 case CURL_HTTP_VERSION_2: 233 #ifdef USE_NGHTTP2 234 /* Using nghttp2, we add the filter "below" us, so when the conn 235 * closes, we tear it down for a fresh reconnect */ 236 result = Curl_http2_switch_at(cf, data); 237 if(result) { 238 ctx->state = CF_HC_FAILURE; 239 ctx->result = result; 240 return result; 241 } 242 #endif 243 break; 244 default: 245 break; 246 } 247 ctx->state = CF_HC_SUCCESS; 248 cf->connected = TRUE; 249 return result; 250 } 251 252 253 static bool time_to_start_next(struct Curl_cfilter *cf, 254 struct Curl_easy *data, 255 size_t idx, struct curltime now) 256 { 257 struct cf_hc_ctx *ctx = cf->ctx; 258 timediff_t elapsed_ms; 259 size_t i; 260 261 if(idx >= ctx->baller_count) 262 return FALSE; 263 if(cf_hc_baller_has_started(&ctx->ballers[idx])) 264 return FALSE; 265 for(i = 0; i < idx; i++) { 266 if(!ctx->ballers[i].result) 267 break; 268 } 269 if(i == idx) { 270 CURL_TRC_CF(data, cf, "all previous attempts failed, starting %s", 271 ctx->ballers[idx].name); 272 return TRUE; 273 } 274 elapsed_ms = curlx_timediff(now, ctx->started); 275 if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { 276 CURL_TRC_CF(data, cf, "hard timeout of %" FMT_TIMEDIFF_T "ms reached, " 277 "starting %s", 278 ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name); 279 return TRUE; 280 } 281 282 if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) { 283 if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) { 284 CURL_TRC_CF(data, cf, "soft timeout of %" FMT_TIMEDIFF_T "ms reached, " 285 "%s has not seen any data, starting %s", 286 ctx->soft_eyeballs_timeout_ms, 287 ctx->ballers[idx - 1].name, ctx->ballers[idx].name); 288 return TRUE; 289 } 290 /* set the effective hard timeout again */ 291 Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms, 292 EXPIRE_ALPN_EYEBALLS); 293 } 294 return FALSE; 295 } 296 297 static CURLcode cf_hc_connect(struct Curl_cfilter *cf, 298 struct Curl_easy *data, 299 bool *done) 300 { 301 struct cf_hc_ctx *ctx = cf->ctx; 302 struct curltime now; 303 CURLcode result = CURLE_OK; 304 size_t i, failed_ballers; 305 306 if(cf->connected) { 307 *done = TRUE; 308 return CURLE_OK; 309 } 310 311 *done = FALSE; 312 now = curlx_now(); 313 switch(ctx->state) { 314 case CF_HC_INIT: 315 DEBUGASSERT(!cf->next); 316 for(i = 0; i < ctx->baller_count; i++) 317 DEBUGASSERT(!ctx->ballers[i].cf); 318 CURL_TRC_CF(data, cf, "connect, init"); 319 ctx->started = now; 320 cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport); 321 if(ctx->baller_count > 1) { 322 Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); 323 CURL_TRC_CF(data, cf, "set next attempt to start in %" FMT_TIMEDIFF_T 324 "ms", ctx->soft_eyeballs_timeout_ms); 325 } 326 ctx->state = CF_HC_CONNECT; 327 FALLTHROUGH(); 328 329 case CF_HC_CONNECT: 330 if(cf_hc_baller_is_active(&ctx->ballers[0])) { 331 result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done); 332 if(!result && *done) { 333 result = baller_connected(cf, data, &ctx->ballers[0]); 334 goto out; 335 } 336 } 337 338 if(time_to_start_next(cf, data, 1, now)) { 339 cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport); 340 } 341 342 if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) { 343 CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name); 344 result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done); 345 if(!result && *done) { 346 result = baller_connected(cf, data, &ctx->ballers[1]); 347 goto out; 348 } 349 } 350 351 failed_ballers = 0; 352 for(i = 0; i < ctx->baller_count; i++) { 353 if(ctx->ballers[i].result) 354 ++failed_ballers; 355 } 356 357 if(failed_ballers == ctx->baller_count) { 358 /* all have failed. we give up */ 359 CURL_TRC_CF(data, cf, "connect, all attempts failed"); 360 for(i = 0; i < ctx->baller_count; i++) { 361 if(ctx->ballers[i].result) { 362 result = ctx->ballers[i].result; 363 break; 364 } 365 } 366 ctx->state = CF_HC_FAILURE; 367 goto out; 368 } 369 result = CURLE_OK; 370 *done = FALSE; 371 break; 372 373 case CF_HC_FAILURE: 374 result = ctx->result; 375 cf->connected = FALSE; 376 *done = FALSE; 377 break; 378 379 case CF_HC_SUCCESS: 380 result = CURLE_OK; 381 cf->connected = TRUE; 382 *done = TRUE; 383 break; 384 } 385 386 out: 387 CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); 388 return result; 389 } 390 391 static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, 392 struct Curl_easy *data, bool *done) 393 { 394 struct cf_hc_ctx *ctx = cf->ctx; 395 size_t i; 396 CURLcode result = CURLE_OK; 397 398 DEBUGASSERT(data); 399 if(cf->connected) { 400 *done = TRUE; 401 return CURLE_OK; 402 } 403 404 /* shutdown all ballers that have not done so already. If one fails, 405 * continue shutting down others until all are shutdown. */ 406 for(i = 0; i < ctx->baller_count; i++) { 407 struct cf_hc_baller *b = &ctx->ballers[i]; 408 bool bdone = FALSE; 409 if(!cf_hc_baller_is_active(b) || b->shutdown) 410 continue; 411 b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone); 412 if(b->result || bdone) 413 b->shutdown = TRUE; /* treat a failed shutdown as done */ 414 } 415 416 *done = TRUE; 417 for(i = 0; i < ctx->baller_count; i++) { 418 if(!ctx->ballers[i].shutdown) 419 *done = FALSE; 420 } 421 if(*done) { 422 for(i = 0; i < ctx->baller_count; i++) { 423 if(ctx->ballers[i].result) 424 result = ctx->ballers[i].result; 425 } 426 } 427 CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); 428 return result; 429 } 430 431 static void cf_hc_adjust_pollset(struct Curl_cfilter *cf, 432 struct Curl_easy *data, 433 struct easy_pollset *ps) 434 { 435 if(!cf->connected) { 436 struct cf_hc_ctx *ctx = cf->ctx; 437 size_t i; 438 439 for(i = 0; i < ctx->baller_count; i++) { 440 struct cf_hc_baller *b = &ctx->ballers[i]; 441 if(!cf_hc_baller_is_active(b)) 442 continue; 443 Curl_conn_cf_adjust_pollset(b->cf, data, ps); 444 } 445 CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); 446 } 447 } 448 449 static bool cf_hc_data_pending(struct Curl_cfilter *cf, 450 const struct Curl_easy *data) 451 { 452 struct cf_hc_ctx *ctx = cf->ctx; 453 size_t i; 454 455 if(cf->connected) 456 return cf->next->cft->has_data_pending(cf->next, data); 457 458 for(i = 0; i < ctx->baller_count; i++) 459 if(cf_hc_baller_data_pending(&ctx->ballers[i], data)) 460 return TRUE; 461 return FALSE; 462 } 463 464 static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf, 465 struct Curl_easy *data, 466 int query) 467 { 468 struct cf_hc_ctx *ctx = cf->ctx; 469 struct curltime t, tmax; 470 size_t i; 471 472 memset(&tmax, 0, sizeof(tmax)); 473 for(i = 0; i < ctx->baller_count; i++) { 474 struct Curl_cfilter *cfb = ctx->ballers[i].cf; 475 memset(&t, 0, sizeof(t)); 476 if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) { 477 if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0) 478 tmax = t; 479 } 480 } 481 return tmax; 482 } 483 484 static CURLcode cf_hc_query(struct Curl_cfilter *cf, 485 struct Curl_easy *data, 486 int query, int *pres1, void *pres2) 487 { 488 struct cf_hc_ctx *ctx = cf->ctx; 489 size_t i; 490 491 if(!cf->connected) { 492 switch(query) { 493 case CF_QUERY_TIMER_CONNECT: { 494 struct curltime *when = pres2; 495 *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); 496 return CURLE_OK; 497 } 498 case CF_QUERY_TIMER_APPCONNECT: { 499 struct curltime *when = pres2; 500 *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); 501 return CURLE_OK; 502 } 503 case CF_QUERY_NEED_FLUSH: { 504 for(i = 0; i < ctx->baller_count; i++) 505 if(cf_hc_baller_needs_flush(&ctx->ballers[i], data)) { 506 *pres1 = TRUE; 507 return CURLE_OK; 508 } 509 break; 510 } 511 default: 512 break; 513 } 514 } 515 return cf->next ? 516 cf->next->cft->query(cf->next, data, query, pres1, pres2) : 517 CURLE_UNKNOWN_OPTION; 518 } 519 520 static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf, 521 struct Curl_easy *data, 522 int event, int arg1, void *arg2) 523 { 524 struct cf_hc_ctx *ctx = cf->ctx; 525 CURLcode result = CURLE_OK; 526 size_t i; 527 528 if(!cf->connected) { 529 for(i = 0; i < ctx->baller_count; i++) { 530 result = cf_hc_baller_cntrl(&ctx->ballers[i], data, event, arg1, arg2); 531 if(result && (result != CURLE_AGAIN)) 532 goto out; 533 } 534 result = CURLE_OK; 535 } 536 out: 537 return result; 538 } 539 540 static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) 541 { 542 CURL_TRC_CF(data, cf, "close"); 543 cf_hc_reset(cf, data); 544 cf->connected = FALSE; 545 546 if(cf->next) { 547 cf->next->cft->do_close(cf->next, data); 548 Curl_conn_cf_discard_chain(&cf->next, data); 549 } 550 } 551 552 static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 553 { 554 struct cf_hc_ctx *ctx = cf->ctx; 555 556 (void)data; 557 CURL_TRC_CF(data, cf, "destroy"); 558 cf_hc_reset(cf, data); 559 Curl_safefree(ctx); 560 } 561 562 struct Curl_cftype Curl_cft_http_connect = { 563 "HTTPS-CONNECT", 564 0, 565 CURL_LOG_LVL_NONE, 566 cf_hc_destroy, 567 cf_hc_connect, 568 cf_hc_close, 569 cf_hc_shutdown, 570 cf_hc_adjust_pollset, 571 cf_hc_data_pending, 572 Curl_cf_def_send, 573 Curl_cf_def_recv, 574 cf_hc_cntrl, 575 Curl_cf_def_conn_is_alive, 576 Curl_cf_def_conn_keep_alive, 577 cf_hc_query, 578 }; 579 580 static CURLcode cf_hc_create(struct Curl_cfilter **pcf, 581 struct Curl_easy *data, 582 enum alpnid *alpnids, size_t alpn_count, 583 unsigned char def_transport) 584 { 585 struct Curl_cfilter *cf = NULL; 586 struct cf_hc_ctx *ctx; 587 CURLcode result = CURLE_OK; 588 size_t i; 589 590 DEBUGASSERT(alpnids); 591 DEBUGASSERT(alpn_count); 592 DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers)); 593 if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) { 594 failf(data, "https-connect filter create with unsupported %zu ALPN ids", 595 alpn_count); 596 return CURLE_FAILED_INIT; 597 } 598 599 ctx = calloc(1, sizeof(*ctx)); 600 if(!ctx) { 601 result = CURLE_OUT_OF_MEMORY; 602 goto out; 603 } 604 for(i = 0; i < alpn_count; ++i) 605 cf_hc_baller_assign(&ctx->ballers[i], alpnids[i], def_transport); 606 for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i) 607 ctx->ballers[i].alpn_id = ALPN_none; 608 ctx->baller_count = alpn_count; 609 610 result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx); 611 if(result) 612 goto out; 613 ctx = NULL; 614 cf_hc_reset(cf, data); 615 616 out: 617 *pcf = result ? NULL : cf; 618 free(ctx); 619 return result; 620 } 621 622 static CURLcode cf_http_connect_add(struct Curl_easy *data, 623 struct connectdata *conn, 624 int sockindex, 625 enum alpnid *alpn_ids, size_t alpn_count, 626 unsigned char def_transport) 627 { 628 struct Curl_cfilter *cf; 629 CURLcode result = CURLE_OK; 630 631 DEBUGASSERT(data); 632 result = cf_hc_create(&cf, data, alpn_ids, alpn_count, def_transport); 633 if(result) 634 goto out; 635 Curl_conn_cf_add(data, conn, sockindex, cf); 636 out: 637 return result; 638 } 639 640 static bool cf_https_alpns_contain(enum alpnid id, 641 enum alpnid *list, size_t len) 642 { 643 size_t i; 644 for(i = 0; i < len; ++i) { 645 if(id == list[i]) 646 return TRUE; 647 } 648 return FALSE; 649 } 650 651 CURLcode Curl_cf_https_setup(struct Curl_easy *data, 652 struct connectdata *conn, 653 int sockindex) 654 { 655 enum alpnid alpn_ids[2]; 656 size_t alpn_count = 0; 657 CURLcode result = CURLE_OK; 658 struct Curl_cfilter cf_fake, *cf = NULL; 659 660 (void)sockindex; 661 /* we want to log for the filter before we create it, fake it. */ 662 memset(&cf_fake, 0, sizeof(cf_fake)); 663 cf_fake.cft = &Curl_cft_http_connect; 664 cf = &cf_fake; 665 666 if(conn->bits.tls_enable_alpn) { 667 #ifdef USE_HTTPSRR 668 /* Is there an HTTPSRR use its ALPNs here. 669 * We are here after having selected a connection to a host+port and 670 * can no longer change that. Any HTTPSRR advice for other hosts and ports 671 * we need to ignore. */ 672 struct Curl_dns_entry *dns = data->state.dns[sockindex]; 673 struct Curl_https_rrinfo *rr = dns ? dns->hinfo : NULL; 674 if(rr && !rr->no_def_alpn && /* ALPNs are defaults */ 675 (!rr->target || /* for same host */ 676 !rr->target[0] || 677 (rr->target[0] == '.' && 678 !rr->target[1])) && 679 (rr->port < 0 || /* for same port */ 680 rr->port == conn->remote_port)) { 681 size_t i; 682 for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) && 683 alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) { 684 enum alpnid alpn = rr->alpns[i]; 685 if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count)) 686 continue; 687 switch(alpn) { 688 case ALPN_h3: 689 if(Curl_conn_may_http3(data, conn, conn->transport_wanted)) 690 break; /* not possible */ 691 if(data->state.http_neg.allowed & CURL_HTTP_V3x) { 692 CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR"); 693 alpn_ids[alpn_count++] = alpn; 694 } 695 break; 696 case ALPN_h2: 697 if(data->state.http_neg.allowed & CURL_HTTP_V2x) { 698 CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR"); 699 alpn_ids[alpn_count++] = alpn; 700 } 701 break; 702 case ALPN_h1: 703 if(data->state.http_neg.allowed & CURL_HTTP_V1x) { 704 CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR"); 705 alpn_ids[alpn_count++] = alpn; 706 } 707 break; 708 default: /* ignore */ 709 break; 710 } 711 } 712 } 713 #endif 714 715 if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && 716 (data->state.http_neg.wanted & CURL_HTTP_V3x) && 717 !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) { 718 result = Curl_conn_may_http3(data, conn, conn->transport_wanted); 719 if(!result) { 720 CURL_TRC_CF(data, cf, "adding wanted h3"); 721 alpn_ids[alpn_count++] = ALPN_h3; 722 } 723 else if(data->state.http_neg.wanted == CURL_HTTP_V3x) 724 goto out; /* only h3 allowed, not possible, error out */ 725 } 726 if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && 727 (data->state.http_neg.wanted & CURL_HTTP_V2x) && 728 !cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) { 729 CURL_TRC_CF(data, cf, "adding wanted h2"); 730 alpn_ids[alpn_count++] = ALPN_h2; 731 } 732 else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && 733 (data->state.http_neg.wanted & CURL_HTTP_V1x) && 734 !cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) { 735 CURL_TRC_CF(data, cf, "adding wanted h1"); 736 alpn_ids[alpn_count++] = ALPN_h1; 737 } 738 } 739 740 /* If we identified ALPNs to use, install our filter. Otherwise, 741 * install nothing, so our call will use a default connect setup. */ 742 if(alpn_count) { 743 result = cf_http_connect_add(data, conn, sockindex, 744 alpn_ids, alpn_count, 745 conn->transport_wanted); 746 } 747 748 out: 749 return result; 750 } 751 752 #endif /* !defined(CURL_DISABLE_HTTP) */