quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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) */