diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h index 417ec017c60c13..06e8d42bda0100 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h @@ -145,6 +145,18 @@ NGTCP2_EXTERN SSL *ngtcp2_crypto_ossl_ctx_get_ssl(ngtcp2_crypto_ossl_ctx *ctx); */ NGTCP2_EXTERN int ngtcp2_crypto_ossl_init(void); +/** + * @function + * + * `ngtcp2_crypto_ossl_free` frees the resources allocated by + * `ngtcp2_crypto_ossl_init`. It is safe to call this function even + * if `ngtcp2_crypto_ossl_init` fails or is not called at all. This + * function might be useful to make some leak detection tools happy. + * + * .. version-added:: 1.24.0 + */ +NGTCP2_EXTERN void ngtcp2_crypto_ossl_free(void); + /** * @function * diff --git a/deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c b/deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c index 6159567aceb6af..3fedb8df8e8dc5 100644 --- a/deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c +++ b/deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c @@ -79,6 +79,60 @@ int ngtcp2_crypto_ossl_init(void) { return 0; } +void ngtcp2_crypto_ossl_free(void) { + if (crypto_hkdf) { + EVP_KDF_free(crypto_hkdf); + crypto_hkdf = NULL; + } + + if (crypto_sha384) { + EVP_MD_free(crypto_sha384); + crypto_sha384 = NULL; + } + + if (crypto_sha256) { + EVP_MD_free(crypto_sha256); + crypto_sha256 = NULL; + } + +#ifndef NGTCP2_NO_CHACHA_POLY1305 + if (crypto_chacha20) { + EVP_CIPHER_free(crypto_chacha20); + crypto_chacha20 = NULL; + } + + if (crypto_chacha20_poly1305) { + EVP_CIPHER_free(crypto_chacha20_poly1305); + crypto_chacha20_poly1305 = NULL; + } +#endif /* !defined(NGTCP2_NO_CHACHA_POLY1305) */ + + if (crypto_aes_256_ecb) { + EVP_CIPHER_free(crypto_aes_256_ecb); + crypto_aes_256_ecb = NULL; + } + + if (crypto_aes_128_ecb) { + EVP_CIPHER_free(crypto_aes_128_ecb); + crypto_aes_128_ecb = NULL; + } + + if (crypto_aes_128_ccm) { + EVP_CIPHER_free(crypto_aes_128_ccm); + crypto_aes_128_ccm = NULL; + } + + if (crypto_aes_256_gcm) { + EVP_CIPHER_free(crypto_aes_256_gcm); + crypto_aes_256_gcm = NULL; + } + + if (crypto_aes_128_gcm) { + EVP_CIPHER_free(crypto_aes_128_gcm); + crypto_aes_128_gcm = NULL; + } +} + static const EVP_CIPHER *crypto_aead_aes_128_gcm(void) { if (crypto_aes_128_gcm) { return crypto_aes_128_gcm; diff --git a/deps/ngtcp2/ngtcp2/examples/http3_server_proto_codec.cc b/deps/ngtcp2/ngtcp2/examples/http3_server_proto_codec.cc index 1a39177e32a6d1..ac0b6132ef0b1d 100644 --- a/deps/ngtcp2/ngtcp2/examples/http3_server_proto_codec.cc +++ b/deps/ngtcp2/ngtcp2/examples/http3_server_proto_codec.cc @@ -603,9 +603,9 @@ std::expected ProtoCodec::start_response(Stream *stream) { nghttp3_pri pri; if (auto rv = - nghttp3_conn_get_stream_priority(httpconn_, &pri, stream->stream_id); + nghttp3_conn_get_stream_priority2(httpconn_, &pri, stream->stream_id); rv != 0) { - std::println(stderr, "nghttp3_conn_get_stream_priority: {}", + std::println(stderr, "nghttp3_conn_get_stream_priority2: {}", nghttp3_strerror(rv)); return std::unexpected{Error::HTTP3}; } diff --git a/deps/ngtcp2/ngtcp2/examples/tls_client_context_ossl.cc b/deps/ngtcp2/ngtcp2/examples/tls_client_context_ossl.cc index d7f9f8f58c8abc..31dc95e158bec3 100644 --- a/deps/ngtcp2/ngtcp2/examples/tls_client_context_ossl.cc +++ b/deps/ngtcp2/ngtcp2/examples/tls_client_context_ossl.cc @@ -36,23 +36,16 @@ #include "client_base.h" #include "template.h" -namespace { -auto _ = [] { - if (ngtcp2_crypto_ossl_init() != 0) { - assert(0); - abort(); - } - - return 0; -}(); -} // namespace - extern Config config; +TLSClientContext::TLSClientContext() { ngtcp2_crypto_ossl_init(); } + TLSClientContext::~TLSClientContext() { if (ssl_ctx_) { SSL_CTX_free(ssl_ctx_); } + + ngtcp2_crypto_ossl_free(); } SSL_CTX *TLSClientContext::get_native_handle() const { return ssl_ctx_; } diff --git a/deps/ngtcp2/ngtcp2/examples/tls_client_context_ossl.h b/deps/ngtcp2/ngtcp2/examples/tls_client_context_ossl.h index fad71595f2579c..4036a6fef9aa13 100644 --- a/deps/ngtcp2/ngtcp2/examples/tls_client_context_ossl.h +++ b/deps/ngtcp2/ngtcp2/examples/tls_client_context_ossl.h @@ -37,7 +37,7 @@ using namespace ngtcp2; class TLSClientContext { public: - TLSClientContext() = default; + TLSClientContext(); ~TLSClientContext(); std::expected init(const char *private_key_file, diff --git a/deps/ngtcp2/ngtcp2/examples/tls_server_context_ossl.cc b/deps/ngtcp2/ngtcp2/examples/tls_server_context_ossl.cc index 054db5caaf00b1..bd4fe2971c15a6 100644 --- a/deps/ngtcp2/ngtcp2/examples/tls_server_context_ossl.cc +++ b/deps/ngtcp2/ngtcp2/examples/tls_server_context_ossl.cc @@ -37,23 +37,16 @@ #include "server_base.h" #include "template.h" -namespace { -auto _ = [] { - if (ngtcp2_crypto_ossl_init() != 0) { - assert(0); - abort(); - } - - return 0; -}(); -} // namespace - extern Config config; +TLSServerContext::TLSServerContext() { ngtcp2_crypto_ossl_init(); } + TLSServerContext::~TLSServerContext() { if (ssl_ctx_) { SSL_CTX_free(ssl_ctx_); } + + ngtcp2_crypto_ossl_free(); } SSL_CTX *TLSServerContext::get_native_handle() const { return ssl_ctx_; } diff --git a/deps/ngtcp2/ngtcp2/examples/tls_server_context_ossl.h b/deps/ngtcp2/ngtcp2/examples/tls_server_context_ossl.h index 619f1310fec093..cc1aa38301d6ec 100644 --- a/deps/ngtcp2/ngtcp2/examples/tls_server_context_ossl.h +++ b/deps/ngtcp2/ngtcp2/examples/tls_server_context_ossl.h @@ -37,7 +37,7 @@ using namespace ngtcp2; class TLSServerContext { public: - TLSServerContext() = default; + TLSServerContext(); ~TLSServerContext(); std::expected init(const char *private_key_file, diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h index 278b30ca07bf18..01f93e8c41ad3b 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -3485,6 +3485,24 @@ typedef int (*ngtcp2_stream_stop_sending)(ngtcp2_conn *conn, int64_t stream_id, void *user_data, void *stream_user_data); +/** + * @functypedef + * + * :type:`ngtcp2_recv_stop_sending` is invoked when a STOP_SENDING frame + * is received from a remote endpoint for a stream identified by + * |stream_id|. |app_error_code| is the application error code carried + * by the STOP_SENDING frame. This callback is called at most + * once per stream. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_recv_stop_sending)(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *user_data, + void *stream_user_data); + /** * @functypedef * @@ -3626,7 +3644,8 @@ typedef int (*ngtcp2_get_path_challenge_data2)(ngtcp2_conn *conn, #define NGTCP2_CALLBACKS_V1 1 #define NGTCP2_CALLBACKS_V2 2 #define NGTCP2_CALLBACKS_V3 3 -#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V3 +#define NGTCP2_CALLBACKS_V4 4 +#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V4 /** * @struct @@ -3964,6 +3983,16 @@ typedef struct ngtcp2_callbacks { * .. version-added:: 1.22.0 */ ngtcp2_get_path_challenge_data2 get_path_challenge_data2; + /* The following fields have been added since + NGTCP2_CALLBACKS_V3. */ + /** + * :member:`recv_stop_sending` is a callback function which is invoked + * when a STOP_SENDING frame is received from a remote endpoint. This + * callback function is optional. + * + * .. version-added:: 1.24.0 + */ + ngtcp2_recv_stop_sending recv_stop_sending; } ngtcp2_callbacks; /** diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h index 7a47cdaba0bdb9..a71100abbaf586 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h @@ -36,7 +36,7 @@ * * Version number of the ngtcp2 library release. */ -#define NGTCP2_VERSION "1.23.0" +#define NGTCP2_VERSION "1.24.0" /** * @macro @@ -46,6 +46,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGTCP2_VERSION_NUM 0x011700 +#define NGTCP2_VERSION_NUM 0x011800 #endif /* !defined(NGTCP2_VERSION_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c index cdeb29bb506523..b4cf2b02bbf422 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c @@ -289,9 +289,8 @@ static void bbr_handle_recovery(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, ngtcp2_tstamp initial_ts) { - ngtcp2_window_filter_init(&bbr->max_bw_filter, NGTCP2_BBR_MAX_BW_FILTERLEN); - ngtcp2_window_filter_init(&bbr->extra_acked_filter, - NGTCP2_BBR_EXTRA_ACKED_FILTERLEN); + ngtcp2_wf_init(&bbr->max_bw_filter, NGTCP2_BBR_MAX_BW_FILTERLEN); + ngtcp2_wf_init(&bbr->extra_acked_filter, NGTCP2_BBR_EXTRA_ACKED_FILTERLEN); bbr->min_rtt = cstat->first_rtt_sample_ts == UINT64_MAX ? UINT64_MAX : cstat->smoothed_rtt; @@ -590,10 +589,10 @@ static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, const ngtcp2_conn_stat *cstat, if (cstat->delivery_rate_sec && (cstat->delivery_rate_sec >= bbr->max_bw || !bbr->rst->rs.is_app_limited)) { - ngtcp2_window_filter_update(&bbr->max_bw_filter, cstat->delivery_rate_sec, - bbr->cycle_count); + ngtcp2_wf_update(&bbr->max_bw_filter, cstat->delivery_rate_sec, + bbr->cycle_count); - bbr->max_bw = ngtcp2_window_filter_get_best(&bbr->max_bw_filter); + bbr->max_bw = ngtcp2_wf_get_best(&bbr->max_bw_filter); } } @@ -667,15 +666,14 @@ static void bbr_update_ack_aggregation(ngtcp2_cc_bbr *bbr, } if (bbr->full_bw_reached) { - bbr->extra_acked_filter.window_length = NGTCP2_BBR_EXTRA_ACKED_FILTERLEN; + bbr->extra_acked_filter.win = NGTCP2_BBR_EXTRA_ACKED_FILTERLEN; } else { - bbr->extra_acked_filter.window_length = 1; + bbr->extra_acked_filter.win = 1; } - ngtcp2_window_filter_update(&bbr->extra_acked_filter, extra, - bbr->round_count); + ngtcp2_wf_update(&bbr->extra_acked_filter, extra, bbr->round_count); - bbr->extra_acked = ngtcp2_window_filter_get_best(&bbr->extra_acked_filter); + bbr->extra_acked = ngtcp2_wf_get_best(&bbr->extra_acked_filter); } static void bbr_enter_drain(ngtcp2_cc_bbr *bbr) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h index 81384e6d9f5cda..5a8b470b893f12 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h @@ -32,7 +32,7 @@ #include #include "ngtcp2_cc.h" -#include "ngtcp2_window_filter.h" +#include "ngtcp2_wf.h" typedef struct ngtcp2_rst ngtcp2_rst; typedef struct ngtcp2_pcg32 ngtcp2_pcg32; @@ -67,9 +67,9 @@ typedef struct ngtcp2_cc_bbr { /* max_bw_filter for tracking the maximum recent delivery rate samples for estimating max_bw. */ - ngtcp2_window_filter max_bw_filter; + ngtcp2_wf max_bw_filter; - ngtcp2_window_filter extra_acked_filter; + ngtcp2_wf extra_acked_filter; ngtcp2_duration min_rtt; ngtcp2_tstamp min_rtt_stamp; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c index 1d65d93d566944..cbd1d677275166 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c @@ -63,6 +63,9 @@ size_t ngtcp2_callbackslen_version(int callbacks_version) { switch (callbacks_version) { case NGTCP2_CALLBACKS_VERSION: return sizeof(callbacks); + case NGTCP2_CALLBACKS_V3: + return offsetof(ngtcp2_callbacks, get_path_challenge_data2) + + sizeof(callbacks.get_path_challenge_data2); case NGTCP2_CALLBACKS_V2: return offsetof(ngtcp2_callbacks, begin_path_validation) + sizeof(callbacks.begin_path_validation); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c index 28bbde1233e1e4..4a295d2aab6ac2 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c @@ -230,6 +230,24 @@ static int conn_call_stream_reset(ngtcp2_conn *conn, int64_t stream_id, return 0; } +static int conn_call_recv_stop_sending(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *stream_user_data) { + int rv; + + if (!conn->callbacks.recv_stop_sending) { + return 0; + } + + rv = conn->callbacks.recv_stop_sending(conn, stream_id, app_error_code, + conn->user_data, stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + static int conn_call_extend_max_local_streams_bidi(ngtcp2_conn *conn, uint64_t max_streams) { int rv; @@ -2621,6 +2639,8 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, ngtcp2_unreachable(); } } + + conn->frame_counts.crypto += (size_t)datacnt; } else { left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left); if (left == (size_t)-1) { @@ -2644,6 +2664,8 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, if (rv != 0) { ngtcp2_unreachable(); } + + ++conn->frame_counts.crypto; } *pfrc = nfrc; @@ -3859,6 +3881,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + + ++conn->frame_counts.crypto; } } @@ -4073,6 +4097,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + ++conn->frame_counts.stream; + if (ngtcp2_strm_streamfrq_empty(strm)) { ngtcp2_conn_tx_strmq_pop(conn); continue; @@ -4236,6 +4262,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + ++conn->frame_counts.stream; + vmsg->stream.strm->tx.offset += ndatalen; conn->tx.offset += ndatalen; vmsg->stream.strm->flags |= NGTCP2_STRM_FLAG_ANY_SENT; @@ -7932,6 +7960,12 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, ngtcp2_strm_set_app_error_code(strm, fr->app_error_code); + rv = conn_call_recv_stop_sending(conn, fr->stream_id, fr->app_error_code, + strm->stream_user_data); + if (rv != 0) { + return rv; + } + /* No RESET_STREAM is required if we have sent FIN and all data have been acknowledged. */ if (!ngtcp2_strm_is_all_tx_data_fin_acked(strm) && @@ -12035,6 +12069,12 @@ ngtcp2_ssize ngtcp2_conn_write_stream_versioned( stream_id, v, datacnt, ts); } +static int conn_no_app_data_written(const ngtcp2_conn *conn) { + const ngtcp2_frame_counts *counts = &conn->frame_counts; + + return counts->crypto == 0 && counts->stream == 0; +} + static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, ngtcp2_pkt_info *pi, @@ -12053,14 +12093,9 @@ conn_write_vmsg_wrapper(ngtcp2_conn *conn, ngtcp2_path *path, if (cstat->bytes_in_flight >= cstat->cwnd) { conn->rst.is_cwnd_limited = 1; - } else if ((cstat->cwnd >= cstat->ssthresh || - cstat->bytes_in_flight * 2 < cstat->cwnd) && - nwrite == 0 && conn_pacing_pkt_tx_allowed(conn, ts) && - (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && - /* Because NGTCP2_CONN_FLAG_AGGREGATE_PKTS is set after a - packet is produced, if it is set, we are sure that we - are not app-limited. */ - !(conn->flags & NGTCP2_CONN_FLAG_AGGREGATE_PKTS)) { + } else if (conn_pacing_pkt_tx_allowed(conn, ts) && !vmsg && + conn_no_app_data_written(conn) && + (!path || ngtcp2_path_eq(&conn->dcid.current.ps.path, path))) { conn->rst.app_limited = ngtcp2_max(conn->rst.delivered + cstat->bytes_in_flight, 1); } @@ -12238,8 +12273,12 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, origlen = destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen); - if (!ppe_pending && pi) { - pi->ecn = NGTCP2_ECN_NOT_ECT; + if (!ppe_pending) { + if (pi) { + pi->ecn = NGTCP2_ECN_NOT_ECT; + } + + conn->frame_counts = (ngtcp2_frame_counts){0}; } switch (conn->state) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h index 548c296e8ae1d0..18af11727e0646 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h @@ -308,6 +308,11 @@ typedef struct ngtcp2_early_transport_params { uint64_t max_datagram_frame_size; } ngtcp2_early_transport_params; +typedef struct ngtcp2_frame_counts { + size_t crypto; + size_t stream; +} ngtcp2_frame_counts; + ngtcp2_static_ringbuf_def(path_challenge, 4, sizeof(ngtcp2_path_challenge_entry)) @@ -644,6 +649,7 @@ struct ngtcp2_conn { confirmed. For server, it is confirmed when completed. */ ngtcp2_tstamp handshake_confirmed_ts; ngtcp2_pcg32 pcg; + ngtcp2_frame_counts frame_counts; void *user_data; uint32_t client_chosen_version; uint32_t negotiated_version; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h index 86346f49295add..4ae71aa6daa6d3 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h @@ -31,8 +31,6 @@ #include -#include "ngtcp2_window_filter.h" - typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; typedef struct ngtcp2_conn_stat ngtcp2_conn_stat; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_wf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_wf.c new file mode 100644 index 00000000000000..1adfc9ffd9ac1d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_wf.c @@ -0,0 +1,81 @@ +/* + * ngtcp2 + * + * Copyright (c) 2026 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_wf.h" + +void ngtcp2_wf_init(ngtcp2_wf *wf, uint64_t win) { + *wf = (ngtcp2_wf){ + .win = win, + }; +} + +void ngtcp2_wf_update(ngtcp2_wf *wf, uint64_t value, uint64_t ts) { + ngtcp2_wf_sample s = { + .value = value, + .ts = ts, + }; + + if (wf->samples[0].value <= value || ts - wf->samples[2].ts > wf->win) { + wf->samples[0] = wf->samples[1] = wf->samples[2] = s; + + return; + } + + if (wf->samples[1].value <= value) { + wf->samples[1] = wf->samples[2] = s; + } else if (wf->samples[2].value <= value) { + wf->samples[2] = s; + } + + if (ts - wf->samples[1].ts > wf->win) { + wf->samples[0] = wf->samples[2]; + wf->samples[1] = wf->samples[2] = s; + + return; + } + + if (ts - wf->samples[0].ts > wf->win) { + wf->samples[0] = wf->samples[1]; + wf->samples[1] = wf->samples[2]; + wf->samples[2] = s; + + return; + } + + if (wf->samples[0].value == wf->samples[1].value && + ts - wf->samples[0].ts > wf->win / 4) { + wf->samples[1] = wf->samples[2] = s; + + return; + } + + if (wf->samples[1].value == wf->samples[2].value && + ts - wf->samples[0].ts > wf->win / 2) { + wf->samples[2] = s; + } +} + +uint64_t ngtcp2_wf_get_best(const ngtcp2_wf *wf) { + return wf->samples[0].value; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_wf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_wf.h new file mode 100644 index 00000000000000..72bdf28c865473 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_wf.h @@ -0,0 +1,55 @@ +/* + * ngtcp2 + * + * Copyright (c) 2026 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_WF_H +#define NGTCP2_WF_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* defined(HAVE_CONFIG_H) */ + +#include + +/* + * ngtcp2_wf implements Kathleen Nichols's windowed min/max tracking + * algorithm. + */ + +typedef struct ngtcp2_wf_sample { + uint64_t value; + uint64_t ts; +} ngtcp2_wf_sample; + +typedef struct ngtcp2_wf { + uint64_t win; + ngtcp2_wf_sample samples[3]; +} ngtcp2_wf; + +void ngtcp2_wf_init(ngtcp2_wf *wf, uint64_t win); + +void ngtcp2_wf_update(ngtcp2_wf *wf, uint64_t value, uint64_t ts); + +uint64_t ngtcp2_wf_get_best(const ngtcp2_wf *wf); + +#endif /* !defined(NGTCP2_WF_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c deleted file mode 100644 index 707cd570799e46..00000000000000 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2021 ngtcp2 contributors - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Translated to C from the original C++ code - * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h - * with the following license: - * - * // Copyright (c) 2016 The Chromium Authors. All rights reserved. - * // Use of this source code is governed by a BSD-style license that can be - * // found in the LICENSE file. - */ -#include "ngtcp2_window_filter.h" - -#include - -void ngtcp2_window_filter_init(ngtcp2_window_filter *wf, - uint64_t window_length) { - wf->window_length = window_length; - memset(wf->estimates, 0xFF, sizeof(wf->estimates)); -} - -void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample, - uint64_t new_time) { - /* Reset all estimates if they have not yet been initialized, if new - sample is a new best, or if the newest recorded estimate is too - old. */ - if (wf->estimates[0].sample == UINT64_MAX || - new_sample > wf->estimates[0].sample || - new_time - wf->estimates[2].time > wf->window_length) { - ngtcp2_window_filter_reset(wf, new_sample, new_time); - return; - } - - if (new_sample > wf->estimates[1].sample) { - wf->estimates[1].sample = new_sample; - wf->estimates[1].time = new_time; - wf->estimates[2] = wf->estimates[1]; - } else if (new_sample > wf->estimates[2].sample) { - wf->estimates[2].sample = new_sample; - wf->estimates[2].time = new_time; - } - - /* Expire and update estimates as necessary. */ - if (new_time - wf->estimates[0].time > wf->window_length) { - /* The best estimate hasn't been updated for an entire window, so - promote second and third best estimates. */ - wf->estimates[0] = wf->estimates[1]; - wf->estimates[1] = wf->estimates[2]; - wf->estimates[2].sample = new_sample; - wf->estimates[2].time = new_time; - - /* Need to iterate one more time. Check if the new best estimate - is outside the window as well, since it may also have been - recorded a long time ago. Don't need to iterate once more - since we cover that case at the beginning of the method. */ - if (new_time - wf->estimates[0].time > wf->window_length) { - wf->estimates[0] = wf->estimates[1]; - wf->estimates[1] = wf->estimates[2]; - } - return; - } - - if (wf->estimates[1].sample == wf->estimates[0].sample && - new_time - wf->estimates[1].time > wf->window_length >> 2) { - /* A quarter of the window has passed without a better sample, so - the second-best estimate is taken from the second quarter of - the window. */ - wf->estimates[2].sample = new_sample; - wf->estimates[2].time = new_time; - wf->estimates[1] = wf->estimates[2]; - return; - } - - if (wf->estimates[2].sample == wf->estimates[1].sample && - new_time - wf->estimates[2].time > wf->window_length >> 1) { - /* We've passed a half of the window without a better estimate, so - take a third-best estimate from the second half of the - window. */ - wf->estimates[2].sample = new_sample; - wf->estimates[2].time = new_time; - } -} - -void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample, - uint64_t new_time) { - wf->estimates[0].sample = new_sample; - wf->estimates[0].time = new_time; - wf->estimates[1] = wf->estimates[2] = wf->estimates[0]; -} - -uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf) { - return wf->estimates[0].sample; -} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h deleted file mode 100644 index c90a9fdb9078da..00000000000000 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * ngtcp2 - * - * Copyright (c) 2021 ngtcp2 contributors - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Translated to C from the original C++ code - * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h - * with the following license: - * - * // Copyright (c) 2016 The Chromium Authors. All rights reserved. - * // Use of this source code is governed by a BSD-style license that can be - * // found in the LICENSE file. - */ -#ifndef NGTCP2_WINDOW_FILTER_H -#define NGTCP2_WINDOW_FILTER_H - -#ifdef HAVE_CONFIG_H -# include -#endif /* defined(HAVE_CONFIG_H) */ - -#include - -typedef struct ngtcp2_window_filter_sample { - uint64_t sample; - uint64_t time; -} ngtcp2_window_filter_sample; - -typedef struct ngtcp2_window_filter { - uint64_t window_length; - ngtcp2_window_filter_sample estimates[3]; -} ngtcp2_window_filter; - -void ngtcp2_window_filter_init(ngtcp2_window_filter *wf, - uint64_t window_length); - -void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample, - uint64_t new_time); - -void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample, - uint64_t new_time); - -uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf); - -#endif /* !defined(NGTCP2_WINDOW_FILTER_H) */