From c36db68a5f58ec6c9135eff778b6e77c30e58088 Mon Sep 17 00:00:00 2001 From: dartraiden Date: Wed, 11 Sep 2024 21:49:43 +0300 Subject: libcurl: update to 8.10.0 --- libs/libcurl/src/vquic/curl_msh3.c | 66 +++---- libs/libcurl/src/vquic/curl_ngtcp2.c | 191 ++++++++++---------- libs/libcurl/src/vquic/curl_osslq.c | 163 +++++++++-------- libs/libcurl/src/vquic/curl_quiche.c | 331 ++++++++++++++++++----------------- libs/libcurl/src/vquic/vquic-tls.c | 2 +- libs/libcurl/src/vquic/vquic.c | 4 +- 6 files changed, 402 insertions(+), 355 deletions(-) (limited to 'libs/libcurl/src/vquic') diff --git a/libs/libcurl/src/vquic/curl_msh3.c b/libs/libcurl/src/vquic/curl_msh3.c index a780acc31f..464f7c5af6 100644 --- a/libs/libcurl/src/vquic/curl_msh3.c +++ b/libs/libcurl/src/vquic/curl_msh3.c @@ -119,16 +119,38 @@ struct cf_msh3_ctx { struct cf_call_data call_data; struct curltime connect_started; /* time the current attempt started */ struct curltime handshake_at; /* time connect handshake finished */ - struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */ + struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */ /* Flags written by msh3/msquic thread */ 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 */ @@ -158,7 +180,7 @@ struct stream_ctx { }; #define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)((data && ctx)? \ - Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL)) static void h3_stream_ctx_free(struct stream_ctx *stream) { @@ -191,7 +213,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); CURL_TRC_CF(data, cf, "data setup"); - if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) { h3_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -207,7 +229,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { CURL_TRC_CF(data, cf, "easy handle is done"); - Curl_hash_offt_remove(&ctx->streams, data->id); + Curl_hash_offt_remove(&ctx->streams, data->mid); } } @@ -593,7 +615,8 @@ out: } static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_msh3_ctx *ctx = cf->ctx; struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -603,7 +626,6 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, size_t nheader, i; ssize_t nwritten = -1; struct cf_call_data save; - bool eos; CF_DATA_SAVE(save, cf, data); @@ -646,21 +668,6 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, nva[i].ValueLength = e->valuelen; } - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - /* known request body size or -1 */ - eos = FALSE; - break; - default: - /* there is not request body */ - eos = TRUE; - stream->upload_done = TRUE; - break; - } - CURL_TRC_CF(data, cf, "req: send %zu headers", nheader); stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data, nva, nheader, @@ -813,7 +820,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, 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; @@ -904,7 +911,6 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "handshake succeeded"); cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; cf->connected = TRUE; cf->conn->alpn = CURL_HTTP_VERSION_3; *done = TRUE; @@ -940,7 +946,6 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) 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. @@ -979,10 +984,11 @@ static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 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, @@ -1081,9 +1087,7 @@ CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, 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); @@ -1091,7 +1095,7 @@ out: *pcf = (!result)? cf : NULL; if(result) { Curl_safefree(cf); - Curl_safefree(ctx); + cf_msh3_ctx_free(ctx); } return result; diff --git a/libs/libcurl/src/vquic/curl_ngtcp2.c b/libs/libcurl/src/vquic/curl_ngtcp2.c index 790e3c6ff4..54f3ce6929 100644 --- a/libs/libcurl/src/vquic/curl_ngtcp2.c +++ b/libs/libcurl/src/vquic/curl_ngtcp2.c @@ -132,12 +132,13 @@ struct cf_ngtcp2_ctx { struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ struct dynbuf scratch; /* temp buffer for header construction */ - struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */ + struct Curl_hash streams; /* hash `data->mid` 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 */ 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 */ }; @@ -146,6 +147,34 @@ struct cf_ngtcp2_ctx { #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, @@ -174,7 +203,7 @@ struct h3_stream_ctx { }; #define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\ - data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL)) #define H3_STREAM_CTX_ID(ctx,id) ((struct h3_stream_ctx *)(\ Curl_hash_offt_get(&(ctx)->streams, (id)))) @@ -197,10 +226,8 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - if(!data) { - failf(data, "initialization failure, transfer not http initialized"); + if(!data) return CURLE_FAILED_INIT; - } if(stream) return CURLE_OK; @@ -216,7 +243,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, stream->sendbuf_len_in_flight = 0; Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) { h3_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -241,7 +268,7 @@ static void cf_ngtcp2_stream_close(struct Curl_cfilter *cf, NGHTTP3_H3_REQUEST_CANCELLED); result = cf_progress_egress(cf, data, NULL); if(result) - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cancel stream -> %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cancel stream -> %d", stream->id, result); } } @@ -252,10 +279,10 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] easy handle is done", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] easy handle is done", stream->id); cf_ngtcp2_stream_close(cf, data, stream); - Curl_hash_offt_remove(&ctx->streams, data->id); + Curl_hash_offt_remove(&ctx->streams, data->mid); } } @@ -265,7 +292,6 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, struct h3_stream_ctx **pstream) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *sdata; struct h3_stream_ctx *stream; (void)cf; @@ -275,8 +301,10 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, return data; } else { + struct Curl_llist_node *e; DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn != data->conn) continue; stream = H3_STREAM_CTX(ctx, sdata); @@ -490,12 +518,12 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, if(!data) data = CF_DATA_CURRENT(cf); if(data) - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read_stream(len=%zu) -> %zd", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read_stream(len=%zu) -> %zd", stream_id, buflen, nconsumed); if(nconsumed < 0) { struct h3_stream_ctx *stream = H3_STREAM_CTX_ID(ctx, stream_id); if(data && stream) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] error on known stream, " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] error on known stream, " "reset=%d, closed=%d", stream_id, stream->reset, stream->closed); } @@ -555,8 +583,8 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, } rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] quic close(app_error=%" - CURL_PRIu64 ") -> %d", stream_id, (curl_uint64_t)app_error_code, + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] quic close(app_error=%" + FMT_PRIu64 ") -> %d", stream_id, (curl_uint64_t)app_error_code, rv); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { cf_ngtcp2_h3_err_set(cf, data, rv); @@ -581,7 +609,7 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t sid, (void)data; rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -619,8 +647,8 @@ static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, (void)tconn; ctx->max_bidi_streams = max_streams; if(data) - CURL_TRC_CF(data, cf, "max bidi streams now %" CURL_PRIu64 - ", used %" CURL_PRIu64, (curl_uint64_t)ctx->max_bidi_streams, + CURL_TRC_CF(data, cf, "max bidi streams now %" FMT_PRIu64 + ", used %" FMT_PRIu64, (curl_uint64_t)ctx->max_bidi_streams, (curl_uint64_t)ctx->used_bidi_streams); return 0; } @@ -646,8 +674,7 @@ static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t sid, } s_data = get_stream_easy(cf, data, stream_id, &stream); if(s_data && stream && stream->quic_flow_blocked) { - CURL_TRC_CF(s_data, cf, "[%" CURL_PRId64 "] unblock quic flow", - stream_id); + CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow", stream_id); stream->quic_flow_blocked = FALSE; h3_drain_stream(cf, s_data); } @@ -705,6 +732,11 @@ static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, return 0; } +#if defined(_MSC_VER) && defined(_DLL) +# pragma warning(push) +# pragma warning(disable:4232) /* MSVC extension, dllimport identity */ +#endif + static ngtcp2_callbacks ng_callbacks = { ngtcp2_crypto_client_initial_cb, NULL, /* recv_client_initial */ @@ -748,6 +780,10 @@ static ngtcp2_callbacks ng_callbacks = { NULL, /* early_data_rejected */ }; +#if defined(_MSC_VER) && defined(_DLL) +# pragma warning(pop) +#endif + /** * Connection maintenance like timeouts on packet ACKs etc. are done by us, not * the OS like for TCP. POLL events on the socket therefore are not @@ -858,11 +894,11 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid, if(stream->error3 != NGHTTP3_H3_NO_ERROR) { stream->reset = TRUE; stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] RESET: error %" CURL_PRIu64, + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] RESET: error %" FMT_PRIu64, stream->id, stream->error3); } else { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] CLOSED", stream->id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->id); } h3_drain_stream(cf, data); return 0; @@ -878,7 +914,7 @@ static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, if(!stream->xfer_result) { stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); if(stream->xfer_result) - CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] error %d writing %zu " + CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] error %d writing %zu " "bytes of headers", stream->id, stream->xfer_result, blen); } } @@ -894,7 +930,7 @@ static void h3_xfer_write_resp(struct Curl_cfilter *cf, stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); /* If the transfer write is errored, we do not want any more data */ if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] error %d writing %zu bytes " + CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] error %d writing %zu bytes " "of data", stream->id, stream->xfer_result, blen); } } @@ -917,12 +953,12 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, h3_xfer_write_resp(cf, data, stream, (char *)buf, blen, FALSE); if(blen) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] ACK %zu bytes of DATA", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] ACK %zu bytes of DATA", stream->id, blen); ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, blen); ngtcp2_conn_extend_max_offset(ctx->qconn, blen); } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu", stream->id, blen); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu", stream->id, blen); return 0; } @@ -960,7 +996,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, /* add a CRLF only if we have received some headers */ h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] end_headers, status=%d", stream_id, stream->status_code); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; @@ -1008,7 +1044,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, if(!result) h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch), Curl_dyn_len(&ctx->scratch), FALSE); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s", stream_id, Curl_dyn_ptr(&ctx->scratch)); if(result) { return -1; @@ -1016,7 +1052,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, } else { /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s", stream_id, (int)h3name.len, h3name.base, (int)h3val.len, h3val.base); Curl_dyn_reset(&ctx->scratch); @@ -1049,7 +1085,7 @@ static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, app_error_code); if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; @@ -1068,9 +1104,9 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid, rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, app_error_code); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv); if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; @@ -1163,14 +1199,13 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, (void)cf; if(stream->reset) { - failf(data, - "HTTP/3 stream %" CURL_PRId64 " reset by server", stream->id); + failf(data, "HTTP/3 stream %" FMT_PRId64 " reset by server", stream->id); *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; goto out; } else if(!stream->resp_hds_complete) { failf(data, - "HTTP/3 stream %" CURL_PRId64 " was closed cleanly, but before " + "HTTP/3 stream %" FMT_PRId64 " was closed cleanly, but before " "getting all response header fields, treated as error", stream->id); *err = CURLE_HTTP3; @@ -1217,7 +1252,7 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] xfer write failed", stream->id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id); cf_ngtcp2_stream_close(cf, data, stream); *err = stream->xfer_result; nread = -1; @@ -1242,7 +1277,7 @@ out: nread = -1; } } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_recv(blen=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(blen=%zu) -> %zd, %d", stream? stream->id : -1, blen, nread, *err); CF_DATA_RESTORE(cf, save); return nread; @@ -1275,7 +1310,7 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { int rv = nghttp3_conn_resume_stream(conn, stream_id); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return NGHTTP3_ERR_CALLBACK_FAILURE; } } return 0; @@ -1333,14 +1368,13 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, } else if(!nwritten) { /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> AGAIN", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> AGAIN", stream->id); return NGHTTP3_ERR_WOULDBLOCK; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" - CURL_FORMAT_CURL_OFF_T ")", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> " + "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", stream->id, (int)nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", nwritten, Curl_bufq_len(&stream->sendbuf), @@ -1454,12 +1488,12 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, if(rc) { switch(rc) { case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%" CURL_PRId64 "] failed to send, " + CURL_TRC_CF(data, cf, "h3sid[%" FMT_PRId64 "] failed to send, " "connection is closing", stream->id); break; default: - CURL_TRC_CF(data, cf, "h3sid[%" CURL_PRId64 "] failed to send -> " - "%d (%s)", stream->id, rc, ngtcp2_strerror(rc)); + CURL_TRC_CF(data, cf, "h3sid[%" FMT_PRId64 "] failed to send -> " + "%d (%s)", stream->id, rc, nghttp3_strerror(rc)); break; } *err = CURLE_SEND_ERROR; @@ -1468,10 +1502,10 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, } if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" CURL_PRId64 "] OPENED stream for %s", + infof(data, "[HTTP/3] [%" FMT_PRId64 "] OPENED stream for %s", stream->id, data->state.url); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" CURL_PRId64 "] [%.*s: %.*s]", stream->id, + infof(data, "[HTTP/3] [%" FMT_PRId64 "] [%.*s: %.*s]", stream->id, (int)nva[i].namelen, nva[i].name, (int)nva[i].valuelen, nva[i].value); } @@ -1484,7 +1518,8 @@ out: } static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -1500,6 +1535,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, pktx_init(&pktx, cf, data); *err = CURLE_OK; + (void)eos; /* TODO: use for stream EOF and block handling */ result = cf_progress_ingress(cf, data, &pktx); if(result) { *err = result; @@ -1521,7 +1557,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, stream = H3_STREAM_CTX(ctx, data); } else if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] xfer write failed", stream->id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id); cf_ngtcp2_stream_close(cf, data, stream); *err = stream->xfer_result; sent = -1; @@ -1534,13 +1570,13 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, * body. This happens on 30x or 40x responses. * We silently discard the data sent, since this is not a transport * error situation. */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] discarding data" + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data" "on closed stream with response", stream->id); *err = CURLE_OK; sent = (ssize_t)len; goto out; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) " "-> stream closed", stream->id, len); *err = CURLE_HTTP3; sent = -1; @@ -1554,7 +1590,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else { sent = Curl_bufq_write(&stream->sendbuf, buf, len, err); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send, add to " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to " "sendbuf(len=%zu) -> %zd, %d", stream->id, len, sent, *err); if(sent < 0) { @@ -1576,7 +1612,7 @@ out: *err = result; sent = -1; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %zd, %d", stream? stream->id : -1, len, sent, *err); CF_DATA_RESTORE(cf, save); return sent; @@ -1589,7 +1625,6 @@ static CURLcode qng_verify_peer(struct Curl_cfilter *cf, cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -1723,7 +1758,7 @@ static ssize_t read_pkt_to_send(void *userp, struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); DEBUGASSERT(ndatalen == -1); nghttp3_conn_block_stream(ctx->h3conn, stream_id); - CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] block quic flow", + CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] block quic flow", (curl_int64_t)stream_id); DEBUGASSERT(stream); if(stream) @@ -1960,27 +1995,22 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, 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; } @@ -2027,7 +2057,7 @@ static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf, (uint8_t *)buffer, sizeof(buffer), &ctx->last_error, pktx.ts); CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" - CURL_PRIu64 ") -> %d", ctx->last_error.type, + FMT_PRIu64 ") -> %d", ctx->last_error.type, (curl_uint64_t)ctx->last_error.error_code, (int)nwritten); if(nwritten > 0) { Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, @@ -2085,7 +2115,7 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) 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; @@ -2094,18 +2124,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) { - 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 @@ -2187,14 +2210,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, 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; @@ -2389,7 +2405,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, } else /* transport params not arrived yet? take our default. */ *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi); - CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: " + CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: " "MAX_CONCURRENT -> %d (%zu in use)", cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn)); CF_DATA_RESTORE(cf, save); @@ -2509,8 +2525,7 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, 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) @@ -2531,7 +2546,7 @@ out: 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; } diff --git a/libs/libcurl/src/vquic/curl_osslq.c b/libs/libcurl/src/vquic/curl_osslq.c index 59c1f5053e..1f83726e93 100644 --- a/libs/libcurl/src/vquic/curl_osslq.c +++ b/libs/libcurl/src/vquic/curl_osslq.c @@ -290,9 +290,10 @@ struct cf_osslq_ctx { struct curltime first_byte_at; /* when first byte was recvd */ struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ - struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */ + struct Curl_hash streams; /* hash `data->mid` 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 */ @@ -300,19 +301,35 @@ struct cf_osslq_ctx { 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; } @@ -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), NULL, 0); } - cf_osslq_ctx_clear(ctx); + cf_osslq_ctx_close(ctx); } cf->connected = FALSE; @@ -417,8 +434,9 @@ static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) CURL_TRC_CF(data, cf, "destroy"); if(ctx) { CURL_TRC_CF(data, cf, "cf_osslq_destroy()"); - cf_osslq_ctx_clear(ctx); - free(ctx); + if(ctx->tls.ossl.ssl) + cf_osslq_ctx_close(ctx); + cf_osslq_ctx_free(ctx); } cf->ctx = NULL; /* No CF_DATA_RESTORE(cf, save) possible */ @@ -435,7 +453,7 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) { /* rejected, we are full */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] rejecting remote stream", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] rejecting remote stream", stream_id); SSL_free(stream_ssl); return CURLE_FAILED_INIT; @@ -446,12 +464,12 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, nstream->id = stream_id; nstream->ssl = stream_ssl; Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] accepted remote uni stream", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] accepted remote uni stream", stream_id); break; } default: - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reject remote non-uni-read" + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reject remote non-uni-read" " stream", stream_id); SSL_free(stream_ssl); return CURLE_FAILED_INIT; @@ -546,7 +564,6 @@ static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf, cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -573,7 +590,7 @@ struct h3_stream_ctx { }; #define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\ - data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL)) static void h3_stream_ctx_free(struct h3_stream_ctx *stream) { @@ -596,10 +613,8 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, struct cf_osslq_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - if(!data) { - failf(data, "initialization failure, transfer not http initialized"); + if(!data) return CURLE_FAILED_INIT; - } if(stream) return CURLE_OK; @@ -620,7 +635,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, stream->recv_buf_nonflow = 0; Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) { h3_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -635,7 +650,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] easy handle is done", + CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] easy handle is done", stream->s.id); if(ctx->h3.conn && !stream->closed) { nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id); @@ -645,7 +660,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) stream->closed = TRUE; } - Curl_hash_offt_remove(&ctx->streams, data->id); + Curl_hash_offt_remove(&ctx->streams, data->mid); } } @@ -655,7 +670,6 @@ static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf, { struct cf_osslq_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - struct Curl_easy *sdata; if(stream && stream->s.id == stream_id) { return &stream->s; @@ -670,8 +684,10 @@ static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf, return &ctx->h3.s_qpack_dec; } else { + struct Curl_llist_node *e; DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn != data->conn) continue; stream = H3_STREAM_CTX(ctx, sdata); @@ -732,11 +748,11 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, if(stream->error3 != NGHTTP3_H3_NO_ERROR) { stream->reset = TRUE; stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] RESET: error %" CURL_PRIu64, + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] RESET: error %" FMT_PRIu64, stream->s.id, stream->error3); } else { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] CLOSED", stream->s.id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->s.id); } h3_drain_stream(cf, data); return 0; @@ -796,12 +812,12 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, result = write_resp_raw(cf, data, buf, buflen, TRUE); if(result) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu, ERROR %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, ERROR %d", stream->s.id, buflen, result); return NGHTTP3_ERR_CALLBACK_FAILURE; } stream->download_recvd += (curl_off_t)buflen; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu, total=%zd", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, total=%zd", stream->s.id, buflen, stream->download_recvd); h3_drain_stream(cf, data); return 0; @@ -819,7 +835,7 @@ static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, (void)conn; (void)stream_id; if(stream) - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] deferred consume %zu bytes", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] deferred consume %zu bytes", stream->s.id, consumed); return 0; } @@ -857,7 +873,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, return -1; ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", stream->status_code); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", stream_id, line); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s", stream_id, line); result = write_resp_raw(cf, data, line, ncopy, FALSE); if(result) { return -1; @@ -865,7 +881,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, } else { /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s", stream_id, (int)h3name.len, h3name.base, (int)h3val.len, h3val.base); result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); @@ -910,7 +926,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, return -1; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] end_headers, status=%d", stream_id, stream->status_code); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; @@ -934,7 +950,7 @@ static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t sid, if(!stream || !stream->s.ssl) return 0; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] stop_sending", stream_id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] stop_sending", stream_id); cf_osslq_stream_close(&stream->s); return 0; } @@ -954,7 +970,7 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid, SSL_STREAM_RESET_ARGS args = {0}; args.quic_error_code = app_error_code; rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args)); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv); if(!rv) { return NGHTTP3_ERR_CALLBACK_FAILURE; } @@ -1014,14 +1030,13 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, } else if(!nwritten) { /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> AGAIN", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> AGAIN", stream->s.id); return NGHTTP3_ERR_WOULDBLOCK; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" - CURL_FORMAT_CURL_OFF_T ")", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> " + "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", stream->s.id, (int)nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", nwritten, Curl_bufq_len(&stream->sendbuf), @@ -1147,9 +1162,7 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, 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; @@ -1263,7 +1276,7 @@ static ssize_t h3_quic_recv(void *reader_ctx, return -1; } else if(detail == SSL_ERROR_ZERO_RETURN) { - CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] h3_quic_recv -> EOS", + CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] h3_quic_recv -> EOS", x->s->id); x->s->recvd_eos = TRUE; return 0; @@ -1272,8 +1285,8 @@ static ssize_t h3_quic_recv(void *reader_ctx, SSL_STREAM_STATE_RESET_REMOTE) { uint64_t app_error_code = NGHTTP3_H3_NO_ERROR; SSL_get_stream_read_error_code(x->s->ssl, &app_error_code); - CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] h3_quic_recv -> RESET, " - "rv=%d, app_err=%" CURL_PRIu64, + CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] h3_quic_recv -> RESET, " + "rv=%d, app_err=%" FMT_PRIu64, x->s->id, rv, (curl_uint64_t)app_error_code); if(app_error_code != NGHTTP3_H3_NO_ERROR) { x->s->reset = TRUE; @@ -1329,7 +1342,7 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) { nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id, buf, blen, 0); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] forward %zu bytes " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] forward %zu bytes " "to nghttp3 -> %zd", s->id, blen, nread); if(nread < 0) { failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s", @@ -1368,7 +1381,7 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, NGHTTP3_H3_NO_ERROR); s->closed = TRUE; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] close nghttp3 stream -> %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] close nghttp3 stream -> %d", s->id, rv); if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { failf(data, "nghttp3_conn_close_stream returned error: %s", @@ -1381,7 +1394,7 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, } out: if(result) - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_osslq_stream_recv -> %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_osslq_stream_recv -> %d", s->id, result); return result; } @@ -1422,11 +1435,12 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, } if(ctx->h3.conn) { - struct Curl_easy *sdata; + struct Curl_llist_node *e; struct h3_stream_ctx *stream; /* PULL all open streams */ DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) { stream = H3_STREAM_CTX(ctx, sdata); if(stream && !stream->closed && @@ -1449,11 +1463,12 @@ static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *sdata; struct h3_stream_ctx *stream; if(ctx->h3.conn) { - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + struct Curl_llist_node *e; + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn == data->conn) { stream = H3_STREAM_CTX(ctx, sdata); if(stream && stream->s.ssl && stream->s.send_blocked && @@ -1505,7 +1520,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, s = cf_osslq_get_qstream(cf, data, stream_id); if(!s) { failf(data, "nghttp3_conn_writev_stream gave unknown stream %" - CURL_PRId64, (curl_int64_t)stream_id); + FMT_PRId64, (curl_int64_t)stream_id); result = CURLE_SEND_ERROR; goto out; } @@ -1526,7 +1541,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, if(ok) { /* As OpenSSL buffers the data, we count this as acknowledged * from nghttp3's point of view */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send %zu bytes to QUIC ok", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send %zu bytes to QUIC ok", s->id, vec[i].len); acked_len += vec[i].len; } @@ -1536,14 +1551,14 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: /* QUIC blocked us from writing more */ - CURL_TRC_CF(data, cf, "[%"CURL_PRId64 "] send %zu bytes to " + CURL_TRC_CF(data, cf, "[%"FMT_PRId64 "] send %zu bytes to " "QUIC blocked", s->id, vec[i].len); written = 0; nghttp3_conn_block_stream(ctx->h3.conn, s->id); s->send_blocked = blocked = TRUE; break; default: - failf(data, "[%"CURL_PRId64 "] send %zu bytes to QUIC, SSL error %d", + failf(data, "[%"FMT_PRId64 "] send %zu bytes to QUIC, SSL error %d", s->id, vec[i].len, detail); result = cf_osslq_ssl_err(cf, data, detail, CURLE_HTTP3); goto out; @@ -1569,13 +1584,13 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, result = CURLE_SEND_ERROR; goto out; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] forwarded %zu/%zu h3 bytes " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] forwarded %zu/%zu h3 bytes " "to QUIC, eos=%d", s->id, acked_len, total_len, eos); } if(eos && !s->send_blocked && !eos_written) { /* wrote everything and H3 indicates end of stream */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] closing QUIC stream", s->id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] closing QUIC stream", s->id); SSL_stream_conclude(s->ssl, 0); } } @@ -1868,11 +1883,11 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, if(rc) { switch(rc) { case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%"CURL_PRId64"] failed to send, " + CURL_TRC_CF(data, cf, "h3sid[%"FMT_PRId64"] failed to send, " "connection is closing", stream->s.id); break; default: - CURL_TRC_CF(data, cf, "h3sid[%"CURL_PRId64 "] failed to send -> %d (%s)", + CURL_TRC_CF(data, cf, "h3sid[%"FMT_PRId64 "] failed to send -> %d (%s)", stream->s.id, rc, nghttp3_strerror(rc)); break; } @@ -1882,10 +1897,10 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, } if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" CURL_PRId64 "] OPENED stream for %s", + infof(data, "[HTTP/3] [%" FMT_PRId64 "] OPENED stream for %s", stream->s.id, data->state.url); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" CURL_PRId64 "] [%.*s: %.*s]", + infof(data, "[HTTP/3] [%" FMT_PRId64 "] [%.*s: %.*s]", stream->s.id, (int)nva[i].namelen, nva[i].name, (int)nva[i].valuelen, nva[i].value); @@ -1899,7 +1914,8 @@ out: } static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_osslq_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -1907,6 +1923,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, ssize_t nwritten; CURLcode result; + (void)eos; /* TODO: use to end stream */ CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); DEBUGASSERT(ctx->tls.ossl.ssl); @@ -1942,13 +1959,13 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, * body. This happens on 30x or 40x responses. * We silently discard the data sent, since this is not a transport * error situation. */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] discarding data" + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data" "on closed stream with response", stream->s.id); *err = CURLE_OK; nwritten = (ssize_t)len; goto out; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) " "-> stream closed", stream->s.id, len); *err = CURLE_HTTP3; nwritten = -1; @@ -1956,7 +1973,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else { nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send, add to " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to " "sendbuf(len=%zu) -> %zd, %d", stream->s.id, len, nwritten, *err); if(nwritten < 0) { @@ -1974,7 +1991,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, out: result = check_and_set_expiry(cf, data); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %zd, %d", stream? stream->s.id : -1, len, nwritten, *err); CF_DATA_RESTORE(cf, save); return nwritten; @@ -1990,14 +2007,14 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, (void)cf; if(stream->reset) { failf(data, - "HTTP/3 stream %" CURL_PRId64 " reset by server", + "HTTP/3 stream %" FMT_PRId64 " reset by server", stream->s.id); *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; goto out; } else if(!stream->resp_hds_complete) { failf(data, - "HTTP/3 stream %" CURL_PRId64 + "HTTP/3 stream %" FMT_PRId64 " was closed cleanly, but before getting" " all response header fields, treated as error", stream->s.id); @@ -2037,7 +2054,7 @@ static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); if(nread < 0) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->s.id, len, nread, *err); goto out; } @@ -2055,7 +2072,7 @@ static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); if(nread < 0) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->s.id, len, nread, *err); goto out; } @@ -2085,7 +2102,7 @@ out: nread = -1; } } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_recv(len=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(len=%zu) -> %zd, %d", stream? stream->s.id : -1, len, nread, *err); CF_DATA_RESTORE(cf, save); return nread; @@ -2325,7 +2342,7 @@ CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf, 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) @@ -2346,7 +2363,7 @@ out: 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; } diff --git a/libs/libcurl/src/vquic/curl_quiche.c b/libs/libcurl/src/vquic/curl_quiche.c index 66e3592bb2..61b97e2119 100644 --- a/libs/libcurl/src/vquic/curl_quiche.c +++ b/libs/libcurl/src/vquic/curl_quiche.c @@ -98,14 +98,17 @@ struct cf_quiche_ctx { struct curltime handshake_at; /* time connect handshake finished */ struct curltime reconnect_at; /* time the next attempt should start */ 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->mid` 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; @@ -113,17 +116,27 @@ static void quiche_debug_log(const char *line, 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); @@ -132,9 +145,20 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) 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, @@ -148,7 +172,6 @@ struct stream_ctx { struct bufq recvbuf; /* h3 response */ struct h1_req_parser h1; /* h1 request parsing */ curl_uint64_t error3; /* HTTP/3 stream error code */ - curl_off_t upload_left; /* number of request bytes left to upload */ BIT(opened); /* TRUE after stream has been opened */ BIT(closed); /* TRUE on stream close */ BIT(reset); /* TRUE on stream reset */ @@ -159,7 +182,7 @@ struct stream_ctx { }; #define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)(\ - data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL)) static void h3_stream_ctx_free(struct stream_ctx *stream) { @@ -178,17 +201,17 @@ static void check_resumes(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - struct Curl_easy *sdata; - struct stream_ctx *stream; + struct Curl_llist_node *e; DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn == data->conn) { - stream = H3_STREAM_CTX(ctx, sdata); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, sdata); if(stream && stream->quic_flow_blocked) { stream->quic_flow_blocked = FALSE; Curl_expire(data, 0, EXPIRE_RUN_NOW); - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] unblock", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] unblock", stream->id); } } } @@ -212,7 +235,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) { h3_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -228,7 +251,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] easy handle is done", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] easy handle is done", stream->id); if(ctx->qconn && !stream->closed) { quiche_conn_stream_shutdown(ctx->qconn, stream->id, QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR); @@ -242,7 +265,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) if(result) CURL_TRC_CF(data, cf, "data_done, flush egress -> %d", result); } - Curl_hash_offt_remove(&ctx->streams, data->id); + Curl_hash_offt_remove(&ctx->streams, data->mid); } } @@ -255,7 +278,7 @@ static void drain_stream(struct Curl_cfilter *cf, (void)cf; bits = CURL_CSELECT_IN; - if(stream && !stream->send_closed && stream->upload_left) + if(stream && !stream->send_closed) bits |= CURL_CSELECT_OUT; if(data->state.select_bits != bits) { data->state.select_bits = bits; @@ -269,7 +292,6 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, struct stream_ctx **pstream) { struct cf_quiche_ctx *ctx = cf->ctx; - struct Curl_easy *sdata; struct stream_ctx *stream; (void)cf; @@ -279,8 +301,10 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, return data; } else { + struct Curl_llist_node *e; DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn != data->conn) continue; stream = H3_STREAM_CTX(ctx, sdata); @@ -297,11 +321,12 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct Curl_easy *sdata; + struct Curl_llist_node *e; DEBUGASSERT(data->multi); CURL_TRC_CF(data, cf, "conn closed, expire all transfers"); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata == data || sdata->conn != data->conn) continue; CURL_TRC_CF(sdata, cf, "conn closed, expire transfer"); @@ -357,7 +382,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, return CURLE_OK; if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) { - CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] status: %.*s", + CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRIu64 "] status: %.*s", stream->id, (int)value_len, value); result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1); if(!result) @@ -366,7 +391,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, result = write_resp_raw(x->cf, x->data, " \r\n", 3); } else { - CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] header: %.*s: %.*s", + CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRIu64 "] header: %.*s: %.*s", stream->id, (int)name_len, name, (int)value_len, value); result = write_resp_raw(x->cf, x->data, name, name_len); @@ -378,7 +403,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, result = write_resp_raw(x->cf, x->data, "\r\n", 2); } if(result) { - CURL_TRC_CF(x->data, x->cf, "[%"CURL_PRIu64"] on header error %d", + CURL_TRC_CF(x->data, x->cf, "[%"FMT_PRIu64"] on header error %d", stream->id, result); } return result; @@ -435,9 +460,9 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf, stream_resp_read, &cb_ctx, &result); if(nwritten < 0 && result != CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv_body error %zd", + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] recv_body error %zd", stream->id, nwritten); - failf(data, "Error %d in HTTP/3 response body for stream[%"CURL_PRIu64"]", + failf(data, "Error %d in HTTP/3 response body for stream[%"FMT_PRIu64"]", result, stream->id); stream->closed = TRUE; stream->reset = TRUE; @@ -489,10 +514,10 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, rc = quiche_h3_event_for_each_header(ev, cb_each_header, &cb_ctx); if(rc) { failf(data, "Error %d in HTTP/3 response header for stream[%" - CURL_PRIu64"]", rc, stream->id); + FMT_PRIu64"]", rc, stream->id); return CURLE_RECV_ERROR; } - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [HEADERS]", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] <- [HEADERS]", stream->id); break; case QUICHE_H3_EVENT_DATA: @@ -502,7 +527,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break; case QUICHE_H3_EVENT_RESET: - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] RESET", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] RESET", stream->id); stream->closed = TRUE; stream->reset = TRUE; stream->send_closed = TRUE; @@ -510,7 +535,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break; case QUICHE_H3_EVENT_FINISHED: - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] CLOSED", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] CLOSED", stream->id); if(!stream->resp_hds_complete) { result = write_resp_raw(cf, data, "\r\n", 2); if(result) @@ -522,11 +547,11 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break; case QUICHE_H3_EVENT_GOAWAY: - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [GOAWAY]", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] <- [GOAWAY]", stream->id); break; default: - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv, unhandled event %d", + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] recv, unhandled event %d", stream->id, quiche_h3_event_type(ev)); break; } @@ -549,13 +574,13 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, break; } else if(stream3_id < 0) { - CURL_TRC_CF(data, cf, "error poll: %"CURL_PRId64, stream3_id); + CURL_TRC_CF(data, cf, "error poll: %"FMT_PRId64, stream3_id); return CURLE_HTTP3; } sdata = get_stream_easy(cf, data, stream3_id, &stream); if(!sdata || !stream) { - CURL_TRC_CF(data, cf, "discard event %s for unknown [%"CURL_PRId64"]", + CURL_TRC_CF(data, cf, "discard event %s for unknown [%"FMT_PRId64"]", cf_ev_name(ev), stream3_id); } else { @@ -563,7 +588,7 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, drain_stream(cf, sdata); if(result) { CURL_TRC_CF(data, cf, "error processing event %s " - "for [%"CURL_PRIu64"] -> %d", cf_ev_name(ev), + "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev), stream3_id, result); if(data == sdata) { /* Only report this error to the caller if it is about the @@ -793,19 +818,19 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, DEBUGASSERT(stream); if(stream->reset) { failf(data, - "HTTP/3 stream %" CURL_PRIu64 " reset by server", stream->id); + "HTTP/3 stream %" FMT_PRIu64 " reset by server", stream->id); *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, was reset -> %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_recv, was reset -> %d", stream->id, *err); } else if(!stream->resp_got_header) { failf(data, - "HTTP/3 stream %" CURL_PRIu64 " was closed cleanly, but before " + "HTTP/3 stream %" FMT_PRIu64 " was closed cleanly, but before " "getting all response header fields, treated as error", stream->id); /* *err = CURLE_PARTIAL_FILE; */ *err = CURLE_HTTP3; - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, closed incomplete" + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_recv, closed incomplete" " -> %d", stream->id, *err); } else { @@ -833,7 +858,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(!Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->id, len, nread, *err); if(nread < 0) goto out; @@ -850,7 +875,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->id, len, nread, *err); if(nread < 0) goto out; @@ -884,19 +909,70 @@ out: } if(nread > 0) ctx->data_recvd += nread; - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] cf_recv(total=%" - CURL_FORMAT_CURL_OFF_T ") -> %zd, %d", + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] cf_recv(total=%" + FMT_OFF_T ") -> %zd, %d", stream->id, ctx->data_recvd, nread, *err); return nread; } +static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct stream_ctx *stream, + const void *buf, size_t len, bool eos, + CURLcode *err) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + ssize_t nwritten; + + nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, + (uint8_t *)buf, len, eos); + if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) { + /* TODO: we seem to be blocked on flow control and should HOLD + * sending. But when do we open again? */ + if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + "-> window exhausted", stream->id, len); + stream->quic_flow_blocked = TRUE; + } + *err = CURLE_AGAIN; + return -1; + } + else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + "-> invalid stream state", stream->id, len); + *err = CURLE_HTTP3; + return -1; + } + else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + "-> exceeds size", stream->id, len); + *err = CURLE_SEND_ERROR; + return -1; + } + else if(nwritten < 0) { + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + "-> quiche err %zd", stream->id, len, nwritten); + *err = CURLE_SEND_ERROR; + return -1; + } + else { + if(eos && (len == (size_t)nwritten)) + stream->send_closed = TRUE; + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send body(len=%zu, " + "eos=%d) -> %zd", + stream->id, len, stream->send_closed, nwritten); + *err = CURLE_OK; + return nwritten; + } +} + /* Index where :authority header field will appear in request header field list. */ #define AUTHORITY_DST_IDX 3 static ssize_t h3_open_stream(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, + const char *buf, size_t len, bool eos, CURLcode *err) { struct cf_quiche_ctx *ctx = cf->ctx; @@ -952,23 +1028,7 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, nva[i].value_len = e->valuelen; } - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown */ - break; - default: - stream->upload_left = 0; /* no request body */ - break; - } - - if(stream->upload_left == 0) + if(eos && ((size_t)nwritten == len)) stream->send_closed = TRUE; stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader, @@ -977,14 +1037,14 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { /* quiche seems to report this error if the connection window is * exhausted. Which happens frequently and intermittent. */ - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] blocked", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] blocked", stream->id); stream->quic_flow_blocked = TRUE; *err = CURLE_AGAIN; nwritten = -1; goto out; } else { - CURL_TRC_CF(data, cf, "send_request(%s) -> %" CURL_PRIu64, + CURL_TRC_CF(data, cf, "send_request(%s) -> %" FMT_PRIu64, data->state.url, stream3_id); } *err = CURLE_SEND_ERROR; @@ -1000,15 +1060,31 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, stream->reset = FALSE; if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" CURL_PRIu64 "] OPENED stream for %s", + infof(data, "[HTTP/3] [%" FMT_PRIu64 "] OPENED stream for %s", stream->id, data->state.url); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" CURL_PRIu64 "] [%.*s: %.*s]", stream->id, + infof(data, "[HTTP/3] [%" FMT_PRIu64 "] [%.*s: %.*s]", stream->id, (int)nva[i].name_len, nva[i].name, (int)nva[i].value_len, nva[i].value); } } + if(nwritten > 0 && ((size_t)nwritten < len)) { + /* after the headers, there was request BODY data */ + size_t hds_len = (size_t)nwritten; + ssize_t bwritten; + + bwritten = cf_quiche_send_body(cf, data, stream, + buf + hds_len, len - hds_len, eos, err); + if((bwritten < 0) && (CURLE_AGAIN != *err)) { + /* real error, fail */ + nwritten = -1; + } + else if(bwritten > 0) { + nwritten += bwritten; + } + } + out: free(nva); Curl_dynhds_free(&h2_headers); @@ -1016,7 +1092,8 @@ out: } static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_quiche_ctx *ctx = cf->ctx; struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -1032,7 +1109,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, } if(!stream || !stream->opened) { - nwritten = h3_open_stream(cf, data, buf, len, err); + nwritten = h3_open_stream(cf, data, buf, len, eos, err); if(nwritten < 0) goto out; stream = H3_STREAM_CTX(ctx, data); @@ -1047,70 +1124,20 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, * sending the 30x response. * This is sort of a race: had the transfer loop called recv first, * it would see the response and stop/discard sending on its own- */ - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] discarding data" + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] discarding data" "on closed stream with response", stream->id); *err = CURLE_OK; nwritten = (ssize_t)len; goto out; } - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " "-> stream closed", stream->id, len); *err = CURLE_HTTP3; nwritten = -1; goto out; } else { - bool eof = (stream->upload_left >= 0 && - (curl_off_t)len >= stream->upload_left); - nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, - (uint8_t *)buf, len, eof); - if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) { - /* TODO: we seem to be blocked on flow control and should HOLD - * sending. But when do we open again? */ - if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " - "-> window exhausted", stream->id, len); - stream->quic_flow_blocked = TRUE; - } - *err = CURLE_AGAIN; - nwritten = -1; - goto out; - } - else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " - "-> invalid stream state", stream->id, len); - *err = CURLE_HTTP3; - nwritten = -1; - goto out; - } - else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " - "-> exceeds size", stream->id, len); - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - else if(nwritten < 0) { - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " - "-> quiche err %zd", stream->id, len, nwritten); - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - else { - /* quiche accepted all or at least a part of the buf */ - if(stream->upload_left > 0) { - stream->upload_left = (nwritten < stream->upload_left)? - (stream->upload_left - nwritten) : 0; - } - if(stream->upload_left == 0) - stream->send_closed = TRUE; - - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send body(len=%zu, " - "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd", - stream->id, len, stream->upload_left, nwritten); - *err = CURLE_OK; - } + nwritten = cf_quiche_send_body(cf, data, stream, buf, len, eos, err); } out: @@ -1119,8 +1146,8 @@ out: *err = result; nwritten = -1; } - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_send(len=%zu) -> %zd, %d", - stream? stream->id : (uint64_t)~0, len, nwritten, *err); + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_send(len=%zu) -> %zd, %d", + stream? stream->id : (curl_uint64_t)~0, len, nwritten, *err); return nwritten; } @@ -1215,10 +1242,9 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, ssize_t sent; stream->send_closed = TRUE; - stream->upload_left = 0; body[0] = 'X'; - sent = cf_quiche_send(cf, data, body, 0, &result); - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] DONE_SEND -> %zd, %d", + sent = cf_quiche_send(cf, data, body, 0, TRUE, &result); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] DONE_SEND -> %zd, %d", stream->id, sent, result); } break; @@ -1238,8 +1264,8 @@ static CURLcode cf_quiche_data_event(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; @@ -1247,19 +1273,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, 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) @@ -1366,7 +1380,6 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -1401,7 +1414,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, } 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; @@ -1513,23 +1526,20 @@ out: 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, @@ -1545,7 +1555,7 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn); } *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams; - CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: " + CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: " "MAX_CONCURRENT -> %d (%zu in use)", cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn)); return CURLE_OK; @@ -1650,6 +1660,7 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } + cf_quiche_ctx_init(ctx); result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) @@ -1669,7 +1680,7 @@ out: 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; diff --git a/libs/libcurl/src/vquic/vquic-tls.c b/libs/libcurl/src/vquic/vquic-tls.c index d3ddce2c53..f51ec0cc22 100644 --- a/libs/libcurl/src/vquic/vquic-tls.c +++ b/libs/libcurl/src/vquic/vquic-tls.c @@ -162,7 +162,7 @@ static CURLcode Curl_wssl_init_ctx(struct curl_tls_ctx *ctx, #ifdef CURL_CA_FALLBACK else { /* verifying the peer without any CA certificates will not work so - use wolfssl's built-in default as fallback */ + use wolfSSL's built-in default as fallback */ wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx); } #endif diff --git a/libs/libcurl/src/vquic/vquic.c b/libs/libcurl/src/vquic/vquic.c index 13d9929ee2..f68a8d2912 100644 --- a/libs/libcurl/src/vquic/vquic.c +++ b/libs/libcurl/src/vquic/vquic.c @@ -22,7 +22,7 @@ * ***************************************************************************/ -/* WIP, experimental: use recvmmsg() on linux +/* WIP, experimental: use recvmmsg() on Linux * we have no configure check, yet * and also it is only available for _GNU_SOURCE, which * we do not use otherwise. @@ -720,7 +720,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn) { if(conn->transport == TRNSPRT_UNIX) { - /* cannot do QUIC over a unix domain socket */ + /* cannot do QUIC over a Unix domain socket */ return CURLE_QUIC_CONNECT_ERROR; } if(!(conn->handler->flags & PROTOPT_SSL)) { -- cgit v1.2.3