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