http2+h3 filters: fix ctx init

Members of the filter context, like stream hash and buffers, need to be
initialized early and protected by a flag to also avoid double cleanup.

This allow the context to be used safely before a connect() is started
and the other parts of the context are set up.

Closes #14505
This commit is contained in:
Stefan Eissing 2024-08-12 15:42:41 +02:00 committed by Daniel Stenberg
parent 2cc56eb758
commit cb17c069a8
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
5 changed files with 186 additions and 129 deletions

View File

@ -142,6 +142,8 @@ struct cf_h2_ctx {
uint32_t goaway_error; /* goaway error code from server */ uint32_t goaway_error; /* goaway error code from server */
int32_t remote_max_sid; /* max id processed by server */ int32_t remote_max_sid; /* max id processed by server */
int32_t local_max_sid; /* max id processed by us */ int32_t local_max_sid; /* max id processed by us */
BIT(initialized);
BIT(via_h1_upgrade);
BIT(conn_closed); BIT(conn_closed);
BIT(rcvd_goaway); BIT(rcvd_goaway);
BIT(sent_goaway); BIT(sent_goaway);
@ -154,13 +156,23 @@ struct cf_h2_ctx {
#define CF_CTX_CALL_DATA(cf) \ #define CF_CTX_CALL_DATA(cf) \
((struct cf_h2_ctx *)(cf)->ctx)->call_data ((struct cf_h2_ctx *)(cf)->ctx)->call_data
static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx) static void h2_stream_hash_free(void *stream);
{
struct cf_call_data save = ctx->call_data;
if(ctx->h2) { static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
nghttp2_session_del(ctx->h2); {
Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
ctx->remote_max_sid = 2147483647;
ctx->via_h1_upgrade = via_h1_upgrade;
ctx->initialized = TRUE;
} }
static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
{
if(ctx && ctx->initialized) {
Curl_bufq_free(&ctx->inbufq); Curl_bufq_free(&ctx->inbufq);
Curl_bufq_free(&ctx->outbufq); Curl_bufq_free(&ctx->outbufq);
Curl_bufcp_free(&ctx->stream_bufcp); Curl_bufcp_free(&ctx->stream_bufcp);
@ -168,14 +180,14 @@ static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
Curl_hash_clean(&ctx->streams); Curl_hash_clean(&ctx->streams);
Curl_hash_destroy(&ctx->streams); Curl_hash_destroy(&ctx->streams);
memset(ctx, 0, sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx));
ctx->call_data = save; }
free(ctx);
} }
static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) static void cf_h2_ctx_close(struct cf_h2_ctx *ctx)
{ {
if(ctx) { if(ctx->h2) {
cf_h2_ctx_clear(ctx); nghttp2_session_del(ctx->h2);
free(ctx);
} }
} }
@ -390,7 +402,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
DEBUGASSERT(ctx); DEBUGASSERT(ctx);
if(!stream) if(!stream || !ctx->initialized)
return; return;
if(ctx->h2) { if(ctx->h2) {
@ -489,12 +501,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
static int error_callback(nghttp2_session *session, const char *msg, static int error_callback(nghttp2_session *session, const char *msg,
size_t len, void *userp); size_t len, void *userp);
/* static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
* Initialize the cfilter context struct Curl_easy *data)
*/
static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool via_h1_upgrade)
{ {
struct cf_h2_ctx *ctx = cf->ctx; struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream; struct h2_stream_ctx *stream;
@ -503,12 +511,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
nghttp2_session_callbacks *cbs = NULL; nghttp2_session_callbacks *cbs = NULL;
DEBUGASSERT(!ctx->h2); DEBUGASSERT(!ctx->h2);
Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES); DEBUGASSERT(ctx->initialized);
Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
ctx->remote_max_sid = 2147483647;
rc = nghttp2_session_callbacks_new(&cbs); rc = nghttp2_session_callbacks_new(&cbs);
if(rc) { if(rc) {
@ -537,7 +540,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
} }
ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
if(via_h1_upgrade) { if(ctx->via_h1_upgrade) {
/* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
* in the H1 request and we upgrade from there. This stream * in the H1 request and we upgrade from there. This stream
* is opened implicitly as #1. */ * is opened implicitly as #1. */
@ -603,7 +606,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
/* all set, traffic will be send on connect */ /* all set, traffic will be send on connect */
result = CURLE_OK; result = CURLE_OK;
CURL_TRC_CF(data, cf, "[0] created h2 session%s", CURL_TRC_CF(data, cf, "[0] created h2 session%s",
via_h1_upgrade? " (via h1 upgrade)" : ""); ctx->via_h1_upgrade? " (via h1 upgrade)" : "");
out: out:
if(cbs) if(cbs)
@ -2450,8 +2453,9 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
*done = FALSE; *done = FALSE;
CF_DATA_SAVE(save, cf, data); CF_DATA_SAVE(save, cf, data);
DEBUGASSERT(ctx->initialized);
if(!ctx->h2) { if(!ctx->h2) {
result = cf_h2_ctx_init(cf, data, FALSE); result = cf_h2_ctx_open(cf, data);
if(result) if(result)
goto out; goto out;
} }
@ -2486,7 +2490,7 @@ static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
struct cf_call_data save; struct cf_call_data save;
CF_DATA_SAVE(save, cf, data); CF_DATA_SAVE(save, cf, data);
cf_h2_ctx_clear(ctx); cf_h2_ctx_close(ctx);
CF_DATA_RESTORE(cf, save); CF_DATA_RESTORE(cf, save);
cf->connected = FALSE; cf->connected = FALSE;
} }
@ -2735,6 +2739,7 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
ctx = calloc(1, sizeof(*ctx)); ctx = calloc(1, sizeof(*ctx));
if(!ctx) if(!ctx)
goto out; goto out;
cf_h2_ctx_init(ctx, via_h1_upgrade);
result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx); result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx);
if(result) if(result)
@ -2742,7 +2747,6 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
ctx = NULL; ctx = NULL;
Curl_conn_cf_add(data, conn, sockindex, cf); Curl_conn_cf_add(data, conn, sockindex, cf);
result = cf_h2_ctx_init(cf, data, via_h1_upgrade);
out: out:
if(result) if(result)
@ -2763,6 +2767,7 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
ctx = calloc(1, sizeof(*ctx)); ctx = calloc(1, sizeof(*ctx));
if(!ctx) if(!ctx)
goto out; goto out;
cf_h2_ctx_init(ctx, via_h1_upgrade);
result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx); result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx);
if(result) if(result)
@ -2770,7 +2775,6 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
ctx = NULL; ctx = NULL;
Curl_conn_cf_insert_after(cf, cf_h2); Curl_conn_cf_insert_after(cf, cf_h2);
result = cf_h2_ctx_init(cf_h2, data, via_h1_upgrade);
out: out:
if(result) if(result)

