cf-https-connect.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) Daniel Stenberg, <[email protected]>, 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. #include "curl_setup.h"
  25. #if !defined(CURL_DISABLE_HTTP)
  26. #include "urldata.h"
  27. #include <curl/curl.h>
  28. #include "curl_trc.h"
  29. #include "cfilters.h"
  30. #include "connect.h"
  31. #include "hostip.h"
  32. #include "multiif.h"
  33. #include "cf-https-connect.h"
  34. #include "http2.h"
  35. #include "vquic/vquic.h"
  36. /* The last 3 #include files should be in this order */
  37. #include "curl_printf.h"
  38. #include "curl_memory.h"
  39. #include "memdebug.h"
  40. typedef enum {
  41. CF_HC_INIT,
  42. CF_HC_CONNECT,
  43. CF_HC_SUCCESS,
  44. CF_HC_FAILURE
  45. } cf_hc_state;
  46. struct cf_hc_baller {
  47. const char *name;
  48. struct Curl_cfilter *cf;
  49. CURLcode result;
  50. struct curltime started;
  51. int reply_ms;
  52. enum alpnid alpn_id;
  53. BIT(shutdown);
  54. };
  55. static void cf_hc_baller_reset(struct cf_hc_baller *b,
  56. struct Curl_easy *data)
  57. {
  58. if(b->cf) {
  59. Curl_conn_cf_close(b->cf, data);
  60. Curl_conn_cf_discard_chain(&b->cf, data);
  61. b->cf = NULL;
  62. }
  63. b->result = CURLE_OK;
  64. b->reply_ms = -1;
  65. }
  66. static bool cf_hc_baller_is_active(struct cf_hc_baller *b)
  67. {
  68. return b->cf && !b->result;
  69. }
  70. static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
  71. {
  72. return !!b->cf;
  73. }
  74. static int cf_hc_baller_reply_ms(struct cf_hc_baller *b,
  75. struct Curl_easy *data)
  76. {
  77. if(b->cf && (b->reply_ms < 0))
  78. b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS,
  79. &b->reply_ms, NULL);
  80. return b->reply_ms;
  81. }
  82. static bool cf_hc_baller_data_pending(struct cf_hc_baller *b,
  83. const struct Curl_easy *data)
  84. {
  85. return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data);
  86. }
  87. static bool cf_hc_baller_needs_flush(struct cf_hc_baller *b,
  88. struct Curl_easy *data)
  89. {
  90. return b->cf && !b->result && Curl_conn_cf_needs_flush(b->cf, data);
  91. }
  92. static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b,
  93. struct Curl_easy *data,
  94. int event, int arg1, void *arg2)
  95. {
  96. if(b->cf && !b->result)
  97. return Curl_conn_cf_cntrl(b->cf, data, FALSE, event, arg1, arg2);
  98. return CURLE_OK;
  99. }
  100. struct cf_hc_ctx {
  101. cf_hc_state state;
  102. const struct Curl_dns_entry *remotehost;
  103. struct curltime started; /* when connect started */
  104. CURLcode result; /* overall result */
  105. struct cf_hc_baller ballers[2];
  106. size_t baller_count;
  107. unsigned int soft_eyeballs_timeout_ms;
  108. unsigned int hard_eyeballs_timeout_ms;
  109. };
  110. static void cf_hc_baller_assign(struct cf_hc_baller *b,
  111. enum alpnid alpn_id)
  112. {
  113. b->alpn_id = alpn_id;
  114. switch(b->alpn_id) {
  115. case ALPN_h3:
  116. b->name = "h3";
  117. break;
  118. case ALPN_h2:
  119. b->name = "h2";
  120. break;
  121. case ALPN_h1:
  122. b->name = "h1";
  123. break;
  124. default:
  125. b->result = CURLE_FAILED_INIT;
  126. break;
  127. }
  128. }
  129. static void cf_hc_baller_init(struct cf_hc_baller *b,
  130. struct Curl_cfilter *cf,
  131. struct Curl_easy *data,
  132. int transport)
  133. {
  134. struct cf_hc_ctx *ctx = cf->ctx;
  135. struct Curl_cfilter *save = cf->next;
  136. cf->next = NULL;
  137. b->started = Curl_now();
  138. switch(b->alpn_id) {
  139. case ALPN_h3:
  140. transport = TRNSPRT_QUIC;
  141. break;
  142. default:
  143. break;
  144. }
  145. if(!b->result)
  146. b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost,
  147. transport, CURL_CF_SSL_ENABLE);
  148. b->cf = cf->next;
  149. cf->next = save;
  150. }
  151. static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
  152. struct Curl_cfilter *cf,
  153. struct Curl_easy *data,
  154. bool *done)
  155. {
  156. struct Curl_cfilter *save = cf->next;
  157. cf->next = b->cf;
  158. b->result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
  159. b->cf = cf->next; /* it might mutate */
  160. cf->next = save;
  161. return b->result;
  162. }
  163. static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
  164. {
  165. struct cf_hc_ctx *ctx = cf->ctx;
  166. size_t i;
  167. if(ctx) {
  168. for(i = 0; i < ctx->baller_count; ++i)
  169. cf_hc_baller_reset(&ctx->ballers[i], data);
  170. ctx->state = CF_HC_INIT;
  171. ctx->result = CURLE_OK;
  172. ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
  173. ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2;
  174. }
  175. }
  176. static CURLcode baller_connected(struct Curl_cfilter *cf,
  177. struct Curl_easy *data,
  178. struct cf_hc_baller *winner)
  179. {
  180. struct cf_hc_ctx *ctx = cf->ctx;
  181. CURLcode result = CURLE_OK;
  182. int reply_ms;
  183. size_t i;
  184. DEBUGASSERT(winner->cf);
  185. for(i = 0; i < ctx->baller_count; ++i)
  186. if(winner != &ctx->ballers[i])
  187. cf_hc_baller_reset(&ctx->ballers[i], data);
  188. reply_ms = cf_hc_baller_reply_ms(winner, data);
  189. if(reply_ms >= 0)
  190. CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
  191. winner->name, (int)Curl_timediff(Curl_now(), winner->started),
  192. reply_ms);
  193. else
  194. CURL_TRC_CF(data, cf, "deferred handshake %s: %dms",
  195. winner->name, (int)Curl_timediff(Curl_now(), winner->started));
  196. cf->next = winner->cf;
  197. winner->cf = NULL;
  198. switch(cf->conn->alpn) {
  199. case CURL_HTTP_VERSION_3:
  200. break;
  201. case CURL_HTTP_VERSION_2:
  202. #ifdef USE_NGHTTP2
  203. /* Using nghttp2, we add the filter "below" us, so when the conn
  204. * closes, we tear it down for a fresh reconnect */
  205. result = Curl_http2_switch_at(cf, data);
  206. if(result) {
  207. ctx->state = CF_HC_FAILURE;
  208. ctx->result = result;
  209. return result;
  210. }
  211. #endif
  212. break;
  213. default:
  214. break;
  215. }
  216. ctx->state = CF_HC_SUCCESS;
  217. cf->connected = TRUE;
  218. return result;
  219. }
  220. static bool time_to_start_next(struct Curl_cfilter *cf,
  221. struct Curl_easy *data,
  222. size_t idx, struct curltime now)
  223. {
  224. struct cf_hc_ctx *ctx = cf->ctx;
  225. timediff_t elapsed_ms;
  226. size_t i;
  227. if(idx >= ctx->baller_count)
  228. return FALSE;
  229. if(cf_hc_baller_has_started(&ctx->ballers[idx]))
  230. return FALSE;
  231. for(i = 0; i < idx; i++) {
  232. if(!ctx->ballers[i].result)
  233. break;
  234. }
  235. if(i == idx) {
  236. CURL_TRC_CF(data, cf, "all previous ballers have failed, time to start "
  237. "baller %zu [%s]", idx, ctx->ballers[idx].name);
  238. return TRUE;
  239. }
  240. elapsed_ms = Curl_timediff(now, ctx->started);
  241. if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
  242. CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting %s",
  243. ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name);
  244. return TRUE;
  245. }
  246. if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) {
  247. if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) {
  248. CURL_TRC_CF(data, cf, "soft timeout of %dms reached, %s has not "
  249. "seen any data, starting %s",
  250. ctx->soft_eyeballs_timeout_ms,
  251. ctx->ballers[idx - 1].name, ctx->ballers[idx].name);
  252. return TRUE;
  253. }
  254. /* set the effective hard timeout again */
  255. Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms,
  256. EXPIRE_ALPN_EYEBALLS);
  257. }
  258. return FALSE;
  259. }
  260. static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
  261. struct Curl_easy *data,
  262. bool blocking, bool *done)
  263. {
  264. struct cf_hc_ctx *ctx = cf->ctx;
  265. struct curltime now;
  266. CURLcode result = CURLE_OK;
  267. size_t i, failed_ballers;
  268. (void)blocking;
  269. if(cf->connected) {
  270. *done = TRUE;
  271. return CURLE_OK;
  272. }
  273. *done = FALSE;
  274. now = Curl_now();
  275. switch(ctx->state) {
  276. case CF_HC_INIT:
  277. DEBUGASSERT(!cf->next);
  278. for(i = 0; i < ctx->baller_count; i++)
  279. DEBUGASSERT(!ctx->ballers[i].cf);
  280. CURL_TRC_CF(data, cf, "connect, init");
  281. ctx->started = now;
  282. cf_hc_baller_init(&ctx->ballers[0], cf, data, cf->conn->transport);
  283. if(ctx->baller_count > 1) {
  284. Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
  285. CURL_TRC_CF(data, cf, "set expire for starting next baller in %ums",
  286. ctx->soft_eyeballs_timeout_ms);
  287. }
  288. ctx->state = CF_HC_CONNECT;
  289. FALLTHROUGH();
  290. case CF_HC_CONNECT:
  291. if(cf_hc_baller_is_active(&ctx->ballers[0])) {
  292. result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done);
  293. if(!result && *done) {
  294. result = baller_connected(cf, data, &ctx->ballers[0]);
  295. goto out;
  296. }
  297. }
  298. if(time_to_start_next(cf, data, 1, now)) {
  299. cf_hc_baller_init(&ctx->ballers[1], cf, data, cf->conn->transport);
  300. }
  301. if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) {
  302. CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name);
  303. result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done);
  304. if(!result && *done) {
  305. result = baller_connected(cf, data, &ctx->ballers[1]);
  306. goto out;
  307. }
  308. }
  309. failed_ballers = 0;
  310. for(i = 0; i < ctx->baller_count; i++) {
  311. if(ctx->ballers[i].result)
  312. ++failed_ballers;
  313. }
  314. if(failed_ballers == ctx->baller_count) {
  315. /* all have failed. we give up */
  316. CURL_TRC_CF(data, cf, "connect, all failed");
  317. for(i = 0; i < ctx->baller_count; i++) {
  318. if(ctx->ballers[i].result) {
  319. result = ctx->ballers[i].result;
  320. break;
  321. }
  322. }
  323. ctx->state = CF_HC_FAILURE;
  324. goto out;
  325. }
  326. result = CURLE_OK;
  327. *done = FALSE;
  328. break;
  329. case CF_HC_FAILURE:
  330. result = ctx->result;
  331. cf->connected = FALSE;
  332. *done = FALSE;
  333. break;
  334. case CF_HC_SUCCESS:
  335. result = CURLE_OK;
  336. cf->connected = TRUE;
  337. *done = TRUE;
  338. break;
  339. }
  340. out:
  341. CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
  342. return result;
  343. }
  344. static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
  345. struct Curl_easy *data, bool *done)
  346. {
  347. struct cf_hc_ctx *ctx = cf->ctx;
  348. size_t i;
  349. CURLcode result = CURLE_OK;
  350. DEBUGASSERT(data);
  351. if(cf->connected) {
  352. *done = TRUE;
  353. return CURLE_OK;
  354. }
  355. /* shutdown all ballers that have not done so already. If one fails,
  356. * continue shutting down others until all are shutdown. */
  357. for(i = 0; i < ctx->baller_count; i++) {
  358. struct cf_hc_baller *b = &ctx->ballers[i];
  359. bool bdone = FALSE;
  360. if(!cf_hc_baller_is_active(b) || b->shutdown)
  361. continue;
  362. b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone);
  363. if(b->result || bdone)
  364. b->shutdown = TRUE; /* treat a failed shutdown as done */
  365. }
  366. *done = TRUE;
  367. for(i = 0; i < ctx->baller_count; i++) {
  368. if(!ctx->ballers[i].shutdown)
  369. *done = FALSE;
  370. }
  371. if(*done) {
  372. for(i = 0; i < ctx->baller_count; i++) {
  373. if(ctx->ballers[i].result)
  374. result = ctx->ballers[i].result;
  375. }
  376. }
  377. CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
  378. return result;
  379. }
  380. static void cf_hc_adjust_pollset(struct Curl_cfilter *cf,
  381. struct Curl_easy *data,
  382. struct easy_pollset *ps)
  383. {
  384. if(!cf->connected) {
  385. struct cf_hc_ctx *ctx = cf->ctx;
  386. size_t i;
  387. for(i = 0; i < ctx->baller_count; i++) {
  388. struct cf_hc_baller *b = &ctx->ballers[i];
  389. if(!cf_hc_baller_is_active(b))
  390. continue;
  391. Curl_conn_cf_adjust_pollset(b->cf, data, ps);
  392. }
  393. CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
  394. }
  395. }
  396. static bool cf_hc_data_pending(struct Curl_cfilter *cf,
  397. const struct Curl_easy *data)
  398. {
  399. struct cf_hc_ctx *ctx = cf->ctx;
  400. size_t i;
  401. if(cf->connected)
  402. return cf->next->cft->has_data_pending(cf->next, data);
  403. CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
  404. for(i = 0; i < ctx->baller_count; i++)
  405. if(cf_hc_baller_data_pending(&ctx->ballers[i], data))
  406. return TRUE;
  407. return FALSE;
  408. }
  409. static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
  410. struct Curl_easy *data,
  411. int query)
  412. {
  413. struct cf_hc_ctx *ctx = cf->ctx;
  414. struct curltime t, tmax;
  415. size_t i;
  416. memset(&tmax, 0, sizeof(tmax));
  417. for(i = 0; i < ctx->baller_count; i++) {
  418. struct Curl_cfilter *cfb = ctx->ballers[i].cf;
  419. memset(&t, 0, sizeof(t));
  420. if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
  421. if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
  422. tmax = t;
  423. }
  424. }
  425. return tmax;
  426. }
  427. static CURLcode cf_hc_query(struct Curl_cfilter *cf,
  428. struct Curl_easy *data,
  429. int query, int *pres1, void *pres2)
  430. {
  431. struct cf_hc_ctx *ctx = cf->ctx;
  432. size_t i;
  433. if(!cf->connected) {
  434. switch(query) {
  435. case CF_QUERY_TIMER_CONNECT: {
  436. struct curltime *when = pres2;
  437. *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
  438. return CURLE_OK;
  439. }
  440. case CF_QUERY_TIMER_APPCONNECT: {
  441. struct curltime *when = pres2;
  442. *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
  443. return CURLE_OK;
  444. }
  445. case CF_QUERY_NEED_FLUSH: {
  446. for(i = 0; i < ctx->baller_count; i++)
  447. if(cf_hc_baller_needs_flush(&ctx->ballers[i], data)) {
  448. *pres1 = TRUE;
  449. return CURLE_OK;
  450. }
  451. break;
  452. }
  453. default:
  454. break;
  455. }
  456. }
  457. return cf->next ?
  458. cf->next->cft->query(cf->next, data, query, pres1, pres2) :
  459. CURLE_UNKNOWN_OPTION;
  460. }
  461. static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf,
  462. struct Curl_easy *data,
  463. int event, int arg1, void *arg2)
  464. {
  465. struct cf_hc_ctx *ctx = cf->ctx;
  466. CURLcode result = CURLE_OK;
  467. size_t i;
  468. if(!cf->connected) {
  469. for(i = 0; i < ctx->baller_count; i++) {
  470. result = cf_hc_baller_cntrl(&ctx->ballers[i], data, event, arg1, arg2);
  471. if(result && (result != CURLE_AGAIN))
  472. goto out;
  473. }
  474. result = CURLE_OK;
  475. }
  476. out:
  477. return result;
  478. }
  479. static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
  480. {
  481. CURL_TRC_CF(data, cf, "close");
  482. cf_hc_reset(cf, data);
  483. cf->connected = FALSE;
  484. if(cf->next) {
  485. cf->next->cft->do_close(cf->next, data);
  486. Curl_conn_cf_discard_chain(&cf->next, data);
  487. }
  488. }
  489. static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
  490. {
  491. struct cf_hc_ctx *ctx = cf->ctx;
  492. (void)data;
  493. CURL_TRC_CF(data, cf, "destroy");
  494. cf_hc_reset(cf, data);
  495. Curl_safefree(ctx);
  496. }
  497. struct Curl_cftype Curl_cft_http_connect = {
  498. "HTTPS-CONNECT",
  499. 0,
  500. CURL_LOG_LVL_NONE,
  501. cf_hc_destroy,
  502. cf_hc_connect,
  503. cf_hc_close,
  504. cf_hc_shutdown,
  505. Curl_cf_def_get_host,
  506. cf_hc_adjust_pollset,
  507. cf_hc_data_pending,
  508. Curl_cf_def_send,
  509. Curl_cf_def_recv,
  510. cf_hc_cntrl,
  511. Curl_cf_def_conn_is_alive,
  512. Curl_cf_def_conn_keep_alive,
  513. cf_hc_query,
  514. };
  515. static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
  516. struct Curl_easy *data,
  517. const struct Curl_dns_entry *remotehost,
  518. enum alpnid *alpnids, size_t alpn_count)
  519. {
  520. struct Curl_cfilter *cf = NULL;
  521. struct cf_hc_ctx *ctx;
  522. CURLcode result = CURLE_OK;
  523. size_t i;
  524. DEBUGASSERT(alpnids);
  525. DEBUGASSERT(alpn_count);
  526. DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers));
  527. if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) {
  528. failf(data, "https-connect filter create with unsupported %zu ALPN ids",
  529. alpn_count);
  530. return CURLE_FAILED_INIT;
  531. }
  532. ctx = calloc(1, sizeof(*ctx));
  533. if(!ctx) {
  534. result = CURLE_OUT_OF_MEMORY;
  535. goto out;
  536. }
  537. ctx->remotehost = remotehost;
  538. for(i = 0; i < alpn_count; ++i)
  539. cf_hc_baller_assign(&ctx->ballers[i], alpnids[i]);
  540. for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i)
  541. ctx->ballers[i].alpn_id = ALPN_none;
  542. ctx->baller_count = alpn_count;
  543. result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
  544. CURL_TRC_CF(data, cf, "created with %zu ALPNs -> %d",
  545. ctx->baller_count, result);
  546. if(result)
  547. goto out;
  548. ctx = NULL;
  549. cf_hc_reset(cf, data);
  550. out:
  551. *pcf = result ? NULL : cf;
  552. free(ctx);
  553. return result;
  554. }
  555. static CURLcode cf_http_connect_add(struct Curl_easy *data,
  556. struct connectdata *conn,
  557. int sockindex,
  558. const struct Curl_dns_entry *remotehost,
  559. enum alpnid *alpn_ids, size_t alpn_count)
  560. {
  561. struct Curl_cfilter *cf;
  562. CURLcode result = CURLE_OK;
  563. DEBUGASSERT(data);
  564. result = cf_hc_create(&cf, data, remotehost, alpn_ids, alpn_count);
  565. if(result)
  566. goto out;
  567. Curl_conn_cf_add(data, conn, sockindex, cf);
  568. out:
  569. return result;
  570. }
  571. CURLcode Curl_cf_https_setup(struct Curl_easy *data,
  572. struct connectdata *conn,
  573. int sockindex,
  574. const struct Curl_dns_entry *remotehost)
  575. {
  576. enum alpnid alpn_ids[2];
  577. size_t alpn_count = 0;
  578. CURLcode result = CURLE_OK;
  579. (void)sockindex;
  580. (void)remotehost;
  581. if(conn->bits.tls_enable_alpn) {
  582. switch(data->state.httpwant) {
  583. case CURL_HTTP_VERSION_NONE:
  584. /* No preferences by transfer setup. Choose best defaults */
  585. #ifdef USE_HTTPSRR
  586. if(conn->dns_entry && conn->dns_entry->hinfo &&
  587. !conn->dns_entry->hinfo->no_def_alpn) {
  588. size_t i, j;
  589. for(i = 0; i < CURL_ARRAYSIZE(conn->dns_entry->hinfo->alpns) &&
  590. alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) {
  591. bool present = FALSE;
  592. enum alpnid alpn = conn->dns_entry->hinfo->alpns[i];
  593. for(j = 0; j < alpn_count; ++j) {
  594. if(alpn == alpn_ids[j]) {
  595. present = TRUE;
  596. break;
  597. }
  598. }
  599. if(!present) {
  600. switch(alpn) {
  601. case ALPN_h3:
  602. if(Curl_conn_may_http3(data, conn))
  603. break; /* not possible */
  604. FALLTHROUGH();
  605. case ALPN_h2:
  606. case ALPN_h1:
  607. alpn_ids[alpn_count++] = alpn;
  608. break;
  609. default: /* ignore */
  610. break;
  611. }
  612. }
  613. }
  614. }
  615. #endif
  616. if(!alpn_count)
  617. alpn_ids[alpn_count++] = ALPN_h2;
  618. break;
  619. case CURL_HTTP_VERSION_3ONLY:
  620. result = Curl_conn_may_http3(data, conn);
  621. if(result) /* cannot do it */
  622. goto out;
  623. alpn_ids[alpn_count++] = ALPN_h3;
  624. break;
  625. case CURL_HTTP_VERSION_3:
  626. /* We assume that silently not even trying H3 is ok here */
  627. if(Curl_conn_may_http3(data, conn) == CURLE_OK)
  628. alpn_ids[alpn_count++] = ALPN_h3;
  629. alpn_ids[alpn_count++] = ALPN_h2;
  630. break;
  631. case CURL_HTTP_VERSION_2_0:
  632. case CURL_HTTP_VERSION_2TLS:
  633. case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
  634. alpn_ids[alpn_count++] = ALPN_h2;
  635. break;
  636. case CURL_HTTP_VERSION_1_0:
  637. case CURL_HTTP_VERSION_1_1:
  638. alpn_ids[alpn_count++] = ALPN_h1;
  639. break;
  640. default:
  641. alpn_ids[alpn_count++] = ALPN_h2;
  642. break;
  643. }
  644. }
  645. /* If we identified ALPNs to use, install our filter. Otherwise,
  646. * install nothing, so our call will use a default connect setup. */
  647. if(alpn_count) {
  648. result = cf_http_connect_add(data, conn, sockindex, remotehost,
  649. alpn_ids, alpn_count);
  650. }
  651. out:
  652. return result;
  653. }
  654. #endif /* !defined(CURL_DISABLE_HTTP) */