diff --git a/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
index dc1c2836e4..9428fd5f1a 100644
--- a/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
+++ b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
@@ -2054,6 +2054,13 @@ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
*/
NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_idle_timeout(ngtcp2_conn *conn);
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_pto` returns Probe Timeout (PTO).
+ */
+NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn);
+
/**
* @function
*
@@ -2540,7 +2547,9 @@ NGTCP2_EXTERN const ngtcp2_addr *ngtcp2_conn_get_remote_addr(ngtcp2_conn *conn);
* @function
*
* `ngtcp2_conn_initiate_migration` starts connection migration to the
- * given |path|. Only client can initiate migration.
+ * given |path|. Only client can initiate migration. This function
+ * does immediate migration; it does not probe peer reachability from
+ * a new local address.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
diff --git a/deps/ngtcp2/lib/ngtcp2_acktr.c b/deps/ngtcp2/lib/ngtcp2_acktr.c
index 37836f8211..e7403db618 100644
--- a/deps/ngtcp2/lib/ngtcp2_acktr.c
+++ b/deps/ngtcp2/lib/ngtcp2_acktr.c
@@ -47,13 +47,12 @@ void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) {
}
static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
- return lhs->i > rhs->i;
+ return *lhs->i > *rhs->i;
}
int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
const ngtcp2_mem *mem) {
int rv;
- ngtcp2_ksl_key inf_key = {-1};
rv = ngtcp2_ringbuf_init(&acktr->acks, 128, sizeof(ngtcp2_acktr_ack_entry),
mem);
@@ -61,7 +60,7 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
return rv;
}
- rv = ngtcp2_ksl_init(&acktr->ents, greater, &inf_key, mem);
+ rv = ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem);
if (rv != 0) {
ngtcp2_ringbuf_free(&acktr->acks);
return rv;
@@ -98,9 +97,11 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
ngtcp2_acktr_entry *ent, *prev_ent, *delent;
int rv;
int added = 0;
+ ngtcp2_ksl_key key, old_key;
if (ngtcp2_ksl_len(&acktr->ents)) {
- it = ngtcp2_ksl_lower_bound(&acktr->ents, (const ngtcp2_ksl_key *)&pkt_num);
+ it = ngtcp2_ksl_lower_bound(&acktr->ents,
+ ngtcp2_ksl_key_ptr(&key, &pkt_num));
if (ngtcp2_ksl_it_end(&it)) {
ngtcp2_ksl_it_prev(&it);
ent = ngtcp2_ksl_it_get(&it);
@@ -119,8 +120,8 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
if (ngtcp2_ksl_it_begin(&it)) {
if (ent->pkt_num + 1 == pkt_num) {
ngtcp2_ksl_update_key(&acktr->ents,
- (const ngtcp2_ksl_key *)&ent->pkt_num,
- (const ngtcp2_ksl_key *)&pkt_num);
+ ngtcp2_ksl_key_ptr(&key, &ent->pkt_num),
+ ngtcp2_ksl_key_ptr(&old_key, &pkt_num));
ent->pkt_num = pkt_num;
ent->tstamp = ts;
++ent->len;
@@ -135,17 +136,14 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
if (ent->pkt_num + 1 == pkt_num) {
if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) {
prev_ent->len += ent->len + 1;
- rv = ngtcp2_ksl_remove(&acktr->ents, NULL,
- (const ngtcp2_ksl_key *)&ent->pkt_num);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&acktr->ents, NULL,
+ ngtcp2_ksl_key_ptr(&key, &ent->pkt_num));
ngtcp2_acktr_entry_del(ent, acktr->mem);
added = 1;
} else {
ngtcp2_ksl_update_key(&acktr->ents,
- (const ngtcp2_ksl_key *)&ent->pkt_num,
- (const ngtcp2_ksl_key *)&pkt_num);
+ ngtcp2_ksl_key_ptr(&key, &ent->pkt_num),
+ ngtcp2_ksl_key_ptr(&old_key, &pkt_num));
ent->pkt_num = pkt_num;
ent->tstamp = ts;
++ent->len;
@@ -165,7 +163,7 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
return rv;
}
rv = ngtcp2_ksl_insert(&acktr->ents, NULL,
- (const ngtcp2_ksl_key *)&ent->pkt_num, ent);
+ ngtcp2_ksl_key_ptr(&key, &ent->pkt_num), ent);
if (rv != 0) {
ngtcp2_acktr_entry_del(ent, acktr->mem);
return rv;
@@ -183,36 +181,28 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
it = ngtcp2_ksl_end(&acktr->ents);
ngtcp2_ksl_it_prev(&it);
delent = ngtcp2_ksl_it_get(&it);
- rv = ngtcp2_ksl_remove(&acktr->ents, NULL,
- (const ngtcp2_ksl_key *)&delent->pkt_num);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&acktr->ents, NULL,
+ ngtcp2_ksl_key_ptr(&key, &delent->pkt_num));
ngtcp2_acktr_entry_del(delent, acktr->mem);
}
return 0;
}
-int ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) {
+void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) {
ngtcp2_ksl_it it;
- int rv;
+ ngtcp2_ksl_key key;
it = ngtcp2_ksl_lower_bound(&acktr->ents,
- (const ngtcp2_ksl_key *)&ent->pkt_num);
- assert(ngtcp2_ksl_it_key(&it).i == (int64_t)ent->pkt_num);
+ ngtcp2_ksl_key_ptr(&key, &ent->pkt_num));
+ assert(*ngtcp2_ksl_it_key(&it).i == (int64_t)ent->pkt_num);
for (; !ngtcp2_ksl_it_end(&it);) {
ent = ngtcp2_ksl_it_get(&it);
- rv = ngtcp2_ksl_remove(&acktr->ents, &it,
- (const ngtcp2_ksl_key *)&ent->pkt_num);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&acktr->ents, &it,
+ ngtcp2_ksl_key_ptr(&key, &ent->pkt_num));
ngtcp2_acktr_entry_del(ent, acktr->mem);
}
-
- return 0;
}
ngtcp2_ksl_it ngtcp2_acktr_get(ngtcp2_acktr *acktr) {
@@ -233,34 +223,21 @@ ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr,
/*
* acktr_remove removes |ent| from |acktr|. The iterator which points
* to the entry next to |ent| is assigned to |it|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory.
*/
-static int acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it,
- ngtcp2_acktr_entry *ent) {
- int rv;
-
- rv = ngtcp2_ksl_remove(&acktr->ents, it,
- (const ngtcp2_ksl_key *)&ent->pkt_num);
- if (rv != 0) {
- return rv;
- }
+static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it,
+ ngtcp2_acktr_entry *ent) {
+ ngtcp2_ksl_key key;
+ ngtcp2_ksl_remove(&acktr->ents, it, ngtcp2_ksl_key_ptr(&key, &ent->pkt_num));
ngtcp2_acktr_entry_del(ent, acktr->mem);
-
- return 0;
}
-static int acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb,
- size_t ack_ent_offset) {
+static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb,
+ size_t ack_ent_offset) {
ngtcp2_acktr_ack_entry *ack_ent;
ngtcp2_acktr_entry *ent;
ngtcp2_ksl_it it;
- int rv;
+ ngtcp2_ksl_key key;
assert(ngtcp2_ringbuf_len(rb));
@@ -268,13 +245,10 @@ static int acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb,
/* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
it = ngtcp2_ksl_lower_bound(&acktr->ents,
- (const ngtcp2_ksl_key *)&ack_ent->largest_ack);
+ ngtcp2_ksl_key_ptr(&key, &ack_ent->largest_ack));
for (; !ngtcp2_ksl_it_end(&it);) {
ent = ngtcp2_ksl_it_get(&it);
- rv = acktr_remove(acktr, &it, ent);
- if (rv != 0) {
- return rv;
- }
+ acktr_remove(acktr, &it, ent);
}
if (ngtcp2_ksl_len(&acktr->ents)) {
@@ -289,17 +263,14 @@ static int acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb,
}
ngtcp2_ringbuf_resize(rb, ack_ent_offset);
-
- return 0;
}
-int ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) {
+void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) {
ngtcp2_acktr_ack_entry *ent;
int64_t largest_ack = fr->largest_ack, min_ack;
size_t i, j;
ngtcp2_ringbuf *rb = &acktr->acks;
size_t nacks = ngtcp2_ringbuf_len(rb);
- int rv;
/* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
for (j = 0; j < nacks; ++j) {
@@ -309,17 +280,14 @@ int ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) {
}
}
if (j == nacks) {
- return 0;
+ return;
}
min_ack = largest_ack - (int64_t)fr->first_ack_blklen;
if (min_ack <= ent->pkt_num && ent->pkt_num <= largest_ack) {
- rv = acktr_on_ack(acktr, rb, j);
- if (rv != 0) {
- return rv;
- }
- return 0;
+ acktr_on_ack(acktr, rb, j);
+ return;
}
for (i = 0; i < fr->num_blks && j < nacks; ++i) {
@@ -330,7 +298,7 @@ int ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) {
if (ent->pkt_num > largest_ack) {
++j;
if (j == nacks) {
- return 0;
+ return;
}
ent = ngtcp2_ringbuf_get(rb, j);
continue;
@@ -338,11 +306,12 @@ int ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) {
if (ent->pkt_num < min_ack) {
break;
}
- return acktr_on_ack(acktr, rb, j);
+ acktr_on_ack(acktr, rb, j);
+ return;
}
}
- return 0;
+ return;
}
void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) {
diff --git a/deps/ngtcp2/lib/ngtcp2_acktr.h b/deps/ngtcp2/lib/ngtcp2_acktr.h
index 585e13b921..35e00d76bd 100644
--- a/deps/ngtcp2/lib/ngtcp2_acktr.h
+++ b/deps/ngtcp2/lib/ngtcp2_acktr.h
@@ -166,14 +166,8 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
* ngtcp2_acktr_forget removes all entries which have the packet
* number that is equal to or less than ent->pkt_num. This function
* assumes that |acktr| includes |ent|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory.
*/
-int ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent);
+void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent);
/*
* ngtcp2_acktr_get returns the pointer to pointer to the entry which
@@ -196,16 +190,8 @@ ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, int64_t pkt_num, int64_t largest_ack);
* |pkt_num| is a packet number which includes |fr|. If we receive
* ACK which acknowledges the ACKs added by ngtcp2_acktr_add_ack,
* ngtcp2_acktr_entry which the outgoing ACK acknowledges is removed.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_CALLBACK_FAILURE
- * User-defined callback function failed.
- * NGTCP2_ERR_NOMEM
- * Out of memory.
*/
-int ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr);
+void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr);
/*
* ngtcp2_acktr_commit_ack tells |acktr| that ACK frame is generated.
diff --git a/deps/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/lib/ngtcp2_cid.c
index 465859e0ac..c53a61f99a 100644
--- a/deps/ngtcp2/lib/ngtcp2_cid.c
+++ b/deps/ngtcp2/lib/ngtcp2_cid.c
@@ -86,11 +86,13 @@ void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
memset(dcid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN);
}
ngtcp2_path_storage_zero(&dcid->ps);
+ dcid->ts_retired = UINT64_MAX;
}
void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) {
ngtcp2_dcid_init(dest, src->seq, &src->cid, src->token);
ngtcp2_path_copy(&dest->ps.path, &src->ps.path);
+ dest->ts_retired = src->ts_retired;
}
int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq,
diff --git a/deps/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/lib/ngtcp2_cid.h
index 69350fabe9..53d97d7c8e 100644
--- a/deps/ngtcp2/lib/ngtcp2_cid.h
+++ b/deps/ngtcp2/lib/ngtcp2_cid.h
@@ -65,6 +65,9 @@ typedef struct {
/* path is a path which cid is bound to. The addresses are zero
length if cid has not been bound to a particular path yet. */
ngtcp2_path_storage ps;
+ /* ts_retired is the timestamp when peer tells that this CID is
+ retired. */
+ ngtcp2_tstamp ts_retired;
/* token is a stateless reset token associated to this CID.
Actually, the stateless reset token is tied to the connection,
not to the particular connection ID. */
diff --git a/deps/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/lib/ngtcp2_conn.c
index 8263ede22f..46c4c31073 100644
--- a/deps/ngtcp2/lib/ngtcp2_conn.c
+++ b/deps/ngtcp2/lib/ngtcp2_conn.c
@@ -423,35 +423,6 @@ static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) {
ngtcp2_gaptr_free(&pktns->rx.pngap);
}
-/*
- * inf_cid is used as the "last" key in ngtcp2_ksl. We don't accept
- * this as valid connection ID. It is reasonable because it is too
- * predictable.
- */
-static ngtcp2_cid inf_cid = {
- NGTCP2_MAX_CIDLEN,
- {
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- },
-};
-
static int cid_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
return ngtcp2_cid_less(lhs->ptr, rhs->ptr);
}
@@ -517,8 +488,14 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
goto fail_dcid_unused_init;
}
- rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less,
- ngtcp2_ksl_key_ptr(&key, &inf_cid), mem);
+ rv =
+ ngtcp2_ringbuf_init(&(*pconn)->dcid.retired, NGTCP2_MAX_DCID_RETIRED_SIZE,
+ sizeof(ngtcp2_dcid), mem);
+ if (rv != 0) {
+ goto fail_dcid_retired_init;
+ }
+
+ rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem);
if (rv != 0) {
goto fail_scid_set_init;
}
@@ -648,6 +625,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
delete_scid(&(*pconn)->scid.set, mem);
ngtcp2_ksl_free(&(*pconn)->scid.set);
fail_scid_set_init:
+ ngtcp2_ringbuf_free(&(*pconn)->dcid.retired);
+fail_dcid_retired_init:
ngtcp2_ringbuf_free(&(*pconn)->dcid.unused);
fail_dcid_unused_init:
ngtcp2_ringbuf_free(&(*pconn)->dcid.bound);
@@ -771,6 +750,7 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) {
ngtcp2_pq_free(&conn->scid.used);
delete_scid(&conn->scid.set, conn->mem);
ngtcp2_ksl_free(&conn->scid.set);
+ ngtcp2_ringbuf_free(&conn->dcid.retired);
ngtcp2_ringbuf_free(&conn->dcid.unused);
ngtcp2_ringbuf_free(&conn->dcid.bound);
@@ -920,10 +900,7 @@ static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr,
/* TODO Just remove entries which cannot fit into a single ACK frame
for now. */
if (!ngtcp2_ksl_it_end(&it)) {
- rv = ngtcp2_acktr_forget(acktr, ngtcp2_ksl_it_get(&it));
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_acktr_forget(acktr, ngtcp2_ksl_it_get(&it));
}
*pfr = fr;
@@ -1044,13 +1021,17 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) {
*/
static uint64_t conn_cwnd_left(ngtcp2_conn *conn) {
uint64_t bytes_in_flight = ngtcp2_conn_get_bytes_in_flight(conn);
+ uint64_t cwnd =
+ conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)
+ ? NGTCP2_MIN_CWND
+ : conn->ccs.cwnd;
- /* We might send more than bytes_in_flight if TLP/RTO packets are
+ /* We might send more than bytes_in_flight if probe packets are
involved. */
- if (bytes_in_flight >= conn->ccs.cwnd) {
+ if (bytes_in_flight >= cwnd) {
return 0;
}
- return conn->ccs.cwnd - bytes_in_flight;
+ return cwnd - bytes_in_flight;
}
/*
@@ -1096,7 +1077,7 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
uint64_t offset, end_offset;
size_t idx, end_idx;
size_t base_offset, end_base_offset;
- ngtcp2_psl_it gapit;
+ ngtcp2_ksl_it gapit;
ngtcp2_range gap;
ngtcp2_rtb *rtb = &pktns->rtb;
ngtcp2_vec *v;
@@ -1118,7 +1099,7 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
gapit =
ngtcp2_gaptr_get_first_gap_after(&rtb->crypto->tx.acked_offset, offset);
- gap = ngtcp2_psl_it_range(&gapit);
+ gap = *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit).ptr;
if (gap.begin < offset) {
gap.begin = offset;
}
@@ -1559,7 +1540,7 @@ static ssize_t conn_write_handshake_pkt(ngtcp2_conn *conn, uint8_t *dest,
}
if (ackfr) {
- rv = conn_ppe_write_frame(conn, &ppe, &hd, ackfr);
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, ackfr);
if (rv != 0) {
assert(NGTCP2_ERR_NOBUF == rv);
} else {
@@ -1997,7 +1978,7 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) {
return rv;
}
- if (cid.datalen != cidlen || ngtcp2_cid_eq(&inf_cid, &cid)) {
+ if (cid.datalen != cidlen) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -2078,6 +2059,7 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn,
ngtcp2_duration timeout;
ngtcp2_scid *scid;
ngtcp2_ksl_key key;
+ ngtcp2_dcid *dcid;
int rv;
timeout = conn_compute_pto(conn);
@@ -2098,15 +2080,20 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn,
return rv;
}
- rv = ngtcp2_ksl_remove(&conn->scid.set, NULL,
- ngtcp2_ksl_key_ptr(&key, &scid->cid));
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&conn->scid.set, NULL,
+ ngtcp2_ksl_key_ptr(&key, &scid->cid));
ngtcp2_pq_pop(&conn->scid.used);
ngtcp2_mem_free(conn->mem, scid);
}
+ for (; ngtcp2_ringbuf_len(&conn->dcid.retired);) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0);
+ if (dcid->ts_retired + timeout >= ts) {
+ break;
+ }
+ ngtcp2_ringbuf_pop_front(&conn->dcid.retired);
+ }
+
return 0;
}
@@ -2278,7 +2265,7 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen,
}
if (ackfr) {
- rv = conn_ppe_write_frame(conn, &ppe, &hd, ackfr);
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, ackfr);
if (rv != 0) {
assert(NGTCP2_ERR_NOBUF == rv);
} else {
@@ -3040,11 +3027,22 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) {
* NGTCP2_ERR_NOMEM
* Out of memory
*/
-static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid) {
+static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
+ ngtcp2_tstamp ts) {
ngtcp2_pktns *pktns = &conn->pktns;
ngtcp2_frame_chain *nfrc;
+ ngtcp2_ringbuf *rb = &conn->dcid.retired;
+ ngtcp2_dcid *dest;
int rv;
+ if (ngtcp2_ringbuf_full(rb)) {
+ ngtcp2_ringbuf_pop_front(rb);
+ }
+
+ dest = ngtcp2_ringbuf_push_back(rb);
+ ngtcp2_dcid_copy(dest, dcid);
+ dest->ts_retired = ts;
+
rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
if (rv != 0) {
return rv;
@@ -3069,7 +3067,7 @@ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid) {
* NGTCP2_ERR_NOMEM
* Out of memory
*/
-static int conn_stop_pv(ngtcp2_conn *conn) {
+static int conn_stop_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
int rv = 0;
ngtcp2_pv *pv = conn->pv;
@@ -3078,7 +3076,7 @@ static int conn_stop_pv(ngtcp2_conn *conn) {
}
if (pv->flags & NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH) {
- rv = conn_retire_dcid(conn, &pv->dcid);
+ rv = conn_retire_dcid(conn, &pv->dcid, ts);
}
ngtcp2_pv_del(pv);
@@ -3099,7 +3097,8 @@ static int conn_stop_pv(ngtcp2_conn *conn) {
* NGTCP2_ERR_CALLBACK_FAILURE
* User-defined callback function failed.
*/
-static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv) {
+static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv,
+ ngtcp2_tstamp ts) {
int rv;
/* If path validation fails, the bound DCID is no longer
@@ -3114,7 +3113,11 @@ static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv) {
}
}
- return conn_stop_pv(conn);
+ if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) {
+ ngtcp2_dcid_copy(&conn->dcid.current, &pv->fallback_dcid);
+ }
+
+ return conn_stop_pv(conn, ts);
}
/*
@@ -3142,7 +3145,7 @@ static ssize_t conn_write_path_challenge(ngtcp2_conn *conn, ngtcp2_path *path,
if (ngtcp2_pv_validation_timed_out(pv, ts)) {
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV,
"path validation was timed out");
- return conn_on_path_validation_failed(conn, pv);
+ return conn_on_path_validation_failed(conn, pv, ts);
}
ngtcp2_pv_handle_entry_expiry(pv, ts);
@@ -3190,7 +3193,7 @@ static ssize_t conn_write_path_challenge(ngtcp2_conn *conn, ngtcp2_path *path,
* Out of memory
*/
static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid,
- const ngtcp2_path *path) {
+ const ngtcp2_path *path, ngtcp2_tstamp ts) {
ngtcp2_pv *pv = conn->pv;
ngtcp2_dcid *dcid, *ndcid;
size_t i, len;
@@ -3216,7 +3219,7 @@ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid,
dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
if (ngtcp2_ringbuf_full(&conn->dcid.bound)) {
- rv = conn_retire_dcid(conn, ngtcp2_ringbuf_get(&conn->dcid.bound, 0));
+ rv = conn_retire_dcid(conn, ngtcp2_ringbuf_get(&conn->dcid.bound, 0), ts);
if (rv != 0) {
return rv;
}
@@ -3291,7 +3294,7 @@ static ssize_t conn_write_path_response(ngtcp2_conn *conn, ngtcp2_path *path,
conn->pv. */
assert(conn->server);
- rv = conn_bind_dcid(conn, &dcid, &pcent->ps.path);
+ rv = conn_bind_dcid(conn, &dcid, &pcent->ps.path, ts);
if (rv != 0) {
if (ngtcp2_err_is_fatal(rv)) {
return rv;
@@ -3606,12 +3609,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
/* Just freeing memory is dangerous because we might free twice. */
- rv = ngtcp2_rtb_remove_all(rtb, &frc);
- if (rv != 0) {
- assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_list_del(frc, conn->mem);
- return rv;
- }
+ ngtcp2_rtb_remove_all(rtb, &frc);
rv = conn_resched_frames(conn, &conn->pktns, &frc);
if (rv != 0) {
@@ -3621,12 +3619,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
}
frc = NULL;
- rv = ngtcp2_rtb_remove_all(in_rtb, &frc);
- if (rv != 0) {
- assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_list_del(frc, conn->mem);
- return rv;
- }
+ ngtcp2_rtb_remove_all(in_rtb, &frc);
rv = conn_resched_frames(conn, &conn->in_pktns, &frc);
if (rv != 0) {
@@ -3656,13 +3649,7 @@ int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
int rv;
ngtcp2_duration pto = conn_compute_pto(conn);
- rv = ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, &frc, rcs, pto, ts);
- if (rv != 0) {
- /* TODO assert this */
- assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_list_del(frc, conn->mem);
- return rv;
- }
+ ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, &frc, rcs, pto, ts);
rv = conn_resched_frames(conn, pktns, &frc);
if (rv != 0) {
@@ -3698,10 +3685,7 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr,
return rv;
}
- rv = ngtcp2_acktr_recv_ack(&pktns->acktr, fr);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_acktr_recv_ack(&pktns->acktr, fr);
num_acked = ngtcp2_rtb_recv_ack(&pktns->rtb, fr, conn, ts);
if (num_acked < 0) {
@@ -4042,10 +4026,7 @@ static int conn_emit_pending_crypto_data(ngtcp2_conn *conn,
return rv;
}
- rv = ngtcp2_rob_pop(&strm->rx.rob, rx_offset - datalen, datalen);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_rob_pop(&strm->rx.rob, rx_offset - datalen, datalen);
}
}
@@ -4066,7 +4047,7 @@ static void conn_recv_path_challenge(ngtcp2_conn *conn, const ngtcp2_path *path,
}
static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_path *path,
- ngtcp2_path_response *fr) {
+ ngtcp2_path_response *fr, ngtcp2_tstamp ts) {
int rv;
ngtcp2_pv *pv = conn->pv, *npv = NULL;
ngtcp2_duration timeout;
@@ -4078,20 +4059,22 @@ static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_path *path,
rv = ngtcp2_pv_validate(pv, path, fr->data);
if (rv != 0) {
if (rv == NGTCP2_ERR_PATH_VALIDATION_FAILED) {
- return conn_on_path_validation_failed(conn, pv);
+ return conn_on_path_validation_failed(conn, pv, ts);
}
return 0;
}
- if (pv->flags & NGTCP2_PV_FLAG_VERIFY_OLD_PATH_ON_SUCCESS) {
+ /* If validation succeeds, we don't have to throw DCID away. */
+ pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH;
+
+ if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) {
timeout = conn_compute_pto(conn);
timeout = ngtcp2_max(timeout,
(ngtcp2_duration)(6ULL * NGTCP2_DEFAULT_INITIAL_RTT));
- rv = ngtcp2_pv_new(&npv, &conn->dcid.current, timeout,
- NGTCP2_PV_FLAG_DONT_CARE |
- NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH,
- &conn->log, conn->mem);
+ rv = ngtcp2_pv_new(&npv, &pv->fallback_dcid, timeout,
+ NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH, &conn->log,
+ conn->mem);
if (rv != 0) {
return rv;
}
@@ -4100,15 +4083,14 @@ static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_path *path,
/* TODO Retire all DCIDs in conn->bound_dcid */
if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) {
- if (!(pv->flags & NGTCP2_PV_FLAG_VERIFY_OLD_PATH_ON_SUCCESS)) {
- rv = conn_retire_dcid(conn, &conn->dcid.current);
+ if (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) {
+ rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
if (rv != 0) {
goto fail;
}
+ ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid);
}
- ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid);
-
rv = conn_call_path_validation(conn, &pv->dcid.ps.path,
NGTCP2_PATH_VALIDATION_RESULT_SUCCESS);
if (rv != 0) {
@@ -4116,7 +4098,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_path *path,
}
}
- rv = conn_stop_pv(conn);
+ rv = conn_stop_pv(conn, ts);
if (rv != 0) {
goto fail;
}
@@ -4190,8 +4172,9 @@ static int pktns_pkt_num_is_duplicate(ngtcp2_pktns *pktns, int64_t pkt_num) {
*/
static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num) {
int rv;
- ngtcp2_psl_it it;
- ngtcp2_range key;
+ ngtcp2_ksl_it it;
+ ngtcp2_ksl_key key;
+ ngtcp2_range range;
if (pktns->rx.max_pkt_num + 1 != pkt_num) {
ngtcp2_acktr_immediate_ack(&pktns->acktr);
@@ -4205,10 +4188,11 @@ static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num) {
return rv;
}
- if (ngtcp2_psl_len(&pktns->rx.pngap.gap) > 256) {
- it = ngtcp2_psl_begin(&pktns->rx.pngap.gap);
- key = ngtcp2_psl_it_range(&it);
- return ngtcp2_psl_remove(&pktns->rx.pngap.gap, NULL, &key);
+ if (ngtcp2_ksl_len(&pktns->rx.pngap.gap) > 256) {
+ it = ngtcp2_ksl_begin(&pktns->rx.pngap.gap);
+ range = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr;
+ ngtcp2_ksl_remove(&pktns->rx.pngap.gap, NULL,
+ ngtcp2_ksl_key_ptr(&key, &range));
}
return 0;
@@ -4843,10 +4827,7 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm,
return rv;
}
- rv = ngtcp2_rob_pop(&strm->rx.rob, rx_offset - datalen, datalen);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_rob_pop(&strm->rx.rob, rx_offset - datalen, datalen);
}
}
@@ -5489,7 +5470,10 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const uint8_t *payload,
}
if (ngtcp2_verify_stateless_retry_token(conn->dcid.current.token,
- sr.stateless_reset_token) != 0) {
+ sr.stateless_reset_token) != 0 &&
+ (!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) ||
+ ngtcp2_verify_stateless_retry_token(conn->pv->fallback_dcid.token,
+ sr.stateless_reset_token) != 0)) {
len = ngtcp2_ringbuf_len(&conn->dcid.bound);
for (i = 0; i < len; ++i) {
dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
@@ -5500,18 +5484,7 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const uint8_t *payload,
}
if (i == len) {
- len = ngtcp2_ringbuf_len(&conn->dcid.unused);
- for (i = 0; i < len; ++i) {
- dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, i);
- if (ngtcp2_verify_stateless_retry_token(
- dcid->token, sr.stateless_reset_token) == 0) {
- break;
- }
- }
-
- if (i == len) {
- return NGTCP2_ERR_INVALID_ARGUMENT;
- }
+ return NGTCP2_ERR_INVALID_ARGUMENT;
}
}
@@ -5901,7 +5874,8 @@ static void conn_reset_congestion_state(ngtcp2_conn *conn) {
* Out of memory
*/
static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
- const ngtcp2_path *path) {
+ const ngtcp2_path *path,
+ ngtcp2_tstamp ts) {
ngtcp2_dcid *dcid, *last_dcid;
ngtcp2_ringbuf *rb;
@@ -5912,6 +5886,21 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
assert(conn->server);
+ if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)) {
+ /* If new path equals fallback path, that means connection
+ migrated back to the original path. Fallback path is
+ considered to be validated. */
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV,
+ "path is migrated back to the original path");
+ ngtcp2_dcid_copy(&conn->dcid.current, &conn->pv->fallback_dcid);
+ rv = conn_stop_pv(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ return 0;
+ }
+
len = ngtcp2_ringbuf_len(&conn->dcid.bound);
for (i = 0; i < len; ++i) {
@@ -5931,16 +5920,6 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
rb = &conn->dcid.unused;
}
- if (conn->pv) {
- ngtcp2_log_info(
- &conn->log, NGTCP2_LOG_EVENT_PTV,
- "path migration is aborted because new migration has started");
- rv = conn_stop_pv(conn);
- if (rv != 0) {
- return rv;
- }
- }
-
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
"remote address has changed");
@@ -5951,14 +5930,33 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
ngtcp2_max(timeout, (ngtcp2_duration)(6ULL * NGTCP2_DEFAULT_INITIAL_RTT));
rv = ngtcp2_pv_new(&pv, dcid, timeout,
- NGTCP2_PV_FLAG_VERIFY_OLD_PATH_ON_SUCCESS, &conn->log,
- conn->mem);
+ NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE |
+ NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH,
+ &conn->log, conn->mem);
if (rv != 0) {
return rv;
}
- conn->pv = pv;
ngtcp2_path_copy(&pv->dcid.ps.path, path);
+ if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) {
+ ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->pv->fallback_dcid);
+ } else {
+ ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current);
+ }
+ ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid);
+
+ if (conn->pv) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_PTV,
+ "path migration is aborted because new migration has started");
+ rv = conn_stop_pv(conn, ts);
+ if (rv != 0) {
+ ngtcp2_pv_del(pv);
+ return rv;
+ }
+ }
+
+ conn->pv = pv;
if (rb == &conn->dcid.unused) {
ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
@@ -6396,7 +6394,7 @@ static ssize_t conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
conn_recv_path_challenge(conn, path, &fr->path_challenge);
break;
case NGTCP2_FRAME_PATH_RESPONSE:
- rv = conn_recv_path_response(conn, path, &fr->path_response);
+ rv = conn_recv_path_response(conn, path, &fr->path_response, ts);
if (rv != 0) {
return rv;
}
@@ -6429,7 +6427,7 @@ static ssize_t conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
pktns->rx.max_pkt_num < hd.pkt_num &&
!ngtcp2_path_eq(&conn->dcid.current.ps.path, path) &&
!conn_path_validation_in_progress(conn, path)) {
- rv = conn_recv_non_probing_pkt_on_new_path(conn, path);
+ rv = conn_recv_non_probing_pkt_on_new_path(conn, path, ts);
if (rv != 0) {
if (ngtcp2_err_is_fatal(rv)) {
return rv;
@@ -6631,6 +6629,24 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
return 0;
}
+/*
+ * conn_is_retired_path returns nonzero if |path| is inclued in
+ * retired path list.
+ */
+static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) {
+ size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired);
+ ngtcp2_dcid *dcid;
+
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i);
+ if (ngtcp2_path_eq(&dcid->ps.path, path)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts) {
int rv = 0;
@@ -6646,7 +6662,8 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
/* client does not expect a packet from unknown path. */
if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) &&
- (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path))) {
+ (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path)) &&
+ !conn_is_retired_path(conn, path)) {
ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
"ignore packet from unknown path");
return 0;
@@ -6852,8 +6869,8 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) {
timeout =
ngtcp2_max(timeout, (ngtcp2_duration)(6ULL * NGTCP2_DEFAULT_INITIAL_RTT));
- rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log,
- conn->mem);
+ rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH,
+ &conn->log, conn->mem);
if (rv != 0) {
/* TODO Call ngtcp2_dcid_free here if it is introduced */
return rv;
@@ -8184,12 +8201,7 @@ int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) {
conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED;
- rv = ngtcp2_rtb_remove_all(rtb, &frc);
- if (rv != 0) {
- assert(ngtcp2_err_is_fatal(rv));
- ngtcp2_frame_chain_list_del(frc, conn->mem);
- return rv;
- }
+ ngtcp2_rtb_remove_all(rtb, &frc);
rv = conn_resched_frames(conn, pktns, &frc);
if (rv != 0) {
@@ -8505,8 +8517,6 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
ngtcp2_tstamp ts) {
int rv;
ngtcp2_dcid *dcid;
- ngtcp2_pv *pv;
- ngtcp2_duration timeout;
assert(!conn->server);
@@ -8525,29 +8535,22 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
- rv = conn_stop_pv(conn);
+ rv = conn_stop_pv(conn, ts);
if (rv != 0) {
return rv;
}
- timeout = conn_compute_pto(conn);
- timeout =
- ngtcp2_max(timeout, (ngtcp2_duration)(6ULL * NGTCP2_DEFAULT_INITIAL_RTT));
-
- rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log,
- conn->mem);
+ rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
if (rv != 0) {
return rv;
}
- conn->pv = pv;
-
- ngtcp2_path_copy(&pv->dcid.ps.path, path);
+ ngtcp2_dcid_copy(&conn->dcid.current, dcid);
+ ngtcp2_path_copy(&conn->dcid.current.ps.path, path);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
conn_reset_congestion_state(conn);
- ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
-
return 0;
}
@@ -8572,6 +8575,10 @@ ngtcp2_duration ngtcp2_conn_get_idle_timeout(ngtcp2_conn *conn) {
trpto);
}
+ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) {
+ return conn_compute_pto(conn);
+}
+
void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
const ngtcp2_path *path,
const uint8_t *data) {
diff --git a/deps/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/lib/ngtcp2_conn.h
index 9b9744f9b4..947b4a5458 100644
--- a/deps/ngtcp2/lib/ngtcp2_conn.h
+++ b/deps/ngtcp2/lib/ngtcp2_conn.h
@@ -110,6 +110,9 @@ typedef enum {
connection ID the remote endpoint provides to store. It must be
the power of 2. */
#define NGTCP2_MAX_DCID_POOL_SIZE 16
+/* NGTCP2_MAX_DCID_RETIRED_SIZE is the maximum number of retired DCID
+ kept to catch in-flight packet on retired path. */
+#define NGTCP2_MAX_DCID_RETIRED_SIZE 2
/* NGTCP2_MIN_SCID_POOL_SIZE is the minimum number of source
connection ID the local endpoint provides to the remote endpoint.
It must be at least 8 as per the spec. */
@@ -309,6 +312,9 @@ struct ngtcp2_conn {
ngtcp2_ringbuf bound;
/* unused is a set of unused CID received from peer. */
ngtcp2_ringbuf unused;
+ /* retired is a set of CID retired by local endpoint. Keep them
+ in 3*PTO to catch packets in flight along the old path. */
+ ngtcp2_ringbuf retired;
} dcid;
struct {
diff --git a/deps/ngtcp2/lib/ngtcp2_gaptr.c b/deps/ngtcp2/lib/ngtcp2_gaptr.c
index cfcef78570..cdbecd1df1 100644
--- a/deps/ngtcp2/lib/ngtcp2_gaptr.c
+++ b/deps/ngtcp2/lib/ngtcp2_gaptr.c
@@ -31,15 +31,19 @@
int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) {
int rv;
- ngtcp2_range key = {0, UINT64_MAX};
- rv = ngtcp2_psl_init(&gaptr->gap, mem);
+ ngtcp2_range range = {0, UINT64_MAX};
+ ngtcp2_ksl_key key;
+
+ rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar,
+ sizeof(ngtcp2_range), mem);
if (rv != 0) {
return rv;
}
- rv = ngtcp2_psl_insert(&gaptr->gap, NULL, &key, NULL);
+ rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, ngtcp2_ksl_key_ptr(&key, &range),
+ NULL);
if (rv != 0) {
- ngtcp2_psl_free(&gaptr->gap);
+ ngtcp2_ksl_free(&gaptr->gap);
return rv;
}
@@ -53,65 +57,73 @@ void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) {
return;
}
- ngtcp2_psl_free(&gaptr->gap);
+ ngtcp2_ksl_free(&gaptr->gap);
}
int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) {
int rv;
ngtcp2_range k, m, l, r, q = {offset, offset + datalen};
- ngtcp2_psl_it it;
+ ngtcp2_ksl_it it;
+ ngtcp2_ksl_key key, old_key;
- it = ngtcp2_psl_lower_bound(&gaptr->gap, &q);
+ it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, ngtcp2_ksl_key_ptr(&key, &q),
+ ngtcp2_ksl_range_exclusive_compar);
- for (; !ngtcp2_psl_it_end(&it);) {
- k = ngtcp2_psl_it_range(&it);
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr;
m = ngtcp2_range_intersect(&q, &k);
if (!ngtcp2_range_len(&m)) {
break;
}
if (ngtcp2_range_eq(&k, &m)) {
- rv = ngtcp2_psl_remove(&gaptr->gap, &it, &k);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&gaptr->gap, &it, ngtcp2_ksl_key_ptr(&key, &k));
continue;
}
ngtcp2_range_cut(&l, &r, &k, &m);
if (ngtcp2_range_len(&l)) {
- ngtcp2_psl_update_range(&gaptr->gap, &k, &l);
+ ngtcp2_ksl_update_key(&gaptr->gap, ngtcp2_ksl_key_ptr(&old_key, &k),
+ ngtcp2_ksl_key_ptr(&key, &l));
if (ngtcp2_range_len(&r)) {
- rv = ngtcp2_psl_insert(&gaptr->gap, &it, &r, NULL);
+ rv = ngtcp2_ksl_insert(&gaptr->gap, &it, ngtcp2_ksl_key_ptr(&key, &r),
+ NULL);
if (rv != 0) {
return rv;
}
}
} else if (ngtcp2_range_len(&r)) {
- ngtcp2_psl_update_range(&gaptr->gap, &k, &r);
+ ngtcp2_ksl_update_key(&gaptr->gap, ngtcp2_ksl_key_ptr(&old_key, &k),
+ ngtcp2_ksl_key_ptr(&key, &r));
}
- ngtcp2_psl_it_next(&it);
+ ngtcp2_ksl_it_next(&it);
}
return 0;
}
uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) {
- ngtcp2_psl_it it = ngtcp2_psl_begin(&gaptr->gap);
- ngtcp2_range r = ngtcp2_psl_it_range(&it);
+ ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap);
+ ngtcp2_range r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr;
return r.begin;
}
-ngtcp2_psl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
+ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
uint64_t offset) {
ngtcp2_range q = {offset, offset + 1};
- return ngtcp2_psl_lower_bound(&gaptr->gap, &q);
+ ngtcp2_ksl_key key;
+ return ngtcp2_ksl_lower_bound_compar(&gaptr->gap,
+ ngtcp2_ksl_key_ptr(&key, &q),
+ ngtcp2_ksl_range_exclusive_compar);
}
int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset,
size_t datalen) {
+ ngtcp2_ksl_key key;
ngtcp2_range q = {offset, offset + datalen};
- ngtcp2_psl_it it = ngtcp2_psl_lower_bound(&gaptr->gap, &q);
- ngtcp2_range k = ngtcp2_psl_it_range(&it);
+ ngtcp2_ksl_it it =
+ ngtcp2_ksl_lower_bound_compar(&gaptr->gap, ngtcp2_ksl_key_ptr(&key, &q),
+ ngtcp2_ksl_range_exclusive_compar);
+ ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr;
ngtcp2_range m = ngtcp2_range_intersect(&q, &k);
return ngtcp2_range_len(&m) == 0;
}
diff --git a/deps/ngtcp2/lib/ngtcp2_gaptr.h b/deps/ngtcp2/lib/ngtcp2_gaptr.h
index 7f08ee3fce..1affbb7e8b 100644
--- a/deps/ngtcp2/lib/ngtcp2_gaptr.h
+++ b/deps/ngtcp2/lib/ngtcp2_gaptr.h
@@ -33,7 +33,7 @@
#include "ngtcp2_mem.h"
#include "ngtcp2_range.h"
-#include "ngtcp2_psl.h"
+#include "ngtcp2_ksl.h"
/*
* ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX).
@@ -41,7 +41,7 @@
typedef struct {
/* gap maintains the range of offset which is not received
yet. Initially, its range is [0, UINT64_MAX). */
- ngtcp2_psl gap;
+ ngtcp2_ksl gap;
/* mem is custom memory allocator */
const ngtcp2_mem *mem;
} ngtcp2_gaptr;
@@ -84,7 +84,7 @@ uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr);
* ngtcp2_gaptr_get_first_gap_after returns the iterator pointing to
* the first gap which overlaps or comes after |offset|.
*/
-ngtcp2_psl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
+ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
uint64_t offset);
/*
diff --git a/deps/ngtcp2/lib/ngtcp2_ksl.c b/deps/ngtcp2/lib/ngtcp2_ksl.c
index f4d9d728da..6af35eac67 100644
--- a/deps/ngtcp2/lib/ngtcp2_ksl.c
+++ b/deps/ngtcp2/lib/ngtcp2_ksl.c
@@ -31,45 +31,87 @@
#include "ngtcp2_macro.h"
#include "ngtcp2_mem.h"
+#include "ngtcp2_range.h"
-int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar,
- const ngtcp2_ksl_key *inf_key, const ngtcp2_mem *mem) {
+static size_t ksl_nodelen(size_t keylen) {
+ return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) &
+ (size_t)~0xf;
+}
+
+static size_t ksl_blklen(size_t nodelen) {
+ return sizeof(ngtcp2_ksl_blk) + nodelen * NGTCP2_KSL_MAX_NBLK -
+ sizeof(uint64_t);
+}
+
+/*
+ * ksl_node_set_key sets |key| to |node|.
+ */
+static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node,
+ const void *key) {
+ memcpy(&node->key, key, ksl->keylen);
+}
+
+/*
+ * ksl_node_key assigns the pointer to key of |node| to key->ptr and
+ * returns |key|.
+ */
+static ngtcp2_ksl_key *ksl_node_key(ngtcp2_ksl_key *key,
+ ngtcp2_ksl_node *node) {
+ key->ptr = &node->key;
+ return key;
+}
+
+/*
+ * ksl_nth_node returns |n|th node under |blk|.
+ */
+static ngtcp2_ksl_node *ksl_nth_node(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
+ size_t n) {
+ return (ngtcp2_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n);
+}
+
+ngtcp2_ksl_node *ngtcp2_ksl_nth_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
+ size_t n) {
+ return ksl_nth_node(ksl, blk, n);
+}
+
+int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
+ const ngtcp2_mem *mem) {
+ size_t nodelen = ksl_nodelen(keylen);
+ size_t blklen = ksl_blklen(nodelen);
ngtcp2_ksl_blk *head;
- ksl->head = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_ksl_blk));
+ ksl->head = ngtcp2_mem_malloc(mem, blklen);
if (!ksl->head) {
return NGTCP2_ERR_NOMEM;
}
ksl->front = ksl->back = ksl->head;
ksl->compar = compar;
- ksl->inf_key = *inf_key;
+ ksl->keylen = keylen;
+ ksl->nodelen = nodelen;
ksl->n = 0;
ksl->mem = mem;
head = ksl->head;
-
head->next = head->prev = NULL;
- head->n = 1;
+ head->n = 0;
head->leaf = 1;
- head->nodes[0].key = ksl->inf_key;
- head->nodes[0].data = NULL;
return 0;
}
/*
- * free_blk frees |blk| recursively.
+ * ksl_free_blk frees |blk| recursively.
*/
-static void free_blk(ngtcp2_ksl_blk *blk, const ngtcp2_mem *mem) {
+static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
size_t i;
if (!blk->leaf) {
for (i = 0; i < blk->n; ++i) {
- free_blk(blk->nodes[i].blk, mem);
+ ksl_free_blk(ksl, ksl_nth_node(ksl, blk, i)->blk);
}
}
- ngtcp2_mem_free(mem, blk);
+ ngtcp2_mem_free(ksl->mem, blk);
}
void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
@@ -77,7 +119,7 @@ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
return;
}
- free_blk(ksl->head, ksl->mem);
+ ksl_free_blk(ksl, ksl->head);
}
/*
@@ -91,7 +133,7 @@ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
ngtcp2_ksl_blk *rblk;
- rblk = ngtcp2_mem_malloc(ksl->mem, sizeof(ngtcp2_ksl_blk));
+ rblk = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
if (rblk == NULL) {
return NULL;
}
@@ -108,8 +150,8 @@ static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
rblk->n = blk->n / 2;
- memcpy(rblk->nodes, &blk->nodes[blk->n - rblk->n],
- sizeof(ngtcp2_ksl_node) * rblk->n);
+ memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n),
+ ksl->nodelen * rblk->n);
blk->n -= rblk->n;
@@ -131,22 +173,25 @@ static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
* Out of memory.
*/
static int ksl_split_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
- ngtcp2_ksl_blk *lblk = blk->nodes[i].blk, *rblk;
+ ngtcp2_ksl_node *node;
+ ngtcp2_ksl_blk *lblk = ksl_nth_node(ksl, blk, i)->blk, *rblk;
rblk = ksl_split_blk(ksl, lblk);
if (rblk == NULL) {
return NGTCP2_ERR_NOMEM;
}
- memmove(&blk->nodes[i + 2], &blk->nodes[i + 1],
- sizeof(ngtcp2_ksl_node) * (blk->n - (i + 1)));
-
- blk->nodes[i + 1].blk = rblk;
+ memmove(blk->nodes + (i + 2) * ksl->nodelen,
+ blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+ node = ksl_nth_node(ksl, blk, i + 1);
+ node->blk = rblk;
++blk->n;
+ ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
- blk->nodes[i].key = lblk->nodes[lblk->n - 1].key;
- blk->nodes[i + 1].key = rblk->nodes[rblk->n - 1].key;
+ node = ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
return 0;
}
@@ -163,6 +208,7 @@ static int ksl_split_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
*/
static int ksl_split_head(ngtcp2_ksl *ksl) {
ngtcp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL;
+ ngtcp2_ksl_node *node;
rblk = ksl_split_blk(ksl, ksl->head);
if (rblk == NULL) {
@@ -171,7 +217,7 @@ static int ksl_split_head(ngtcp2_ksl *ksl) {
lblk = ksl->head;
- nhead = ngtcp2_mem_malloc(ksl->mem, sizeof(ngtcp2_ksl_blk));
+ nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
if (nhead == NULL) {
ngtcp2_mem_free(ksl->mem, rblk);
return NGTCP2_ERR_NOMEM;
@@ -180,10 +226,13 @@ static int ksl_split_head(ngtcp2_ksl *ksl) {
nhead->n = 2;
nhead->leaf = 0;
- nhead->nodes[0].key = lblk->nodes[lblk->n - 1].key;
- nhead->nodes[0].blk = lblk;
- nhead->nodes[1].key = rblk->nodes[rblk->n - 1].key;
- nhead->nodes[1].blk = rblk;
+ node = ksl_nth_node(ksl, nhead, 0);
+ ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ node->blk = lblk;
+
+ node = ksl_nth_node(ksl, nhead, 1);
+ ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+ node->blk = rblk;
ksl->head = nhead;
@@ -196,26 +245,46 @@ static int ksl_split_head(ngtcp2_ksl *ksl) {
* of nodes contained by |blk| is strictly less than
* NGTCP2_KSL_MAX_NBLK.
*/
-static void insert_node(ngtcp2_ksl_blk *blk, size_t i,
- const ngtcp2_ksl_key *key, void *data) {
+static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i,
+ const ngtcp2_ksl_key *key, void *data) {
ngtcp2_ksl_node *node;
assert(blk->n < NGTCP2_KSL_MAX_NBLK);
- memmove(&blk->nodes[i + 1], &blk->nodes[i],
- sizeof(ngtcp2_ksl_node) * (blk->n - i));
+ memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen,
+ ksl->nodelen * (blk->n - i));
- node = &blk->nodes[i];
- node->key = *key;
+ node = ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node, key->ptr);
node->data = data;
++blk->n;
}
+static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
+ const ngtcp2_ksl_key *key, ngtcp2_ksl_compar compar) {
+ ssize_t left = -1, right = (ssize_t)blk->n, mid;
+ ngtcp2_ksl_node *node;
+ ngtcp2_ksl_key node_key;
+
+ while (right - left > 1) {
+ mid = (left + right) / 2;
+ node = ksl_nth_node(ksl, blk, (size_t)mid);
+ if (compar(ksl_node_key(&node_key, node), key)) {
+ left = mid;
+ } else {
+ right = mid;
+ }
+ }
+
+ return (size_t)right;
+}
+
int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
const ngtcp2_ksl_key *key, void *data) {
ngtcp2_ksl_blk *blk = ksl->head;
ngtcp2_ksl_node *node;
+ ngtcp2_ksl_key node_key;
size_t i;
int rv;
@@ -228,26 +297,51 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
}
for (;;) {
- for (i = 0, node = &blk->nodes[i]; ksl->compar(&node->key, key);
- ++i, ++node)
- ;
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
if (blk->leaf) {
- insert_node(blk, i, key, data);
+ ksl_insert_node(ksl, blk, i, key, data);
++ksl->n;
if (it) {
- ngtcp2_ksl_it_init(it, blk, i, ksl->compar, &ksl->inf_key);
+ ngtcp2_ksl_it_init(it, ksl, blk, i);
}
return 0;
}
+ if (i == blk->n) {
+ /* This insertion extends the largest key in this subtree. */
+ for (; !blk->leaf;) {
+ node = ksl_nth_node(ksl, blk, blk->n - 1);
+ if (node->blk->n == NGTCP2_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, blk->n - 1);
+ if (rv != 0) {
+ return rv;
+ }
+ node = ksl_nth_node(ksl, blk, blk->n - 1);
+ }
+ ksl_node_set_key(ksl, node, key->ptr);
+ blk = node->blk;
+ }
+ ksl_insert_node(ksl, blk, blk->n, key, data);
+ ++ksl->n;
+ if (it) {
+ ngtcp2_ksl_it_init(it, ksl, blk, blk->n - 1);
+ }
+ return 0;
+ }
+
+ node = ksl_nth_node(ksl, blk, i);
+
if (node->blk->n == NGTCP2_KSL_MAX_NBLK) {
rv = ksl_split_node(ksl, blk, i);
if (rv != 0) {
return rv;
}
- if (ksl->compar(&node->key, key)) {
- node = &blk->nodes[i + 1];
+ if (ksl->compar(ksl_node_key(&node_key, node), key)) {
+ node = ksl_nth_node(ksl, blk, i + 1);
+ if (ksl->compar(ksl_node_key(&node_key, node), key)) {
+ ksl_node_set_key(ksl, node, key->ptr);
+ }
}
}
@@ -256,11 +350,12 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
}
/*
- * remove_node removes the node included in |blk| at the index of |i|.
+ * ksl_remove_node removes the node included in |blk| at the index of
+ * |i|.
*/
-static void remove_node(ngtcp2_ksl_blk *blk, size_t i) {
- memmove(&blk->nodes[i], &blk->nodes[i + 1],
- sizeof(ngtcp2_ksl_node) * (blk->n - (i + 1)));
+static void ksl_remove_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
--blk->n;
}
@@ -281,13 +376,13 @@ static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
assert(i + 1 < blk->n);
- lblk = blk->nodes[i].blk;
- rblk = blk->nodes[i + 1].blk;
+ lblk = ksl_nth_node(ksl, blk, i)->blk;
+ rblk = ksl_nth_node(ksl, blk, i + 1)->blk;
assert(lblk->n + rblk->n < NGTCP2_KSL_MAX_NBLK);
- memcpy(&lblk->nodes[lblk->n], &rblk->nodes[0],
- sizeof(ngtcp2_ksl_node) * rblk->n);
+ memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes,
+ ksl->nodelen * rblk->n);
lblk->n += rblk->n;
lblk->next = rblk->next;
@@ -303,144 +398,68 @@ static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
ngtcp2_mem_free(ksl->mem, ksl->head);
ksl->head = lblk;
} else {
- remove_node(blk, i + 1);
- blk->nodes[i].key = lblk->nodes[lblk->n - 1].key;
+ ksl_remove_node(ksl, blk, i + 1);
+ ksl_node_set_key(ksl, ksl_nth_node(ksl, blk, i),
+ &ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
}
return lblk;
}
/*
- * ksl_relocate_node replaces the key at the index |*pi| in
- * *pblk->nodes with something other without violating contract. It
- * might involve merging 2 nodes or moving a node to left or right.
- *
- * It assigns the index of the block in |*pblk| where the node is
- * moved to |*pi|. If merging 2 nodes occurs and it becomes new head,
- * the new head is assigned to |*pblk| and it still contains the key.
- * The caller should handle this situation.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory.
- */
-static int ksl_relocate_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk **pblk,
- size_t *pi) {
- ngtcp2_ksl_blk *blk = *pblk;
- size_t i = *pi;
- ngtcp2_ksl_node *node = &blk->nodes[i];
- ngtcp2_ksl_node *rnode = &blk->nodes[i + 1];
- size_t j;
- int rv;
-
- assert(i + 1 < blk->n);
-
- if (node->blk->n == NGTCP2_KSL_MIN_NBLK &&
- node->blk->n + rnode->blk->n < NGTCP2_KSL_MAX_NBLK) {
- j = node->blk->n - 1;
- blk = ksl_merge_node(ksl, blk, i);
- if (blk != ksl->head) {
- return 0;
- }
- *pblk = blk;
- i = j;
- if (blk->leaf) {
- *pi = i;
- return 0;
- }
- node = &blk->nodes[i];
- rnode = &blk->nodes[i + 1];
-
- if (node->blk->n == NGTCP2_KSL_MIN_NBLK &&
- node->blk->n + rnode->blk->n < NGTCP2_KSL_MAX_NBLK) {
- j = node->blk->n - 1;
- blk = ksl_merge_node(ksl, blk, i);
- assert(blk != ksl->head);
- *pi = j;
- return 0;
- }
- }
-
- if (node->blk->n < rnode->blk->n) {
- node->blk->nodes[node->blk->n] = rnode->blk->nodes[0];
- memmove(&rnode->blk->nodes[0], &rnode->blk->nodes[1],
- sizeof(ngtcp2_ksl_node) * (rnode->blk->n - 1));
- --rnode->blk->n;
- ++node->blk->n;
- node->key = node->blk->nodes[node->blk->n - 1].key;
- *pi = i;
- return 0;
- }
-
- if (rnode->blk->n == NGTCP2_KSL_MAX_NBLK) {
- rv = ksl_split_node(ksl, blk, i + 1);
- if (rv != 0) {
- return rv;
- }
- }
-
- memmove(&rnode->blk->nodes[1], &rnode->blk->nodes[0],
- sizeof(ngtcp2_ksl_node) * rnode->blk->n);
-
- rnode->blk->nodes[0] = node->blk->nodes[node->blk->n - 1];
- ++rnode->blk->n;
-
- --node->blk->n;
-
- node->key = node->blk->nodes[node->blk->n - 1].key;
-
- *pi = i + 1;
- return 0;
-}
-
-/*
- * shift_left moves the first node in blk->nodes[i]->blk->nodes to
+ * ksl_shift_left moves the first node in blk->nodes[i]->blk->nodes to
* blk->nodes[i - 1]->blk->nodes.
*/
-static void shift_left(ngtcp2_ksl_blk *blk, size_t i) {
- ngtcp2_ksl_node *lnode, *rnode;
+static void ksl_shift_left(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ ngtcp2_ksl_node *lnode, *rnode, *dest, *src;
assert(i > 0);
- lnode = &blk->nodes[i - 1];
- rnode = &blk->nodes[i];
+ lnode = ksl_nth_node(ksl, blk, i - 1);
+ rnode = ksl_nth_node(ksl, blk, i);
assert(lnode->blk->n < NGTCP2_KSL_MAX_NBLK);
assert(rnode->blk->n > NGTCP2_KSL_MIN_NBLK);
- lnode->blk->nodes[lnode->blk->n] = rnode->blk->nodes[0];
- lnode->key = lnode->blk->nodes[lnode->blk->n].key;
+ dest = ksl_nth_node(ksl, lnode->blk, lnode->blk->n);
+ src = ksl_nth_node(ksl, rnode->blk, 0);
+
+ memcpy(dest, src, ksl->nodelen);
+ ksl_node_set_key(ksl, lnode, &dest->key);
++lnode->blk->n;
--rnode->blk->n;
- memmove(&rnode->blk->nodes[0], &rnode->blk->nodes[1],
- sizeof(ngtcp2_ksl_node) * rnode->blk->n);
+ memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen,
+ ksl->nodelen * rnode->blk->n);
}
/*
- * shift_right moves the last node in blk->nodes[i]->blk->nodes to
+ * ksl_shift_right moves the last node in blk->nodes[i]->blk->nodes to
* blk->nodes[i + 1]->blk->nodes.
*/
-static void shift_right(ngtcp2_ksl_blk *blk, size_t i) {
- ngtcp2_ksl_node *lnode, *rnode;
+static void ksl_shift_right(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ ngtcp2_ksl_node *lnode, *rnode, *dest, *src;
assert(i < blk->n - 1);
- lnode = &blk->nodes[i];
- rnode = &blk->nodes[i + 1];
+ lnode = ksl_nth_node(ksl, blk, i);
+ rnode = ksl_nth_node(ksl, blk, i + 1);
assert(lnode->blk->n > NGTCP2_KSL_MIN_NBLK);
assert(rnode->blk->n < NGTCP2_KSL_MAX_NBLK);
- memmove(&rnode->blk->nodes[1], &rnode->blk->nodes[0],
- sizeof(ngtcp2_ksl_node) * rnode->blk->n);
+ memmove(rnode->blk->nodes + ksl->nodelen, rnode->blk->nodes,
+ ksl->nodelen * rnode->blk->n);
++rnode->blk->n;
- rnode->blk->nodes[0] = lnode->blk->nodes[lnode->blk->n - 1];
+
+ dest = ksl_nth_node(ksl, rnode->blk, 0);
+ src = ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1);
+
+ memcpy(dest, src, ksl->nodelen);
--lnode->blk->n;
- lnode->key = lnode->blk->nodes[lnode->blk->n - 1].key;
+ ksl_node_set_key(ksl, lnode,
+ &ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
}
/*
@@ -452,100 +471,133 @@ static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs,
return !compar(lhs, rhs) && !compar(rhs, lhs);
}
-int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
- const ngtcp2_ksl_key *key) {
+void ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_key *key) {
ngtcp2_ksl_blk *blk = ksl->head, *lblk, *rblk;
ngtcp2_ksl_node *node;
- size_t i, j;
- int rv;
+ size_t i;
- if (!blk->leaf && blk->n == NGTCP2_KSL_MAX_NBLK) {
- rv = ksl_split_head(ksl);
- if (rv != 0) {
- return rv;
- }
- blk = ksl->head;
+ if (!blk->leaf && blk->n == 2 &&
+ ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK &&
+ ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) {
+ blk = ksl_merge_node(ksl, ksl->head, 0);
}
for (;;) {
- for (i = 0, node = &blk->nodes[i]; ksl->compar(&node->key, key);
- ++i, ++node)
- ;
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ assert(i < blk->n);
if (blk->leaf) {
assert(i < blk->n);
- remove_node(blk, i);
+ ksl_remove_node(ksl, blk, i);
--ksl->n;
if (it) {
- if (blk->n == i) {
- ngtcp2_ksl_it_init(it, blk->next, 0, ksl->compar, &ksl->inf_key);
+ if (blk->n == i && blk->next) {
+ ngtcp2_ksl_it_init(it, ksl, blk->next, 0);
} else {
- ngtcp2_ksl_it_init(it, blk, i, ksl->compar, &ksl->inf_key);
+ ngtcp2_ksl_it_init(it, ksl, blk, i);
}
}
- return 0;
+ return;
}
- if (node->blk->n == NGTCP2_KSL_MAX_NBLK) {
- rv = ksl_split_node(ksl, blk, i);
- if (rv != 0) {
- return rv;
- }
- if (ksl->compar(&node->key, key)) {
- ++i;
- node = &blk->nodes[i];
+ node = ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n == NGTCP2_KSL_MIN_NBLK) {
+ if (i > 0 && (lblk = ksl_nth_node(ksl, blk, i - 1)->blk)->n >
+ NGTCP2_KSL_MIN_NBLK) {
+ ksl_shift_right(ksl, blk, i - 1);
+ } else if (i + 1 < blk->n &&
+ (rblk = ksl_nth_node(ksl, blk, i + 1)->blk)->n >
+ NGTCP2_KSL_MIN_NBLK) {
+ ksl_shift_left(ksl, blk, i + 1);
+ } else if (i > 0) {
+ assert(lblk);
+ assert(lblk->n + node->blk->n < NGTCP2_KSL_MAX_NBLK);
+ blk = ksl_merge_node(ksl, blk, i - 1);
+ } else {
+ assert(i + 1 < blk->n);
+ assert(rblk);
+ assert(node->blk->n + rblk->n < NGTCP2_KSL_MAX_NBLK);
+ blk = ksl_merge_node(ksl, blk, i);
}
+ } else {
+ blk = node->blk;
}
+ }
+}
- if (key_equal(ksl->compar, &node->key, key)) {
- rv = ksl_relocate_node(ksl, &blk, &i);
- if (rv != 0) {
- return rv;
- }
- if (!blk->leaf) {
- node = &blk->nodes[i];
- blk = node->blk;
- }
- } else if (node->blk->n == NGTCP2_KSL_MIN_NBLK) {
- j = i == 0 ? 0 : i - 1;
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key) {
+ ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_it it;
+ size_t i;
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
- lblk = blk->nodes[j].blk;
- rblk = blk->nodes[j + 1].blk;
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
- if (lblk->n + rblk->n < NGTCP2_KSL_MAX_NBLK) {
- blk = ksl_merge_node(ksl, blk, j);
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
} else {
- if (i == j) {
- shift_left(blk, j + 1);
- } else {
- shift_right(blk, j);
- }
- blk = node->blk;
+ i = blk->n;
}
- } else {
- blk = node->blk;
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
}
+ blk = ksl_nth_node(ksl, blk, i)->blk;
}
}
-ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl,
- const ngtcp2_ksl_key *key) {
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key,
+ ngtcp2_ksl_compar compar) {
ngtcp2_ksl_blk *blk = ksl->head;
- ngtcp2_ksl_node *node;
+ ngtcp2_ksl_it it;
size_t i;
for (;;) {
- for (i = 0, node = &blk->nodes[i]; ksl->compar(&node->key, key);
- ++i, node = &blk->nodes[i])
- ;
+ i = ksl_bsearch(ksl, blk, key, compar);
if (blk->leaf) {
- ngtcp2_ksl_it it;
- ngtcp2_ksl_it_init(&it, blk, i, ksl->compar, &ksl->inf_key);
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
return it;
}
- blk = node->blk;
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = ksl_nth_node(ksl, blk, i)->blk;
}
}
@@ -553,43 +605,49 @@ void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key,
const ngtcp2_ksl_key *new_key) {
ngtcp2_ksl_blk *blk = ksl->head;
ngtcp2_ksl_node *node;
+ ngtcp2_ksl_key node_key;
size_t i;
for (;;) {
- for (i = 0, node = &blk->nodes[i]; ksl->compar(&node->key, old_key);
- ++i, node = &blk->nodes[i])
- ;
+ i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
+
+ assert(i < blk->n);
+ node = ksl_nth_node(ksl, blk, i);
if (blk->leaf) {
- assert(key_equal(ksl->compar, &node->key, old_key));
- node->key = *new_key;
+ assert(key_equal(ksl->compar, ksl_node_key(&node_key, node), old_key));
+ ksl_node_set_key(ksl, node, new_key->ptr);
return;
}
- if (key_equal(ksl->compar, &node->key, old_key)) {
- node->key = *new_key;
+ ksl_node_key(&node_key, node);
+ if (key_equal(ksl->compar, &node_key, old_key) ||
+ ksl->compar(&node_key, new_key)) {
+ ksl_node_set_key(ksl, node, new_key->ptr);
}
blk = node->blk;
}
}
-static void ksl_print(ngtcp2_ksl *ksl, const ngtcp2_ksl_blk *blk,
- size_t level) {
+static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) {
size_t i;
+ ngtcp2_ksl_node *node;
+ ngtcp2_ksl_key node_key;
fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n);
if (blk->leaf) {
for (i = 0; i < blk->n; ++i) {
- fprintf(stderr, " %" PRId64, blk->nodes[i].key.i);
+ node = ksl_nth_node(ksl, blk, i);
+ fprintf(stderr, " %" PRId64, *ksl_node_key(&node_key, node)->i);
}
fprintf(stderr, "\n");
return;
}
for (i = 0; i < blk->n; ++i) {
- ksl_print(ksl, blk->nodes[i].blk, level + 1);
+ ksl_print(ksl, ksl_nth_node(ksl, blk, i)->blk, level + 1);
}
}
@@ -601,7 +659,7 @@ void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) {
if (!ksl->head->leaf) {
for (i = 0; i < ksl->head->n; ++i) {
- free_blk(ksl->head->nodes[i].blk, ksl->mem);
+ ksl_free_blk(ksl, ksl_nth_node(ksl, ksl->head, i)->blk);
}
}
@@ -611,42 +669,40 @@ void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) {
head = ksl->head;
head->next = head->prev = NULL;
- head->n = 1;
+ head->n = 0;
head->leaf = 1;
- head->nodes[0].key = ksl->inf_key;
- head->nodes[0].data = NULL;
}
void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); }
ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) {
ngtcp2_ksl_it it;
- ngtcp2_ksl_it_init(&it, ksl->front, 0, ksl->compar, &ksl->inf_key);
+ ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0);
return it;
}
ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) {
ngtcp2_ksl_it it;
- ngtcp2_ksl_it_init(&it, ksl->back, ksl->back->n - 1, ksl->compar,
- &ksl->inf_key);
+ ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
return it;
}
-void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl_blk *blk, size_t i,
- ngtcp2_ksl_compar compar,
- const ngtcp2_ksl_key *inf_key) {
+void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
+ ngtcp2_ksl_blk *blk, size_t i) {
+ it->ksl = ksl;
it->blk = blk;
it->i = i;
- it->compar = compar;
- it->inf_key = *inf_key;
}
void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) {
- return it->blk->nodes[it->i].data;
+ assert(it->i < it->blk->n);
+ return ksl_nth_node(it->ksl, it->blk, it->i)->data;
}
void ngtcp2_ksl_it_next(ngtcp2_ksl_it *it) {
- if (++it->i == it->blk->n) {
+ assert(!ngtcp2_ksl_it_end(it));
+
+ if (++it->i == it->blk->n && it->blk->next) {
it->blk = it->blk->next;
it->i = 0;
}
@@ -664,7 +720,7 @@ void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it) {
}
int ngtcp2_ksl_it_end(const ngtcp2_ksl_it *it) {
- return key_equal(it->compar, &it->blk->nodes[it->i].key, &it->inf_key);
+ return it->blk->n == it->i && it->blk->next == NULL;
}
int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it) {
@@ -672,15 +728,27 @@ int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it) {
}
ngtcp2_ksl_key ngtcp2_ksl_it_key(const ngtcp2_ksl_it *it) {
- return it->blk->nodes[it->i].key;
-}
+ ngtcp2_ksl_key node_key;
-ngtcp2_ksl_key *ngtcp2_ksl_key_i(ngtcp2_ksl_key *key, int64_t i) {
- key->i = i;
- return key;
+ assert(it->i < it->blk->n);
+
+ return *ksl_node_key(&node_key, ksl_nth_node(it->ksl, it->blk, it->i));
}
ngtcp2_ksl_key *ngtcp2_ksl_key_ptr(ngtcp2_ksl_key *key, const void *ptr) {
key->ptr = ptr;
return key;
}
+
+int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ const ngtcp2_range *a = lhs->ptr, *b = rhs->ptr;
+ return a->begin < b->begin;
+}
+
+int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ const ngtcp2_range *a = lhs->ptr, *b = rhs->ptr;
+ return a->begin < b->begin &&
+ !(ngtcp2_max(a->begin, b->begin) < ngtcp2_min(a->end, b->end));
+}
diff --git a/deps/ngtcp2/lib/ngtcp2_ksl.h b/deps/ngtcp2/lib/ngtcp2_ksl.h
index deede10200..c9ce128a22 100644
--- a/deps/ngtcp2/lib/ngtcp2_ksl.h
+++ b/deps/ngtcp2/lib/ngtcp2_ksl.h
@@ -49,7 +49,9 @@
* ngtcp2_ksl_key represents key in ngtcp2_ksl.
*/
typedef union {
- int64_t i;
+ /* i is defined to retrieve int64_t key for convenience. */
+ const int64_t *i;
+ /* ptr points to the key. */
const void *ptr;
} ngtcp2_ksl_key;
@@ -62,16 +64,22 @@ typedef struct ngtcp2_ksl_blk ngtcp2_ksl_blk;
/*
* ngtcp2_ksl_node is a node which contains either ngtcp2_ksl_blk or
* opaque data. If a node is an internal node, it contains
- * ngtcp2_ksl_blk. Otherwise, it has data. The invariant is that the
- * key of internal node dictates the maximum key in its descendants,
- * and the corresponding leaf node must exist.
+ * ngtcp2_ksl_blk. Otherwise, it has data. The key is stored at the
+ * location starting at key.
*/
struct ngtcp2_ksl_node {
- ngtcp2_ksl_key key;
union {
ngtcp2_ksl_blk *blk;
void *data;
};
+ union {
+ uint64_t align;
+ /* key is a buffer to include key associated to this node.
+ Because the length of key is unknown until ngtcp2_ksl_init is
+ called, the actual buffer will be allocated after this
+ field. */
+ uint8_t key[1];
+ };
};
/*
@@ -86,7 +94,15 @@ struct ngtcp2_ksl_blk {
size_t n;
/* leaf is nonzero if this block contains leaf nodes. */
int leaf;
- ngtcp2_ksl_node nodes[NGTCP2_KSL_MAX_NBLK];
+ union {
+ uint64_t align;
+ /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK
+ ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is
+ allocated along with the additional variable length key
+ storage, the size of buffer is unknown until ngtcp2_ksl_init is
+ called. */
+ uint8_t nodes[1];
+ };
};
/*
@@ -96,6 +112,9 @@ struct ngtcp2_ksl_blk {
typedef int (*ngtcp2_ksl_compar)(const ngtcp2_ksl_key *lhs,
const ngtcp2_ksl_key *rhs);
+struct ngtcp2_ksl;
+typedef struct ngtcp2_ksl ngtcp2_ksl;
+
struct ngtcp2_ksl_it;
typedef struct ngtcp2_ksl_it ngtcp2_ksl_it;
@@ -103,15 +122,11 @@ typedef struct ngtcp2_ksl_it ngtcp2_ksl_it;
* ngtcp2_ksl_it is a forward iterator to iterate nodes.
*/
struct ngtcp2_ksl_it {
- const ngtcp2_ksl_blk *blk;
+ const ngtcp2_ksl *ksl;
+ ngtcp2_ksl_blk *blk;
size_t i;
- ngtcp2_ksl_compar compar;
- ngtcp2_ksl_key inf_key;
};
-struct ngtcp2_ksl;
-typedef struct ngtcp2_ksl ngtcp2_ksl;
-
/*
* ngtcp2_ksl is a deterministic paged skip list.
*/
@@ -123,14 +138,18 @@ struct ngtcp2_ksl {
/* back points to the last leaf block. */
ngtcp2_ksl_blk *back;
ngtcp2_ksl_compar compar;
- ngtcp2_ksl_key inf_key;
size_t n;
+ /* keylen is the size of key */
+ size_t keylen;
+ /* nodelen is the actual size of ngtcp2_ksl_node including key
+ storage. */
+ size_t nodelen;
const ngtcp2_mem *mem;
};
/*
* ngtcp2_ksl_init initializes |ksl|. |compar| specifies compare
- * function. |inf_key| specifies the "infinite" key.
+ * function. |keylen| is the length of key.
*
* It returns 0 if it succeeds, or one of the following negative error
* codes:
@@ -138,8 +157,8 @@ struct ngtcp2_ksl {
* NGTCP2_ERR_NOMEM
* Out of memory.
*/
-int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar,
- const ngtcp2_ksl_key *inf_key, const ngtcp2_mem *mem);
+int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
+ const ngtcp2_mem *mem);
/*
* ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is
@@ -171,15 +190,9 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
* This function assigns the iterator to |*it|, which points to the
* node which is located at the right next of the removed node if |it|
* is not NULL.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory.
*/
-int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
- const ngtcp2_ksl_key *key);
+void ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_key *key);
/*
* ngtcp2_ksl_lower_bound returns the iterator which points to the
@@ -191,6 +204,14 @@ int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl,
const ngtcp2_ksl_key *key);
+/*
+ * ngtcp2_ksl_lower_bound_compar works like ngtcp2_ksl_lower_bound,
+ * but it takes custom function |compar| to do lower bound search.
+ */
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key,
+ ngtcp2_ksl_compar compar);
+
/*
* ngtcp2_ksl_update_key replaces the key of nodes which has |old_key|
* with |new_key|. |new_key| must be strictly greater than the
@@ -225,22 +246,29 @@ size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl);
void ngtcp2_ksl_clear(ngtcp2_ksl *ksl);
/*
- * ngtcp2_ksl_print prints its internal state in stderr. This
- * function should be used for the debugging purpose only.
+ * ngtcp2_ksl_nth_node returns the |n|th node under |blk|. This
+ * function is provided for unit testing.
+ */
+ngtcp2_ksl_node *ngtcp2_ksl_nth_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
+ size_t n);
+
+/*
+ * ngtcp2_ksl_print prints its internal state in stderr. It assumes
+ * that the key is of type int64_t. This function should be used for
+ * the debugging purpose only.
*/
void ngtcp2_ksl_print(ngtcp2_ksl *ksl);
/*
* ngtcp2_ksl_it_init initializes |it|.
*/
-void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl_blk *blk, size_t i,
- ngtcp2_ksl_compar compar,
- const ngtcp2_ksl_key *inf_key);
+void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
+ ngtcp2_ksl_blk *blk, size_t i);
/*
* ngtcp2_ksl_it_get returns the data associated to the node which
- * |it| points to. If this function is called when
- * ngtcp2_ksl_it_end(it) returns nonzero, it returns NULL.
+ * |it| points to. It is undefined to call this function when
+ * ngtcp2_ksl_it_end(it) returns nonzero.
*/
void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it);
@@ -273,21 +301,34 @@ int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it);
/*
* ngtcp2_ksl_key returns the key of the node which |it| points to.
- * It is OK to call this function when ngtcp2_ksl_it_end(it) returns
- * nonzero. In this case, this function returns inf_key.
+ * It is undefined to call this function when ngtcp2_ksl_it_end(it)
+ * returns nonzero.
*/
ngtcp2_ksl_key ngtcp2_ksl_it_key(const ngtcp2_ksl_it *it);
-/*
- * ngtcp2_ksl_key_i is a convenient function which initializes |key|
- * with |i| and returns |key|.
- */
-ngtcp2_ksl_key *ngtcp2_ksl_key_i(ngtcp2_ksl_key *key, int64_t i);
-
/*
* ngtcp2_ksl_key_ptr is a convenient function which initializes |key|
* with |ptr| and returns |key|.
*/
ngtcp2_ksl_key *ngtcp2_ksl_key_ptr(ngtcp2_ksl_key *key, const void *ptr);
+/*
+ * ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar.
+ * lhs->ptr and rhs->ptr must point to ngtcp2_range object and the
+ * function returns nonzero if (const ngtcp2_range *)(lhs->ptr)->begin
+ * < (const ngtcp2_range *)(rhs->ptr)->begin.
+ */
+int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs);
+
+/*
+ * ngtcp2_ksl_range_exclusive_compar is an implementation of
+ * ngtcp2_ksl_compar. lhs->ptr and rhs->ptr must point to
+ * ngtcp2_range object and the function returns nonzero if (const
+ * ngtcp2_range *)(lhs->ptr)->begin < (const ngtcp2_range
+ * *)(rhs->ptr)->begin and the 2 ranges do not intersect.
+ */
+int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs);
+
#endif /* NGTCP2_KSL_H */
diff --git a/deps/ngtcp2/lib/ngtcp2_pv.h b/deps/ngtcp2/lib/ngtcp2_pv.h
index 56ff0c447e..2b92036625 100644
--- a/deps/ngtcp2/lib/ngtcp2_pv.h
+++ b/deps/ngtcp2/lib/ngtcp2_pv.h
@@ -58,15 +58,16 @@ typedef enum {
NGTCP2_PV_FLAG_NONE,
/* NGTCP2_PV_FLAG_DONT_CARE indicates that the outcome of the path
validation does not matter. */
- NGTCP2_PV_FLAG_DONT_CARE = 0x02,
+ NGTCP2_PV_FLAG_DONT_CARE = 0x01,
/* NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH indicates that DCID should
- be retired after path validation finishes regardless of its
- result. */
- NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH = 0x04,
- /* NGTCP2_PV_FLAG_VERIFY_OLD_PATH_ON_SUCCESS indicates that the path
- validation against the old path should be done after successful
- path validation. */
- NGTCP2_PV_FLAG_VERIFY_OLD_PATH_ON_SUCCESS = 0x08,
+ be retired after path validation is aborted or failed. DCID is
+ not retired if path validation succeeds. */
+ NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH = 0x02,
+ /* NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE indicates that fallback DCID
+ is available in ngtcp2_pv. If path validation fails, fallback to
+ the fallback DCID. If path validation succeeds, start path
+ validation against fallback DCID. */
+ NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE = 0x04,
} ngtcp2_pv_flag;
struct ngtcp2_pv;
@@ -80,6 +81,9 @@ struct ngtcp2_pv {
ngtcp2_log *log;
/* dcid is DCID and path this path validation uses. */
ngtcp2_dcid dcid;
+ /* fallback_dcid is the usually validated DCID and used as a
+ fallback if this path validation fails. */
+ ngtcp2_dcid fallback_dcid;
/* ents is the ring buffer of ngtcp2_pv_entry */
ngtcp2_ringbuf ents;
/* timeout is the duration within which this path validation should
diff --git a/deps/ngtcp2/lib/ngtcp2_rob.c b/deps/ngtcp2/lib/ngtcp2_rob.c
index 90179d096e..6c0aca7e90 100644
--- a/deps/ngtcp2/lib/ngtcp2_rob.c
+++ b/deps/ngtcp2/lib/ngtcp2_rob.c
@@ -68,10 +68,12 @@ void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem) {
int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) {
int rv;
ngtcp2_rob_gap *g;
+ ngtcp2_ksl_key key;
- rv = ngtcp2_psl_init(&rob->gappsl, mem);
+ rv = ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar,
+ sizeof(ngtcp2_range), mem);
if (rv != 0) {
- goto fail_gappsl_psl_init;
+ goto fail_gapksl_ksl_init;
}
rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem);
@@ -79,14 +81,16 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) {
goto fail_rob_gap_new;
}
- rv = ngtcp2_psl_insert(&rob->gappsl, NULL, &g->range, g);
+ rv = ngtcp2_ksl_insert(&rob->gapksl, NULL,
+ ngtcp2_ksl_key_ptr(&key, &g->range), g);
if (rv != 0) {
- goto fail_gappsl_psl_insert;
+ goto fail_gapksl_ksl_insert;
}
- rv = ngtcp2_psl_init(&rob->datapsl, mem);
+ rv = ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar,
+ sizeof(ngtcp2_range), mem);
if (rv != 0) {
- goto fail_datapsl_psl_init;
+ goto fail_dataksl_ksl_init;
}
rob->chunk = chunk;
@@ -94,35 +98,34 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) {
return 0;
-fail_datapsl_psl_init:
-fail_gappsl_psl_insert:
+fail_dataksl_ksl_init:
+fail_gapksl_ksl_insert:
ngtcp2_rob_gap_del(g, mem);
fail_rob_gap_new:
- ngtcp2_psl_free(&rob->gappsl);
-fail_gappsl_psl_init:
+ ngtcp2_ksl_free(&rob->gapksl);
+fail_gapksl_ksl_init:
return rv;
}
void ngtcp2_rob_free(ngtcp2_rob *rob) {
- static const ngtcp2_range r = {0, 0};
- ngtcp2_psl_it it;
+ ngtcp2_ksl_it it;
if (rob == NULL) {
return;
}
- for (it = ngtcp2_psl_lower_bound(&rob->datapsl, &r); !ngtcp2_psl_it_end(&it);
- ngtcp2_psl_it_next(&it)) {
- ngtcp2_rob_data_del(ngtcp2_psl_it_get(&it), rob->mem);
+ for (it = ngtcp2_ksl_begin(&rob->dataksl); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_rob_data_del(ngtcp2_ksl_it_get(&it), rob->mem);
}
- for (it = ngtcp2_psl_lower_bound(&rob->gappsl, &r); !ngtcp2_psl_it_end(&it);
- ngtcp2_psl_it_next(&it)) {
- ngtcp2_rob_gap_del(ngtcp2_psl_it_get(&it), rob->mem);
+ for (it = ngtcp2_ksl_begin(&rob->gapksl); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_rob_gap_del(ngtcp2_ksl_it_get(&it), rob->mem);
}
- ngtcp2_psl_free(&rob->datapsl);
- ngtcp2_psl_free(&rob->gappsl);
+ ngtcp2_ksl_free(&rob->dataksl);
+ ngtcp2_ksl_free(&rob->gapksl);
}
static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
@@ -131,11 +134,18 @@ static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
int rv;
ngtcp2_rob_data *d;
ngtcp2_range range = {offset, offset + len};
- ngtcp2_psl_it it;
-
- for (it = ngtcp2_psl_lower_bound(&rob->datapsl, &range); len;
- ngtcp2_psl_it_next(&it)) {
- d = ngtcp2_psl_it_get(&it);
+ ngtcp2_ksl_it it;
+ ngtcp2_ksl_key key;
+
+ for (it = ngtcp2_ksl_lower_bound_compar(&rob->dataksl,
+ ngtcp2_ksl_key_ptr(&key, &range),
+ ngtcp2_ksl_range_exclusive_compar);
+ len; ngtcp2_ksl_it_next(&it)) {
+ if (ngtcp2_ksl_it_end(&it)) {
+ d = NULL;
+ } else {
+ d = ngtcp2_ksl_it_get(&it);
+ }
if (d == NULL || offset < d->range.begin) {
rv = ngtcp2_rob_data_new(&d, (offset / rob->chunk) * rob->chunk,
@@ -144,7 +154,8 @@ static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
return rv;
}
- rv = ngtcp2_psl_insert(&rob->datapsl, &it, &d->range, d);
+ rv = ngtcp2_ksl_insert(&rob->dataksl, &it,
+ ngtcp2_ksl_key_ptr(&key, &d->range), d);
if (rv != 0) {
ngtcp2_rob_data_del(d, rob->mem);
return rv;
@@ -167,22 +178,21 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
int rv;
ngtcp2_rob_gap *g;
ngtcp2_range m, l, r, q = {offset, offset + datalen};
- ngtcp2_psl_it it;
+ ngtcp2_ksl_it it;
+ ngtcp2_ksl_key key, old_key;
- it = ngtcp2_psl_lower_bound(&rob->gappsl, &q);
+ it = ngtcp2_ksl_lower_bound_compar(&rob->gapksl, ngtcp2_ksl_key_ptr(&key, &q),
+ ngtcp2_ksl_range_exclusive_compar);
- for (; !ngtcp2_psl_it_end(&it);) {
- g = ngtcp2_psl_it_get(&it);
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ g = ngtcp2_ksl_it_get(&it);
m = ngtcp2_range_intersect(&q, &g->range);
if (!ngtcp2_range_len(&m)) {
break;
}
if (ngtcp2_range_eq(&g->range, &m)) {
- rv = ngtcp2_psl_remove(&rob->gappsl, &it, &g->range);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&rob->gapksl, &it, ngtcp2_ksl_key_ptr(&key, &g->range));
ngtcp2_rob_gap_del(g, rob->mem);
rv = rob_write_data(rob, m.begin, data + (m.begin - offset),
ngtcp2_range_len(&m));
@@ -194,7 +204,9 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
}
ngtcp2_range_cut(&l, &r, &g->range, &m);
if (ngtcp2_range_len(&l)) {
- ngtcp2_psl_update_range(&rob->gappsl, &g->range, &l);
+ ngtcp2_ksl_update_key(&rob->gapksl,
+ ngtcp2_ksl_key_ptr(&old_key, &g->range),
+ ngtcp2_ksl_key_ptr(&key, &l));
g->range = l;
if (ngtcp2_range_len(&r)) {
@@ -203,14 +215,17 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
if (rv != 0) {
return rv;
}
- rv = ngtcp2_psl_insert(&rob->gappsl, &it, &ng->range, ng);
+ rv = ngtcp2_ksl_insert(&rob->gapksl, &it,
+ ngtcp2_ksl_key_ptr(&key, &ng->range), ng);
if (rv != 0) {
ngtcp2_rob_gap_del(ng, rob->mem);
return rv;
}
}
} else if (ngtcp2_range_len(&r)) {
- ngtcp2_psl_update_range(&rob->gappsl, &g->range, &r);
+ ngtcp2_ksl_update_key(&rob->gapksl,
+ ngtcp2_ksl_key_ptr(&old_key, &g->range),
+ ngtcp2_ksl_key_ptr(&key, &r));
g->range = r;
}
rv = rob_write_data(rob, m.begin, data + (m.begin - offset),
@@ -218,7 +233,7 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
if (rv != 0) {
return rv;
}
- ngtcp2_psl_it_next(&it);
+ ngtcp2_ksl_it_next(&it);
}
return 0;
}
@@ -226,40 +241,36 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) {
ngtcp2_rob_gap *g;
ngtcp2_rob_data *d;
- ngtcp2_psl_it it;
- int rv;
+ ngtcp2_ksl_it it;
+ ngtcp2_ksl_key key, old_key;
- it = ngtcp2_psl_begin(&rob->gappsl);
+ it = ngtcp2_ksl_begin(&rob->gapksl);
- for (; !ngtcp2_psl_it_end(&it);) {
- g = ngtcp2_psl_it_get(&it);
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ g = ngtcp2_ksl_it_get(&it);
if (offset <= g->range.begin) {
break;
}
if (offset < g->range.end) {
ngtcp2_range r = {offset, g->range.end};
- ngtcp2_psl_update_range(&rob->gappsl, &g->range, &r);
+ ngtcp2_ksl_update_key(&rob->gapksl,
+ ngtcp2_ksl_key_ptr(&old_key, &g->range),
+ ngtcp2_ksl_key_ptr(&key, &r));
g->range.begin = offset;
break;
}
- rv = ngtcp2_psl_remove(&rob->gappsl, &it, &g->range);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&rob->gapksl, &it, ngtcp2_ksl_key_ptr(&key, &g->range));
ngtcp2_rob_gap_del(g, rob->mem);
}
- it = ngtcp2_psl_begin(&rob->datapsl);
+ it = ngtcp2_ksl_begin(&rob->dataksl);
- for (; !ngtcp2_psl_it_end(&it);) {
- d = ngtcp2_psl_it_get(&it);
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ d = ngtcp2_ksl_it_get(&it);
if (offset < d->range.begin + rob->chunk) {
return 0;
}
- rv = ngtcp2_psl_remove(&rob->datapsl, &it, &d->range);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&rob->dataksl, &it, ngtcp2_ksl_key_ptr(&key, &d->range));
ngtcp2_rob_data_del(d, rob->mem);
}
@@ -270,21 +281,21 @@ size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest,
uint64_t offset) {
ngtcp2_rob_gap *g;
ngtcp2_rob_data *d;
- ngtcp2_psl_it it;
+ ngtcp2_ksl_it it;
- it = ngtcp2_psl_begin(&rob->gappsl);
- if (ngtcp2_psl_it_end(&it)) {
+ it = ngtcp2_ksl_begin(&rob->gapksl);
+ if (ngtcp2_ksl_it_end(&it)) {
return 0;
}
- g = ngtcp2_psl_it_get(&it);
+ g = ngtcp2_ksl_it_get(&it);
if (g->range.begin <= offset) {
return 0;
}
- it = ngtcp2_psl_begin(&rob->datapsl);
- d = ngtcp2_psl_it_get(&it);
+ it = ngtcp2_ksl_begin(&rob->dataksl);
+ d = ngtcp2_ksl_it_get(&it);
assert(d);
assert(d->range.begin <= offset);
@@ -295,38 +306,33 @@ size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest,
return ngtcp2_min(g->range.begin, d->range.begin + rob->chunk) - offset;
}
-int ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) {
- ngtcp2_psl_it it;
+void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) {
+ ngtcp2_ksl_it it;
+ ngtcp2_ksl_key key;
ngtcp2_rob_data *d;
- int rv;
- it = ngtcp2_psl_begin(&rob->datapsl);
- d = ngtcp2_psl_it_get(&it);
+ it = ngtcp2_ksl_begin(&rob->dataksl);
+ d = ngtcp2_ksl_it_get(&it);
assert(d);
if (offset + len < d->range.begin + rob->chunk) {
- return 0;
+ return;
}
- rv = ngtcp2_psl_remove(&rob->datapsl, NULL, &d->range);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&rob->dataksl, NULL, ngtcp2_ksl_key_ptr(&key, &d->range));
ngtcp2_rob_data_del(d, rob->mem);
-
- return 0;
}
uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob) {
- ngtcp2_psl_it it = ngtcp2_psl_begin(&rob->gappsl);
+ ngtcp2_ksl_it it = ngtcp2_ksl_begin(&rob->gapksl);
ngtcp2_rob_gap *g;
- if (ngtcp2_psl_it_end(&it)) {
+ if (ngtcp2_ksl_it_end(&it)) {
return UINT64_MAX;
}
- g = ngtcp2_psl_it_get(&it);
+ g = ngtcp2_ksl_it_get(&it);
return g->range.begin;
}
diff --git a/deps/ngtcp2/lib/ngtcp2_rob.h b/deps/ngtcp2/lib/ngtcp2_rob.h
index 78cf43ca3a..28ae1da6b7 100644
--- a/deps/ngtcp2/lib/ngtcp2_rob.h
+++ b/deps/ngtcp2/lib/ngtcp2_rob.h
@@ -33,7 +33,7 @@
#include "ngtcp2_mem.h"
#include "ngtcp2_range.h"
-#include "ngtcp2_psl.h"
+#include "ngtcp2_ksl.h"
struct ngtcp2_rob_gap;
typedef struct ngtcp2_rob_gap ngtcp2_rob_gap;
@@ -113,12 +113,12 @@ void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem);
* received in out of order.
*/
typedef struct {
- /* gappsl maintains the range of offset which is not received
+ /* gapksl maintains the range of offset which is not received
yet. Initially, its range is [0, UINT64_MAX). */
- ngtcp2_psl gappsl;
- /* datapsl maintains the list of buffers which store received data
+ ngtcp2_ksl gapksl;
+ /* dataksl maintains the list of buffers which store received data
ordered by stream offset. */
- ngtcp2_psl datapsl;
+ ngtcp2_ksl dataksl;
/* mem is custom memory allocator */
const ngtcp2_mem *mem;
/* chunk is the size of each buffer in data field */
@@ -186,14 +186,8 @@ size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest,
*
* Caller should call this function from offset 0 in non-decreasing
* order.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory
*/
-int ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len);
+void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len);
/*
* ngtcp2_rob_first_gap_offset returns the offset to the first gap.
diff --git a/deps/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/lib/ngtcp2_rtb.c
index bcc1f4167f..29d7fcff5a 100644
--- a/deps/ngtcp2/lib/ngtcp2_rtb.c
+++ b/deps/ngtcp2/lib/ngtcp2_rtb.c
@@ -154,15 +154,13 @@ void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem) {
}
static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
- return lhs->i > rhs->i;
+ return *lhs->i > *rhs->i;
}
void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_crypto_level crypto_level,
ngtcp2_strm *crypto, ngtcp2_default_cc *cc,
ngtcp2_log *log, const ngtcp2_mem *mem) {
- ngtcp2_ksl_key inf_key = {-1};
-
- ngtcp2_ksl_init(&rtb->ents, greater, &inf_key, mem);
+ ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem);
rtb->crypto = crypto;
rtb->cc = cc;
rtb->log = log;
@@ -234,11 +232,12 @@ static void rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent) {
int rv;
+ ngtcp2_ksl_key key;
ent->next = NULL;
rv = ngtcp2_ksl_insert(&rtb->ents, NULL,
- (const ngtcp2_ksl_key *)&ent->hd.pkt_num, ent);
+ ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num), ent);
if (rv != 0) {
return rv;
}
@@ -252,18 +251,13 @@ ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb) {
return ngtcp2_ksl_begin(&rtb->ents);
}
-static int rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
- ngtcp2_rtb_entry *ent) {
- int rv;
+static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
+ ngtcp2_rtb_entry *ent) {
+ ngtcp2_ksl_key key;
- rv = ngtcp2_ksl_remove(&rtb->ents, it,
- (const ngtcp2_ksl_key *)&ent->hd.pkt_num);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&rtb->ents, it, ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num));
rtb_on_remove(rtb, ent);
ngtcp2_rtb_entry_del(ent, rtb->mem);
- return 0;
}
static int rtb_call_acked_stream_offset(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
@@ -376,8 +370,8 @@ ssize_t ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
ngtcp2_max(rtb->largest_acked_tx_pkt_num, largest_ack);
/* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
- it = ngtcp2_ksl_lower_bound(&rtb->ents, (const ngtcp2_ksl_key *)&largest_ack);
-
+ it = ngtcp2_ksl_lower_bound(&rtb->ents,
+ ngtcp2_ksl_key_ptr(&key, &largest_ack));
if (ngtcp2_ksl_it_end(&it)) {
return 0;
}
@@ -386,14 +380,14 @@ ssize_t ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
for (; !ngtcp2_ksl_it_end(&it);) {
key = ngtcp2_ksl_it_key(&it);
- if (min_ack <= key.i && key.i <= largest_ack) {
+ if (min_ack <= *key.i && *key.i <= largest_ack) {
ent = ngtcp2_ksl_it_get(&it);
if (conn) {
rv = rtb_call_acked_stream_offset(rtb, ent, conn);
if (rv != 0) {
return rv;
}
- if (largest_ack == key.i) {
+ if (largest_ack == *key.i) {
largest_pkt_sent_ts = ent->ts;
largest_pkt_acked = 1;
}
@@ -407,10 +401,7 @@ ssize_t ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
/* At this point, it is invalided because rtb->ents might be
modified. */
}
- rv = rtb_remove(rtb, &it, ent);
- if (rv != 0) {
- return rv;
- }
+ rtb_remove(rtb, &it, ent);
++num_acked;
continue;
}
@@ -422,14 +413,14 @@ ssize_t ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
min_ack = largest_ack - (int64_t)fr->blks[i].blklen;
it = ngtcp2_ksl_lower_bound(&rtb->ents,
- (const ngtcp2_ksl_key *)&largest_ack);
+ ngtcp2_ksl_key_ptr(&key, &largest_ack));
if (ngtcp2_ksl_it_end(&it)) {
break;
}
for (; !ngtcp2_ksl_it_end(&it);) {
key = ngtcp2_ksl_it_key(&it);
- if (key.i < min_ack) {
+ if (*key.i < min_ack) {
break;
}
ent = ngtcp2_ksl_it_get(&it);
@@ -446,10 +437,7 @@ ssize_t ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
}
rtb_on_pkt_acked(rtb, ent);
}
- rv = rtb_remove(rtb, &it, ent);
- if (rv != 0) {
- return rv;
- }
+ rtb_remove(rtb, &it, ent);
++num_acked;
}
@@ -486,17 +474,17 @@ static ngtcp2_duration compute_pkt_loss_delay(const ngtcp2_rcvry_stat *rcs) {
return ngtcp2_max(loss_delay, NGTCP2_GRANULARITY);
}
-int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
- ngtcp2_rcvry_stat *rcs, ngtcp2_duration pto,
- ngtcp2_tstamp ts) {
+void ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
+ ngtcp2_rcvry_stat *rcs, ngtcp2_duration pto,
+ ngtcp2_tstamp ts) {
ngtcp2_rtb_entry *ent;
ngtcp2_duration loss_delay;
ngtcp2_tstamp lost_send_time;
ngtcp2_ksl_it it;
int64_t lost_pkt_num;
- int rv;
ngtcp2_tstamp latest_ts, oldest_ts;
int64_t last_lost_pkt_num;
+ ngtcp2_ksl_key key;
rtb->loss_time = 0;
loss_delay = compute_pkt_loss_delay(rcs);
@@ -504,7 +492,7 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
lost_pkt_num = rtb->largest_acked_tx_pkt_num - NGTCP2_PACKET_THRESHOLD;
it = ngtcp2_ksl_lower_bound(
- &rtb->ents, (const ngtcp2_ksl_key *)&rtb->largest_acked_tx_pkt_num);
+ &rtb->ents, ngtcp2_ksl_key_ptr(&key, &rtb->largest_acked_tx_pkt_num));
for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
ent = ngtcp2_ksl_it_get(&it);
@@ -515,11 +503,8 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
for (; !ngtcp2_ksl_it_end(&it);) {
ent = ngtcp2_ksl_it_get(&it);
- rv = ngtcp2_ksl_remove(&rtb->ents, &it,
- (const ngtcp2_ksl_key *)&ent->hd.pkt_num);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&rtb->ents, &it,
+ ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num));
if (last_lost_pkt_num == ent->hd.pkt_num + 1) {
last_lost_pkt_num = ent->hd.pkt_num;
@@ -539,17 +524,15 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
rtb->cc, latest_ts - oldest_ts, pto);
}
- return 0;
+ return;
}
}
-
- return 0;
}
-int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) {
+void ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) {
ngtcp2_rtb_entry *ent;
ngtcp2_ksl_it it;
- int rv;
+ ngtcp2_ksl_key key;
it = ngtcp2_ksl_begin(&rtb->ents);
@@ -561,11 +544,8 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) {
ngtcp2_log_pkt_lost(rtb->log, &ent->hd, ent->ts);
rtb_on_remove(rtb, ent);
- rv = ngtcp2_ksl_remove(&rtb->ents, &it,
- (const ngtcp2_ksl_key *)&ent->hd.pkt_num);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&rtb->ents, &it,
+ ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num));
if (!(ent->flags & NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED)) {
frame_chain_insert(pfrc, ent->frc);
@@ -578,8 +558,6 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) {
ngtcp2_rtb_entry_del(ent, rtb->mem);
}
-
- return 0;
}
int ngtcp2_rtb_on_crypto_timeout(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) {
@@ -587,11 +565,12 @@ int ngtcp2_rtb_on_crypto_timeout(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) {
ngtcp2_ksl_it it;
ngtcp2_crypto_frame_chain *nfrc;
ngtcp2_frame_chain *frc;
- ngtcp2_psl_it gapit;
+ ngtcp2_ksl_it gapit;
ngtcp2_range gap, range;
ngtcp2_crypto *fr;
int all_acked;
int rv;
+ ngtcp2_ksl_key key;
it = ngtcp2_ksl_begin(&rtb->ents);
@@ -618,7 +597,7 @@ int ngtcp2_rtb_on_crypto_timeout(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) {
been acknowledged */
gapit = ngtcp2_gaptr_get_first_gap_after(&rtb->crypto->tx.acked_offset,
fr->offset);
- gap = ngtcp2_psl_it_range(&gapit);
+ gap = *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit).ptr;
range.begin = fr->offset;
range.end = fr->offset + ngtcp2_vec_len(fr->data, fr->datacnt);
@@ -642,11 +621,8 @@ int ngtcp2_rtb_on_crypto_timeout(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) {
/* If the frames that ent contains have been acknowledged,
remove it from rtb. Otherwise crypto timer keeps firing. */
rtb_on_remove(rtb, ent);
- rv = ngtcp2_ksl_remove(&rtb->ents, &it,
- (const ngtcp2_ksl_key *)&ent->hd.pkt_num);
- if (rv != 0) {
- return rv;
- }
+ ngtcp2_ksl_remove(&rtb->ents, &it,
+ ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num));
ngtcp2_rtb_entry_del(ent, rtb->mem);
continue;
}
diff --git a/deps/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/lib/ngtcp2_rtb.h
index 7eb5a4dddd..c78f24a385 100644
--- a/deps/ngtcp2/lib/ngtcp2_rtb.h
+++ b/deps/ngtcp2/lib/ngtcp2_rtb.h
@@ -310,29 +310,17 @@ ssize_t ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
* frames contained them to |*pfrc|. Even when this function fails,
* some frames might be prepended to |*pfrc| and the caller should
* handle them. |pto| is PTO.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory
*/
-int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
- ngtcp2_rcvry_stat *rcs, ngtcp2_duration pto,
- ngtcp2_tstamp ts);
+void ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
+ ngtcp2_rcvry_stat *rcs, ngtcp2_duration pto,
+ ngtcp2_tstamp ts);
/*
* ngtcp2_rtb_remove_all removes all packets from |rtb| and prepends
* all frames to |*pfrc|. Even when this function fails, some frames
* might be prepended to |*pfrc| and the caller should handle them.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- * Out of memory
*/
-int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc);
+void ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc);
/*
* ngtcp2_rtb_on_crypto_timeout copies all unacknowledged CRYPTO
diff --git a/doc/api/errors.md b/doc/api/errors.md
index 43b1d99e67..667a40276a 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -1571,6 +1571,11 @@ compiled with ICU support.
A given value is out of the accepted range.
+
+### ERR_QUIC_ERROR
+
+TBD
+
### ERR_QUICCLIENTSESSION_FAILED
diff --git a/doc/api/quic.md b/doc/api/quic.md
index 4b286f40b1..ed5df95e76 100644
--- a/doc/api/quic.md
+++ b/doc/api/quic.md
@@ -17,17 +17,14 @@ const quic = require('quic');
const key = getTLSKeySomehow();
const cert = getTLSCertSomehow();
-const ca = getTLSCAListSomehow();
-// The default export of the quic module is the
-// createSocket function.
-const createSocket = require('quic');
+const { createSocket } = require('quic');
// Create the local QUIC UDP socket...
const socket = createSocket({ type: 'udp4', port: 1234 });
// Tell the socket to operate as a server...
-socket.listen({ key, cert, ca });
+socket.listen({ key, cert });
socket.on('session', (session) => {
// A new server side session has been created!
@@ -62,28 +59,40 @@ socket.on('listening', () => {
});
```
-### quic.createSocket([options])
+## quic.createSocket([options])
* `options` {Object}
- * `address` {string} The local address to bind to.
- * `ipv6Only` {boolean}
- * `lookup` {Function}
+ * `address` {string} The local address to bind to. This may be an IPv4 or IPv6
+ address or a hostname. If a hostname is given, it will be resolved to an IP
+ address.
+ * `client` {Object} A default configuration for QUIC client sessions created
+ using `quicsocket.connect()`.
+ * `lookup` {Function} A custom DNS lookup function. Default `dns.lookup()`.
+ * `maxConnectionsPerHost` {number} The maximum number of inbound connections
+ per remote host. Default: `100`.
* `port` {number} The local port to bind to.
- * `resuseAddr` {boolean}
+ * `retryTokenTimeout` {number} The maximum number of seconds for retry token
+ validation. Defaults: `10`.
+ * `server` {Object} A default configuration for QUIC server sessions.
* `type` {string} Either `'udp4'` or `'upd6'` to use either IPv4 or IPv6,
respectively.
Creates a new `QuicSocket` instance.
-### Class: QuicSession
+## Class: QuicSession exends EventEmitter
* Extends: {EventEmitter}
+The `QuicSession` is an abstract base class that defines events, methods, and
+properties that are shared by both `QuicClientSession` and `QuicServerSession`.
+
+Users will not create instances of `QuicSession` directly.
+
### Event: `'close'`
-Emitted after the `'close'` event if the `QuicSession` was destroyed with
+Emitted before the `'close'` event if the `QuicSession` was destroyed with
an error.
+### Event: `'extendMaxBidiStreams'`
+
+
+Emitted when the maximum number of bidirectional streams has been extended.
+
+The callback will be invoked with a single argument:
+
+* `maxStreams` {number} The new maximum number of bidirectional streams
+
+### Event: `'extendMaxUniStreams'`
+
+
+Emitted when the maximum number of unidirectional streams has been extended.
+
+The callback will be invoked with a single argument:
+
+* `maxStreams` {number} The new maximum number of unidirectional streams
+
### Event: `'secure'`
+* Type: {object}
+ * `name` {string} The cipher algorithm name.
+ * `type` {string} The TLS version (currently always `'TLSv1.3'`).
+
+Information about the cipher algorithm selected for the session.
+
+### quicsession.close([code[, callback]])
+
+
+* `code` {number} The error code to when closing the session. Default: `0`.
* `callback` {Function} Callback invoked when the close operation is completed
-Closes the `QuicSession`.
+Begins a graceful close of the `QuicSession`. Existing `QuicStream` instances will be
+permitted to close naturally. New `QuicStream` instances will not be permitted. Once
+all `QuicStream` instances have closed, the `QuicSession` instance will be destroyed.
+
+### quicsession.closing
+
+
+* Type: {boolean}
+
+Set the `true` if the `QuicSession` is in the process of a graceful shutdown.
### quicsession.destroy([error])
+* Returns: {Object} A [Certificate Object][].
+
+Returns an object representing the local certificate. The returned object has some
+properties corresponding to the fields of the certificate.
+
+If there is no local certificate, or if the `QuicSession` has been destroyed, an empty
+object will be returned.
+
### quicsession.getPeerCertificate([detailed])
-* `detailed` {boolean} Defaults to `false`
+* `detailed` {boolean} Include the full certificate chain if `true`, otherwise include
+ just the peer's certificate.
+* Returns: {Object} A [Certificate Object][].
+
+Returns an object representing the peer's certificate. If the peer does not provide a
+certificate, or if the `QuicSession` has been destroyed, an empty object will be returned.
+
+If the full certificate chain was requested, each certificate will include an `issuerCertificate`
+property containing an object representing its issuer's certificate.
+
+### quicsession.handshakeComplete
+
+
+* Type: {boolean}
+
+True if the TLS handshake has completed.
### quicsession.openStream([options])
* Extends: {QuicSession}
-TBD
+The `QuicClientSession` class implements the client side of a QUIC connection.
+Instances are created using the `quicsocket.connect()` method.
-#### Event: `'sessionTicket'`
+### Event: `'sessionTicket'`
The `'sessionTicket'` event is emitted when a new TLS session ticket has been
generated for the current `QuicClientSession`. The callback is invoked with
@@ -224,40 +312,96 @@ three arguments:
The `sessionTicket` and `remoteTransportParams` are useful when creating a new
`QuicClientSession` to more quickly resume an existing session.
-#### quicclientsession.ephemeralKeyInfo
+### quicclientsession.ephemeralKeyInfo
+
+
+* Type: {Object}
+
+An object representing the type, name, and size of parameter of an ephemeral
+key exchange in Perfect Forward Secrecy on a client connection. It is an
+empty object when the key exchange is not ephemeral. The supported types are
+`'DH'` and `'ECDH'`. The `name` property is available only when type is `'ECDH'`.
+
+For example: `{ type: 'ECDH', name: 'prime256v1', size: 256 }`.
+
+### quicclientsession.ready
-### Class: QuicServerSession
+* Type: {boolean}
+
+True if the `QuicClientSession` is ready for use. False if the `QuicSocket` has not
+yet been bound.
+
+### quicclientsession.readyToMigrate
+
+
+* Type: {boolean}
+
+Once established, a `QuicClientSession` can be migrated from one `QuicSocket` instance
+to another, without requiring the TLS handshake to be reestablished. Migration, however,
+can only occur once the TLS handshake is complete and the underlying session has had an
+opportunity to generate a pool of extra connection identifiers.
+
+### quicclientsession.setSocket(socket, callback])
+
+
+* `socket` {QuicSocket} A `QuicSocket` instance to move this session to.
+* `callback` {Function} A callback function that will be invoked once the migration to
+ the new `QuicSocket` is complete.
+
+Migrates the `QuicClientSession` to the given `QuicSocket` instance. If the new `QuicSocket`
+has not yet been bound to a local UDP port, it will be bound prior to attempting the
+migration. If `quicclientsession.readyToMigrate` is `false`, an error will be thrown.
+
+## Class: QuicServerSession extends QuicSession
* Extends: {QuicSession}
-TBD
+The `QuicServerSession` class implements the server side of a QUIC connection.
+Instances are created internally and are emitted using the `QuicSocket` `'session'`
+event.
-### Class: QuicSocket
+## Class: QuicSocket
+New instances of `QuicSocket` are created using the `quic.createSocket()` method.
+
+Once created, a `QuicSocket` can be configured to work as both a client and a server.
+
### Event: `'close'`
+Emitted after the `QuicSocket` has been destroyed and is no longer usable.
+
### Event: `'error'`
+Emitted before the `'close'` event if the `QuicSocket` was destroyed with an `error`.
+
### Event: `'ready'`
+Emitted once the `QuicSocket` has been bound to a local UDP port.
+
### Event: `'session'`
* `options` {Object}
+ * `alpn` {string} An ALPN protocol identifier.
* `ca` {string|string[]|Buffer|Buffer[]} Optionally override the trusted CA
certificates. Default is to trust the well-known CAs curated by Mozilla.
Mozilla's CAs are completely replaced when CAs are explicitly specified
@@ -492,6 +678,7 @@ added: REPLACEME
preferences instead of the client's. When `true`, causes
`SSL_OP_CIPHER_SERVER_PREFERENCE` to be set in `secureOptions`, see
[OpenSSL Options][] for more information.
+ * `idleTimeout` {number}
* `key` {string|string[]|Buffer|Buffer[]|Object[]} Private keys in PEM format.
PEM allows the option of private keys being encrypted. Encrypted keys will
be decrypted with `options.passphrase`. Multiple keys using different
@@ -500,6 +687,16 @@ added: REPLACEME
passphrase: ]}`. The object form can only occur in an array.
`object.passphrase` is optional. Encrypted keys will be decrypted with
`object.passphrase` if provided, or `options.passphrase` if it is not.
+ * `maxAckDelay` {number}
+ * `maxCidLen` {number}
+ * `maxData` {number}
+ * `maxPacketSize` {number}
+ * `maxStreamsBidi` {number}
+ * `maxStreamsUni` {number}
+ * `maxStreamDataBidiLocal` {number}
+ * `maxStreamDataBidiRemote` {number}
+ * `maxStreamDataUni` {number}
+ * `minCidLen` {number}
* `passphrase` {string} Shared passphrase used for a single private key and/or
a PFX.
* `pfx` {string|string[]|Buffer|Buffer[]|Object[]} PFX or PKCS12 encoded
@@ -511,6 +708,10 @@ added: REPLACEME
occur in an array. `object.passphrase` is optional. Encrypted PFX will be
decrypted with `object.passphrase` if provided, or `options.passphrase` if
it is not.
+ * `preferredAddress` {Object}
+ * `address` {string}
+ * `port` {number}
+ * `type` {string} `'udp4'` or `'udp6'`.
* `secureOptions` {number} Optionally affect the OpenSSL protocol behavior,
which is not usually necessary. This should be used carefully if at all!
Value is a numeric bitmask of the `SSL_OP_*` options from
@@ -546,6 +747,9 @@ added: REPLACEME
* `on` {boolean}
+Sets or clears the `SO_BROADCAST` socket option. When set to `true`, UDP packets may be sent
+to a local interface's broadcast address.
+
### quicsocket.setMulticastLoopback([on])
+On most systems, where scope format uses the interface name:
+
+```js
+const socket = quic.createSocket({ type: 'udp6', port: 1234 });
+
+socket.on('ready', () => {
+ socket.setMulticastInterface('::%eth1');
+});
+```
+
+On Windows, where scope format uses an interface number:
+
+```js
+const socket = quic.createSocket({ type: 'udp6', port: 1234 });
+
+socket.on('ready', () => {
+ socket.setMulticastInterface('::%2');
+});
+```
+
+#### Example: IPv4 Outgoing Multicast Interface
+
+All systems use an IP of the host on the desired physical interface:
+
+```js
+const socket = quic.createSocket({ type: 'udp4', port: 1234 });
+
+socket.on('ready', () => {
+ socket.setMulticastInterface('10.0.0.2');
+});
+```
+
+#### Call Results#
+
+A call on a socket that is not ready to send or no longer open may throw a Not running Error.
+
+If multicastInterface can not be parsed into an IP then an `EINVAL` System Error is thrown.
+
+On IPv4, if `multicastInterface` is a valid address but does not match any interface, or if
+the address does not match the family then a System Error such as `EADDRNOTAVAIL` or
+`EPROTONOSUP` is thrown.
+
+On IPv6, most errors with specifying or omitting scope will result in the socket continuing
+to use (or returning to) the system's default interface selection.
+
+A socket's address family's ANY address (IPv4 `'0.0.0.0'` or IPv6 `'::'`) can be used to
+return control of the sockets default outgoing interface to the system for future multicast packets.
+
### quicsocket.setMulticastTTL(ttl)
-### Class: QuicStream
+## Class: QuicStream extends stream.Duplex
@@ -606,6 +901,24 @@ added: REPLACEME
added: REPLACEME
-->
+### quicstream.bidirectional
+
+
+* Type: {boolean}
+
+True if the `QuicStream` is bidirectional.
+
+### quicstream.clientInitiated
+
+
+* Type: {boolean}
+
+True if the `QuicStream` was initiated by a `QuicClientSession` instance.
+
### quicstream.id
+
+* Type: {boolean}
+
+True if the `QuicStream` was initiated by a `QuicServerSession` instance.
+
### quicstream.session
* Type: {QuicSession}
+
+The `QuicServerSession` or `QuicClientSession`.
+
+### quicstream.unidirectional
+
+
+* Type: {boolean}
+
+True if the `QuicStream` is unidirectional.
+
+
+
+[RFC 4007]: https://tools.ietf.org/html/rfc4007
+[Certificate Object]: https://nodejs.org/dist/latest-v12.x/docs/api/tls.html#tls_certificate_object
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 1f6ff6e894..0054fc3633 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -1036,6 +1036,28 @@ E('ERR_QUICSOCKET_LISTENING',
'This QuicSocket is already listening', Error);
E('ERR_QUICSOCKET_UNBOUND',
'Cannot call %s before a QuicSocket has been bound', Error);
+E('ERR_QUIC_ERROR', function(code, family) {
+ const {
+ constants: {
+ QUIC_ERROR_APPLICATION,
+ QUIC_ERROR_CRYPTO,
+ QUIC_ERROR_SESSION,
+ }
+ } = internalBinding('quic');
+ let familyType = 'unknown';
+ switch (family) {
+ case QUIC_ERROR_APPLICATION:
+ familyType = 'application';
+ break;
+ case QUIC_ERROR_CRYPTO:
+ familyType = 'crypto';
+ break;
+ case QUIC_ERROR_SESSION:
+ familyType = 'session';
+ break;
+ }
+ return `QUIC session closed with ${familyType} error code ${code}`;
+}, Error);
E('ERR_QUIC_TLS13_REQUIRED', 'QUIC requires TLS version 1.3', Error);
E('ERR_REQUIRE_ESM', 'Must use import to load ES Module: %s', Error);
E('ERR_SCRIPT_EXECUTION_INTERRUPTED',
diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js
index b8eb1ae45d..c84a792a50 100644
--- a/lib/internal/quic/core.js
+++ b/lib/internal/quic/core.js
@@ -9,11 +9,15 @@ const {
assertCrypto();
+const { Error } = primordials;
+const { Buffer } = require('buffer');
+const { isArrayBufferView } = require('internal/util/types');
const {
getAllowUnauthorized,
getSocketType,
lookup4,
lookup6,
+ validateCloseCode,
validateTransportParams,
validateQuicClientSessionOptions,
validateQuicSocketOptions,
@@ -58,6 +62,7 @@ const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_CALLBACK,
+ ERR_QUIC_ERROR,
ERR_QUICSESSION_DESTROYED,
ERR_QUICSOCKET_CLOSING,
ERR_QUICSOCKET_DESTROYED,
@@ -102,10 +107,14 @@ const {
IDX_QUIC_SESSION_MAX_PACKET_SIZE_DEFAULT,
IDX_QUIC_SESSION_MAX_ACK_DELAY,
IDX_QUIC_SESSION_STATE_CONNECTION_ID_COUNT,
+ IDX_QUIC_SESSION_STATE_CERT_ENABLED,
+ IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED,
IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED,
ERR_INVALID_REMOTE_TRANSPORT_PARAMS,
ERR_INVALID_TLS_SESSION_TICKET,
NGTCP2_PATH_VALIDATION_RESULT_FAILURE,
+ NGTCP2_NO_ERROR,
+ QUIC_ERROR_APPLICATION,
}
} = internalBinding('quic');
@@ -117,6 +126,8 @@ const emit = EventEmitter.prototype.emit;
const kAddSession = Symbol('kAddSession');
const kAddStream = Symbol('kAddStream');
+const kCert = Symbol('kCert');
+const kClientHello = Symbol('kClientHello');
const kContinueBind = Symbol('kContinueBind');
const kContinueConnect = Symbol('kContinueConnect');
const kContinueListen = Symbol('kContinueListen');
@@ -132,6 +143,7 @@ const kReceiveStop = Symbol('kReceiveStop');
const kRemoveSession = Symbol('kRemove');
const kRemoveStream = Symbol('kRemoveStream');
const kReset = Symbol('kReset');
+const kSetCloseCode = Symbol('kSetCloseCode');
const kSetSocket = Symbol('kSetSocket');
const kTrackWriteState = Symbol('kTrackWriteState');
const kWriteGeneric = Symbol('kWriteGeneric');
@@ -208,10 +220,79 @@ function onSessionReady(sessionHandle) {
}
// Called when a QuicSession is closed
-function onSessionClose(code) {
+function onSessionClose(code, family) {
+ this[owner_symbol][kSetCloseCode](code, family);
this[owner_symbol].destroy();
}
+// This callback is invoked at the start of the TLS handshake to provide
+// some basic information about the ALPN, SNI, and Ciphers that are
+// being requested. It is only called if the 'clientHello' event is
+// listened for.
+function onSessionClientHello(alpn, servername, ciphers, callback) {
+ callback = callback.bind(this);
+ this[owner_symbol][kClientHello](
+ alpn,
+ servername,
+ ciphers,
+ (err, ...args) => {
+ if (err) {
+ this[owner_symbol].destroy(err);
+ return;
+ }
+ try {
+ callback(...args);
+ } catch (err) {
+ this[owner_symbol].destroy(err);
+ }
+ });
+}
+
+// This callback is only ever invoked for QuicServerSession instances,
+// and is used to trigger OCSP request processing when needed. The
+// user callback must invoke the callback function in order for the
+// TLS handshake to continue.
+function onSessionCert(servername, callback) {
+ callback = callback.bind(this);
+ this[owner_symbol][kCert](servername, (err, context, ocspResponse) => {
+ if (err) {
+ this[owner_symbol].destroy(err);
+ return;
+ }
+ if (context != null && !context.context) {
+ this[owner_symbol].destroy(
+ new ERR_INVALID_ARG_TYPE(
+ 'context',
+ 'SecureContext',
+ context));
+ }
+ if (ocspResponse != null) {
+ if (typeof ocspResponse === 'string')
+ ocspResponse = Buffer.from(ocspResponse);
+ if (!isArrayBufferView(ocspResponse)) {
+ this[owner_symbol].destroy(
+ new ERR_INVALID_ARG_TYPE(
+ 'ocspResponse',
+ ['string', 'Buffer', 'TypedArray', 'DataView'],
+ ocspResponse));
+ }
+ }
+ try {
+ callback(context ? context.context : undefined, ocspResponse);
+ } catch (err) {
+ this[owner_symbol].destroy(err);
+ }
+ });
+}
+
+// This callback is only ever invoked for QuicClientSession instances,
+// and is used to deliver the OCSP response as provided by the server.
+// If the requestOCSP configuration option is false, this will never
+// be called.
+function onSessionStatus(response) {
+ this[owner_symbol][kCert](response);
+}
+
function onSessionHandshake(
servername,
alpn,
@@ -257,7 +338,8 @@ function onSessionExtend(bidi, maxStreams) {
}
function onSessionKeylog(line) {
- process.nextTick(emit.bind(this[owner_symbol], 'keylog', line));
+ this[owner_symbol].emit('keylog', line);
+// process.nextTick(emit.bind(this[owner_symbol], 'keylog', line));
}
// Called when a new QuicStream is ready to use
@@ -270,7 +352,10 @@ function onStreamReady(streamHandle, id) {
assert(!session.closing);
// TODO(@jasnell): Get default options from session
- const stream = new QuicStream({ /* options */ }, session, id, streamHandle);
+ const uni = id & 0b10;
+ const stream = new QuicStream({ writable: !uni }, session, id, streamHandle);
+ if (uni)
+ stream.end();
session[kAddStream](id, stream);
process.nextTick(emit.bind(session, 'stream', stream));
}
@@ -297,11 +382,14 @@ setCallbacks({
onSocketClose,
onSocketError,
onSessionReady,
+ onSessionCert,
+ onSessionClientHello,
onSessionClose,
onSessionError,
onSessionExtend,
onSessionHandshake,
onSessionKeylog,
+ onSessionStatus,
onSessionTicket,
onStreamReady,
onStreamClose,
@@ -368,6 +456,83 @@ function connectAfterBind(session, lookup, address, type) {
connectAfterLookup.bind(session, type));
}
+function createSecureContext(options, init_cb) {
+ const {
+ ca,
+ cert,
+ ciphers = DEFAULT_QUIC_CIPHERS,
+ clientCertEngine,
+ crl,
+ dhparam,
+ ecdhCurve,
+ groups = DEFAULT_GROUPS,
+ honorCipherOrder,
+ key,
+ passphrase,
+ pfx,
+ sessionIdContext,
+ secureProtocol
+ } = { ...options };
+
+ if (typeof ciphers !== 'string')
+ throw new ERR_INVALID_ARG_TYPE('option.ciphers', 'string', ciphers);
+ if (typeof groups !== 'string')
+ throw new ERR_INVALID_ARG_TYPE('option.groups', 'string', groups);
+
+ const sc = _createSecureContext({
+ secureProtocol,
+ ca,
+ cert,
+ ciphers: ciphers || DEFAULT_QUIC_CIPHERS,
+ clientCertEngine,
+ crl,
+ dhparam,
+ ecdhCurve,
+ honorCipherOrder,
+ key,
+ passphrase,
+ pfx,
+ sessionIdContext
+ });
+ // Perform additional QUIC specific initialization on the SecureContext
+ init_cb(sc.context, groups || DEFAULT_GROUPS);
+ return sc;
+}
+
+function onNewListener(event) {
+ if (this[kHandle] === undefined || this.listenerCount(event) !== 0)
+ return;
+
+ switch (event) {
+ case 'keylog':
+ this[kHandle].state[IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED] = 1;
+ break;
+ case 'clientHello':
+ this[kHandle].state[IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] = 1;
+ break;
+ case 'OCSPRequest':
+ this[kHandle].state[IDX_QUIC_SESSION_STATE_CERT_ENABLED] = 1;
+ break;
+ }
+}
+
+function onRemoveListener(event) {
+ if (this[kHandle] === undefined || this.listenerCount(event) !== 0)
+ return;
+
+ switch (event) {
+ case 'keylog':
+ this[kHandle].state[IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED] = 0;
+ break;
+ case 'clientHello':
+ this[kHandle].state[IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] = 0;
+ break;
+ case 'OCSPRequest':
+ this[kHandle].state[IDX_QUIC_SESSION_STATE_CERT_ENABLED] = 0;
+ break;
+ }
+}
+
// QuicSocket wraps a UDP socket plus the associated TLS context and QUIC
// Protocol state. There may be *multiple* QUIC connections (QuicSession)
// associated with a single QuicSocket.
@@ -385,6 +550,7 @@ class QuicSocket extends EventEmitter {
#sessions = new Set();
#state = kSocketUnbound;
#type = undefined;
+ #alpn = undefined;
constructor(options) {
const {
@@ -496,12 +662,19 @@ class QuicSocket extends EventEmitter {
port,
type = AF_INET,
} = { ...preferredAddress };
+ const {
+ rejectUnauthorized = !getAllowUnauthorized(),
+ requestCert = false,
+ } = transportParams;
setTransportParams(transportParams);
this[kHandle].listen(
this.#serverSecureContext.context,
address,
type,
- port);
+ port,
+ this.#alpn,
+ rejectUnauthorized,
+ requestCert);
process.nextTick(emit.bind(this, 'listening'));
}
@@ -530,6 +703,10 @@ class QuicSocket extends EventEmitter {
...options
};
+ const { alpn } = options;
+ if (alpn !== undefined && typeof alpn !== 'string')
+ throw new ERR_INVALID_ARG_TYPE('options.alpn', 'string', alpn);
+
if (callback) {
if (typeof callback !== 'function')
throw new ERR_INVALID_CALLBACK();
@@ -542,7 +719,7 @@ class QuicSocket extends EventEmitter {
// since we do not need to access this anywhere else.
this.#serverSecureContext = createSecureContext(options, initSecureContext);
this.#serverListening = true;
-
+ this.#alpn = alpn;
const doListen =
continueListen.bind(
this,
@@ -633,6 +810,10 @@ class QuicSocket extends EventEmitter {
return;
this.#state = kSocketClosing;
+ // Otherwise, gracefully close each QuicSession, with
+ // [kMaybeDestroy]() being called after each closes.
+ const maybeDestroy = this[kMaybeDestroy].bind(this);
+
// If there are no sessions, call [kMaybeDestroy]()
// immediately to destroy the QuicSocket
if (this.#sessions.size === 0) {
@@ -641,9 +822,6 @@ class QuicSocket extends EventEmitter {
return;
}
- // Otherwise, gracefully close each QuicSession, with
- // [kMaybeDestroy]() being called after each closes.
- const maybeDestroy = this[kMaybeDestroy].bind(this);
for (const session of this.#sessions)
session.close(maybeDestroy);
}
@@ -683,6 +861,10 @@ class QuicSocket extends EventEmitter {
return this;
}
+ get serverSecureContext() {
+ return this.#serverSecureContext;
+ }
+
get address() {
const out = {};
if (this.#state !== kSocketDestroyed) {
@@ -795,72 +977,12 @@ class QuicSocket extends EventEmitter {
}
}
-function createSecureContext(options, init_cb) {
- const {
- ca,
- cert,
- ciphers = DEFAULT_QUIC_CIPHERS,
- clientCertEngine,
- crl,
- dhparam,
- ecdhCurve,
- groups = DEFAULT_GROUPS,
- honorCipherOrder,
- key,
- passphrase,
- pfx,
- sessionIdContext,
- secureProtocol
- } = { ...options };
-
- if (typeof ciphers !== 'string')
- throw new ERR_INVALID_ARG_TYPE('option.ciphers', 'string', ciphers);
- if (typeof groups !== 'string')
- throw new ERR_INVALID_ARG_TYPE('option.groups', 'string', groups);
-
- const sc = _createSecureContext({
- secureProtocol,
- ca,
- cert,
- ciphers: ciphers || DEFAULT_QUIC_CIPHERS,
- clientCertEngine,
- crl,
- dhparam,
- ecdhCurve,
- honorCipherOrder,
- key,
- passphrase,
- pfx,
- sessionIdContext
- });
- // Perform additional QUIC specific initialization on the SecureContext
- init_cb(sc.context, groups || DEFAULT_GROUPS);
- return sc;
-}
-
-function onNewKeylogListener(event) {
- if (event !== 'keylog' ||
- this[kHandle] === undefined ||
- this.listenerCount('keylog') !== 0) {
- return;
- }
- this[kHandle].state[IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED] = 1;
-}
-
-function onRemoveKeylogListener(event) {
- if (event !== 'keylog' ||
- this[kHandle] === undefined ||
- this.listenerCount('keylog') !== 0) {
- return;
- }
- this[kHandle].state[IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED] = 0;
-}
-
class QuicSession extends EventEmitter {
#alpn = undefined;
#cipher = undefined;
#cipherVersion = undefined;
- #closeCode = 0;
+ #closeCode = NGTCP2_NO_ERROR;
+ #closeFamily = QUIC_ERROR_APPLICATION;
#closing = false;
#destroyed = false;
#handshakeComplete = false;
@@ -873,13 +995,31 @@ class QuicSession extends EventEmitter {
constructor(socket, servername) {
super();
- this.on('newListener', onNewKeylogListener);
- this.on('removeListener', onRemoveKeylogListener);
+ this.on('newListener', onNewListener);
+ this.on('removeListener', onRemoveListener);
this.#socket = socket;
socket[kAddSession](this);
this.#servername = servername;
}
+ [kSetCloseCode](code, family) {
+ this.#closeCode = code;
+ this.#closeFamily = family;
+ }
+
+ [kInspect]() {
+ const obj = {
+ alpn: this.#alpn,
+ cipher: this.cipher,
+ closing: this.closing,
+ closeCode: this.closeCode,
+ destroyed: this.destroyed,
+ servername: this.servername,
+ streams: this.#streams.size,
+ };
+ return `${this.constructor.name} ${util.format(obj)}`;
+ }
+
[kSetSocket](socket) {
this.#socket = socket;
}
@@ -953,11 +1093,15 @@ class QuicSession extends EventEmitter {
if (typeof code === 'function') {
callback = code;
- code = 0;
+ code = NGTCP2_NO_ERROR;
}
- if (code !== undefined && typeof code !== 'number')
- throw new ERR_INVALID_ARG_TYPE('code', 'number', code);
+ const {
+ closeCode,
+ closeFamily
+ } = validateCloseCode(code);
+ this.#closeCode = closeCode;
+ this.#closeFamily = closeFamily;
if (callback) {
if (typeof callback !== 'function')
@@ -1003,12 +1147,25 @@ class QuicSession extends EventEmitter {
this.#destroyed = true;
this.#closing = false;
+ if (typeof error === 'number' ||
+ (error != null &&
+ typeof error === 'object' &&
+ !(error instanceof Error))) {
+ const {
+ closeCode,
+ closeFamily
+ } = validateCloseCode(error);
+ this.#closeCode = closeCode;
+ this.#closeFamily = closeFamily;
+ error = new ERR_QUIC_ERROR(closeCode, closeFamily);
+ }
+
// Destroy any remaining streams immediately
for (const stream of this.#streams.values())
stream.destroy(error);
- this.removeListener('newListener', onNewKeylogListener);
- this.removeListener('removeListener', onRemoveKeylogListener);
+ this.removeListener('newListener', onNewListener);
+ this.removeListener('removeListener', onRemoveListener);
const handle = this[kHandle];
if (handle !== undefined) {
@@ -1017,7 +1174,7 @@ class QuicSession extends EventEmitter {
// Calling destroy will cause a CONNECTION_CLOSE to be
// sent to the peer and will destroy the QuicSession
// handler immediately.
- handle.destroy();
+ handle.destroy(this.#closeCode, this.#closeFamily);
}
// Remove the QuicSession JavaScript object from the
@@ -1066,6 +1223,13 @@ class QuicSession extends EventEmitter {
return this.#closing;
}
+ get closeCode() {
+ return {
+ code: this.#closeCode,
+ family: this.#closeFamily
+ };
+ }
+
get socket() {
return this.#socket;
}
@@ -1097,21 +1261,67 @@ class QuicSession extends EventEmitter {
this,
id,
handle);
+ if (halfOpen) {
+ stream.push(null);
+ stream.read();
+ }
this.#streams.set(id, stream);
return stream;
}
}
class QuicServerSession extends QuicSession {
+ #contexts = [];
constructor(socket, handle) {
super(socket);
this[kHandle] = handle;
handle[owner_symbol] = this;
}
+ [kClientHello](alpn, servername, ciphers, callback) {
+ this.emit(
+ 'clientHello',
+ alpn,
+ servername,
+ ciphers,
+ callback.bind(this[kHandle]));
+ }
+
[kReady]() {
process.nextTick(emit.bind(this, 'ready'));
}
+
+ [kCert](servername, callback) {
+ const { serverSecureContext } = this.socket;
+ let { context } = serverSecureContext;
+
+ for (var i = 0; i < this.#contexts.length; i++) {
+ const elem = this.#contexts[i];
+ if (elem[0].test(servername))
+ context = elem[1];
+ break;
+ }
+
+ this.emit(
+ 'OCSPRequest',
+ servername,
+ context,
+ callback.bind(this[kHandle]));
+ }
+
+ addContext(servername, context = {}) {
+ if (typeof servername !== 'string')
+ throw new ERR_INVALID_ARG_TYPE('servername', 'string', servername);
+
+ if (context == null || typeof context !== 'object')
+ throw new ERR_INVALID_ARG_TYPE('context', 'Object', context);
+
+ const re = new RegExp('^' +
+ servername.replace(/([.^$+?\-\\[\]{}])/g, '\\$1')
+ .replace(/\*/g, '[^.]*') +
+ '$');
+ this.#contexts.push([re, _createSecureContext(context)]);
+ }
}
function setSocketAfterBind(socket, callback) {
@@ -1142,53 +1352,59 @@ function setSocketAfterBind(socket, callback) {
}
class QuicClientSession extends QuicSession {
+ #alpn = undefined;
+ #dcid = undefined;
#handleReady = false;
#ipv6Only = undefined;
#minDHSize = undefined;
#port = undefined;
+ #remoteTransportParams = undefined;
+ #requestOCSP = undefined;
#secureContext = undefined;
+ #sessionTicket = undefined;
#socketReady = false;
#transportParams = undefined;
- #sessionTicket = undefined;
- #remoteTransportParams = undefined;
- #dcid = undefined;
#preferredAddressPolicy;
constructor(socket, options) {
const sc_options = {
secureProtocol: 'TLSv1_3_client_method',
- rejectUnauthorized: !getAllowUnauthorized(),
...options
};
-
const {
- servername,
- port,
- ipv6Only,
- minDHSize,
- remoteTransportParams,
- sessionTicket,
+ alpn,
dcid,
+ ipv6Only,
maxCidLen,
minCidLen,
+ minDHSize,
+ port,
preferredAddressPolicy,
+ remoteTransportParams,
+ requestOCSP,
+ servername,
+ sessionTicket,
} = validateQuicClientSessionOptions(options);
super(socket, servername);
+ this.#alpn = alpn;
+ this.#dcid = dcid;
+ this.#ipv6Only = ipv6Only;
+ this.#minDHSize = minDHSize;
+ this.#port = port || 0;
+ this.#preferredAddressPolicy = preferredAddressPolicy;
+ this.#remoteTransportParams = remoteTransportParams;
+ this.#requestOCSP = requestOCSP;
+ this.#secureContext =
+ createSecureContext(
+ sc_options,
+ initSecureContextClient);
+ this.#sessionTicket = sessionTicket;
this.#transportParams =
validateTransportParams(
options,
maxCidLen,
minCidLen);
- this.#ipv6Only = ipv6Only;
- this.#minDHSize = minDHSize;
- this.#port = port || 0;
- this.#secureContext = createSecureContext(sc_options,
- initSecureContextClient);
- this.#sessionTicket = sessionTicket;
- this.#remoteTransportParams = remoteTransportParams;
- this.#dcid = dcid;
- this.#preferredAddressPolicy = preferredAddressPolicy;
}
[kHandshakePost]() {
@@ -1197,6 +1413,19 @@ class QuicClientSession extends QuicSession {
this.destroy(new ERR_TLS_DH_PARAM_SIZE(size));
return false;
}
+
+ // TODO(@jasnell): QUIC *requires* that the client verify the
+ // identity of the server so we'll need to do that here.
+ // The current implementation of tls.checkServerIdentity is
+ // less than great and could be rewritten to speed it up
+ // significantly by running at the C++ layer. As it is
+ // currently, the method pulls the peer cert data, converts
+ // it to a javascript object, then processes the javascript
+ // object... which is more expensive than what is strictly
+ // necessary.
+ //
+ // See: _tls_wrap.js onConnectSecure function
+
return true;
}
@@ -1215,7 +1444,9 @@ class QuicClientSession extends QuicSession {
this.#remoteTransportParams,
this.#sessionTicket,
this.#dcid,
- this.#preferredAddressPolicy);
+ this.#preferredAddressPolicy,
+ this.#alpn,
+ this.#requestOCSP);
// We no longer need these, unset them so
// memory can be garbage collected.
this.#remoteTransportParams = undefined;
@@ -1251,6 +1482,10 @@ class QuicClientSession extends QuicSession {
this[kMaybeReady]();
}
+ [kCert](response) {
+ this.emit('OCSPResponse', response);
+ }
+
[kMaybeReady]() {
if (this.#socketReady && this.#handleReady)
process.nextTick(emit.bind(this, 'ready'));
@@ -1297,6 +1532,16 @@ function afterShutdown() {
stream[kMaybeDestroy]();
}
+function streamOnResume() {
+ if (!this.destroyed)
+ this[kHandle].readStart();
+}
+
+function streamOnPause() {
+ if (!this.destroyed /* && !this.pending */)
+ this[kHandle].readStop();
+}
+
class QuicStream extends Duplex {
#didRead = false;
#id = undefined;
@@ -1307,7 +1552,8 @@ class QuicStream extends Duplex {
...options,
allowHalfOpen: true,
decodeStrings: true,
- emitClose: true
+ emitClose: true,
+ autoDestroy: true,
});
handle.onread = onStreamRead;
handle[owner_symbol] = this;
@@ -1316,10 +1562,32 @@ class QuicStream extends Duplex {
this.#id = id;
this.#session = session;
this._readableState.readingMore = true;
+ this.on('pause', streamOnPause);
+
+ // See src/node_quic_stream.h for an explanation
+ // of the initial states for unidirectional streams.
+ if (this.unidirectional) {
+ if (session instanceof QuicServerSession) {
+ if (this.serverInitiated) {
+ // Close the readable side
+ this.push(null);
+ this.read();
+ } else {
+ // Close the writable side
+ this.end();
+ }
+ } else if (this.serverInitiated) {
+ // Close the writable side
+ this.end();
+ } else {
+ this.push(null);
+ this.read();
+ }
+ }
}
get serverInitiated() {
- return this.#id & 0b01;
+ return !!(this.#id & 0b01);
}
get clientInitiated() {
@@ -1327,7 +1595,7 @@ class QuicStream extends Duplex {
}
get unidirectional() {
- return this.#id & 0b10;
+ return !!(this.#id & 0b10);
}
get bidirectional() {
@@ -1340,6 +1608,7 @@ class QuicStream extends Duplex {
// remaining within the duplex writable side queue.
this.end();
this.push(null);
+ this.read();
process.nextTick(emit.bind(this, 'reset', finalSize, appErrorCode));
// TODO(@jasnell): Should we destroy here? It's not yet clear
// what else should be done
@@ -1350,8 +1619,14 @@ class QuicStream extends Duplex {
}
[kInspect]() {
+ const direction = this.bidirectional ? 'bidirectional' : 'unidirectional';
+ const initiated = this.serverInitiated ? 'server' : 'client';
const obj = {
- id: this.#id
+ id: this.#id,
+ direction,
+ initiated,
+ writableState: this._writableState,
+ readableState: this._readableState,
};
return `QuicStream ${util.format(obj)}`;
}
@@ -1412,7 +1687,13 @@ class QuicStream extends Duplex {
this._readableState.readingMore = false;
this.#didRead = true;
}
- this[kHandle].readStart();
+
+ streamOnResume.call(this);
+ // if (!this.pending) {
+ // streamOnResume.call(this);
+ // } else {
+ // this.once('ready', streamOnResume);
+ // }
}
get bufferSize() {
@@ -1433,8 +1714,8 @@ class QuicStream extends Duplex {
const handle = this[kHandle];
if (handle !== undefined) {
this[kHandle] = undefined;
- handle[owner_symbol] = undefined;
handle.destroy();
+ handle[owner_symbol] = undefined;
}
callback(error);
}
diff --git a/lib/internal/quic/util.js b/lib/internal/quic/util.js
index 358bab7cb7..32b1da072c 100644
--- a/lib/internal/quic/util.js
+++ b/lib/internal/quic/util.js
@@ -32,11 +32,13 @@ const {
IDX_QUIC_SESSION_MAX_PACKET_SIZE_DEFAULT,
MAX_RETRYTOKEN_EXPIRATION,
MIN_RETRYTOKEN_EXPIRATION,
+ NGTCP2_NO_ERROR,
NGTCP2_DEFAULT_MAX_ACK_DELAY,
NGTCP2_MAX_CIDLEN,
NGTCP2_MIN_CIDLEN,
QUIC_PREFERRED_ADDRESS_IGNORE,
QUIC_PREFERRED_ADDRESS_ACCEPT,
+ QUIC_ERROR_APPLICATION,
}
} = internalBinding('quic');
@@ -81,6 +83,24 @@ function lookup6(address, callback) {
lookup(address || '::1', 6, callback);
}
+function validateCloseCode(code) {
+ let closeCode;
+ let closeFamily;
+ if (code != null && typeof code === 'object') {
+ closeCode = code.code || NGTCP2_NO_ERROR;
+ closeFamily = code.family || QUIC_ERROR_APPLICATION;
+ } else if (typeof code === 'number') {
+ closeCode = code;
+ closeFamily = QUIC_ERROR_APPLICATION;
+ } else {
+ throw new ERR_INVALID_ARG_TYPE('code', ['number', 'Object'], code);
+ }
+ return {
+ closeCode,
+ closeFamily
+ };
+}
+
function validateBindOptions(port, address) {
if (!isLegalPort(port)) {
throw new ERR_INVALID_ARG_VALUE(
@@ -124,6 +144,8 @@ function validateTransportParams(params, maxCidLen, minCidLen) {
IDX_QUIC_SESSION_MAX_PACKET_SIZE_DEFAULT,
maxAckDelay = NGTCP2_DEFAULT_MAX_ACK_DELAY,
preferredAddress,
+ rejectUnauthorized,
+ requestCert,
} = { ...params };
validateNumberInRange(
maxStreamDataBidiLocal,
@@ -174,22 +196,26 @@ function validateTransportParams(params, maxCidLen, minCidLen) {
preferredAddress,
maxCidLen,
minCidLen,
+ rejectUnauthorized,
+ requestCert,
};
}
function validateQuicClientSessionOptions(options) {
const {
address,
- servername = address,
- port = 0,
- ipv6Only = false,
- minDHSize = 1024,
- remoteTransportParams,
- sessionTicket,
+ alpn,
dcid: dcid_value,
+ ipv6Only = false,
maxCidLen = NGTCP2_MAX_CIDLEN,
minCidLen = NGTCP2_MIN_CIDLEN,
+ minDHSize = 1024,
+ port = 0,
preferredAddressPolicy = 'ignore',
+ remoteTransportParams,
+ requestOCSP = false,
+ servername = address,
+ sessionTicket,
} = { ...options };
if (typeof minDHSize !== 'number')
@@ -224,6 +250,9 @@ function validateQuicClientSessionOptions(options) {
sessionTicket);
}
+ if (alpn !== undefined && typeof alpn !== 'string')
+ throw new ERR_INVALID_ARG_TYPE('options.alpn', 'string', alpn);
+
validateNumberInBoundedRange(
maxCidLen,
'options.maxCidLen',
@@ -274,21 +303,30 @@ function validateQuicClientSessionOptions(options) {
preferredAddressPolicy);
}
+ if (typeof requestOCSP !== 'boolean') {
+ throw new ERR_INVALID_ARG_TYPE(
+ 'options.requestOCSP',
+ 'boolean',
+ requestOCSP);
+ }
+
return {
address,
- servername,
- port,
- ipv6Only,
- minDHSize,
- remoteTransportParams,
- sessionTicket,
+ alpn,
dcid,
+ ipv6Only,
maxCidLen,
minCidLen,
+ minDHSize,
+ port,
preferredAddressPolicy:
preferredAddressPolicy === 'accept' ?
QUIC_PREFERRED_ADDRESS_ACCEPT :
QUIC_PREFERRED_ADDRESS_IGNORE,
+ remoteTransportParams,
+ requestOCSP,
+ servername,
+ sessionTicket,
};
}
@@ -352,6 +390,7 @@ module.exports = {
lookup4,
lookup6,
validateBindOptions,
+ validateCloseCode,
validateNumberInRange,
validateTransportParams,
validateQuicClientSessionOptions,
diff --git a/lib/internal/stream_base_commons.js b/lib/internal/stream_base_commons.js
index 88896083f1..74a21326db 100644
--- a/lib/internal/stream_base_commons.js
+++ b/lib/internal/stream_base_commons.js
@@ -92,7 +92,7 @@ function onWriteComplete(status) {
this.callback(null);
}
-function createWriteWrap(handle) {
+function createWriteWrap(handle, callback) {
const req = new WriteWrap();
req.handle = handle;
@@ -100,12 +100,13 @@ function createWriteWrap(handle) {
req.async = false;
req.bytes = 0;
req.buffer = null;
+ req.callback = callback;
return req;
}
function writevGeneric(self, data, cb) {
- const req = createWriteWrap(self[kHandle]);
+ const req = createWriteWrap(self[kHandle], cb);
const allBuffers = data.allBuffers;
var chunks;
var i;
@@ -126,29 +127,28 @@ function writevGeneric(self, data, cb) {
// Retain chunks
if (err === 0) req._chunks = chunks;
- afterWriteDispatched(self, req, err, cb);
+ afterWriteDispatched(self, req, err);
return req;
}
function writeGeneric(self, data, encoding, cb) {
- const req = createWriteWrap(self[kHandle]);
+ const req = createWriteWrap(self[kHandle], cb);
const err = handleWriteReq(req, data, encoding);
-
- afterWriteDispatched(self, req, err, cb);
+ afterWriteDispatched(self, req, err);
return req;
}
-function afterWriteDispatched(self, req, err, cb) {
+function afterWriteDispatched(self, req, err) {
req.bytes = streamBaseState[kBytesWritten];
req.async = !!streamBaseState[kLastWriteWasAsync];
if (err !== 0)
- return self.destroy(errnoException(err, 'write', req.error), cb);
+ return self.destroy(
+ errnoException(err, 'write', req.error),
+ req.callback());
- if (!req.async) {
- cb();
- } else {
- req.callback = cb;
+ if (!req.async && typeof req.callback === 'function') {
+ req.callback();
}
}
diff --git a/src/aliased_buffer.h b/src/aliased_buffer.h
index 868d495be9..231a56e89d 100644
--- a/src/aliased_buffer.h
+++ b/src/aliased_buffer.h
@@ -32,6 +32,32 @@ template ::value>>
class AliasedBufferBase {
public:
+ /**
+ * Create an AliasedBufferBase over an existing buffer
+ */
+ AliasedBufferBase(
+ v8::Isolate* isolate,
+ const size_t count,
+ NativeT* buffer) :
+ isolate_(isolate),
+ count_(count),
+ byte_offset_(0),
+ buffer_(buffer) {
+ CHECK_GT(count, 0);
+ const v8::HandleScope handle_scope(isolate_);
+ const size_t size_in_bytes =
+ MultiplyWithOverflowCheck(sizeof(NativeT), count);
+
+ v8::Local ab =
+ v8::ArrayBuffer::New(
+ isolate_,
+ buffer,
+ count);
+
+ v8::Local js_array = V8T::New(ab, byte_offset_, count);
+ js_array_ = v8::Global(isolate, js_array);
+ }
+
AliasedBufferBase(v8::Isolate* isolate, const size_t count)
: isolate_(isolate), count_(count), byte_offset_(0) {
CHECK_GT(count, 0);
diff --git a/src/debug_utils.h b/src/debug_utils.h
index ef5a4c0c47..bd942d8791 100644
--- a/src/debug_utils.h
+++ b/src/debug_utils.h
@@ -48,6 +48,22 @@ inline void Debug(Environment* env,
Debug(env, cat, format.c_str(), std::forward(args)...);
}
+inline void Debug(Environment* env,
+ DebugCategory cat,
+ const char* format,
+ va_list args) {
+ if (!UNLIKELY(env->debug_enabled(cat)))
+ return;
+ vfprintf(stderr, format, args);
+}
+
+inline void Debug(Environment* env,
+ DebugCategory cat,
+ const std::string& format,
+ va_list args) {
+ Debug(env, cat, format.c_str(), args);
+}
+
// Used internally by the 'real' Debug(AsyncWrap*, ...) functions below, so that
// the FORCE_INLINE flag on them doesn't apply to the contents of this function
// as well.
diff --git a/src/env.h b/src/env.h
index a847a863b1..2abe2771ae 100644
--- a/src/env.h
+++ b/src/env.h
@@ -313,6 +313,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
V(stack_string, "stack") \
V(start_time_string, "startTime") \
V(state_string, "state") \
+ V(stats_string, "stats") \
V(status_string, "status") \
V(stdio_string, "stdio") \
V(subject_string, "subject") \
@@ -415,11 +416,14 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
V(quic_on_socket_close_function, v8::Function) \
V(quic_on_socket_error_function, v8::Function) \
V(quic_on_session_ready_function, v8::Function) \
+ V(quic_on_session_cert_function, v8::Function) \
+ V(quic_on_session_client_hello_function, v8::Function) \
V(quic_on_session_close_function, v8::Function) \
V(quic_on_session_error_function, v8::Function) \
V(quic_on_session_extend_function, v8::Function) \
V(quic_on_session_handshake_function, v8::Function) \
V(quic_on_session_keylog_function, v8::Function) \
+ V(quic_on_session_status_function, v8::Function) \
V(quic_on_session_ticket_function, v8::Function) \
V(quic_on_session_path_validation_function, v8::Function) \
V(quic_on_stream_ready_function, v8::Function) \
@@ -519,7 +523,8 @@ struct CompileFnEntry {
#define DEBUG_CATEGORY_NAMES(V) \
NODE_ASYNC_PROVIDER_TYPES(V) \
V(INSPECTOR_SERVER) \
- V(INSPECTOR_PROFILER)
+ V(INSPECTOR_PROFILER) \
+ V(NGTCP2_DEBUG)
enum class DebugCategory {
#define V(name) name,
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index b86d228bb4..47006f04aa 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -116,12 +116,6 @@ struct StackOfXASN1Deleter {
};
using StackOfASN1 = std::unique_ptr;
-// OPENSSL_free is a macro, so we need a wrapper function.
-struct OpenSSLBufferDeleter {
- void operator()(char* pointer) const { OPENSSL_free(pointer); }
-};
-using OpenSSLBuffer = std::unique_ptr;
-
static const char* const root_certs[] = {
#include "node_root_certs.h" // NOLINT(build/include_order)
};
@@ -460,15 +454,6 @@ bool EntropySource(unsigned char* buffer, size_t length) {
return RAND_bytes(buffer, length) != -1;
}
-
-template
-static T* MallocOpenSSL(size_t count) {
- void* mem = OPENSSL_malloc(MultiplyWithOverflowCheck(count, sizeof(T)));
- CHECK_IMPLIES(mem == nullptr, count == 0);
- return static_cast(mem);
-}
-
-
void SecureContext::Initialize(Environment* env, Local