View File

@ -124,11 +124,33 @@ struct cf_msh3_ctx {
bool handshake_complete; bool handshake_complete;
bool handshake_succeeded; bool handshake_succeeded;
bool connected; bool connected;
BIT(initialized);
/* Flags written by curl thread */ /* Flags written by curl thread */
BIT(verbose); BIT(verbose);
BIT(active); BIT(active);
}; };
static void h3_stream_hash_free(void *stream);
static void cf_msh3_ctx_init(struct cf_msh3_ctx *ctx,
const struct Curl_addrinfo *ai)
{
DEBUGASSERT(!ctx->initialized);
Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
ctx->initialized = TRUE;
}
static void cf_msh3_ctx_free(struct cf_msh3_ctx *ctx)
{
if(ctx && ctx->initialized) {
Curl_hash_destroy(&ctx->streams);
}
free(ctx);
}
static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data); static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data);
/* How to access `call_data` from a cf_msh3 filter */ /* How to access `call_data` from a cf_msh3 filter */
@ -798,7 +820,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
CURLcode result; CURLcode result;
bool verify; bool verify;
Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); DEBUGASSERT(ctx->initialized);
conn_config = Curl_ssl_cf_get_primary_config(cf); conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!conn_config) if(!conn_config)
return CURLE_FAILED_INIT; return CURLE_FAILED_INIT;
@ -925,7 +947,6 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
MsH3ApiClose(ctx->api); MsH3ApiClose(ctx->api);
ctx->api = NULL; ctx->api = NULL;
} }
Curl_hash_destroy(&ctx->streams);
if(ctx->active) { if(ctx->active) {
/* We share our socket at cf->conn->sock[cf->sockindex] when active. /* We share our socket at cf->conn->sock[cf->sockindex] when active.
@ -964,10 +985,11 @@ static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
CF_DATA_SAVE(save, cf, data); CF_DATA_SAVE(save, cf, data);
cf_msh3_close(cf, data); cf_msh3_close(cf, data);
free(cf->ctx); if(cf->ctx) {
cf_msh3_ctx_free(cf->ctx);
cf->ctx = NULL; cf->ctx = NULL;
}
/* no CF_DATA_RESTORE(cf, save); its gone */ /* no CF_DATA_RESTORE(cf, save); its gone */
} }
static CURLcode cf_msh3_query(struct Curl_cfilter *cf, static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
@ -1066,9 +1088,7 @@ CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto out; goto out;
} }
Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC); cf_msh3_ctx_init(ctx, ai);
ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
@ -1076,7 +1096,7 @@ out:
*pcf = (!result)? cf : NULL; *pcf = (!result)? cf : NULL;
if(result) { if(result) {
Curl_safefree(cf); Curl_safefree(cf);
Curl_safefree(ctx); cf_msh3_ctx_free(ctx);
} }
return result; return result;

