uint32_t goaway_error; /* goaway error code from server */
int32_t remote_max_sid; /* max id processed by server */
int32_t local_max_sid; /* max id processed by us */
+ BIT(initialized);
+ BIT(via_h1_upgrade);
BIT(conn_closed);
BIT(rcvd_goaway);
BIT(sent_goaway);
#define CF_CTX_CALL_DATA(cf) \
((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);
+
+static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
{
- struct cf_call_data save = ctx->call_data;
+ 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;
+}
- if(ctx->h2) {
- nghttp2_session_del(ctx->h2);
+static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
+{
+ if(ctx && ctx->initialized) {
+ Curl_bufq_free(&ctx->inbufq);
+ Curl_bufq_free(&ctx->outbufq);
+ Curl_bufcp_free(&ctx->stream_bufcp);
+ Curl_dyn_free(&ctx->scratch);
+ Curl_hash_clean(&ctx->streams);
+ Curl_hash_destroy(&ctx->streams);
+ memset(ctx, 0, sizeof(*ctx));
}
- Curl_bufq_free(&ctx->inbufq);
- Curl_bufq_free(&ctx->outbufq);
- Curl_bufcp_free(&ctx->stream_bufcp);
- Curl_dyn_free(&ctx->scratch);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
- 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) {
- cf_h2_ctx_clear(ctx);
- free(ctx);
+ if(ctx->h2) {
+ nghttp2_session_del(ctx->h2);
}
}
struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
DEBUGASSERT(ctx);
- if(!stream)
+ if(!stream || !ctx->initialized)
return;
if(ctx->h2) {
static int error_callback(nghttp2_session *session, const char *msg,
size_t len, void *userp);
-/*
- * Initialize the cfilter context
- */
-static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool via_h1_upgrade)
+static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
nghttp2_session_callbacks *cbs = NULL;
DEBUGASSERT(!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;
+ DEBUGASSERT(ctx->initialized);
rc = nghttp2_session_callbacks_new(&cbs);
if(rc) {
}
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
* in the H1 request and we upgrade from there. This stream
* is opened implicitly as #1. */
/* all set, traffic will be send on connect */
result = CURLE_OK;
CURL_TRC_CF(data, cf, "[0] created h2 session%s",
- via_h1_upgrade? " (via h1 upgrade)" : "");
+ ctx->via_h1_upgrade? " (via h1 upgrade)" : "");
out:
if(cbs)
*done = FALSE;
CF_DATA_SAVE(save, cf, data);
+ DEBUGASSERT(ctx->initialized);
if(!ctx->h2) {
- result = cf_h2_ctx_init(cf, data, FALSE);
+ result = cf_h2_ctx_open(cf, data);
if(result)
goto out;
}
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- cf_h2_ctx_clear(ctx);
+ cf_h2_ctx_close(ctx);
CF_DATA_RESTORE(cf, save);
cf->connected = FALSE;
}
ctx = calloc(1, sizeof(*ctx));
if(!ctx)
goto out;
+ cf_h2_ctx_init(ctx, via_h1_upgrade);
result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx);
if(result)
ctx = NULL;
Curl_conn_cf_add(data, conn, sockindex, cf);
- result = cf_h2_ctx_init(cf, data, via_h1_upgrade);
out:
if(result)
ctx = calloc(1, sizeof(*ctx));
if(!ctx)
goto out;
+ cf_h2_ctx_init(ctx, via_h1_upgrade);
result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx);
if(result)
ctx = NULL;
Curl_conn_cf_insert_after(cf, cf_h2);
- result = cf_h2_ctx_init(cf_h2, data, via_h1_upgrade);
out:
if(result)
bool handshake_complete;
bool handshake_succeeded;
bool connected;
+ BIT(initialized);
/* Flags written by curl thread */
BIT(verbose);
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);
/* How to access `call_data` from a cf_msh3 filter */
CURLcode result;
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);
if(!conn_config)
return CURLE_FAILED_INIT;
MsH3ApiClose(ctx->api);
ctx->api = NULL;
}
- Curl_hash_destroy(&ctx->streams);
if(ctx->active) {
/* We share our socket at cf->conn->sock[cf->sockindex] when active.
CF_DATA_SAVE(save, cf, data);
cf_msh3_close(cf, data);
- free(cf->ctx);
- cf->ctx = NULL;
+ if(cf->ctx) {
+ cf_msh3_ctx_free(cf->ctx);
+ cf->ctx = NULL;
+ }
/* no CF_DATA_RESTORE(cf, save); its gone */
-
}
static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
- ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
- ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
+ cf_msh3_ctx_init(ctx, ai);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
*pcf = (!result)? cf : NULL;
if(result) {
Curl_safefree(cf);
- Curl_safefree(ctx);
+ cf_msh3_ctx_free(ctx);
}
return result;
uint64_t used_bidi_streams; /* bidi streams we have opened */
uint64_t max_bidi_streams; /* max bidi streams we can open */
int qlogfd;
+ BIT(initialized);
BIT(shutdown_started); /* queued shutdown packets */
};
#define CF_CTX_CALL_DATA(cf) \
((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;
static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
struct Curl_easy *data,
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;
+ if(!ctx->initialized)
+ return;
if(ctx->qlogfd != -1) {
close(ctx->qlogfd);
}
+ ctx->qlogfd = -1;
Curl_vquic_tls_cleanup(&ctx->tls);
vquic_ctx_free(&ctx->q);
if(ctx->h3conn)
nghttp3_conn_del(ctx->h3conn);
if(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;
}
CF_DATA_SAVE(save, cf, data);
if(ctx && ctx->qconn) {
cf_ngtcp2_conn_close(cf, data);
- cf_ngtcp2_ctx_clear(ctx);
+ cf_ngtcp2_ctx_close(ctx);
CURL_TRC_CF(data, cf, "close");
}
cf->connected = FALSE;
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");
- if(ctx) {
- cf_ngtcp2_ctx_clear(ctx);
- free(ctx);
+ if(cf->ctx) {
+ cf_ngtcp2_ctx_free(cf->ctx);
+ cf->ctx = NULL;
}
- cf->ctx = NULL;
- /* No CF_DATA_RESTORE(cf, save) possible */
- (void)save;
}
#ifdef USE_OPENSSL
const struct Curl_sockaddr_ex *sockaddr = NULL;
int qfd;
- 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);
-
+ DEBUGASSERT(ctx->initialized);
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result)
return result;
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- ctx->qlogfd = -1;
- cf_ngtcp2_ctx_clear(ctx);
+ cf_ngtcp2_ctx_init(ctx);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result)
if(udp_cf)
Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
Curl_safefree(cf);
- Curl_safefree(ctx);
+ cf_ngtcp2_ctx_free(ctx);
}
return result;
}
struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */
size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
+ BIT(initialized);
BIT(got_first_byte); /* if first byte was received */
BIT(x509_store_setup); /* if x509 store has been set up */
BIT(protocol_shutdown); /* QUIC connection is shut down */
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;
cf_osslq_h3conn_cleanup(&ctx->h3);
Curl_vquic_tls_cleanup(&ctx->tls);
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;
}
(SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID),
NULL, 0);
}
- cf_osslq_ctx_clear(ctx);
+ cf_osslq_ctx_close(ctx);
}
cf->connected = FALSE;
CURL_TRC_CF(data, cf, "destroy");
if(ctx) {
CURL_TRC_CF(data, cf, "cf_osslq_destroy()");
- cf_osslq_ctx_clear(ctx);
- free(ctx);
+ cf_osslq_ctx_free(ctx);
}
cf->ctx = NULL;
/* No CF_DATA_RESTORE(cf, save) possible */
BIO *bio = NULL;
BIO_ADDR *baddr = NULL;
- 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);
+ DEBUGASSERT(ctx->initialized);
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result)
goto out;
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- cf_osslq_ctx_clear(ctx);
+ cf_osslq_ctx_init(ctx);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result)
if(udp_cf)
Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
Curl_safefree(cf);
- Curl_safefree(ctx);
+ cf_osslq_ctx_free(ctx);
}
return result;
}
struct bufc_pool stream_bufcp; /* chunk pool for streams */
struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */
curl_off_t data_recvd;
+ BIT(initialized);
BIT(goaway); /* got GOAWAY from server */
BIT(x509_store_setup); /* if x509 store has been set up */
BIT(shutdown_started); /* queued shutdown packets */
};
#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)
{
(void)argp;
}
#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) {
- 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);
+ DEBUGASSERT(!ctx->initialized);
+#ifdef DEBUG_QUICHE
+ 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;
+ ctx->initialized = TRUE;
+}
+
+static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx)
+{
+ if(ctx && ctx->initialized) {
/* quiche just freed it */
ctx->tls.ossl.ssl = NULL;
Curl_vquic_tls_cleanup(&ctx->tls);
Curl_bufcp_free(&ctx->stream_bufcp);
Curl_hash_clean(&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,
return result;
}
-static CURLcode cf_connect_start(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
int rv;
const struct Curl_sockaddr_ex *sockaddr;
DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
-
-#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;
+ DEBUGASSERT(ctx->initialized);
result = vquic_ctx_init(&ctx->q);
if(result)
}
if(!ctx->qconn) {
- result = cf_connect_start(cf, data);
+ result = cf_quiche_ctx_open(cf, data);
if(result)
goto out;
ctx->started_at = ctx->q.last_op;
static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct cf_quiche_ctx *ctx = cf->ctx;
-
- if(ctx) {
+ if(cf->ctx) {
bool 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)
{
- struct cf_quiche_ctx *ctx = cf->ctx;
-
(void)data;
- cf_quiche_ctx_clear(ctx);
- free(ctx);
- cf->ctx = NULL;
+ if(cf->ctx) {
+ cf_quiche_ctx_free(cf->ctx);
+ cf->ctx = NULL;
+ }
}
static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
+ cf_quiche_ctx_init(ctx);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result)
if(udp_cf)
Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
Curl_safefree(cf);
- Curl_safefree(ctx);
+ cf_quiche_ctx_free(ctx);
}
return result;