cf-https-connect: look into httpsrr alpns when available
Improved the filter implementation to be flexible in which order h3 and h2/h1 are attempted. When HTTPSRR is enabled, look at the ALPNs it found and use the order given for connecting in default setups. Closes #16012
This commit is contained in:
parent
bbb91b22ee
commit
a6eac83481
@ -31,6 +31,7 @@
|
|||||||
#include "curl_trc.h"
|
#include "curl_trc.h"
|
||||||
#include "cfilters.h"
|
#include "cfilters.h"
|
||||||
#include "connect.h"
|
#include "connect.h"
|
||||||
|
#include "hostip.h"
|
||||||
#include "multiif.h"
|
#include "multiif.h"
|
||||||
#include "cf-https-connect.h"
|
#include "cf-https-connect.h"
|
||||||
#include "http2.h"
|
#include "http2.h"
|
||||||
@ -42,6 +43,10 @@
|
|||||||
#include "memdebug.h"
|
#include "memdebug.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ARRAYSIZE
|
||||||
|
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CF_HC_INIT,
|
CF_HC_INIT,
|
||||||
CF_HC_CONNECT,
|
CF_HC_CONNECT,
|
||||||
@ -55,7 +60,7 @@ struct cf_hc_baller {
|
|||||||
CURLcode result;
|
CURLcode result;
|
||||||
struct curltime started;
|
struct curltime started;
|
||||||
int reply_ms;
|
int reply_ms;
|
||||||
BIT(enabled);
|
enum alpnid alpn_id;
|
||||||
BIT(shutdown);
|
BIT(shutdown);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,7 +78,7 @@ static void cf_hc_baller_reset(struct cf_hc_baller *b,
|
|||||||
|
|
||||||
static bool cf_hc_baller_is_active(struct cf_hc_baller *b)
|
static bool cf_hc_baller_is_active(struct cf_hc_baller *b)
|
||||||
{
|
{
|
||||||
return b->enabled && b->cf && !b->result;
|
return b->cf && !b->result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
|
static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
|
||||||
@ -84,7 +89,7 @@ static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
|
|||||||
static int cf_hc_baller_reply_ms(struct cf_hc_baller *b,
|
static int cf_hc_baller_reply_ms(struct cf_hc_baller *b,
|
||||||
struct Curl_easy *data)
|
struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
if(b->reply_ms < 0)
|
if(b->cf && (b->reply_ms < 0))
|
||||||
b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS,
|
b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS,
|
||||||
&b->reply_ms, NULL);
|
&b->reply_ms, NULL);
|
||||||
return b->reply_ms;
|
return b->reply_ms;
|
||||||
@ -116,8 +121,8 @@ struct cf_hc_ctx {
|
|||||||
const struct Curl_dns_entry *remotehost;
|
const struct Curl_dns_entry *remotehost;
|
||||||
struct curltime started; /* when connect started */
|
struct curltime started; /* when connect started */
|
||||||
CURLcode result; /* overall result */
|
CURLcode result; /* overall result */
|
||||||
struct cf_hc_baller h3_baller;
|
struct cf_hc_baller ballers[2];
|
||||||
struct cf_hc_baller h21_baller;
|
size_t baller_count;
|
||||||
unsigned int soft_eyeballs_timeout_ms;
|
unsigned int soft_eyeballs_timeout_ms;
|
||||||
unsigned int hard_eyeballs_timeout_ms;
|
unsigned int hard_eyeballs_timeout_ms;
|
||||||
};
|
};
|
||||||
@ -125,17 +130,32 @@ struct cf_hc_ctx {
|
|||||||
static void cf_hc_baller_init(struct cf_hc_baller *b,
|
static void cf_hc_baller_init(struct cf_hc_baller *b,
|
||||||
struct Curl_cfilter *cf,
|
struct Curl_cfilter *cf,
|
||||||
struct Curl_easy *data,
|
struct Curl_easy *data,
|
||||||
const char *name,
|
|
||||||
int transport)
|
int transport)
|
||||||
{
|
{
|
||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
struct Curl_cfilter *save = cf->next;
|
struct Curl_cfilter *save = cf->next;
|
||||||
|
|
||||||
b->name = name;
|
|
||||||
cf->next = NULL;
|
cf->next = NULL;
|
||||||
b->started = Curl_now();
|
b->started = Curl_now();
|
||||||
b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost,
|
switch(b->alpn_id) {
|
||||||
transport, CURL_CF_SSL_ENABLE);
|
case ALPN_h3:
|
||||||
|
b->name = "h3";
|
||||||
|
transport = TRNSPRT_QUIC;
|
||||||
|
break;
|
||||||
|
case ALPN_h2:
|
||||||
|
b->name = "h2";
|
||||||
|
break;
|
||||||
|
case ALPN_h1:
|
||||||
|
b->name = "h1";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
b->result = CURLE_FAILED_INIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!b->result)
|
||||||
|
b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost,
|
||||||
|
transport, CURL_CF_SSL_ENABLE);
|
||||||
b->cf = cf->next;
|
b->cf = cf->next;
|
||||||
cf->next = save;
|
cf->next = save;
|
||||||
}
|
}
|
||||||
@ -157,10 +177,11 @@ static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
|
|||||||
static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
|
static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
if(ctx) {
|
if(ctx) {
|
||||||
cf_hc_baller_reset(&ctx->h3_baller, data);
|
for(i = 0; i < ctx->baller_count; ++i)
|
||||||
cf_hc_baller_reset(&ctx->h21_baller, data);
|
cf_hc_baller_reset(&ctx->ballers[i], data);
|
||||||
ctx->state = CF_HC_INIT;
|
ctx->state = CF_HC_INIT;
|
||||||
ctx->result = CURLE_OK;
|
ctx->result = CURLE_OK;
|
||||||
ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
|
ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
|
||||||
@ -175,12 +196,12 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
|
|||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
int reply_ms;
|
int reply_ms;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
DEBUGASSERT(winner->cf);
|
DEBUGASSERT(winner->cf);
|
||||||
if(winner != &ctx->h3_baller)
|
for(i = 0; i < ctx->baller_count; ++i)
|
||||||
cf_hc_baller_reset(&ctx->h3_baller, data);
|
if(winner != &ctx->ballers[i])
|
||||||
if(winner != &ctx->h21_baller)
|
cf_hc_baller_reset(&ctx->ballers[i], data);
|
||||||
cf_hc_baller_reset(&ctx->h21_baller, data);
|
|
||||||
|
|
||||||
reply_ms = cf_hc_baller_reply_ms(winner, data);
|
reply_ms = cf_hc_baller_reply_ms(winner, data);
|
||||||
if(reply_ms >= 0)
|
if(reply_ms >= 0)
|
||||||
@ -218,31 +239,31 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool time_to_start_h21(struct Curl_cfilter *cf,
|
static bool time_to_start_next(struct Curl_cfilter *cf,
|
||||||
struct Curl_easy *data,
|
struct Curl_easy *data,
|
||||||
struct curltime now)
|
size_t idx, struct curltime now)
|
||||||
{
|
{
|
||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
timediff_t elapsed_ms;
|
timediff_t elapsed_ms;
|
||||||
|
|
||||||
if(!ctx->h21_baller.enabled || cf_hc_baller_has_started(&ctx->h21_baller))
|
if(idx >= ctx->baller_count)
|
||||||
|
return FALSE;
|
||||||
|
if(cf_hc_baller_has_started(&ctx->ballers[idx]))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if(!ctx->h3_baller.enabled || !cf_hc_baller_is_active(&ctx->h3_baller))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
elapsed_ms = Curl_timediff(now, ctx->started);
|
elapsed_ms = Curl_timediff(now, ctx->started);
|
||||||
if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
|
if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
|
||||||
CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting h21",
|
CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting %s",
|
||||||
ctx->hard_eyeballs_timeout_ms);
|
ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) {
|
if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) {
|
||||||
if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) {
|
if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) {
|
||||||
CURL_TRC_CF(data, cf, "soft timeout of %dms reached, h3 has not "
|
CURL_TRC_CF(data, cf, "soft timeout of %dms reached, %s has not "
|
||||||
"seen any data, starting h21",
|
"seen any data, starting %s",
|
||||||
ctx->soft_eyeballs_timeout_ms);
|
ctx->soft_eyeballs_timeout_ms,
|
||||||
|
ctx->ballers[idx - 1].name, ctx->ballers[idx].name);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
/* set the effective hard timeout again */
|
/* set the effective hard timeout again */
|
||||||
@ -259,6 +280,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
|
|||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
struct curltime now;
|
struct curltime now;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
size_t i, failed_ballers;
|
||||||
|
|
||||||
(void)blocking;
|
(void)blocking;
|
||||||
if(cf->connected) {
|
if(cf->connected) {
|
||||||
@ -270,51 +292,54 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
|
|||||||
now = Curl_now();
|
now = Curl_now();
|
||||||
switch(ctx->state) {
|
switch(ctx->state) {
|
||||||
case CF_HC_INIT:
|
case CF_HC_INIT:
|
||||||
DEBUGASSERT(!ctx->h3_baller.cf);
|
|
||||||
DEBUGASSERT(!ctx->h21_baller.cf);
|
|
||||||
DEBUGASSERT(!cf->next);
|
DEBUGASSERT(!cf->next);
|
||||||
|
for(i = 0; i < ctx->baller_count; i++)
|
||||||
|
DEBUGASSERT(!ctx->ballers[i].cf);
|
||||||
CURL_TRC_CF(data, cf, "connect, init");
|
CURL_TRC_CF(data, cf, "connect, init");
|
||||||
ctx->started = now;
|
ctx->started = now;
|
||||||
if(ctx->h3_baller.enabled) {
|
cf_hc_baller_init(&ctx->ballers[0], cf, data, cf->conn->transport);
|
||||||
cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC);
|
if(ctx->baller_count > 1)
|
||||||
if(ctx->h21_baller.enabled)
|
Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
|
||||||
Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
|
|
||||||
}
|
|
||||||
else if(ctx->h21_baller.enabled)
|
|
||||||
cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21",
|
|
||||||
cf->conn->transport);
|
|
||||||
ctx->state = CF_HC_CONNECT;
|
ctx->state = CF_HC_CONNECT;
|
||||||
FALLTHROUGH();
|
FALLTHROUGH();
|
||||||
|
|
||||||
case CF_HC_CONNECT:
|
case CF_HC_CONNECT:
|
||||||
if(cf_hc_baller_is_active(&ctx->h3_baller)) {
|
if(cf_hc_baller_is_active(&ctx->ballers[0])) {
|
||||||
result = cf_hc_baller_connect(&ctx->h3_baller, cf, data, done);
|
result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done);
|
||||||
if(!result && *done) {
|
if(!result && *done) {
|
||||||
result = baller_connected(cf, data, &ctx->h3_baller);
|
result = baller_connected(cf, data, &ctx->ballers[0]);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(time_to_start_h21(cf, data, now)) {
|
if(time_to_start_next(cf, data, 1, now)) {
|
||||||
cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21",
|
cf_hc_baller_init(&ctx->ballers[1], cf, data, cf->conn->transport);
|
||||||
cf->conn->transport);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cf_hc_baller_is_active(&ctx->h21_baller)) {
|
if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) {
|
||||||
CURL_TRC_CF(data, cf, "connect, check h21");
|
CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name);
|
||||||
result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done);
|
result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done);
|
||||||
if(!result && *done) {
|
if(!result && *done) {
|
||||||
result = baller_connected(cf, data, &ctx->h21_baller);
|
result = baller_connected(cf, data, &ctx->ballers[1]);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if((!ctx->h3_baller.enabled || ctx->h3_baller.result) &&
|
failed_ballers = 0;
|
||||||
(!ctx->h21_baller.enabled || ctx->h21_baller.result)) {
|
for(i = 0; i < ctx->baller_count; i++) {
|
||||||
/* both failed or disabled. we give up */
|
if(ctx->ballers[i].result)
|
||||||
|
++failed_ballers;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(failed_ballers == ctx->baller_count) {
|
||||||
|
/* all have failed. we give up */
|
||||||
CURL_TRC_CF(data, cf, "connect, all failed");
|
CURL_TRC_CF(data, cf, "connect, all failed");
|
||||||
result = ctx->result = ctx->h3_baller.enabled ?
|
for(i = 0; i < ctx->baller_count; i++) {
|
||||||
ctx->h3_baller.result : ctx->h21_baller.result;
|
if(ctx->ballers[i].result) {
|
||||||
|
result = ctx->ballers[i].result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
ctx->state = CF_HC_FAILURE;
|
ctx->state = CF_HC_FAILURE;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -344,7 +369,6 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
|
|||||||
struct Curl_easy *data, bool *done)
|
struct Curl_easy *data, bool *done)
|
||||||
{
|
{
|
||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
struct cf_hc_baller *ballers[2];
|
|
||||||
size_t i;
|
size_t i;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
@ -356,10 +380,8 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
|
|||||||
|
|
||||||
/* shutdown all ballers that have not done so already. If one fails,
|
/* shutdown all ballers that have not done so already. If one fails,
|
||||||
* continue shutting down others until all are shutdown. */
|
* continue shutting down others until all are shutdown. */
|
||||||
ballers[0] = &ctx->h3_baller;
|
for(i = 0; i < ctx->baller_count; i++) {
|
||||||
ballers[1] = &ctx->h21_baller;
|
struct cf_hc_baller *b = &ctx->ballers[i];
|
||||||
for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
|
|
||||||
struct cf_hc_baller *b = ballers[i];
|
|
||||||
bool bdone = FALSE;
|
bool bdone = FALSE;
|
||||||
if(!cf_hc_baller_is_active(b) || b->shutdown)
|
if(!cf_hc_baller_is_active(b) || b->shutdown)
|
||||||
continue;
|
continue;
|
||||||
@ -369,14 +391,14 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*done = TRUE;
|
*done = TRUE;
|
||||||
for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
|
for(i = 0; i < ctx->baller_count; i++) {
|
||||||
if(ballers[i] && !ballers[i]->shutdown)
|
if(!ctx->ballers[i].shutdown)
|
||||||
*done = FALSE;
|
*done = FALSE;
|
||||||
}
|
}
|
||||||
if(*done) {
|
if(*done) {
|
||||||
for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
|
for(i = 0; i < ctx->baller_count; i++) {
|
||||||
if(ballers[i] && ballers[i]->result)
|
if(ctx->ballers[i].result)
|
||||||
result = ballers[i]->result;
|
result = ctx->ballers[i].result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
|
CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
|
||||||
@ -389,13 +411,10 @@ static void cf_hc_adjust_pollset(struct Curl_cfilter *cf,
|
|||||||
{
|
{
|
||||||
if(!cf->connected) {
|
if(!cf->connected) {
|
||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
struct cf_hc_baller *ballers[2];
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
ballers[0] = &ctx->h3_baller;
|
for(i = 0; i < ctx->baller_count; i++) {
|
||||||
ballers[1] = &ctx->h21_baller;
|
struct cf_hc_baller *b = &ctx->ballers[i];
|
||||||
for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
|
|
||||||
struct cf_hc_baller *b = ballers[i];
|
|
||||||
if(!cf_hc_baller_is_active(b))
|
if(!cf_hc_baller_is_active(b))
|
||||||
continue;
|
continue;
|
||||||
Curl_conn_cf_adjust_pollset(b->cf, data, ps);
|
Curl_conn_cf_adjust_pollset(b->cf, data, ps);
|
||||||
@ -408,13 +427,16 @@ static bool cf_hc_data_pending(struct Curl_cfilter *cf,
|
|||||||
const struct Curl_easy *data)
|
const struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
if(cf->connected)
|
if(cf->connected)
|
||||||
return cf->next->cft->has_data_pending(cf->next, data);
|
return cf->next->cft->has_data_pending(cf->next, data);
|
||||||
|
|
||||||
CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
|
CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
|
||||||
return cf_hc_baller_data_pending(&ctx->h3_baller, data)
|
for(i = 0; i < ctx->baller_count; i++)
|
||||||
|| cf_hc_baller_data_pending(&ctx->h21_baller, data);
|
if(cf_hc_baller_data_pending(&ctx->ballers[i], data))
|
||||||
|
return TRUE;
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
|
static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
|
||||||
@ -422,21 +444,17 @@ static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
|
|||||||
int query)
|
int query)
|
||||||
{
|
{
|
||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
struct Curl_cfilter *cfb;
|
|
||||||
struct curltime t, tmax;
|
struct curltime t, tmax;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
memset(&tmax, 0, sizeof(tmax));
|
memset(&tmax, 0, sizeof(tmax));
|
||||||
memset(&t, 0, sizeof(t));
|
for(i = 0; i < ctx->baller_count; i++) {
|
||||||
cfb = ctx->h21_baller.enabled ? ctx->h21_baller.cf : NULL;
|
struct Curl_cfilter *cfb = ctx->ballers[i].cf;
|
||||||
if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
|
memset(&t, 0, sizeof(t));
|
||||||
if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
|
if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
|
||||||
tmax = t;
|
if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
|
||||||
}
|
tmax = t;
|
||||||
memset(&t, 0, sizeof(t));
|
}
|
||||||
cfb = ctx->h3_baller.enabled ? ctx->h3_baller.cf : NULL;
|
|
||||||
if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
|
|
||||||
if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
|
|
||||||
tmax = t;
|
|
||||||
}
|
}
|
||||||
return tmax;
|
return tmax;
|
||||||
}
|
}
|
||||||
@ -446,6 +464,7 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf,
|
|||||||
int query, int *pres1, void *pres2)
|
int query, int *pres1, void *pres2)
|
||||||
{
|
{
|
||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
if(!cf->connected) {
|
if(!cf->connected) {
|
||||||
switch(query) {
|
switch(query) {
|
||||||
@ -460,11 +479,11 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf,
|
|||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
case CF_QUERY_NEED_FLUSH: {
|
case CF_QUERY_NEED_FLUSH: {
|
||||||
if(cf_hc_baller_needs_flush(&ctx->h3_baller, data)
|
for(i = 0; i < ctx->baller_count; i++)
|
||||||
|| cf_hc_baller_needs_flush(&ctx->h21_baller, data)) {
|
if(cf_hc_baller_needs_flush(&ctx->ballers[i], data)) {
|
||||||
*pres1 = TRUE;
|
*pres1 = TRUE;
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -482,14 +501,17 @@ static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf,
|
|||||||
{
|
{
|
||||||
struct cf_hc_ctx *ctx = cf->ctx;
|
struct cf_hc_ctx *ctx = cf->ctx;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
if(!cf->connected) {
|
if(!cf->connected) {
|
||||||
result = cf_hc_baller_cntrl(&ctx->h3_baller, data, event, arg1, arg2);
|
for(i = 0; i < ctx->baller_count; i++) {
|
||||||
if(!result || (result == CURLE_AGAIN))
|
result = cf_hc_baller_cntrl(&ctx->ballers[i], data, event, arg1, arg2);
|
||||||
result = cf_hc_baller_cntrl(&ctx->h21_baller, data, event, arg1, arg2);
|
if(result && (result != CURLE_AGAIN))
|
||||||
if(result == CURLE_AGAIN)
|
goto out;
|
||||||
result = CURLE_OK;
|
}
|
||||||
|
result = CURLE_OK;
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,23 +559,37 @@ struct Curl_cftype Curl_cft_http_connect = {
|
|||||||
static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
|
static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
|
||||||
struct Curl_easy *data,
|
struct Curl_easy *data,
|
||||||
const struct Curl_dns_entry *remotehost,
|
const struct Curl_dns_entry *remotehost,
|
||||||
bool try_h3, bool try_h21)
|
enum alpnid *alpnids, size_t alpn_count)
|
||||||
{
|
{
|
||||||
struct Curl_cfilter *cf = NULL;
|
struct Curl_cfilter *cf = NULL;
|
||||||
struct cf_hc_ctx *ctx;
|
struct cf_hc_ctx *ctx;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
DEBUGASSERT(alpnids);
|
||||||
|
DEBUGASSERT(alpn_count);
|
||||||
|
DEBUGASSERT(alpn_count <= ARRAYSIZE(ctx->ballers));
|
||||||
|
if(!alpn_count || (alpn_count > ARRAYSIZE(ctx->ballers))) {
|
||||||
|
failf(data, "https-connect filter create with unsupported %zu ALPN ids",
|
||||||
|
alpn_count);
|
||||||
|
return CURLE_FAILED_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
(void)data;
|
|
||||||
ctx = calloc(1, sizeof(*ctx));
|
ctx = calloc(1, sizeof(*ctx));
|
||||||
if(!ctx) {
|
if(!ctx) {
|
||||||
result = CURLE_OUT_OF_MEMORY;
|
result = CURLE_OUT_OF_MEMORY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ctx->remotehost = remotehost;
|
ctx->remotehost = remotehost;
|
||||||
ctx->h3_baller.enabled = try_h3;
|
for(i = 0; i < alpn_count; ++i)
|
||||||
ctx->h21_baller.enabled = try_h21;
|
ctx->ballers[i].alpn_id = alpnids[i];
|
||||||
|
for(; i < ARRAYSIZE(ctx->ballers); ++i)
|
||||||
|
ctx->ballers[i].alpn_id = ALPN_none;
|
||||||
|
ctx->baller_count = alpn_count;
|
||||||
|
|
||||||
result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
|
result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
|
||||||
|
CURL_TRC_CF(data, cf, "created with %zu ALPNs -> %d",
|
||||||
|
ctx->baller_count, result);
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
ctx = NULL;
|
ctx = NULL;
|
||||||
@ -569,13 +605,13 @@ static CURLcode cf_http_connect_add(struct Curl_easy *data,
|
|||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
int sockindex,
|
int sockindex,
|
||||||
const struct Curl_dns_entry *remotehost,
|
const struct Curl_dns_entry *remotehost,
|
||||||
bool try_h3, bool try_h21)
|
enum alpnid *alpn_ids, size_t alpn_count)
|
||||||
{
|
{
|
||||||
struct Curl_cfilter *cf;
|
struct Curl_cfilter *cf;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
DEBUGASSERT(data);
|
DEBUGASSERT(data);
|
||||||
result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21);
|
result = cf_hc_create(&cf, data, remotehost, alpn_ids, alpn_count);
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
Curl_conn_cf_add(data, conn, sockindex, cf);
|
Curl_conn_cf_add(data, conn, sockindex, cf);
|
||||||
@ -588,31 +624,86 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data,
|
|||||||
int sockindex,
|
int sockindex,
|
||||||
const struct Curl_dns_entry *remotehost)
|
const struct Curl_dns_entry *remotehost)
|
||||||
{
|
{
|
||||||
bool try_h3 = FALSE, try_h21 = TRUE; /* defaults, for now */
|
enum alpnid alpn_ids[2];
|
||||||
|
size_t alpn_count = 0;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
(void)sockindex;
|
(void)sockindex;
|
||||||
(void)remotehost;
|
(void)remotehost;
|
||||||
|
|
||||||
if(!conn->bits.tls_enable_alpn)
|
if(conn->bits.tls_enable_alpn) {
|
||||||
goto out;
|
switch(data->state.httpwant) {
|
||||||
|
case CURL_HTTP_VERSION_NONE:
|
||||||
if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) {
|
/* No preferences by transfer setup. Choose best defaults */
|
||||||
result = Curl_conn_may_http3(data, conn);
|
#ifdef USE_HTTPSRR
|
||||||
if(result) /* cannot do it */
|
if(conn->dns_entry && conn->dns_entry->hinfo &&
|
||||||
goto out;
|
!conn->dns_entry->hinfo->no_def_alpn) {
|
||||||
try_h3 = TRUE;
|
size_t i, j;
|
||||||
try_h21 = FALSE;
|
for(i = 0; i < ARRAYSIZE(conn->dns_entry->hinfo->alpns) &&
|
||||||
}
|
alpn_count < ARRAYSIZE(alpn_ids); ++i) {
|
||||||
else if(data->state.httpwant >= CURL_HTTP_VERSION_3) {
|
bool present = FALSE;
|
||||||
/* We assume that silently not even trying H3 is ok here */
|
enum alpnid alpn = conn->dns_entry->hinfo->alpns[i];
|
||||||
/* TODO: should we fail instead? */
|
for(j = 0; j < alpn_count; ++j) {
|
||||||
try_h3 = (Curl_conn_may_http3(data, conn) == CURLE_OK);
|
if(alpn == alpn_ids[j]) {
|
||||||
try_h21 = TRUE;
|
present = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!present) {
|
||||||
|
switch(alpn) {
|
||||||
|
case ALPN_h3:
|
||||||
|
if(Curl_conn_may_http3(data, conn))
|
||||||
|
break; /* not possible */
|
||||||
|
FALLTHROUGH();
|
||||||
|
case ALPN_h2:
|
||||||
|
case ALPN_h1:
|
||||||
|
alpn_ids[alpn_count++] = alpn;
|
||||||
|
break;
|
||||||
|
default: /* ignore */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if(!alpn_count)
|
||||||
|
alpn_ids[alpn_count++] = ALPN_h2;
|
||||||
|
break;
|
||||||
|
case CURL_HTTP_VERSION_3ONLY:
|
||||||
|
result = Curl_conn_may_http3(data, conn);
|
||||||
|
if(result) /* cannot do it */
|
||||||
|
goto out;
|
||||||
|
alpn_ids[alpn_count++] = ALPN_h3;
|
||||||
|
break;
|
||||||
|
case CURL_HTTP_VERSION_3:
|
||||||
|
/* We assume that silently not even trying H3 is ok here */
|
||||||
|
/* TODO: should we fail instead? */
|
||||||
|
if(Curl_conn_may_http3(data, conn) == CURLE_OK)
|
||||||
|
alpn_ids[alpn_count++] = ALPN_h3;
|
||||||
|
alpn_ids[alpn_count++] = ALPN_h2;
|
||||||
|
break;
|
||||||
|
case CURL_HTTP_VERSION_2_0:
|
||||||
|
case CURL_HTTP_VERSION_2TLS:
|
||||||
|
case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
|
||||||
|
alpn_ids[alpn_count++] = ALPN_h2;
|
||||||
|
break;
|
||||||
|
case CURL_HTTP_VERSION_1_0:
|
||||||
|
case CURL_HTTP_VERSION_1_1:
|
||||||
|
alpn_ids[alpn_count++] = ALPN_h1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
alpn_ids[alpn_count++] = ALPN_h2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we identified ALPNs to use, install our filter. Otherwise,
|
||||||
|
* install nothing, so our call will use a default connect setup. */
|
||||||
|
if(alpn_count) {
|
||||||
|
result = cf_http_connect_add(data, conn, sockindex, remotehost,
|
||||||
|
alpn_ids, alpn_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = cf_http_connect_add(data, conn, sockindex, remotehost,
|
|
||||||
try_h3, try_h21);
|
|
||||||
out:
|
out:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1105,7 +1105,7 @@ static CURLcode doh_decode_rdata_alpn(unsigned char *cp, size_t len,
|
|||||||
if(id != ALPN_none) {
|
if(id != ALPN_none) {
|
||||||
if(idnum == MAX_HTTPSRR_ALPNS)
|
if(idnum == MAX_HTTPSRR_ALPNS)
|
||||||
break;
|
break;
|
||||||
alpns[idnum++] = id;
|
alpns[idnum++] = (unsigned char)id;
|
||||||
}
|
}
|
||||||
Curl_dyn_reset(&dval);
|
Curl_dyn_reset(&dval);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user