View File

@ -138,6 +138,7 @@ struct cf_ngtcp2_ctx {
uint64_t used_bidi_streams; /* bidi streams we have opened */ uint64_t used_bidi_streams; /* bidi streams we have opened */
uint64_t max_bidi_streams; /* max bidi streams we can open */ uint64_t max_bidi_streams; /* max bidi streams we can open */
int qlogfd; int qlogfd;
BIT(initialized);
BIT(shutdown_started); /* queued shutdown packets */ BIT(shutdown_started); /* queued shutdown packets */
}; };
@ -146,6 +147,34 @@ struct cf_ngtcp2_ctx {
#define CF_CTX_CALL_DATA(cf) \ #define CF_CTX_CALL_DATA(cf) \
((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
static void h3_stream_hash_free(void *stream);
static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx)
{
DEBUGASSERT(!ctx->initialized);
ctx->qlogfd = -1;
ctx->version = NGTCP2_PROTO_VER_MAX;
ctx->max_stream_window = H3_STREAM_WINDOW_SIZE;
ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
ctx->initialized = TRUE;
}
static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx)
{
if(ctx && ctx->initialized) {
Curl_bufcp_free(&ctx->stream_bufcp);
Curl_dyn_free(&ctx->scratch);
Curl_hash_clean(&ctx->streams);
Curl_hash_destroy(&ctx->streams);
Curl_ssl_peer_cleanup(&ctx->peer);
}
free(ctx);
}
struct pkt_io_ctx; struct pkt_io_ctx;
static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
struct Curl_easy *data, struct Curl_easy *data,
@ -1963,27 +1992,22 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
return result; return result;
} }
static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx)
{ {
struct cf_call_data save = ctx->call_data; struct cf_call_data save = ctx->call_data;
if(!ctx->initialized)
return;
if(ctx->qlogfd != -1) { if(ctx->qlogfd != -1) {
close(ctx->qlogfd); close(ctx->qlogfd);
} }
ctx->qlogfd = -1;
Curl_vquic_tls_cleanup(&ctx->tls); Curl_vquic_tls_cleanup(&ctx->tls);
vquic_ctx_free(&ctx->q); vquic_ctx_free(&ctx->q);
if(ctx->h3conn) if(ctx->h3conn)
nghttp3_conn_del(ctx->h3conn); nghttp3_conn_del(ctx->h3conn);
if(ctx->qconn) if(ctx->qconn)
ngtcp2_conn_del(ctx->qconn); ngtcp2_conn_del(ctx->qconn);
Curl_bufcp_free(&ctx->stream_bufcp);
Curl_dyn_free(&ctx->scratch);
Curl_hash_clean(&ctx->streams);
Curl_hash_destroy(&ctx->streams);
Curl_ssl_peer_cleanup(&ctx->peer);
memset(ctx, 0, sizeof(*ctx));
ctx->qlogfd = -1;
ctx->call_data = save; ctx->call_data = save;
} }
@ -2088,7 +2112,7 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
CF_DATA_SAVE(save, cf, data); CF_DATA_SAVE(save, cf, data);
if(ctx && ctx->qconn) { if(ctx && ctx->qconn) {
cf_ngtcp2_conn_close(cf, data); cf_ngtcp2_conn_close(cf, data);
cf_ngtcp2_ctx_clear(ctx); cf_ngtcp2_ctx_close(ctx);
CURL_TRC_CF(data, cf, "close"); CURL_TRC_CF(data, cf, "close");
} }
cf->connected = FALSE; cf->connected = FALSE;
@ -2097,18 +2121,11 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{ {
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
CURL_TRC_CF(data, cf, "destroy"); CURL_TRC_CF(data, cf, "destroy");
if(ctx) { if(cf->ctx) {
cf_ngtcp2_ctx_clear(ctx); cf_ngtcp2_ctx_free(cf->ctx);
free(ctx);
}
cf->ctx = NULL; cf->ctx = NULL;
/* No CF_DATA_RESTORE(cf, save) possible */ }
(void)save;
} }
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
@ -2190,14 +2207,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
const struct Curl_sockaddr_ex *sockaddr = NULL; const struct Curl_sockaddr_ex *sockaddr = NULL;
int qfd; int qfd;
ctx->version = NGTCP2_PROTO_VER_MAX; DEBUGASSERT(ctx->initialized);
ctx->max_stream_window = H3_STREAM_WINDOW_SIZE;
ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result) if(result)
return result; return result;
@ -2512,8 +2522,7 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto out; goto out;
} }
ctx->qlogfd = -1; cf_ngtcp2_ctx_init(ctx);
cf_ngtcp2_ctx_clear(ctx);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result) if(result)
@ -2534,7 +2543,7 @@ out:
if(udp_cf) if(udp_cf)
Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
Curl_safefree(cf); Curl_safefree(cf);
Curl_safefree(ctx); cf_ngtcp2_ctx_free(ctx);
} }
return result; return result;
} }

View File

@ -293,6 +293,7 @@ struct cf_osslq_ctx {
struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */ struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */
size_t max_stream_window; /* max flow window for one stream */ size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */ uint64_t max_idle_ms; /* max idle time for QUIC connection */
BIT(initialized);
BIT(got_first_byte); /* if first byte was received */ BIT(got_first_byte); /* if first byte was received */
BIT(x509_store_setup); /* if x509 store has been set up */ BIT(x509_store_setup); /* if x509 store has been set up */
BIT(protocol_shutdown); /* QUIC connection is shut down */ BIT(protocol_shutdown); /* QUIC connection is shut down */
@ -300,19 +301,35 @@ struct cf_osslq_ctx {
BIT(need_send); /* QUIC connection needs to send */ BIT(need_send); /* QUIC connection needs to send */
}; };
static void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx) static void h3_stream_hash_free(void *stream);
static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx)
{
DEBUGASSERT(!ctx->initialized);
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
ctx->initialized = TRUE;
}
static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx)
{
if(ctx && ctx->initialized) {
Curl_bufcp_free(&ctx->stream_bufcp);
Curl_hash_clean(&ctx->streams);
Curl_hash_destroy(&ctx->streams);
Curl_ssl_peer_cleanup(&ctx->peer);
}
free(ctx);
}
static void cf_osslq_ctx_close(struct cf_osslq_ctx *ctx)
{ {
struct cf_call_data save = ctx->call_data; struct cf_call_data save = ctx->call_data;
cf_osslq_h3conn_cleanup(&ctx->h3); cf_osslq_h3conn_cleanup(&ctx->h3);
Curl_vquic_tls_cleanup(&ctx->tls); Curl_vquic_tls_cleanup(&ctx->tls);
vquic_ctx_free(&ctx->q); vquic_ctx_free(&ctx->q);
Curl_bufcp_free(&ctx->stream_bufcp);
Curl_hash_clean(&ctx->streams);
Curl_hash_destroy(&ctx->streams);
Curl_ssl_peer_cleanup(&ctx->peer);
memset(ctx, 0, sizeof(*ctx));
ctx->call_data = save; ctx->call_data = save;
} }
@ -401,7 +418,7 @@ static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data)
(SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID), (SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID),
NULL, 0); NULL, 0);
} }
cf_osslq_ctx_clear(ctx); cf_osslq_ctx_close(ctx);
} }
cf->connected = FALSE; cf->connected = FALSE;
@ -417,8 +434,7 @@ static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
CURL_TRC_CF(data, cf, "destroy"); CURL_TRC_CF(data, cf, "destroy");
if(ctx) { if(ctx) {
CURL_TRC_CF(data, cf, "cf_osslq_destroy()"); CURL_TRC_CF(data, cf, "cf_osslq_destroy()");
cf_osslq_ctx_clear(ctx); cf_osslq_ctx_free(ctx);
free(ctx);
} }
cf->ctx = NULL; cf->ctx = NULL;
/* No CF_DATA_RESTORE(cf, save) possible */ /* No CF_DATA_RESTORE(cf, save) possible */
@ -1148,9 +1164,7 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
BIO *bio = NULL; BIO *bio = NULL;
BIO_ADDR *baddr = NULL; BIO_ADDR *baddr = NULL;
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, DEBUGASSERT(ctx->initialized);
H3_STREAM_POOL_SPARES);
Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result) if(result)
goto out; goto out;
@ -2330,7 +2344,7 @@ CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto out; goto out;
} }
cf_osslq_ctx_clear(ctx); cf_osslq_ctx_init(ctx);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result) if(result)
@ -2351,7 +2365,7 @@ out:
if(udp_cf) if(udp_cf)
Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
Curl_safefree(cf); Curl_safefree(cf);
Curl_safefree(ctx); cf_osslq_ctx_free(ctx);
} }
return result; return result;
} }

View File

@ -100,12 +100,15 @@ struct cf_quiche_ctx {
struct bufc_pool stream_bufcp; /* chunk pool for streams */ struct bufc_pool stream_bufcp; /* chunk pool for streams */
struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */ struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */
curl_off_t data_recvd; curl_off_t data_recvd;
BIT(initialized);
BIT(goaway); /* got GOAWAY from server */ BIT(goaway); /* got GOAWAY from server */
BIT(x509_store_setup); /* if x509 store has been set up */ BIT(x509_store_setup); /* if x509 store has been set up */
BIT(shutdown_started); /* queued shutdown packets */ BIT(shutdown_started); /* queued shutdown packets */
}; };
#ifdef DEBUG_QUICHE #ifdef DEBUG_QUICHE
/* initialize debug log callback only once */
static int debug_log_init = 0;
static void quiche_debug_log(const char *line, void *argp) static void quiche_debug_log(const char *line, void *argp)
{ {
(void)argp; (void)argp;
@ -113,17 +116,27 @@ static void quiche_debug_log(const char *line, void *argp)
} }
#endif #endif
static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) static void h3_stream_hash_free(void *stream);
static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx)
{ {
if(ctx) { DEBUGASSERT(!ctx->initialized);
if(ctx->h3c) #ifdef DEBUG_QUICHE
quiche_h3_conn_free(ctx->h3c); if(!debug_log_init) {
if(ctx->h3config) quiche_enable_debug_logging(quiche_debug_log, NULL);
quiche_h3_config_free(ctx->h3config); debug_log_init = 1;
if(ctx->qconn) }
quiche_conn_free(ctx->qconn); #endif
if(ctx->cfg) Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
quiche_config_free(ctx->cfg); H3_STREAM_POOL_SPARES);
Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
ctx->data_recvd = 0;
ctx->initialized = TRUE;
}
static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx)
{
if(ctx && ctx->initialized) {
/* quiche just freed it */ /* quiche just freed it */
ctx->tls.ossl.ssl = NULL; ctx->tls.ossl.ssl = NULL;
Curl_vquic_tls_cleanup(&ctx->tls); Curl_vquic_tls_cleanup(&ctx->tls);
@ -132,9 +145,20 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
Curl_bufcp_free(&ctx->stream_bufcp); Curl_bufcp_free(&ctx->stream_bufcp);
Curl_hash_clean(&ctx->streams); Curl_hash_clean(&ctx->streams);
Curl_hash_destroy(&ctx->streams); Curl_hash_destroy(&ctx->streams);
memset(ctx, 0, sizeof(*ctx));
} }
free(ctx);
}
static void cf_quiche_ctx_close(struct cf_quiche_ctx *ctx)
{
if(ctx->h3c)
quiche_h3_conn_free(ctx->h3c);
if(ctx->h3config)
quiche_h3_config_free(ctx->h3config);
if(ctx->qconn)
quiche_conn_free(ctx->qconn);
if(ctx->cfg)
quiche_config_free(ctx->cfg);
} }
static CURLcode cf_flush_egress(struct Curl_cfilter *cf, static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
@ -1240,7 +1264,7 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
return result; return result;
} }
static CURLcode cf_connect_start(struct Curl_cfilter *cf, static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
struct Curl_easy *data) struct Curl_easy *data)
{ {
struct cf_quiche_ctx *ctx = cf->ctx; struct cf_quiche_ctx *ctx = cf->ctx;
@ -1249,19 +1273,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
const struct Curl_sockaddr_ex *sockaddr; const struct Curl_sockaddr_ex *sockaddr;
DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD); DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
DEBUGASSERT(ctx->initialized);
#ifdef DEBUG_QUICHE
/* initialize debug log callback only once */
static int debug_log_init = 0;
if(!debug_log_init) {
quiche_enable_debug_logging(quiche_debug_log, NULL);
debug_log_init = 1;
}
#endif
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
ctx->data_recvd = 0;
result = vquic_ctx_init(&ctx->q); result = vquic_ctx_init(&ctx->q);
if(result) if(result)
@ -1403,7 +1415,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
} }
if(!ctx->qconn) { if(!ctx->qconn) {
result = cf_connect_start(cf, data); result = cf_quiche_ctx_open(cf, data);
if(result) if(result)
goto out; goto out;
ctx->started_at = ctx->q.last_op; ctx->started_at = ctx->q.last_op;
@ -1515,24 +1527,21 @@ out:
static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{ {
struct cf_quiche_ctx *ctx = cf->ctx; if(cf->ctx) {
if(ctx) {
bool done; bool done;
(void)cf_quiche_shutdown(cf, data, &done); (void)cf_quiche_shutdown(cf, data, &done);
cf_quiche_ctx_clear(ctx); cf_quiche_ctx_close(cf->ctx);
} }
} }
static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{ {
struct cf_quiche_ctx *ctx = cf->ctx;
(void)data; (void)data;
cf_quiche_ctx_clear(ctx); if(cf->ctx) {
free(ctx); cf_quiche_ctx_free(cf->ctx);
cf->ctx = NULL; cf->ctx = NULL;
} }
}
static CURLcode cf_quiche_query(struct Curl_cfilter *cf, static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
struct Curl_easy *data, struct Curl_easy *data,
@ -1652,6 +1661,7 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto out; goto out;
} }
cf_quiche_ctx_init(ctx);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result) if(result)
@ -1671,7 +1681,7 @@ out:
if(udp_cf) if(udp_cf)
Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
Curl_safefree(cf); Curl_safefree(cf);
Curl_safefree(ctx); cf_quiche_ctx_free(ctx);
} }
return result; return result;