Release 2.4.0
[FEATURE] QUIC and HTTP/3 Internet Draft 23 support
This commit is contained in:
parent
2718741832
commit
92f6e17bdc
|
@ -1,3 +1,7 @@
|
|||
2019-09-18
|
||||
- 2.4.0
|
||||
- [FEATURE] QUIC and HTTP/3 Internet Draft 23 support
|
||||
|
||||
2019-09-13
|
||||
- 2.3.1
|
||||
- [BUGFIX] Fix memory leaks
|
||||
|
|
|
@ -273,3 +273,9 @@ More Compilation Options
|
|||
this flag is specified, sending of packets will randomly fail, about
|
||||
one out of every 10 attempts. Set environment variable
|
||||
LSQUIC_RANDOM_SEND_FAILURE to change this frequency.
|
||||
|
||||
-DLSQUIC_ECN_BLACK_HOLE=1
|
||||
|
||||
When compiled with this flag, setting environment variable
|
||||
LSQUIC_ECN_BLACK_HOLE to 1 will emulate ECN black hole: all received
|
||||
packets with ECN markings are dropped on the floor.
|
||||
|
|
|
@ -24,8 +24,8 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#define LSQUIC_MAJOR_VERSION 2
|
||||
#define LSQUIC_MINOR_VERSION 3
|
||||
#define LSQUIC_PATCH_VERSION 1
|
||||
#define LSQUIC_MINOR_VERSION 4
|
||||
#define LSQUIC_PATCH_VERSION 0
|
||||
|
||||
/**
|
||||
* Engine flags:
|
||||
|
@ -125,9 +125,9 @@ enum lsquic_version
|
|||
#endif
|
||||
|
||||
/**
|
||||
* IETF QUIC Draft-22
|
||||
* IETF QUIC Draft-23
|
||||
*/
|
||||
LSQVER_ID22,
|
||||
LSQVER_ID23,
|
||||
|
||||
/**
|
||||
* Special version to trigger version negotiation.
|
||||
|
@ -139,7 +139,7 @@ enum lsquic_version
|
|||
};
|
||||
|
||||
/**
|
||||
* We currently support versions 39, 43, 46, and IETF Draft-22
|
||||
* We currently support versions 39, 43, 46, and IETF Draft-23
|
||||
* @see lsquic_version
|
||||
*/
|
||||
#define LSQUIC_SUPPORTED_VERSIONS ((1 << N_LSQVER) - 1)
|
||||
|
@ -156,9 +156,9 @@ enum lsquic_version
|
|||
|
||||
#define LSQUIC_GQUIC_HEADER_VERSIONS ((1 << LSQVER_039) | (1 << LSQVER_043))
|
||||
|
||||
#define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID22) | (1 << LSQVER_VERNEG))
|
||||
#define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID23) | (1 << LSQVER_VERNEG))
|
||||
|
||||
#define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID22) | (1 << LSQVER_VERNEG))
|
||||
#define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID23) | (1 << LSQVER_VERNEG))
|
||||
|
||||
enum lsquic_hsk_status
|
||||
{
|
||||
|
@ -358,14 +358,8 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
|
|||
#define LSQUIC_DF_QPACK_ENC_MAX_BLOCKED 100
|
||||
#define LSQUIC_DF_QPACK_ENC_MAX_SIZE 4096
|
||||
|
||||
/** ECN is enabled by default */
|
||||
#define LSQUIC_DF_ECN 1
|
||||
|
||||
/**
|
||||
* The default number of the priority placeholders is higher than the
|
||||
* recommended value of 16 to give the clients even more freedom.
|
||||
*/
|
||||
#define LSQUIC_DF_H3_PLACEHOLDERS 50
|
||||
/** ECN is disabled by default */
|
||||
#define LSQUIC_DF_ECN 0
|
||||
|
||||
/** Allow migration by default */
|
||||
#define LSQUIC_DF_ALLOW_MIGRATION 1
|
||||
|
@ -729,13 +723,6 @@ struct lsquic_engine_settings {
|
|||
*/
|
||||
int es_ecn;
|
||||
|
||||
/**
|
||||
* Number of HTTP/3 priorify placeholders.
|
||||
*
|
||||
* The default is @ref LSQUIC_DF_H3_PLACEHOLDERS
|
||||
*/
|
||||
unsigned es_h3_placeholders;
|
||||
|
||||
/**
|
||||
* Allow peer to migrate connection.
|
||||
*
|
||||
|
|
|
@ -28,14 +28,12 @@ SET(lsquic_STAT_SRCS
|
|||
lsquic_full_conn.c
|
||||
lsquic_full_conn_ietf.c
|
||||
lsquic_global.c
|
||||
lsquic_h3_prio.c
|
||||
lsquic_handshake.c
|
||||
lsquic_hash.c
|
||||
lsquic_hcsi_reader.c
|
||||
lsquic_hcso_writer.c
|
||||
lsquic_headers_stream.c
|
||||
lsquic_hkdf.c
|
||||
lsquic_hq.c
|
||||
lsquic_hspack_valid.c
|
||||
lsquic_http1x_if.c
|
||||
lsquic_logger.c
|
||||
|
|
|
@ -21,7 +21,6 @@ struct conn_stats;
|
|||
#endif
|
||||
struct qpack_enc_hdl;
|
||||
struct qpack_dec_hdl;
|
||||
struct h3_prio_tree;
|
||||
struct network_path;
|
||||
|
||||
struct lsquic_conn_public {
|
||||
|
@ -44,7 +43,6 @@ struct lsquic_conn_public {
|
|||
struct {
|
||||
struct qpack_enc_hdl *qeh;
|
||||
struct qpack_dec_hdl *qdh;
|
||||
struct h3_prio_tree *prio_tree;
|
||||
struct lsquic_hash *promises;
|
||||
} ietf;
|
||||
} u;
|
||||
|
|
|
@ -318,7 +318,7 @@ struct enc_session_funcs_gquic lsquic_enc_session_gquic_gquic_1;
|
|||
extern const struct enc_session_funcs_iquic lsquic_enc_session_iquic_ietf_v1;
|
||||
|
||||
#define select_esf_common_by_ver(ver) ( \
|
||||
ver == LSQVER_ID22 ? &lsquic_enc_session_common_ietf_v1 : \
|
||||
ver == LSQVER_ID23 ? &lsquic_enc_session_common_ietf_v1 : \
|
||||
ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \
|
||||
&lsquic_enc_session_common_gquic_1 )
|
||||
|
||||
|
|
|
@ -78,8 +78,8 @@ static const struct alpn_map {
|
|||
enum lsquic_version version;
|
||||
const unsigned char *alpn;
|
||||
} s_alpns[] = {
|
||||
{ LSQVER_ID22, (unsigned char *) "\x05h3-22", },
|
||||
{ LSQVER_VERNEG, (unsigned char *) "\x05h3-22", },
|
||||
{ LSQVER_ID23, (unsigned char *) "\x05h3-23", },
|
||||
{ LSQVER_VERNEG, (unsigned char *) "\x05h3-23", },
|
||||
};
|
||||
|
||||
struct enc_sess_iquic;
|
||||
|
@ -520,7 +520,7 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
|
|||
- 1 /* One slot is used by peer's SCID */
|
||||
- !!(params.tp_flags & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6));
|
||||
if (!settings->es_allow_migration)
|
||||
params.tp_disable_migration = 1;
|
||||
params.tp_disable_active_migration = 1;
|
||||
|
||||
len = lsquic_tp_encode(¶ms, buf, bufsz);
|
||||
if (len >= 0)
|
||||
|
|
|
@ -74,7 +74,6 @@
|
|||
#include "lsquic_min_heap.h"
|
||||
#include "lsquic_http1x_if.h"
|
||||
#include "lsquic_parse_common.h"
|
||||
#include "lsquic_h3_prio.h"
|
||||
#include "lsquic_handshake.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_ENGINE
|
||||
|
@ -320,7 +319,6 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
|
|||
settings->es_qpack_dec_max_blocked = LSQUIC_DF_QPACK_DEC_MAX_BLOCKED;
|
||||
settings->es_qpack_enc_max_size = LSQUIC_DF_QPACK_ENC_MAX_SIZE;
|
||||
settings->es_qpack_enc_max_blocked = LSQUIC_DF_QPACK_ENC_MAX_BLOCKED;
|
||||
settings->es_h3_placeholders = LSQUIC_DF_H3_PLACEHOLDERS;
|
||||
settings->es_allow_migration = LSQUIC_DF_ALLOW_MIGRATION;
|
||||
}
|
||||
|
||||
|
@ -331,8 +329,6 @@ lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
|
|||
unsigned flags,
|
||||
char *err_buf, size_t err_buf_sz)
|
||||
{
|
||||
unsigned sum;
|
||||
|
||||
if (settings->es_cfcw < LSQUIC_MIN_FCW ||
|
||||
settings->es_sfcw < LSQUIC_MIN_FCW)
|
||||
{
|
||||
|
@ -384,19 +380,6 @@ lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
|
|||
return -1;
|
||||
}
|
||||
|
||||
sum = settings->es_init_max_streams_bidi
|
||||
+ settings->es_init_max_streams_uni
|
||||
+ settings->es_h3_placeholders;
|
||||
if (sum > H3_PRIO_MAX_ELEMS)
|
||||
{
|
||||
if (err_buf)
|
||||
snprintf(err_buf, err_buf_sz, "Combined number of streams and "
|
||||
"placeholders (%u) is greater than the maximum supported "
|
||||
"number of elements in the HTTP/3 priority tree (%u)",
|
||||
sum, H3_PRIO_MAX_ELEMS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (settings->es_cc_algo > 2)
|
||||
{
|
||||
if (err_buf)
|
||||
|
@ -955,8 +938,7 @@ schedule_req_packet (struct lsquic_engine *engine, enum packet_req_type type,
|
|||
LSQ_DEBUGC("scheduled %s packet for cid %"CID_FMT,
|
||||
lsquic_preqt2str[type], CID_BITS(&packet_in->pi_conn_id));
|
||||
else
|
||||
LSQ_DEBUG("cannot schedule %s packet: out of packet request objects",
|
||||
lsquic_preqt2str[type]);
|
||||
LSQ_DEBUG("cannot schedule %s packet", lsquic_preqt2str[type]);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
#include "lsquic_mini_conn_ietf.h"
|
||||
#include "lsquic_tokgen.h"
|
||||
#include "lsquic_full_conn.h"
|
||||
#include "lsquic_h3_prio.h"
|
||||
#include "lsquic_spi.h"
|
||||
#include "lsquic_ietf.h"
|
||||
#include "lsquic_push_promise.h"
|
||||
#include "lsquic_headers.h"
|
||||
|
@ -70,7 +70,6 @@
|
|||
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&conn->ifc_conn)
|
||||
#include "lsquic_logger.h"
|
||||
|
||||
#define MAX_ANY_PACKETS_SINCE_LAST_ACK 20
|
||||
#define MAX_RETR_PACKETS_SINCE_LAST_ACK 2
|
||||
#define ACK_TIMEOUT (TP_DEF_MAX_ACK_DELAY * 1000)
|
||||
#define INITIAL_CHAL_TIMEOUT 25000
|
||||
|
@ -873,7 +872,7 @@ ietf_full_conn_add_scid (struct ietf_full_conn *conn,
|
|||
|
||||
static int
|
||||
ietf_full_conn_init (struct ietf_full_conn *conn,
|
||||
struct lsquic_engine_public *enpub, unsigned flags)
|
||||
struct lsquic_engine_public *enpub, unsigned flags, int ecn)
|
||||
{
|
||||
conn->ifc_conn.cn_if = ietf_full_conn_iface_ptr;
|
||||
if (enpub->enp_settings.es_scid_len)
|
||||
|
@ -907,21 +906,16 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
|
|||
lsquic_rechist_init(&conn->ifc_rechist[PNS_APP], &conn->ifc_conn, 1);
|
||||
lsquic_send_ctl_init(&conn->ifc_send_ctl, &conn->ifc_alset, enpub,
|
||||
flags & IFC_SERVER ? &server_ver_neg : &conn->ifc_u.cli.ifcli_ver_neg,
|
||||
&conn->ifc_pub, SC_IETF|SC_NSTP);
|
||||
&conn->ifc_pub, SC_IETF|SC_NSTP|(ecn ? SC_ECN : 0));
|
||||
lsquic_cfcw_init(&conn->ifc_pub.cfcw, &conn->ifc_pub,
|
||||
conn->ifc_settings->es_cfcw);
|
||||
conn->ifc_pub.all_streams = lsquic_hash_create();
|
||||
if (!conn->ifc_pub.all_streams)
|
||||
goto err0;
|
||||
conn->ifc_pub.u.ietf.prio_tree = lsquic_prio_tree_new(&conn->ifc_conn,
|
||||
flags & IFC_SERVER ? conn->ifc_settings->es_h3_placeholders : 0);
|
||||
if (!conn->ifc_pub.u.ietf.prio_tree)
|
||||
goto err1;
|
||||
return -1;
|
||||
conn->ifc_pub.u.ietf.qeh = &conn->ifc_qeh;
|
||||
conn->ifc_pub.u.ietf.qdh = &conn->ifc_qdh;
|
||||
|
||||
conn->ifc_peer_hq_settings.header_table_size = HQ_DF_QPACK_MAX_TABLE_CAPACITY;
|
||||
conn->ifc_peer_hq_settings.num_placeholders = conn->ifc_settings->es_h3_placeholders;
|
||||
conn->ifc_peer_hq_settings.max_header_list_size = HQ_DF_MAX_HEADER_LIST_SIZE;
|
||||
conn->ifc_peer_hq_settings.qpack_blocked_streams = HQ_DF_QPACK_BLOCKED_STREAMS;
|
||||
|
||||
|
@ -936,11 +930,6 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
|
|||
conn->ifc_idle_to = enpub->enp_settings.es_idle_timeout * 1000 * 1000;
|
||||
conn->ifc_ping_period = enpub->enp_settings.es_ping_period * 1000 * 1000;
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
lsquic_hash_destroy(conn->ifc_pub.all_streams);
|
||||
err0:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -990,7 +979,8 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
|
|||
}
|
||||
conn->ifc_paths[0].cop_path.np_pack_size = max_packet_size;
|
||||
|
||||
if (0 != ietf_full_conn_init(conn, enpub, flags))
|
||||
if (0 != ietf_full_conn_init(conn, enpub, flags,
|
||||
enpub->enp_settings.es_ecn))
|
||||
{
|
||||
free(conn);
|
||||
return NULL;
|
||||
|
@ -1139,7 +1129,8 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
|
|||
/* Set the flags early so that correct CID is used for logging */
|
||||
conn->ifc_conn.cn_flags |= LSCONN_IETF | LSCONN_SERVER;
|
||||
|
||||
if (0 != ietf_full_conn_init(conn, enpub, flags))
|
||||
if (0 != ietf_full_conn_init(conn, enpub, flags,
|
||||
lsquic_mini_conn_ietf_ecn_ok(imc)))
|
||||
{
|
||||
free(conn);
|
||||
return NULL;
|
||||
|
@ -2197,16 +2188,18 @@ process_stream_ready_to_send (struct ietf_full_conn *conn,
|
|||
static void
|
||||
process_streams_ready_to_send (struct ietf_full_conn *conn)
|
||||
{
|
||||
lsquic_stream_t *stream;
|
||||
struct lsquic_stream *stream;
|
||||
struct stream_prio_iter spi;
|
||||
|
||||
assert(!TAILQ_EMPTY(&conn->ifc_pub.sending_streams));
|
||||
|
||||
lsquic_prio_tree_iter_reset(conn->ifc_pub.u.ietf.prio_tree, "send");
|
||||
TAILQ_FOREACH(stream, &conn->ifc_pub.sending_streams, next_send_stream)
|
||||
lsquic_prio_tree_iter_add(conn->ifc_pub.u.ietf.prio_tree, stream);
|
||||
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.sending_streams),
|
||||
TAILQ_LAST(&conn->ifc_pub.sending_streams, lsquic_streams_tailq),
|
||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
|
||||
SMQF_SENDING_FLAGS, &conn->ifc_conn, "send", NULL, NULL);
|
||||
|
||||
while ((stream = lsquic_prio_tree_iter_next(
|
||||
conn->ifc_pub.u.ietf.prio_tree)))
|
||||
for (stream = lsquic_spi_first(&spi); stream;
|
||||
stream = lsquic_spi_next(&spi))
|
||||
if (!process_stream_ready_to_send(conn, stream))
|
||||
break;
|
||||
}
|
||||
|
@ -2375,8 +2368,6 @@ ietf_full_conn_ci_destroy (struct lsquic_conn *lconn)
|
|||
lsquic_malo_destroy(conn->ifc_pub.packet_out_malo);
|
||||
if (conn->ifc_flags & IFC_CREATED_OK)
|
||||
conn->ifc_enpub->enp_stream_if->on_conn_closed(&conn->ifc_conn);
|
||||
if (conn->ifc_pub.u.ietf.prio_tree)
|
||||
lsquic_prio_tree_destroy(conn->ifc_pub.u.ietf.prio_tree);
|
||||
if (conn->ifc_conn.cn_enc_session)
|
||||
conn->ifc_conn.cn_esf.i->esfi_destroy(conn->ifc_conn.cn_enc_session);
|
||||
while (!STAILQ_EMPTY(&conn->ifc_stream_ids_to_ss))
|
||||
|
@ -2533,7 +2524,7 @@ begin_migra_or_retire_cid (struct ietf_full_conn *conn,
|
|||
struct sockaddr_in6 v6;
|
||||
} sockaddr;
|
||||
|
||||
if (params->tp_disable_migration)
|
||||
if (params->tp_disable_active_migration)
|
||||
{
|
||||
LSQ_DEBUG("TP disables migration: retire PreferredAddress CID");
|
||||
retire_cid_from_tp(conn, params);
|
||||
|
@ -3337,39 +3328,32 @@ static void
|
|||
process_streams_read_events (struct ietf_full_conn *conn)
|
||||
{
|
||||
struct lsquic_stream *stream;
|
||||
int have_streams, iters;
|
||||
int iters;
|
||||
enum stream_q_flags q_flags, needs_service;
|
||||
struct stream_prio_iter spi;
|
||||
static const char *const labels[2] = { "read-0", "read-1", };
|
||||
|
||||
if (TAILQ_EMPTY(&conn->ifc_pub.read_streams))
|
||||
return;
|
||||
|
||||
conn->ifc_pub.cp_flags &= ~CP_STREAM_UNBLOCKED;
|
||||
iters = 0;
|
||||
do
|
||||
{
|
||||
have_streams = 0;
|
||||
TAILQ_FOREACH(stream, &conn->ifc_pub.read_streams, next_read_stream)
|
||||
if (lsquic_stream_readable(stream))
|
||||
{
|
||||
if (!have_streams)
|
||||
{
|
||||
++have_streams;
|
||||
lsquic_prio_tree_iter_reset(conn->ifc_pub.u.ietf.prio_tree,
|
||||
labels[iters]);
|
||||
}
|
||||
lsquic_prio_tree_iter_add(conn->ifc_pub.u.ietf.prio_tree,
|
||||
stream);
|
||||
}
|
||||
|
||||
if (!have_streams)
|
||||
break;
|
||||
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.read_streams),
|
||||
TAILQ_LAST(&conn->ifc_pub.read_streams, lsquic_streams_tailq),
|
||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
|
||||
SMQF_WANT_READ, &conn->ifc_conn, labels[iters], NULL, NULL);
|
||||
|
||||
needs_service = 0;
|
||||
while ((stream = lsquic_prio_tree_iter_next(
|
||||
conn->ifc_pub.u.ietf.prio_tree)))
|
||||
for (stream = lsquic_spi_first(&spi); stream;
|
||||
stream = lsquic_spi_next(&spi))
|
||||
{
|
||||
q_flags = stream->sm_qflags & SMQF_SERVICE_FLAGS;
|
||||
lsquic_stream_dispatch_read_events(stream);
|
||||
needs_service |= q_flags ^ (stream->sm_qflags & SMQF_SERVICE_FLAGS);
|
||||
}
|
||||
|
||||
if (needs_service)
|
||||
service_streams(conn);
|
||||
}
|
||||
|
@ -3435,31 +3419,25 @@ write_is_possible (struct ietf_full_conn *conn)
|
|||
}
|
||||
|
||||
|
||||
/* Write events are dispatched in two steps. First, only the high-priority
|
||||
* streams are processed. High-priority streams are critical streams plus
|
||||
* one non-critical streams with the highest priority. In the second step,
|
||||
* all other streams are processed.
|
||||
*/
|
||||
static void
|
||||
process_streams_write_events (struct ietf_full_conn *conn, int high_prio,
|
||||
const struct lsquic_stream **highest_non_crit)
|
||||
process_streams_write_events (struct ietf_full_conn *conn, int high_prio)
|
||||
{
|
||||
struct lsquic_stream *stream;
|
||||
struct h3_prio_tree *const prio_tree = conn->ifc_pub.u.ietf.prio_tree;
|
||||
struct stream_prio_iter spi;
|
||||
|
||||
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.write_streams),
|
||||
TAILQ_LAST(&conn->ifc_pub.write_streams, lsquic_streams_tailq),
|
||||
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
|
||||
SMQF_WANT_WRITE|SMQF_WANT_FLUSH, &conn->ifc_conn,
|
||||
high_prio ? "write-high" : "write-low", NULL, NULL);
|
||||
|
||||
if (high_prio)
|
||||
*highest_non_crit = lsquic_prio_tree_highest_non_crit(prio_tree);
|
||||
lsquic_prio_tree_iter_reset(prio_tree, high_prio ? "write-high" :
|
||||
"write-low");
|
||||
TAILQ_FOREACH(stream, &conn->ifc_pub.write_streams, next_write_stream)
|
||||
if (high_prio ==
|
||||
(stream == *highest_non_crit ||
|
||||
lsquic_stream_is_critical(stream)))
|
||||
lsquic_prio_tree_iter_add(prio_tree, stream);
|
||||
lsquic_spi_drop_non_high(&spi);
|
||||
else
|
||||
lsquic_spi_drop_high(&spi);
|
||||
|
||||
|
||||
while ((stream = lsquic_prio_tree_iter_next(
|
||||
conn->ifc_pub.u.ietf.prio_tree)))
|
||||
for (stream = lsquic_spi_first(&spi); stream && write_is_possible(conn);
|
||||
stream = lsquic_spi_next(&spi))
|
||||
if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
|
||||
lsquic_stream_dispatch_write_events(stream);
|
||||
|
||||
|
@ -3920,6 +3898,9 @@ switch_path_to (struct ietf_full_conn *conn, unsigned char path_id)
|
|||
assert(conn->ifc_cur_path_id != path_id);
|
||||
|
||||
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "switched paths");
|
||||
/* TODO: reset cwnd and RTT estimate.
|
||||
* See [draft-ietf-quic-transport-23] Section 9.4.
|
||||
*/
|
||||
lsquic_send_ctl_repath(&conn->ifc_send_ctl,
|
||||
CUR_NPATH(conn), &conn->ifc_paths[path_id].cop_path);
|
||||
maybe_retire_dcid(conn, &CUR_NPATH(conn)->np_dcid);
|
||||
|
@ -5550,6 +5531,7 @@ process_regular_packet (struct ietf_full_conn *conn,
|
|||
switch (dec_packin)
|
||||
{
|
||||
case DECPI_BADCRYPT:
|
||||
case DECPI_TOO_SHORT:
|
||||
if (conn->ifc_enpub->enp_settings.es_honor_prst
|
||||
/* In server mode, even if we do support stateless reset packets,
|
||||
* they are handled in lsquic_engine.c. No need to have this
|
||||
|
@ -5562,22 +5544,21 @@ process_regular_packet (struct ietf_full_conn *conn,
|
|||
conn->ifc_flags |= IFC_GOT_PRST;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
else if (dec_packin == DECPI_BADCRYPT)
|
||||
{
|
||||
LSQ_INFO("could not decrypt packet (type %s)",
|
||||
lsquic_hety2str[packet_in->pi_header_type]);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LSQ_INFO("packet is too short to be decrypted");
|
||||
return 0;
|
||||
}
|
||||
case DECPI_NOT_YET:
|
||||
return 0;
|
||||
case DECPI_NOMEM:
|
||||
return 0;
|
||||
case DECPI_TOO_SHORT:
|
||||
/* We should not hit this path: packets that are too short should
|
||||
* not be parsed correctly.
|
||||
*/
|
||||
LSQ_INFO("packet is too short to be decrypted");
|
||||
return 0;
|
||||
case DECPI_VIOLATION:
|
||||
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION,
|
||||
"decrypter reports protocol violation");
|
||||
|
@ -5853,7 +5834,6 @@ static enum tick_st
|
|||
ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
|
||||
{
|
||||
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
|
||||
const struct lsquic_stream *highest_non_crit;
|
||||
int have_delayed_packets, s;
|
||||
enum tick_st tick = 0;
|
||||
unsigned n;
|
||||
|
@ -6014,7 +5994,7 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
|
|||
|
||||
if (!TAILQ_EMPTY(&conn->ifc_pub.write_streams))
|
||||
{
|
||||
process_streams_write_events(conn, 1, &highest_non_crit);
|
||||
process_streams_write_events(conn, 1);
|
||||
if (!write_is_possible(conn))
|
||||
goto end_write;
|
||||
}
|
||||
|
@ -6025,7 +6005,7 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
|
|||
goto end_write;
|
||||
|
||||
if (!TAILQ_EMPTY(&conn->ifc_pub.write_streams))
|
||||
process_streams_write_events(conn, 0, &highest_non_crit);
|
||||
process_streams_write_events(conn, 0);
|
||||
|
||||
lsquic_send_ctl_maybe_app_limited(&conn->ifc_send_ctl, CUR_NPATH(conn));
|
||||
|
||||
|
@ -6448,59 +6428,6 @@ static const struct conn_iface *ietf_full_conn_iface_ptr =
|
|||
&ietf_full_conn_iface;
|
||||
|
||||
|
||||
static void
|
||||
on_priority (void *ctx, const struct hq_priority *priority)
|
||||
{
|
||||
struct ietf_full_conn *const conn = ctx;
|
||||
ABORT_QUIETLY(1, HEC_UNEXPECTED_FRAME,
|
||||
"server should not send PRIORITY frames");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_priority_server (void *ctx, const struct hq_priority *priority)
|
||||
{
|
||||
struct ietf_full_conn *const conn = ctx;
|
||||
enum h3_elem_type child_type, parent_type;
|
||||
int s;
|
||||
|
||||
LSQ_DEBUG("%s: %s #%"PRIu64" depends on %s #%"PRIu64"; "
|
||||
"weight: %u", __func__,
|
||||
lsquic_h3pet2str[priority->hqp_prio_type], priority->hqp_prio_id,
|
||||
lsquic_h3det2str[priority->hqp_dep_type], priority->hqp_dep_id,
|
||||
HQP_WEIGHT(priority));
|
||||
|
||||
switch (priority->hqp_dep_type)
|
||||
{
|
||||
case H3DET_REQ_STREAM: parent_type = H3ET_REQ_STREAM; break;
|
||||
case H3DET_PUSH_STREAM: parent_type = H3ET_PUSH_STREAM; break;
|
||||
case H3DET_PLACEHOLDER: parent_type = H3ET_PLACEHOLDER; break;
|
||||
default: parent_type = H3ET_ROOT; break;
|
||||
}
|
||||
|
||||
switch (priority->hqp_prio_type)
|
||||
{
|
||||
case H3PET_REQ_STREAM: child_type = H3ET_REQ_STREAM; break;
|
||||
case H3PET_PUSH_STREAM: child_type = H3ET_PUSH_STREAM; break;
|
||||
case H3PET_PLACEHOLDER: child_type = H3ET_PLACEHOLDER; break;
|
||||
default:
|
||||
ABORT_QUIETLY(1, HEC_MALFORMED_FRAME + HQFT_PRIORITY,
|
||||
"Priority frame on control stream specified current stream "
|
||||
"prioritized type");
|
||||
return;
|
||||
}
|
||||
|
||||
s = lsquic_prio_tree_set_rel(conn->ifc_pub.u.ietf.prio_tree, child_type,
|
||||
priority->hqp_prio_id, priority->hqp_weight, parent_type,
|
||||
priority->hqp_dep_id);
|
||||
/* TODO: differentiate between internal and protocol errors -- perhaps
|
||||
* via a special callback to be called in prio_tree.
|
||||
*/
|
||||
if (s != 0)
|
||||
ABORT_QUIETLY(1, HEC_GENERAL_PROTOCOL_ERROR, "error setting priority");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_cancel_push (void *ctx, uint64_t push_id)
|
||||
{
|
||||
|
@ -6514,7 +6441,7 @@ static void
|
|||
on_max_push_id_client (void *ctx, uint64_t push_id)
|
||||
{
|
||||
struct ietf_full_conn *const conn = ctx;
|
||||
ABORT_QUIETLY(1, HEC_WRONG_STREAM, "client does not expect the server "
|
||||
ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED, "client does not expect the server "
|
||||
"to send MAX_PUSH_ID frame");
|
||||
}
|
||||
|
||||
|
@ -6586,19 +6513,6 @@ on_setting (void *ctx, uint64_t setting_id, uint64_t value)
|
|||
LSQ_DEBUG("Peer's SETTINGS_QPACK_BLOCKED_STREAMS=%"PRIu64, value);
|
||||
conn->ifc_peer_hq_settings.qpack_blocked_streams = value;
|
||||
break;
|
||||
case HQSID_NUM_PLACEHOLDERS:
|
||||
LSQ_DEBUG("Peer's SETTINGS_NUM_PLACEHOLDERS=%"PRIu64, value);
|
||||
if (conn->ifc_flags & IFC_SERVER)
|
||||
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION,
|
||||
"HTTP_WRONG_SETTING_DIRECTION");
|
||||
else
|
||||
{
|
||||
conn->ifc_peer_hq_settings.num_placeholders
|
||||
= MIN(value, conn->ifc_settings->es_h3_placeholders);
|
||||
lsquic_prio_tree_set_ph(conn->ifc_pub.u.ietf.prio_tree,
|
||||
conn->ifc_peer_hq_settings.num_placeholders);
|
||||
}
|
||||
break;
|
||||
case HQSID_QPACK_MAX_TABLE_CAPACITY:
|
||||
LSQ_DEBUG("Peer's SETTINGS_QPACK_MAX_TABLE_CAPACITY=%"PRIu64, value);
|
||||
conn->ifc_peer_hq_settings.header_table_size = value;
|
||||
|
@ -6620,7 +6534,7 @@ static void
|
|||
on_goaway_server (void *ctx, uint64_t stream_id)
|
||||
{
|
||||
struct ietf_full_conn *const conn = ctx;
|
||||
ABORT_QUIETLY(1, HEC_UNEXPECTED_FRAME,
|
||||
ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED,
|
||||
"client should not send GOAWAY frames");
|
||||
}
|
||||
|
||||
|
@ -6636,7 +6550,7 @@ on_goaway (void *ctx, uint64_t stream_id)
|
|||
sit = stream_id & SIT_MASK;
|
||||
if (sit != SIT_BIDI_CLIENT)
|
||||
{
|
||||
ABORT_QUIETLY(1, HEC_MALFORMED_FRAME,
|
||||
ABORT_QUIETLY(1, HEC_ID_ERROR,
|
||||
"stream ID %"PRIu64" in GOAWAY frame", stream_id);
|
||||
return;
|
||||
}
|
||||
|
@ -6669,14 +6583,13 @@ static void
|
|||
on_unexpected_frame (void *ctx, uint64_t frame_type)
|
||||
{
|
||||
struct ietf_full_conn *const conn = ctx;
|
||||
ABORT_QUIETLY(1, HEC_WRONG_STREAM, "Frame type %"PRIu64" is not allowed "
|
||||
"on the control stream", frame_type);
|
||||
ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED, "Frame type %"PRIu64" is not "
|
||||
"allowed on the control stream", frame_type);
|
||||
}
|
||||
|
||||
|
||||
static const struct hcsi_callbacks hcsi_callbacks_server =
|
||||
{
|
||||
.on_priority = on_priority_server,
|
||||
.on_cancel_push = on_cancel_push,
|
||||
.on_max_push_id = on_max_push_id,
|
||||
.on_settings_frame = on_settings_frame,
|
||||
|
@ -6687,7 +6600,6 @@ static const struct hcsi_callbacks hcsi_callbacks_server =
|
|||
|
||||
static const struct hcsi_callbacks hcsi_callbacks =
|
||||
{
|
||||
.on_priority = on_priority,
|
||||
.on_cancel_push = on_cancel_push,
|
||||
.on_max_push_id = on_max_push_id_client,
|
||||
.on_settings_frame = on_settings_frame,
|
||||
|
@ -6815,8 +6727,10 @@ apply_uni_stream_class (struct ietf_full_conn *conn,
|
|||
}
|
||||
else
|
||||
{
|
||||
ABORT_WARN("Incoming QPACK encoder stream already exists");
|
||||
/* TODO: special error code? */
|
||||
ABORT_QUIETLY(1, HEC_STREAM_CREATION_ERROR,
|
||||
"Incoming QPACK encoder stream %"PRIu64" already exists: "
|
||||
"cannot create second stream %"PRIu64,
|
||||
conn->ifc_qdh.qdh_enc_sm_in->id, stream->id);
|
||||
lsquic_stream_close(stream);
|
||||
}
|
||||
break;
|
||||
|
@ -6830,8 +6744,10 @@ apply_uni_stream_class (struct ietf_full_conn *conn,
|
|||
}
|
||||
else
|
||||
{
|
||||
ABORT_WARN("Incoming QPACK decoder stream already exists");
|
||||
/* TODO: special error code? */
|
||||
ABORT_QUIETLY(1, HEC_STREAM_CREATION_ERROR,
|
||||
"Incoming QPACK decoder stream %"PRIu64" already exists: "
|
||||
"cannot create second stream %"PRIu64,
|
||||
conn->ifc_qeh.qeh_dec_sm_in->id, stream->id);
|
||||
lsquic_stream_close(stream);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -1,854 +0,0 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "lsquic_types.h"
|
||||
#include "lsquic_int_types.h"
|
||||
#include "lsquic_sfcw.h"
|
||||
#include "lsquic_varint.h"
|
||||
#include "lsquic_hq.h"
|
||||
#include "lsquic_hash.h"
|
||||
#include "lsquic_stream.h"
|
||||
#include "lsquic_h3_prio.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_PRIO
|
||||
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(tree->h3pt_conn)
|
||||
#include "lsquic_logger.h"
|
||||
|
||||
/* The tree supports up to 2^16 - 1 elements, which should suffice.
|
||||
* Zero is not a valid value (thus -1 elements).
|
||||
*/
|
||||
typedef unsigned short elem_idx_t;
|
||||
|
||||
/* Because stream IDs are 62-bit integers and we the HTTP/3 element type
|
||||
* only has four values (enum h3_elem_type), we can combine the two into
|
||||
* a single value. h3_id_t's lower 62 bits contain the ID, while the
|
||||
* high 2 bits contain element type. This makes searching faster.
|
||||
*/
|
||||
typedef uint64_t h3_id_t;
|
||||
|
||||
typedef unsigned char active_mark_t;
|
||||
|
||||
#define H3_EL_ID(type, id) ((((uint64_t) (type)) << 62) | id)
|
||||
|
||||
#define ROOT_IDX 1
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
struct h3_prio_elem
|
||||
{
|
||||
h3_id_t h3pe_id;
|
||||
struct lsquic_stream *h3pe_stream;
|
||||
|
||||
/* Time at which stream was closed: */
|
||||
lsquic_time_t h3pe_closed_at;
|
||||
|
||||
/* Tree neighbors: */
|
||||
elem_idx_t h3pe_parent,
|
||||
h3pe_first_child,
|
||||
h3pe_left,
|
||||
h3pe_right;
|
||||
|
||||
/* Closed streams are kept on a separate queue for efficient pruning: */
|
||||
elem_idx_t h3pe_next_closed;
|
||||
|
||||
/* Used as tiebreaker between elements with the same weight: streams
|
||||
* added earlier to the iterator have higher priority.
|
||||
*/
|
||||
elem_idx_t h3pe_iter_order;
|
||||
#define h3pe_taken h3pe_iter_order
|
||||
|
||||
enum {
|
||||
H3PE_FLAG_CLOSED = 1 << 0,
|
||||
} h3pe_flags:8;
|
||||
h3_weight_t h3pe_weight;
|
||||
|
||||
/* These marks are part of the iterator state */
|
||||
active_mark_t h3pe_active_self;
|
||||
active_mark_t h3pe_active_path;
|
||||
};
|
||||
|
||||
#define EL_TYPE(el) ((enum h3_elem_type)((el)->h3pe_id >> 62))
|
||||
#define EL_ID(el) ((uint64_t)((el)->h3pe_id & ((1ull << 62) - 1)))
|
||||
|
||||
#define CALC_EL_IDX(tree, el) (+((el) - (tree)->h3pt_els))
|
||||
|
||||
/* The weight and the iterator order are combined into a single value to
|
||||
* reduce the number of branches.
|
||||
*/
|
||||
typedef uint32_t iter_prio_t;
|
||||
#define EL_ITER_PRIO(el) (((uint32_t) ((el)->h3pe_weight << 16)) | \
|
||||
(el)->h3pe_iter_order)
|
||||
#define MAX_ITER_PRIO ((1u << 24) - 1)
|
||||
|
||||
#define MAX_CRIT_STREAMS (4 /* crypto streams */ \
|
||||
+ 3 /* outgoing control, encoder, and decoder */ \
|
||||
+ 3 /* incoming control, encoder, and decoder */)
|
||||
|
||||
struct h3_iter
|
||||
{
|
||||
const char *h3it_log_id;
|
||||
elem_idx_t h3it_cursor;
|
||||
elem_idx_t h3it_count;
|
||||
active_mark_t h3it_active;
|
||||
|
||||
/* Critical streams do not participate in the regular HTTP/3 priority
|
||||
* mechanism. They have an implicit priority which is higher than
|
||||
* that of the regular request or push streams. The iterator holds
|
||||
* references to them only for the duration of the iteration.
|
||||
*/
|
||||
unsigned h3it_crit_off;
|
||||
unsigned h3it_crit_count;
|
||||
struct lsquic_stream *h3it_crit_streams[MAX_CRIT_STREAMS];
|
||||
};
|
||||
|
||||
struct h3_prio_tree
|
||||
{
|
||||
const struct lsquic_conn *h3pt_conn; /* Used for logging */
|
||||
|
||||
/* Element 0 does not contain a valid value. Its only use is to store
|
||||
* the linear search sentinel.
|
||||
*/
|
||||
struct h3_prio_elem *h3pt_els;
|
||||
|
||||
struct h3_iter h3pt_iter;
|
||||
|
||||
unsigned h3pt_nalloc; /* Including element 0 */
|
||||
unsigned h3pt_nelem; /* Including element 0 */
|
||||
unsigned h3pt_max_ph; /* Maximum placeholder ID */
|
||||
|
||||
/* STAILQ analog: first element is the oldest, newly closed stream
|
||||
* elements are inserted at the end.
|
||||
*/
|
||||
elem_idx_t h3pt_closed_first,
|
||||
h3pt_closed_last;
|
||||
};
|
||||
|
||||
|
||||
struct h3_prio_tree *
|
||||
lsquic_prio_tree_new (const struct lsquic_conn *conn, unsigned n_placeholders)
|
||||
{
|
||||
struct h3_prio_tree *tree;
|
||||
struct h3_prio_elem *els;
|
||||
unsigned nalloc, nelem;
|
||||
|
||||
tree = calloc(1, sizeof(*tree));
|
||||
if (!tree)
|
||||
return NULL;
|
||||
|
||||
nelem = 1 /* element 0 */ + 1 /* root */;
|
||||
nalloc = nelem + 4;
|
||||
els = malloc(nalloc * sizeof(els[0]));
|
||||
if (!els)
|
||||
{
|
||||
free(tree);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
els[ROOT_IDX] = (struct h3_prio_elem) { .h3pe_id = H3_EL_ID(H3ET_ROOT, 0) };
|
||||
|
||||
tree->h3pt_conn = conn;
|
||||
tree->h3pt_els = els;
|
||||
tree->h3pt_nalloc = nalloc;
|
||||
tree->h3pt_nelem = nelem;
|
||||
tree->h3pt_max_ph = n_placeholders;
|
||||
LSQ_DEBUG("create tree with maximum %u placeholders", n_placeholders);
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
static struct h3_prio_elem *
|
||||
prio_tree_find_by_h3_id (struct h3_prio_tree *tree, h3_id_t h3_id)
|
||||
{
|
||||
struct h3_prio_elem *el;
|
||||
|
||||
tree->h3pt_els[0].h3pe_id = h3_id;
|
||||
for (el = &tree->h3pt_els[tree->h3pt_nelem - 1]; el->h3pe_id != h3_id; --el)
|
||||
;
|
||||
|
||||
if (el > tree->h3pt_els)
|
||||
return el;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static struct h3_prio_elem *
|
||||
prio_tree_find (struct h3_prio_tree *tree, enum h3_elem_type type, uint64_t id)
|
||||
{
|
||||
if (type == H3ET_ROOT)
|
||||
return &tree->h3pt_els[ROOT_IDX];
|
||||
else
|
||||
return prio_tree_find_by_h3_id(tree, H3_EL_ID(type, id));
|
||||
}
|
||||
|
||||
|
||||
static struct h3_prio_elem *
|
||||
prio_tree_alloc_elem (struct h3_prio_tree *tree)
|
||||
{
|
||||
struct h3_prio_elem *els;
|
||||
unsigned nalloc;
|
||||
|
||||
if (tree->h3pt_nalloc > tree->h3pt_nelem)
|
||||
return &tree->h3pt_els[ tree->h3pt_nelem++ ];
|
||||
|
||||
nalloc = MIN(H3_PRIO_MAX_ELEMS + 1, tree->h3pt_nalloc * 2);
|
||||
if (nalloc <= tree->h3pt_nelem)
|
||||
{
|
||||
LSQ_ERROR("number of elements reached maximum");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
els = realloc(tree->h3pt_els, nalloc * sizeof(tree->h3pt_els[0]));
|
||||
if (!els)
|
||||
{
|
||||
LSQ_WARN("memory allocation failure");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tree->h3pt_els = els;
|
||||
tree->h3pt_nalloc = nalloc;
|
||||
return &tree->h3pt_els[ tree->h3pt_nelem++ ];
|
||||
}
|
||||
|
||||
|
||||
static struct h3_prio_elem *
|
||||
prio_tree_create_elem (struct h3_prio_tree *tree, enum h3_elem_type type,
|
||||
uint64_t id)
|
||||
{
|
||||
struct h3_prio_elem *el, *root;
|
||||
|
||||
assert(type != H3ET_ROOT);
|
||||
if (type == H3ET_PLACEHOLDER && id >= tree->h3pt_max_ph)
|
||||
{
|
||||
LSQ_INFO("invalid placeholder id %"PRIu64" is invalid (maximum "
|
||||
"is %u placeholders", id, tree->h3pt_max_ph);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
el = prio_tree_alloc_elem(tree);
|
||||
if (el)
|
||||
{
|
||||
root = &tree->h3pt_els[ROOT_IDX];
|
||||
*el = (struct h3_prio_elem) { .h3pe_id = H3_EL_ID(type, id),
|
||||
.h3pe_parent = ROOT_IDX,
|
||||
.h3pe_right = root->h3pe_first_child,
|
||||
.h3pe_weight = H3_DEFAULT_WEIGHT, };
|
||||
if (root->h3pe_first_child)
|
||||
tree->h3pt_els[ root->h3pe_first_child ].h3pe_left
|
||||
= el - tree->h3pt_els;
|
||||
root->h3pe_first_child = el - tree->h3pt_els;
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
prio_tree_reparent (struct h3_prio_tree *tree,
|
||||
struct h3_prio_elem *const child,
|
||||
struct h3_prio_elem *const parent)
|
||||
{
|
||||
struct h3_prio_elem *orig_parent;
|
||||
elem_idx_t child_idx;
|
||||
|
||||
child_idx = CALC_EL_IDX(tree, child);
|
||||
orig_parent = &tree->h3pt_els[child->h3pe_parent];
|
||||
|
||||
if (orig_parent->h3pe_first_child == child_idx)
|
||||
orig_parent->h3pe_first_child = child->h3pe_right;
|
||||
else
|
||||
tree->h3pt_els[child->h3pe_left].h3pe_right = child->h3pe_right;
|
||||
if (child->h3pe_right)
|
||||
tree->h3pt_els[child->h3pe_right].h3pe_left = child->h3pe_left;
|
||||
|
||||
child->h3pe_left = 0;
|
||||
child->h3pe_right = parent->h3pe_first_child;
|
||||
if (child->h3pe_right)
|
||||
tree->h3pt_els[child->h3pe_right].h3pe_left = child_idx;
|
||||
parent->h3pe_first_child = child_idx;
|
||||
child->h3pe_parent = CALC_EL_IDX(tree, parent);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
prio_tree_is_parent (struct h3_prio_tree *tree,
|
||||
const struct h3_prio_elem *parent, const struct h3_prio_elem *child)
|
||||
{
|
||||
elem_idx_t idx;
|
||||
|
||||
assert(parent != child);
|
||||
tree->h3pt_els[0].h3pe_id = parent->h3pe_id;
|
||||
idx = child->h3pe_parent;
|
||||
while (tree->h3pt_els[idx].h3pe_id != parent->h3pe_id)
|
||||
idx = tree->h3pt_els[idx].h3pe_parent;
|
||||
return idx > 0;
|
||||
}
|
||||
|
||||
|
||||
static const char el_type2char[] =
|
||||
{
|
||||
[H3ET_ROOT] = 'R',
|
||||
[H3ET_REQ_STREAM] = 'Q',
|
||||
[H3ET_PUSH_STREAM] = 'P',
|
||||
[H3ET_PLACEHOLDER] = 'H',
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
lsquic_prio_tree_set_rel (struct h3_prio_tree *tree,
|
||||
enum h3_elem_type child_type, uint64_t child_id, h3_weight_t child_weight,
|
||||
enum h3_elem_type parent_type, uint64_t parent_id)
|
||||
{
|
||||
struct h3_prio_elem *parent, *child;
|
||||
|
||||
parent = prio_tree_find(tree, parent_type, parent_id);
|
||||
if (!parent)
|
||||
{
|
||||
parent = prio_tree_create_elem(tree, parent_type, parent_id);
|
||||
if (!parent)
|
||||
return -1;
|
||||
}
|
||||
const elem_idx_t parent_idx = CALC_EL_IDX(tree, parent);
|
||||
|
||||
child = prio_tree_find(tree, child_type, child_id);
|
||||
if (!child)
|
||||
{
|
||||
child = prio_tree_create_elem(tree, child_type, child_id);
|
||||
if (!child)
|
||||
return -1;
|
||||
/* create() above may have realloced */
|
||||
parent = &tree->h3pt_els[ parent_idx ];
|
||||
}
|
||||
|
||||
if (child->h3pe_parent != parent_idx)
|
||||
{
|
||||
if (prio_tree_is_parent(tree, child, parent))
|
||||
prio_tree_reparent(tree, parent,
|
||||
&tree->h3pt_els[child->h3pe_parent]);
|
||||
prio_tree_reparent(tree, child, parent);
|
||||
}
|
||||
else if (child == parent)
|
||||
return -1; /* This is unlikely, so check for it last */
|
||||
|
||||
child->h3pe_weight = child_weight;
|
||||
|
||||
LSQ_DEBUG("add rel to %c:%"PRIu64" -> %c:%"PRIu64" with w=%u",
|
||||
el_type2char[ child_type ], child_id,
|
||||
el_type2char[ parent_type ], parent_id, child_weight);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Assume that unidirectional streams are push streams */
|
||||
static enum h3_elem_type
|
||||
stream_id_2_elem_type (lsquic_stream_id_t stream_id)
|
||||
{
|
||||
enum stream_dir dir;
|
||||
|
||||
dir = 1 & (stream_id >> SD_SHIFT);
|
||||
if (dir == SD_BIDI)
|
||||
return H3ET_REQ_STREAM;
|
||||
else
|
||||
return H3ET_PUSH_STREAM;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lsquic_prio_tree_add_stream (struct h3_prio_tree *tree,
|
||||
struct lsquic_stream *stream, enum h3_elem_type parent_type,
|
||||
uint64_t parent_id, h3_weight_t weight)
|
||||
{
|
||||
struct h3_prio_elem *parent, *child;
|
||||
enum h3_elem_type type;
|
||||
elem_idx_t child_idx;
|
||||
|
||||
assert(!lsquic_stream_is_critical(stream));
|
||||
|
||||
type = stream_id_2_elem_type(stream->id);
|
||||
child = prio_tree_find(tree, type, stream->id);
|
||||
if (child)
|
||||
{
|
||||
/* Prioritization information already exists: set the pointer
|
||||
* and ignore PRIORITY frame information on the request stream.
|
||||
*/
|
||||
if (!child->h3pe_stream)
|
||||
{
|
||||
child_idx = CALC_EL_IDX(tree, child);
|
||||
LSQ_DEBUG("reference stream %c:%"PRIu64" in prio element",
|
||||
el_type2char[ type ], stream->id);
|
||||
goto link;
|
||||
}
|
||||
LSQ_WARN("stream %"PRIu64" is already referenced", stream->id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
child = prio_tree_create_elem(tree, type, stream->id);
|
||||
if (!child)
|
||||
return -1;
|
||||
child_idx = CALC_EL_IDX(tree, child);
|
||||
|
||||
parent = prio_tree_find(tree, parent_type, parent_id);
|
||||
if (!parent)
|
||||
{
|
||||
parent = prio_tree_create_elem(tree, parent_type, parent_id);
|
||||
if (!parent)
|
||||
return -1;
|
||||
/* create() above may have realloced */
|
||||
child = &tree->h3pt_els[ child_idx ];
|
||||
}
|
||||
|
||||
prio_tree_reparent(tree, child, parent);
|
||||
child->h3pe_weight = weight;
|
||||
LSQ_DEBUG("add stream %c:%"PRIu64" -> %c:%"PRIu64" with w=%u",
|
||||
el_type2char[ type ], stream->id,
|
||||
el_type2char[ parent_type ], parent_id, weight);
|
||||
link:
|
||||
child->h3pe_stream = stream;
|
||||
stream->sm_h3_prio_idx = child_idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lsquic_prio_tree_remove_stream (struct h3_prio_tree *tree,
|
||||
struct lsquic_stream *stream, lsquic_time_t now)
|
||||
{
|
||||
struct h3_prio_elem *el;
|
||||
|
||||
el = &tree->h3pt_els[ stream->sm_h3_prio_idx ];
|
||||
|
||||
if (stream->sm_h3_prio_idx > 0
|
||||
&& stream->sm_h3_prio_idx < tree->h3pt_nelem
|
||||
&& el->h3pe_stream == stream
|
||||
&& !(el->h3pe_flags & H3PE_FLAG_CLOSED))
|
||||
{
|
||||
assert(el->h3pe_stream == stream);
|
||||
if (tree->h3pt_closed_first)
|
||||
{
|
||||
tree->h3pt_els[ tree->h3pt_closed_last ].h3pe_next_closed
|
||||
= stream->sm_h3_prio_idx;
|
||||
tree->h3pt_closed_last = stream->sm_h3_prio_idx;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree->h3pt_closed_first = stream->sm_h3_prio_idx;
|
||||
tree->h3pt_closed_last = stream->sm_h3_prio_idx;
|
||||
}
|
||||
el->h3pe_stream = NULL;
|
||||
el->h3pe_closed_at = now;
|
||||
el->h3pe_flags |= H3PE_FLAG_CLOSED;
|
||||
stream->sm_h3_prio_idx = 0;
|
||||
LSQ_DEBUG("removed reference to stream %"PRIu64, stream->id);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LSQ_WARN("cannot remove stream %"PRIu64, stream->id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lsquic_prio_tree_drop_el (struct h3_prio_tree *tree, struct h3_prio_elem *el)
|
||||
{
|
||||
const elem_idx_t
|
||||
right = el->h3pe_right,
|
||||
left = el->h3pe_left,
|
||||
parent = el->h3pe_parent,
|
||||
el_idx = CALC_EL_IDX(tree, el);
|
||||
elem_idx_t idx, last;
|
||||
|
||||
/* Update links around the element: */
|
||||
idx = el->h3pe_first_child;
|
||||
if (idx == 0)
|
||||
{
|
||||
if (left)
|
||||
tree->h3pt_els[ left ].h3pe_right = right;
|
||||
else
|
||||
tree->h3pt_els[ parent ].h3pe_first_child = right;
|
||||
|
||||
if (right)
|
||||
tree->h3pt_els[ right ].h3pe_left = left;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (left)
|
||||
{
|
||||
tree->h3pt_els[ left ].h3pe_right = idx;
|
||||
tree->h3pt_els[ idx ].h3pe_left = left;
|
||||
}
|
||||
else
|
||||
tree->h3pt_els[ parent ].h3pe_first_child = idx;
|
||||
|
||||
do
|
||||
{
|
||||
last = idx;
|
||||
tree->h3pt_els[ idx ].h3pe_parent = parent;
|
||||
idx = tree->h3pt_els[ idx ].h3pe_right;
|
||||
}
|
||||
while (idx);
|
||||
|
||||
if (right)
|
||||
{
|
||||
tree->h3pt_els[ right ].h3pe_left = last;
|
||||
tree->h3pt_els[ last ].h3pe_right = right;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move last element into its spot */
|
||||
if (--tree->h3pt_nelem > el_idx)
|
||||
{
|
||||
el = &tree->h3pt_els[ el_idx ];
|
||||
*el = tree->h3pt_els[ tree->h3pt_nelem ];
|
||||
for (idx = el->h3pe_first_child; idx;
|
||||
idx = tree->h3pt_els[ idx ].h3pe_right)
|
||||
tree->h3pt_els[ idx ].h3pe_parent = el_idx;
|
||||
if (el->h3pe_left)
|
||||
tree->h3pt_els[ el->h3pe_left ].h3pe_right = el_idx;
|
||||
if (el->h3pe_right)
|
||||
tree->h3pt_els[ el->h3pe_right ].h3pe_left = el_idx;
|
||||
if (tree->h3pt_els[ el->h3pe_parent ].h3pe_first_child
|
||||
== tree->h3pt_nelem)
|
||||
tree->h3pt_els[ el->h3pe_parent ].h3pe_first_child = el_idx;
|
||||
if (el->h3pe_stream)
|
||||
el->h3pe_stream->sm_h3_prio_idx = el_idx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lsquic_prio_tree_prune (struct h3_prio_tree *tree, lsquic_time_t cutoff)
|
||||
{
|
||||
struct h3_prio_elem *el;
|
||||
unsigned count = 0;
|
||||
|
||||
while (tree->h3pt_closed_first
|
||||
&& tree->h3pt_els[ tree->h3pt_closed_first ].h3pe_closed_at < cutoff)
|
||||
{
|
||||
el = &tree->h3pt_els[ tree->h3pt_closed_first ];
|
||||
tree->h3pt_closed_first = el->h3pe_next_closed;
|
||||
if (tree->h3pt_closed_first == 0)
|
||||
tree->h3pt_closed_last = 0;
|
||||
++count;
|
||||
lsquic_prio_tree_drop_el(tree, el);
|
||||
}
|
||||
|
||||
LSQ_DEBUG("pruned %u element%.*s from the tree", count, count != 1, "s");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lsquic_prio_tree_destroy (struct h3_prio_tree *tree)
|
||||
{
|
||||
LSQ_DEBUG("destroyed");
|
||||
free(tree->h3pt_els);
|
||||
free(tree);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lsquic_prio_tree_iter_reset (struct h3_prio_tree *tree, const char *log_id)
|
||||
{
|
||||
struct h3_iter *const iter = &tree->h3pt_iter;
|
||||
unsigned i;
|
||||
|
||||
iter->h3it_log_id = log_id;
|
||||
iter->h3it_count = 0;
|
||||
iter->h3it_crit_count = 0;
|
||||
iter->h3it_crit_off = 0;
|
||||
iter->h3it_cursor = ROOT_IDX;
|
||||
iter->h3it_active++;
|
||||
if (0 == iter->h3it_active)
|
||||
{
|
||||
for (i = 0; i < tree->h3pt_nelem; ++i)
|
||||
{
|
||||
tree->h3pt_els[i].h3pe_active_self = 0;
|
||||
tree->h3pt_els[i].h3pe_active_path = 0;
|
||||
}
|
||||
iter->h3it_active++;
|
||||
}
|
||||
|
||||
LSQ_DEBUG("reset iterator; log id: `%s'; active mark: %u", log_id,
|
||||
iter->h3it_active);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
prio_tree_iter_add_critical (struct h3_prio_tree *tree,
|
||||
struct lsquic_stream *stream)
|
||||
{
|
||||
struct h3_iter *const iter = &tree->h3pt_iter;
|
||||
|
||||
if (iter->h3it_crit_count < sizeof(iter->h3it_crit_streams)
|
||||
/ sizeof(iter->h3it_crit_streams[0]))
|
||||
{
|
||||
iter->h3it_crit_streams[ iter->h3it_crit_count++ ] = stream;
|
||||
LSQ_DEBUG("%s: add critical stream %"PRIu64" at position %u",
|
||||
iter->h3it_log_id, stream->id, iter->h3it_crit_count - 1);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LSQ_WARN("could not add critical stream %"PRIu64" to the iterator: "
|
||||
"no room", stream->id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
prio_tree_iter_add_regular (struct h3_prio_tree *tree,
|
||||
struct lsquic_stream *stream)
|
||||
{
|
||||
struct h3_iter *const iter = &tree->h3pt_iter;
|
||||
struct h3_prio_elem *el;
|
||||
|
||||
if (stream->sm_h3_prio_idx > 0
|
||||
&& stream->sm_h3_prio_idx < tree->h3pt_nelem)
|
||||
{
|
||||
el = &tree->h3pt_els[stream->sm_h3_prio_idx];
|
||||
assert(el->h3pe_stream == stream);
|
||||
el->h3pe_active_self = iter->h3it_active;
|
||||
el->h3pe_iter_order = iter->h3it_count++;
|
||||
while (el->h3pe_parent)
|
||||
{
|
||||
el = &tree->h3pt_els[ el->h3pe_parent ];
|
||||
el->h3pe_active_path = iter->h3it_active;
|
||||
}
|
||||
LSQ_DEBUG("%s: added stream %"PRIu64" to the iterator",
|
||||
iter->h3it_log_id, stream->id);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LSQ_WARN("%s: stream %"PRIu64" has invalid priority index value: %u",
|
||||
iter->h3it_log_id, stream->id, stream->sm_h3_prio_idx);
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lsquic_prio_tree_iter_add (struct h3_prio_tree *tree,
|
||||
struct lsquic_stream *stream)
|
||||
{
|
||||
if (lsquic_stream_is_critical(stream))
|
||||
return prio_tree_iter_add_critical(tree, stream);
|
||||
else
|
||||
return prio_tree_iter_add_regular(tree, stream);
|
||||
}
|
||||
|
||||
|
||||
struct lsquic_stream *
|
||||
lsquic_prio_tree_iter_next (struct h3_prio_tree *tree)
|
||||
{
|
||||
struct h3_iter *const iter = &tree->h3pt_iter;
|
||||
struct h3_prio_elem *el_self, *el_path;
|
||||
iter_prio_t prio_self, prio_path;
|
||||
elem_idx_t idx;
|
||||
|
||||
if (iter->h3it_crit_off < iter->h3it_crit_count)
|
||||
{
|
||||
LSQ_DEBUG("%s: return critical stream %"PRIu64" at position %u",
|
||||
iter->h3it_log_id, iter->h3it_crit_streams[iter->h3it_crit_off]->id,
|
||||
iter->h3it_crit_off);
|
||||
return iter->h3it_crit_streams[ iter->h3it_crit_off++ ];
|
||||
}
|
||||
|
||||
top0:
|
||||
if (!iter->h3it_cursor)
|
||||
{
|
||||
LSQ_DEBUG("%s: out of streams", iter->h3it_log_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
top1:
|
||||
el_self = NULL, el_path = NULL;
|
||||
prio_self = MAX_ITER_PRIO + 1;
|
||||
prio_path = MAX_ITER_PRIO + 1;
|
||||
for (idx = tree->h3pt_els[ iter->h3it_cursor ].h3pe_first_child;
|
||||
idx;
|
||||
idx = tree->h3pt_els[ idx ].h3pe_right)
|
||||
{
|
||||
if (tree->h3pt_els[ idx ].h3pe_active_self == iter->h3it_active
|
||||
&& EL_ITER_PRIO(&tree->h3pt_els[ idx ]) < prio_self)
|
||||
{
|
||||
el_self = &tree->h3pt_els[ idx ];
|
||||
prio_self = EL_ITER_PRIO(el_self);
|
||||
}
|
||||
if (tree->h3pt_els[ idx ].h3pe_active_path == iter->h3it_active
|
||||
&& EL_ITER_PRIO(&tree->h3pt_els[ idx ]) < prio_path)
|
||||
{
|
||||
el_path = &tree->h3pt_els[ idx ];
|
||||
prio_path = EL_ITER_PRIO(el_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (el_self)
|
||||
{
|
||||
el_self->h3pe_active_self = 0;
|
||||
LSQ_DEBUG("%s: return %c stream %"PRIu64, iter->h3it_log_id,
|
||||
el_type2char[ EL_TYPE(el_self) ], EL_ID(el_self));
|
||||
return el_self->h3pe_stream;
|
||||
}
|
||||
else if (el_path)
|
||||
{
|
||||
iter->h3it_cursor = CALC_EL_IDX(tree, el_path);
|
||||
LSQ_DEBUG("%s: step down to %c:%"PRIu64, iter->h3it_log_id,
|
||||
el_type2char[ EL_TYPE(el_path) ], EL_ID(el_path));
|
||||
goto top1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree->h3pt_els[ iter->h3it_cursor ].h3pe_active_path = 0;
|
||||
iter->h3it_cursor = tree->h3pt_els[ iter->h3it_cursor ].h3pe_parent;
|
||||
LSQ_DEBUG("%s: step up to %c:%"PRIu64, iter->h3it_log_id,
|
||||
el_type2char[ EL_TYPE(&tree->h3pt_els[ iter->h3it_cursor ]) ],
|
||||
EL_ID(&tree->h3pt_els[ iter->h3it_cursor ]));
|
||||
goto top0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct lsquic_stream *
|
||||
lsquic_prio_tree_highest_non_crit (struct h3_prio_tree *tree)
|
||||
{
|
||||
elem_idx_t idx, parent;
|
||||
struct h3_prio_elem *el;
|
||||
unsigned weight;
|
||||
|
||||
parent = ROOT_IDX;
|
||||
|
||||
new_level:
|
||||
/* Look for the stream */
|
||||
weight = 1u << sizeof(h3_weight_t) * 8;
|
||||
el = NULL;
|
||||
for (idx = tree->h3pt_els[ parent ].h3pe_first_child;
|
||||
idx;
|
||||
idx = tree->h3pt_els[ idx ].h3pe_right)
|
||||
if (tree->h3pt_els[ idx ].h3pe_stream
|
||||
&& tree->h3pt_els[ idx ].h3pe_weight < weight)
|
||||
{
|
||||
el = &tree->h3pt_els[ idx ];
|
||||
weight = el->h3pe_weight;
|
||||
}
|
||||
else
|
||||
/* Clear new level of crumbs */
|
||||
tree->h3pt_els[ idx ].h3pe_taken = 0;
|
||||
|
||||
if (el)
|
||||
return el->h3pe_stream;
|
||||
|
||||
old_level:
|
||||
/* Look for paths not taken */
|
||||
weight = 1u << sizeof(h3_weight_t) * 8;
|
||||
el = NULL;
|
||||
for (idx = tree->h3pt_els[ parent ].h3pe_first_child;
|
||||
idx;
|
||||
idx = tree->h3pt_els[ idx ].h3pe_right)
|
||||
if (tree->h3pt_els[ idx ].h3pe_first_child
|
||||
&& !tree->h3pt_els[ idx ].h3pe_taken
|
||||
&& tree->h3pt_els[ idx ].h3pe_weight < weight)
|
||||
{
|
||||
el = &tree->h3pt_els[ idx ];
|
||||
weight = el->h3pe_weight;
|
||||
}
|
||||
|
||||
if (el)
|
||||
{
|
||||
parent = CALC_EL_IDX(tree, el);
|
||||
goto new_level;
|
||||
}
|
||||
|
||||
tree->h3pt_els[ parent ].h3pe_taken = 1;
|
||||
parent = tree->h3pt_els[ parent ].h3pe_parent;
|
||||
if (parent)
|
||||
goto old_level;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
prio_tree_node_to_str (const struct h3_prio_tree *tree,
|
||||
const struct h3_prio_elem *el, char *const buf, char *const end)
|
||||
{
|
||||
elem_idx_t next_idx;
|
||||
char *p;
|
||||
int sz, comma;
|
||||
|
||||
if (buf >= end)
|
||||
return 0;
|
||||
|
||||
p = buf;
|
||||
sz = snprintf(p, end - p, "(t: %c; id: %"PRIu64"; w: %u",
|
||||
el_type2char[EL_TYPE(el)], EL_ID(el), el->h3pe_weight);
|
||||
if (sz > end - p)
|
||||
return end - buf;
|
||||
p += sz;
|
||||
|
||||
if (el->h3pe_first_child)
|
||||
{
|
||||
sz = snprintf(p, end - p, "; c: [");
|
||||
if (sz > end - p)
|
||||
return end - buf;
|
||||
p += sz;
|
||||
next_idx = el->h3pe_first_child;
|
||||
comma = 0;
|
||||
do
|
||||
{
|
||||
if (comma)
|
||||
{
|
||||
sz = snprintf(p, end - p, ",");
|
||||
if (sz > end - p)
|
||||
return end - buf;
|
||||
p += sz;
|
||||
}
|
||||
else
|
||||
++comma;
|
||||
el = &tree->h3pt_els[ next_idx ];
|
||||
p += prio_tree_node_to_str(tree, el, p, end);
|
||||
if (p >= end)
|
||||
return end - buf;
|
||||
}
|
||||
while ((next_idx = el->h3pe_right));
|
||||
sz = snprintf(p, end - p, "])");
|
||||
if (sz > end - p)
|
||||
return end - buf;
|
||||
p += sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
sz = snprintf(p, end - p, ")");
|
||||
if (sz > end - p)
|
||||
return end - buf;
|
||||
p += sz;
|
||||
}
|
||||
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
lsquic_prio_tree_to_str (const struct h3_prio_tree *tree, char *buf,
|
||||
size_t buf_sz)
|
||||
{
|
||||
return prio_tree_node_to_str(tree, &tree->h3pt_els[ROOT_IDX], buf,
|
||||
buf + buf_sz);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lsquic_prio_tree_set_ph (struct h3_prio_tree *tree, unsigned ph)
|
||||
{
|
||||
LSQ_DEBUG("set max placeholders to %u", ph);
|
||||
tree->h3pt_max_ph = ph;
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#ifndef LSQUIC_H3_PRIO_H
|
||||
#define LSQUIC_H3_PRIO_H 1
|
||||
|
||||
#define H3_PRIO_MAX_ELEMS ((1 << 16) - 1)
|
||||
|
||||
struct h3_prio_tree;
|
||||
struct lsquic_conn;
|
||||
struct lsquic_stream;
|
||||
|
||||
/* Same deal as with GQUIC priorities: lower value means higher priority */
|
||||
typedef uint8_t h3_weight_t;
|
||||
|
||||
/* This corresponds to 16: */
|
||||
#define H3_DEFAULT_WEIGHT 240
|
||||
|
||||
enum h3_elem_type
|
||||
{
|
||||
H3ET_ROOT,
|
||||
H3ET_REQ_STREAM,
|
||||
H3ET_PUSH_STREAM,
|
||||
H3ET_PLACEHOLDER,
|
||||
};
|
||||
|
||||
struct h3_prio_tree *
|
||||
lsquic_prio_tree_new (const struct lsquic_conn *, unsigned);
|
||||
|
||||
void
|
||||
lsquic_prio_tree_set_ph (struct h3_prio_tree *, unsigned);
|
||||
|
||||
void
|
||||
lsquic_prio_tree_destroy (struct h3_prio_tree *);
|
||||
|
||||
/* Call for PRIORITY frames arriving on request stream */
|
||||
int
|
||||
lsquic_prio_tree_add_stream (struct h3_prio_tree *, struct lsquic_stream *,
|
||||
enum h3_elem_type parent_type, uint64_t parent_id, h3_weight_t);
|
||||
|
||||
/* Call for PRIORITY frames on the control stream */
|
||||
int
|
||||
lsquic_prio_tree_set_rel (struct h3_prio_tree *,
|
||||
enum h3_elem_type child_type, uint64_t child_id, h3_weight_t child_weight,
|
||||
enum h3_elem_type parent_type, uint64_t parent_id);
|
||||
|
||||
int
|
||||
lsquic_prio_tree_remove_stream (struct h3_prio_tree *, struct lsquic_stream *,
|
||||
lsquic_time_t now);
|
||||
|
||||
void
|
||||
lsquic_prio_tree_prune (struct h3_prio_tree *, lsquic_time_t cutoff);
|
||||
|
||||
/* To begin to use the iterator, reset it first */
|
||||
void
|
||||
lsquic_prio_tree_iter_reset (struct h3_prio_tree *tree, const char *);
|
||||
|
||||
/* Then, add one or more stream objects */
|
||||
int
|
||||
lsquic_prio_tree_iter_add (struct h3_prio_tree *tree, struct lsquic_stream *);
|
||||
|
||||
struct lsquic_stream *
|
||||
lsquic_prio_tree_highest_non_crit (struct h3_prio_tree *);
|
||||
|
||||
/* Then, call next() until NULL is returned. It is OK to abandon the iterator
|
||||
* at any time.
|
||||
*/
|
||||
struct lsquic_stream *
|
||||
lsquic_prio_tree_iter_next (struct h3_prio_tree *tree);
|
||||
|
||||
size_t
|
||||
lsquic_prio_tree_to_str (const struct h3_prio_tree *tree, char *, size_t);
|
||||
|
||||
#endif
|
|
@ -25,13 +25,16 @@ struct lsquic_hash
|
|||
struct hels_head *qh_buckets,
|
||||
qh_all;
|
||||
struct lsquic_hash_elem *qh_iter_next;
|
||||
int (*qh_cmp)(const void *, const void *, size_t);
|
||||
unsigned (*qh_hash)(const void *, size_t, unsigned seed);
|
||||
unsigned qh_count;
|
||||
unsigned qh_nbits;
|
||||
};
|
||||
|
||||
|
||||
struct lsquic_hash *
|
||||
lsquic_hash_create (void)
|
||||
lsquic_hash_create_ext (int (*cmp)(const void *, const void *, size_t),
|
||||
unsigned (*hashf)(const void *, size_t, unsigned seed))
|
||||
{
|
||||
struct hels_head *buckets;
|
||||
struct lsquic_hash *hash;
|
||||
|
@ -53,6 +56,8 @@ lsquic_hash_create (void)
|
|||
TAILQ_INIT(&buckets[i]);
|
||||
|
||||
TAILQ_INIT(&hash->qh_all);
|
||||
hash->qh_cmp = cmp;
|
||||
hash->qh_hash = hashf;
|
||||
hash->qh_buckets = buckets;
|
||||
hash->qh_nbits = nbits;
|
||||
hash->qh_iter_next = NULL;
|
||||
|
@ -61,6 +66,13 @@ lsquic_hash_create (void)
|
|||
}
|
||||
|
||||
|
||||
struct lsquic_hash *
|
||||
lsquic_hash_create (void)
|
||||
{
|
||||
return lsquic_hash_create_ext(memcmp, XXH32);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lsquic_hash_destroy (struct lsquic_hash *hash)
|
||||
{
|
||||
|
@ -116,7 +128,7 @@ lsquic_hash_insert (struct lsquic_hash *hash, const void *key,
|
|||
0 != lsquic_hash_grow(hash))
|
||||
return NULL;
|
||||
|
||||
hash_val = XXH64(key, key_sz, (uintptr_t) hash);
|
||||
hash_val = hash->qh_hash(key, key_sz, (uintptr_t) hash);
|
||||
buckno = BUCKNO(hash->qh_nbits, hash_val);
|
||||
TAILQ_INSERT_TAIL(&hash->qh_all, el, qhe_next_all);
|
||||
TAILQ_INSERT_TAIL(&hash->qh_buckets[buckno], el, qhe_next_bucket);
|
||||
|
@ -136,12 +148,12 @@ lsquic_hash_find (struct lsquic_hash *hash, const void *key, unsigned key_sz)
|
|||
unsigned buckno, hash_val;
|
||||
struct lsquic_hash_elem *el;
|
||||
|
||||
hash_val = XXH64(key, key_sz, (uintptr_t) hash);
|
||||
hash_val = hash->qh_hash(key, key_sz, (uintptr_t) hash);
|
||||
buckno = BUCKNO(hash->qh_nbits, hash_val);
|
||||
TAILQ_FOREACH(el, &hash->qh_buckets[buckno], qhe_next_bucket)
|
||||
if (hash_val == el->qhe_hash_val &&
|
||||
key_sz == el->qhe_key_len &&
|
||||
0 == memcmp(key, el->qhe_key_data, key_sz))
|
||||
0 == hash->qh_cmp(key, el->qhe_key_data, key_sz))
|
||||
{
|
||||
return el;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ struct lsquic_hash_elem
|
|||
struct lsquic_hash *
|
||||
lsquic_hash_create (void);
|
||||
|
||||
struct lsquic_hash *
|
||||
lsquic_hash_create_ext (int (*cmp)(const void *, const void *, size_t),
|
||||
unsigned (*hash)(const void *, size_t, unsigned seed));
|
||||
|
||||
void
|
||||
lsquic_hash_destroy (struct lsquic_hash *);
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
|||
const unsigned char *const end = p + bufsz;
|
||||
|
||||
const unsigned char *orig_p;
|
||||
enum h3_prio_frame_read_status prio_status;
|
||||
uint64_t len;
|
||||
int s;
|
||||
|
||||
|
@ -75,9 +74,6 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
|||
reader->hr_state = HR_READ_FRAME_BEGIN;
|
||||
}
|
||||
break;
|
||||
case HQFT_PRIORITY:
|
||||
reader->hr_state = HR_READ_PRIORITY_BEGIN;
|
||||
break;
|
||||
case HQFT_GOAWAY:
|
||||
reader->hr_state = HR_READ_VARINT;
|
||||
break;
|
||||
|
@ -118,7 +114,7 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
|||
if (reader->hr_nread != reader->hr_frame_length)
|
||||
{
|
||||
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
|
||||
HEC_MALFORMED_FRAME + reader->hr_frame_type,
|
||||
HEC_FRAME_ERROR,
|
||||
"Frame length does not match actual payload length");
|
||||
reader->hr_state = HR_ERROR;
|
||||
return -1;
|
||||
|
@ -166,8 +162,7 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
|||
if (reader->hr_nread > reader->hr_frame_length)
|
||||
{
|
||||
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
|
||||
HEC_MALFORMED_FRAME + HQFT_SETTINGS,
|
||||
"SETTING frame contents too long");
|
||||
HEC_FRAME_ERROR, "SETTING frame contents too long");
|
||||
reader->hr_state = HR_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
@ -184,31 +179,6 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
|
|||
else
|
||||
reader->hr_state = HR_READ_SETTING_BEGIN;
|
||||
break;
|
||||
case HR_READ_PRIORITY_BEGIN:
|
||||
reader->hr_u.prio.h3pfrs_state = 0;
|
||||
reader->hr_nread = 0;
|
||||
reader->hr_state = HR_READ_PRIORITY_CONTINUE;
|
||||
/* fall-through */
|
||||
case HR_READ_PRIORITY_CONTINUE:
|
||||
orig_p = p;
|
||||
prio_status = lsquic_h3_prio_frame_read(&p, end - p,
|
||||
&reader->hr_u.prio);
|
||||
reader->hr_nread += p - orig_p;
|
||||
if (prio_status == H3PFR_STATUS_DONE)
|
||||
{
|
||||
if (reader->hr_nread != reader->hr_frame_length)
|
||||
{
|
||||
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
|
||||
HEC_MALFORMED_FRAME + HQFT_PRIORITY, "PRIORITY frame "
|
||||
"contents size does not match frame length");
|
||||
reader->hr_state = HR_ERROR;
|
||||
return -1;
|
||||
}
|
||||
reader->hr_state = HR_READ_FRAME_BEGIN;
|
||||
reader->hr_cb->on_priority(reader->hr_ctx,
|
||||
&reader->hr_u.prio.h3pfrs_prio);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
/* fall-through */
|
||||
|
|
|
@ -11,7 +11,6 @@ struct lsquic_conn;
|
|||
|
||||
struct hcsi_callbacks
|
||||
{
|
||||
void (*on_priority)(void *ctx, const struct hq_priority *);
|
||||
void (*on_cancel_push)(void *ctx, uint64_t push_id);
|
||||
void (*on_max_push_id)(void *ctx, uint64_t push_id);
|
||||
/* Gets called at the *end* of the SETTING frame */
|
||||
|
@ -30,8 +29,6 @@ struct hcsi_reader
|
|||
HR_SKIPPING,
|
||||
HR_READ_SETTING_BEGIN,
|
||||
HR_READ_SETTING_CONTINUE,
|
||||
HR_READ_PRIORITY_BEGIN,
|
||||
HR_READ_PRIORITY_CONTINUE,
|
||||
HR_READ_VARINT,
|
||||
HR_READ_VARINT_CONTINUE,
|
||||
HR_ERROR,
|
||||
|
@ -43,7 +40,6 @@ struct hcsi_reader
|
|||
{
|
||||
struct varint_read_state vint_state;
|
||||
struct varint_read2_state vint2_state;
|
||||
struct h3_prio_frame_read_state prio;
|
||||
} hr_u;
|
||||
const struct hcsi_callbacks *hr_cb;
|
||||
void *hr_ctx;
|
||||
|
|
|
@ -131,10 +131,10 @@ lsquic_hcso_write_settings (struct hcso_writer *writer,
|
|||
const unsigned frame_size_len = 2;
|
||||
#endif
|
||||
unsigned char buf[1 /* Frame type */ + /* Frame size */ frame_size_len
|
||||
/* There are maximum four settings that need to be written out and
|
||||
/* There are maximum three settings that need to be written out and
|
||||
* each value can be encoded in maximum 8 bytes:
|
||||
*/
|
||||
+ 4 * (
|
||||
+ 3 * (
|
||||
#ifdef NDEBUG
|
||||
1 /* Each setting needs 1-byte varint number, */
|
||||
#else
|
||||
|
@ -146,18 +146,6 @@ lsquic_hcso_write_settings (struct hcso_writer *writer,
|
|||
*p++ = HQFT_SETTINGS;
|
||||
p += frame_size_len;
|
||||
|
||||
if (is_server)
|
||||
if (settings->es_h3_placeholders != HQ_DF_NUM_PLACEHOLDERS)
|
||||
{
|
||||
/* Write out SETTINGS_NUM_PLACEHOLDERS */
|
||||
bits = hcso_setting_type2bits(writer, HQSID_NUM_PLACEHOLDERS);
|
||||
vint_write(p, HQSID_NUM_PLACEHOLDERS, bits, 1 << bits);
|
||||
p += 1 << bits;
|
||||
bits = vint_val2bits(settings->es_h3_placeholders);
|
||||
vint_write(p, settings->es_h3_placeholders, bits, 1 << bits);
|
||||
p += 1 << bits;
|
||||
}
|
||||
|
||||
if (settings->es_max_header_list_size != HQ_DF_MAX_HEADER_LIST_SIZE)
|
||||
{
|
||||
/* Write out SETTINGS_MAX_HEADER_LIST_SIZE */
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#ifndef LSQUIC_HKDF_H
|
||||
#define LSQUIC_HKDF_H 1
|
||||
|
||||
/* [draft-ietf-quic-tls-22] Section 5.2 */
|
||||
#define HSK_SALT_BUF "\x7f\xbc\xdb\x0e\x7c\x66\xbb\xe9\x19\x3a" \
|
||||
"\x96\xcd\x21\x51\x9e\xbd\x7a\x02\x64\x4a"
|
||||
/* [draft-ietf-quic-tls-23] Section 5.2 */
|
||||
#define HSK_SALT_BUF "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" \
|
||||
"\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"
|
||||
#define HSK_SALT ((unsigned char *) HSK_SALT_BUF)
|
||||
#define HSK_SALT_SZ (sizeof(HSK_SALT_BUF) - 1)
|
||||
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lsquic_types.h"
|
||||
#include "lsquic_varint.h"
|
||||
#include "lsquic_hq.h"
|
||||
|
||||
const char *const lsquic_h3det2str[] =
|
||||
{
|
||||
[H3DET_REQ_STREAM] = "request stream",
|
||||
[H3DET_PUSH_STREAM] = "push stream",
|
||||
[H3DET_PLACEHOLDER] = "placeholder",
|
||||
[H3DET_ROOT] = "root of the tree",
|
||||
};
|
||||
|
||||
const char *const lsquic_h3pet2str[] =
|
||||
{
|
||||
[H3PET_REQ_STREAM] = "request stream",
|
||||
[H3PET_PUSH_STREAM] = "push stream",
|
||||
[H3PET_PLACEHOLDER] = "placeholder",
|
||||
[H3PET_CUR_STREAM] = "current stream",
|
||||
};
|
||||
|
||||
|
||||
enum h3_prio_frame_read_status
|
||||
lsquic_h3_prio_frame_read (const unsigned char **bufp, size_t bufsz,
|
||||
struct h3_prio_frame_read_state *state)
|
||||
{
|
||||
const unsigned char *p = *bufp;
|
||||
const unsigned char *const end = p + bufsz;
|
||||
int s;
|
||||
|
||||
while (p < end)
|
||||
{
|
||||
switch (state->h3pfrs_state)
|
||||
{
|
||||
case H3PFRS_STATE_TYPE:
|
||||
state->h3pfrs_prio.hqp_prio_type = (p[0] >> HQ_PT_SHIFT) & 3;
|
||||
state->h3pfrs_prio.hqp_dep_type = (p[0] >> HQ_DT_SHIFT) & 3;
|
||||
++p;
|
||||
if (state->h3pfrs_prio.hqp_prio_type == H3PET_CUR_STREAM
|
||||
&& state->h3pfrs_prio.hqp_dep_type == H3DET_ROOT)
|
||||
state->h3pfrs_state = H3PFRS_STATE_WEIGHT;
|
||||
else
|
||||
{
|
||||
state->h3pfrs_flags = 0;
|
||||
state->h3pfrs_state = H3PFRS_STATE_VINT_BEGIN;
|
||||
}
|
||||
break;
|
||||
case H3PFRS_STATE_VINT_BEGIN:
|
||||
state->h3pfrs_vint.pos = 0;
|
||||
state->h3pfrs_state = H3PFRS_STATE_VINT_CONTINUE;
|
||||
/* fall-through */
|
||||
case H3PFRS_STATE_VINT_CONTINUE:
|
||||
s = lsquic_varint_read_nb(&p, end, &state->h3pfrs_vint);
|
||||
if (0 == s)
|
||||
{
|
||||
if (state->h3pfrs_prio.hqp_prio_type == H3PET_CUR_STREAM
|
||||
|| (state->h3pfrs_flags & H3PFRS_FLAG_HAVE_PRIO_ID))
|
||||
{
|
||||
state->h3pfrs_prio.hqp_dep_id = state->h3pfrs_vint.val;
|
||||
state->h3pfrs_state = H3PFRS_STATE_WEIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->h3pfrs_prio.hqp_prio_id = state->h3pfrs_vint.val;
|
||||
state->h3pfrs_flags |= H3PFRS_FLAG_HAVE_PRIO_ID;
|
||||
if (state->h3pfrs_prio.hqp_dep_type == H3DET_ROOT)
|
||||
state->h3pfrs_state = H3PFRS_STATE_WEIGHT;
|
||||
else
|
||||
state->h3pfrs_state = H3PFRS_STATE_VINT_BEGIN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(p == end);
|
||||
*bufp = p;
|
||||
return H3PFR_STATUS_NEED;
|
||||
}
|
||||
case H3PFRS_STATE_WEIGHT:
|
||||
state->h3pfrs_prio.hqp_weight = *p++;
|
||||
*bufp = p;
|
||||
return H3PFR_STATUS_DONE;
|
||||
default:
|
||||
assert(0);
|
||||
return H3PFR_STATUS_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
*bufp = p;
|
||||
return H3PFR_STATUS_NEED;
|
||||
}
|
|
@ -11,7 +11,6 @@ enum hq_frame_type
|
|||
{
|
||||
HQFT_DATA = 0,
|
||||
HQFT_HEADERS = 1,
|
||||
HQFT_PRIORITY = 2,
|
||||
HQFT_CANCEL_PUSH = 3,
|
||||
HQFT_SETTINGS = 4,
|
||||
HQFT_PUSH_PROMISE = 5,
|
||||
|
@ -26,52 +25,18 @@ enum hq_frame_type
|
|||
};
|
||||
|
||||
|
||||
enum h3_prio_el_type
|
||||
{
|
||||
H3PET_REQ_STREAM = 0,
|
||||
H3PET_PUSH_STREAM = 1,
|
||||
H3PET_PLACEHOLDER = 2,
|
||||
H3PET_CUR_STREAM = 3,
|
||||
};
|
||||
|
||||
|
||||
enum h3_dep_el_type
|
||||
{
|
||||
H3DET_REQ_STREAM = 0,
|
||||
H3DET_PUSH_STREAM = 1,
|
||||
H3DET_PLACEHOLDER = 2,
|
||||
H3DET_ROOT = 3,
|
||||
};
|
||||
|
||||
|
||||
#define HQ_PT_SHIFT 6
|
||||
#define HQ_DT_SHIFT 4
|
||||
|
||||
|
||||
enum hq_setting_id
|
||||
{
|
||||
HQSID_QPACK_MAX_TABLE_CAPACITY = 1,
|
||||
HQSID_MAX_HEADER_LIST_SIZE = 6,
|
||||
HQSID_QPACK_BLOCKED_STREAMS = 7,
|
||||
HQSID_NUM_PLACEHOLDERS = 9,
|
||||
};
|
||||
|
||||
/* As of 12/18/2018: */
|
||||
#define HQ_DF_QPACK_MAX_TABLE_CAPACITY 0
|
||||
#define HQ_DF_NUM_PLACEHOLDERS 0
|
||||
#define HQ_DF_MAX_HEADER_LIST_SIZE 0
|
||||
#define HQ_DF_QPACK_BLOCKED_STREAMS 0
|
||||
|
||||
struct hq_priority
|
||||
{
|
||||
lsquic_stream_id_t hqp_prio_id;
|
||||
lsquic_stream_id_t hqp_dep_id;
|
||||
enum h3_prio_el_type hqp_prio_type:8;
|
||||
enum h3_dep_el_type hqp_dep_type:8;
|
||||
uint8_t hqp_weight;
|
||||
};
|
||||
|
||||
#define HQP_WEIGHT(p) ((p)->hqp_weight + 1)
|
||||
|
||||
/* [draft-ietf-quic-http-19] Section 10.6,
|
||||
* [draft-ietf-quic-qpack-07] Section 8.2
|
||||
|
@ -84,69 +49,33 @@ enum hq_uni_stream_type
|
|||
HQUST_QPACK_DEC = 3,
|
||||
};
|
||||
|
||||
extern const char *const lsquic_h3det2str[];
|
||||
extern const char *const lsquic_h3pet2str[];
|
||||
|
||||
/* [draft-ietf-quic-http-22] Section 8.1 and
|
||||
/* [draft-ietf-quic-http-23] Section 8.1 and
|
||||
* [draft-ietf-quic-qpack-08], Section 8.3
|
||||
*/
|
||||
enum http_error_code
|
||||
{
|
||||
HEC_NO_ERROR = 0x00,
|
||||
HEC_GENERAL_PROTOCOL_ERROR = 0x01,
|
||||
/* Error code 0x2 is reserved and has no meaning */
|
||||
HEC_INTERNAL_ERROR = 0x03,
|
||||
/* Error code 0x4 is reserved and has no meaning */
|
||||
HEC_REQUEST_CANCELLED = 0x05,
|
||||
HEC_INCOMPLETE_REQUEST = 0x06,
|
||||
HEC_CONNECT_ERROR = 0x07,
|
||||
HEC_EXCESSIVE_LOAD = 0x08,
|
||||
HEC_VERSION_FALLBACK = 0x09,
|
||||
HEC_WRONG_STREAM = 0x0A,
|
||||
HEC_ID_ERROR = 0x0B,
|
||||
/* Error code 0xC is reserved and has no meaning */
|
||||
HEC_STREAM_CREATION_ERROR = 0x0D,
|
||||
/* Error code 0xE is reserved and has no meaning */
|
||||
HEC_CLOSED_CRITICAL_STREAM = 0x0F,
|
||||
/* Error code 0x10 is reserved and has no meaning */
|
||||
HEC_EARLY_RESPONSE = 0x0011,
|
||||
HEC_MISSING_SETTINGS = 0x0012,
|
||||
HEC_UNEXPECTED_FRAME = 0x0013,
|
||||
HEC_REQUEST_REJECTED = 0x14,
|
||||
HEC_SETTINGS_ERROR = 0x00FF,
|
||||
HEC_MALFORMED_FRAME = 0x0100, /* add frame type */
|
||||
HEC_NO_ERROR = 0x100,
|
||||
HEC_GENERAL_PROTOCOL_ERROR = 0x101,
|
||||
HEC_INTERNAL_ERROR = 0x102,
|
||||
HEC_STREAM_CREATION_ERROR = 0x103,
|
||||
HEC_CLOSED_CRITICAL_STREAM = 0x104,
|
||||
HEC_FRAME_UNEXPECTED = 0x105,
|
||||
HEC_FRAME_ERROR = 0x106,
|
||||
HEC_EXCESSIVE_LOAD = 0x107,
|
||||
HEC_ID_ERROR = 0x108,
|
||||
HEC_SETTINGS_ERROR = 0x109,
|
||||
HEC_MISSING_SETTINGS = 0x10A,
|
||||
HEC_REQUEST_REJECTED = 0x10B,
|
||||
HEC_REQUEST_CANCELLED = 0x10C,
|
||||
HEC_REQUEST_INCOMPLETE = 0x10D,
|
||||
HEC_EARLY_RESPONSE = 0x10E,
|
||||
HEC_CONNECT_ERROR = 0x10F,
|
||||
HEC_VERSION_FALLBACK = 0x110,
|
||||
HEC_QPACK_DECOMPRESSION_FAILED = 0x200,
|
||||
HEC_QPACK_ENCODER_STREAM_ERROR = 0x201,
|
||||
HEC_QPACK_DECODER_STREAM_ERROR = 0x202,
|
||||
};
|
||||
|
||||
|
||||
struct h3_prio_frame_read_state
|
||||
{
|
||||
struct varint_read_state h3pfrs_vint;
|
||||
struct hq_priority h3pfrs_prio;
|
||||
enum {
|
||||
H3PFRS_STATE_TYPE = 0,
|
||||
H3PFRS_STATE_VINT_BEGIN,
|
||||
H3PFRS_STATE_VINT_CONTINUE,
|
||||
H3PFRS_STATE_WEIGHT,
|
||||
} h3pfrs_state;
|
||||
enum {
|
||||
H3PFRS_FLAG_HAVE_PRIO_ID = 1 << 0,
|
||||
} h3pfrs_flags;
|
||||
};
|
||||
|
||||
|
||||
enum h3_prio_frame_read_status
|
||||
{
|
||||
H3PFR_STATUS_DONE,
|
||||
H3PFR_STATUS_NEED,
|
||||
};
|
||||
|
||||
|
||||
/* When first called, h3pfrs_state should be set to 0 */
|
||||
enum h3_prio_frame_read_status
|
||||
lsquic_h3_prio_frame_read (const unsigned char **, size_t,
|
||||
struct h3_prio_frame_read_state *);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,7 +18,6 @@ enum trans_error_code
|
|||
TEC_TRANSPORT_PARAMETER_ERROR = 0x8,
|
||||
TEC_VERSION_NEGOTIATION_ERROR = 0x9,
|
||||
TEC_PROTOCOL_VIOLATION = 0xA,
|
||||
TEC_INVALID_MIGRATION = 0xC,
|
||||
TEC_CRYPTO_BUFFER_EXCEEDED = 0xD,
|
||||
};
|
||||
|
||||
|
|
|
@ -63,11 +63,41 @@ imico_destroy_packet (struct ietf_mini_conn *conn,
|
|||
}
|
||||
|
||||
|
||||
int
|
||||
lsquic_mini_conn_ietf_ecn_ok (const struct ietf_mini_conn *conn)
|
||||
{
|
||||
packno_set_t acked;
|
||||
|
||||
/* First flight has only Initial and Handshake packets */
|
||||
acked = conn->imc_acked_packnos[PNS_INIT]
|
||||
| conn->imc_acked_packnos[PNS_HSK]
|
||||
;
|
||||
return 0 != (conn->imc_ecn_packnos & acked);
|
||||
}
|
||||
|
||||
|
||||
#define imico_ecn_ok lsquic_mini_conn_ietf_ecn_ok
|
||||
|
||||
|
||||
static enum ecn
|
||||
imico_get_ecn (struct ietf_mini_conn *conn)
|
||||
{
|
||||
if (!conn->imc_enpub->enp_settings.es_ecn)
|
||||
return ECN_NOT_ECT;
|
||||
else if (!conn->imc_sent_packnos /* We set ECT0 in first flight */
|
||||
|| imico_ecn_ok(conn))
|
||||
return ECN_ECT0;
|
||||
else
|
||||
return ECN_NOT_ECT;
|
||||
}
|
||||
|
||||
|
||||
static struct lsquic_packet_out *
|
||||
imico_get_packet_out (struct ietf_mini_conn *conn,
|
||||
enum header_type header_type, size_t need)
|
||||
{
|
||||
struct lsquic_packet_out *packet_out;
|
||||
enum ecn ecn;
|
||||
|
||||
if (need)
|
||||
TAILQ_FOREACH(packet_out, &conn->imc_packets_out, po_next)
|
||||
|
@ -94,8 +124,8 @@ imico_get_packet_out (struct ietf_mini_conn *conn,
|
|||
packet_out->po_packno = conn->imc_next_packno++;
|
||||
packet_out->po_flags |= PO_MINI;
|
||||
lsquic_packet_out_set_pns(packet_out, lsquic_hety2pns[header_type]);
|
||||
if (conn->imc_enpub->enp_settings.es_ecn)
|
||||
packet_out->po_lflags |= ECN_ECT0 << POECN_SHIFT;
|
||||
ecn = imico_get_ecn(conn);
|
||||
packet_out->po_lflags |= ecn << POECN_SHIFT;
|
||||
TAILQ_INSERT_TAIL(&conn->imc_packets_out, packet_out, po_next);
|
||||
packet_out->po_loss_chain = packet_out;
|
||||
return packet_out;
|
||||
|
@ -903,6 +933,11 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
|
|||
enum dec_packin dec_packin;
|
||||
enum packnum_space pns;
|
||||
|
||||
if (conn->imc_flags & IMC_ERROR)
|
||||
{
|
||||
LSQ_DEBUG("ignore incoming packet: connection is in error state");
|
||||
return;
|
||||
}
|
||||
|
||||
pns = lsquic_hety2pns[ packet_in->pi_header_type ];
|
||||
if (pns == PNS_INIT && (conn->imc_flags & IMC_IGNORE_INIT))
|
||||
|
@ -957,11 +992,15 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
|
|||
packet_in->pi_packno > highest_bit_set(conn->imc_recvd_packnos[pns]))
|
||||
conn->imc_largest_recvd[pns] = packet_in->pi_received;
|
||||
conn->imc_recvd_packnos[pns] |= 1ULL << packet_in->pi_packno;
|
||||
conn->imc_flags |= IMC_QUEUED_ACK_INIT << pns;
|
||||
|
||||
if (0 != imico_parse_regular_packet(conn, packet_in))
|
||||
{
|
||||
LSQ_DEBUG("connection is now in error state");
|
||||
conn->imc_flags |= IMC_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
conn->imc_flags |= IMC_QUEUED_ACK_INIT << pns;
|
||||
++conn->imc_ecn_counts_in[pns][ lsquic_packet_in_ecn(packet_in) ];
|
||||
conn->imc_incoming_ecn <<= 1;
|
||||
conn->imc_incoming_ecn |= lsquic_packet_in_ecn(packet_in) != ECN_NOT_ECT;
|
||||
|
@ -974,6 +1013,8 @@ ietf_mini_conn_ci_packet_sent (struct lsquic_conn *lconn,
|
|||
{
|
||||
struct ietf_mini_conn *conn = (struct ietf_mini_conn *) lconn;
|
||||
conn->imc_sent_packnos |= 1ULL << packet_out->po_packno;
|
||||
conn->imc_ecn_packnos |= !!lsquic_packet_out_ecn(packet_out)
|
||||
<< packet_out->po_packno;
|
||||
#if 0
|
||||
if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
|
||||
{
|
||||
|
@ -983,6 +1024,8 @@ ietf_mini_conn_ci_packet_sent (struct lsquic_conn *lconn,
|
|||
#endif
|
||||
++conn->imc_ecn_counts_out[ lsquic_packet_out_pns(packet_out) ]
|
||||
[ lsquic_packet_out_ecn(packet_out) ];
|
||||
if (packet_out->po_header_type == HETY_HANDSHAKE)
|
||||
conn->imc_flags |= IMC_HSK_PACKET_SENT;
|
||||
LSQ_DEBUG("%s: packet %"PRIu64" sent", __func__, packet_out->po_packno);
|
||||
}
|
||||
|
||||
|
@ -1028,6 +1071,7 @@ imico_repackage_packet (struct ietf_mini_conn *conn,
|
|||
"resending as packet %"PRIu64, oldno, packno);
|
||||
packet_out->po_packno = packno;
|
||||
packet_out->po_flags &= ~PO_SENT;
|
||||
lsquic_packet_out_set_ecn(packet_out, imico_get_ecn(conn));
|
||||
if (packet_out->po_flags & PO_ENCRYPTED)
|
||||
imico_return_enc_data(conn, packet_out);
|
||||
TAILQ_INSERT_TAIL(&conn->imc_packets_out, packet_out, po_next);
|
||||
|
@ -1245,20 +1289,13 @@ imico_generate_conn_close (struct ietf_mini_conn *conn)
|
|||
{
|
||||
struct lsquic_packet_out *packet_out;
|
||||
enum header_type header_type;
|
||||
enum packnum_space pns;
|
||||
enum packnum_space pns, pns_max;
|
||||
unsigned error_code;
|
||||
const char *reason;
|
||||
size_t need;
|
||||
int sz, rlen, is_app;
|
||||
char reason_buf[0x20];
|
||||
|
||||
pns = (conn->imc_flags >> IMCBIT_PNS_BIT_SHIFT) & 3;
|
||||
header_type = pns2hety[pns];
|
||||
need = 30; /* Guess */ /* TODO: calculate, don't guess */
|
||||
packet_out = imico_get_packet_out(conn, header_type, need);
|
||||
if (!packet_out)
|
||||
return;
|
||||
|
||||
if (conn->imc_flags & IMC_ABORT_ERROR)
|
||||
{
|
||||
is_app = !!(conn->imc_flags & IMC_ABORT_ISAPP);
|
||||
|
@ -1302,18 +1339,59 @@ imico_generate_conn_close (struct ietf_mini_conn *conn)
|
|||
rlen = 0;
|
||||
}
|
||||
|
||||
sz = conn->imc_conn.cn_pf->pf_gen_connect_close_frame(
|
||||
packet_out->po_data + packet_out->po_data_sz,
|
||||
lsquic_packet_out_avail(packet_out), is_app, error_code, reason,
|
||||
rlen);
|
||||
if (sz >= 0)
|
||||
|
||||
/* [draft-ietf-quic-transport-23] Section 12.2:
|
||||
*
|
||||
" A client will always know whether the server has Handshake keys (see
|
||||
" Section 17.2.2.1), but it is possible that a server does not know
|
||||
" whether the client has Handshake keys. Under these circumstances, a
|
||||
" server SHOULD send a CONNECTION_CLOSE frame in both Handshake and
|
||||
" Initial packets to ensure that at least one of them is processable by
|
||||
" the client.
|
||||
*/
|
||||
|
||||
pns = (conn->imc_flags >> IMCBIT_PNS_BIT_SHIFT) & 3;
|
||||
switch ((!!(conn->imc_flags & IMC_HSK_PACKET_SENT) << 1)
|
||||
| (pns == PNS_HSK) /* Handshake packet received */)
|
||||
{
|
||||
packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
|
||||
packet_out->po_data_sz += sz;
|
||||
LSQ_DEBUG("generated CONNECTION_CLOSE frame");
|
||||
case (0 << 1) | 0:
|
||||
pns = PNS_INIT;
|
||||
pns_max = PNS_INIT;
|
||||
break;
|
||||
case (1 << 1) | 0:
|
||||
pns = PNS_INIT;
|
||||
pns_max = PNS_HSK;
|
||||
break;
|
||||
default:
|
||||
pns = PNS_HSK;
|
||||
pns_max = PNS_HSK;
|
||||
break;
|
||||
}
|
||||
else
|
||||
LSQ_WARN("could not generate CONNECTION_CLOSE frame");
|
||||
|
||||
LSQ_DEBUG("will generate %u CONNECTION_CLOSE frame%.*s",
|
||||
pns_max - pns + 1, pns_max > pns, "s");
|
||||
do
|
||||
{
|
||||
header_type = pns2hety[pns];
|
||||
need = 30; /* Guess */ /* TODO: calculate, don't guess */
|
||||
packet_out = imico_get_packet_out(conn, header_type, need);
|
||||
if (!packet_out)
|
||||
return;
|
||||
sz = conn->imc_conn.cn_pf->pf_gen_connect_close_frame(
|
||||
packet_out->po_data + packet_out->po_data_sz,
|
||||
lsquic_packet_out_avail(packet_out), is_app, error_code, reason,
|
||||
rlen);
|
||||
if (sz >= 0)
|
||||
{
|
||||
packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
|
||||
packet_out->po_data_sz += sz;
|
||||
LSQ_DEBUG("generated CONNECTION_CLOSE frame");
|
||||
}
|
||||
else
|
||||
LSQ_WARN("could not generate CONNECTION_CLOSE frame");
|
||||
++pns;
|
||||
}
|
||||
while (pns <= pns_max);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ struct ietf_mini_conn
|
|||
IMC_ABORT_ISAPP = 1 << 15,
|
||||
IMC_BAD_TRANS_PARAMS = 1 << 16,
|
||||
IMC_ADDR_VALIDATED = 1 << 17,
|
||||
IMC_HSK_PACKET_SENT = 1 << 18,
|
||||
} imc_flags;
|
||||
struct mini_crypto_stream imc_streams[N_ENC_LEVS];
|
||||
void *imc_stream_ps[N_ENC_LEVS];
|
||||
|
@ -72,6 +73,12 @@ struct ietf_mini_conn
|
|||
unsigned imc_bytes_out;
|
||||
unsigned char imc_next_packno;
|
||||
unsigned char imc_hsk_count;
|
||||
/* We don't send more than eight in the first flight, and so it's OK to
|
||||
* use uint8_t. This value is also used as a boolean: when ECN black
|
||||
* hole is detected, it is set to zero to indicate that black hole
|
||||
* detection is no longer active.
|
||||
*/
|
||||
uint8_t imc_ecn_packnos;
|
||||
uint8_t imc_ack_exp;
|
||||
uint8_t imc_ecn_counts_in[N_PNS][4];
|
||||
uint8_t imc_ecn_counts_out[N_PNS][4];
|
||||
|
@ -85,4 +92,6 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *,
|
|||
const struct lsquic_packet_in *,
|
||||
enum lsquic_version, int is_ipv4, const struct lsquic_cid *);
|
||||
|
||||
int
|
||||
lsquic_mini_conn_ietf_ecn_ok (const struct ietf_mini_conn *);
|
||||
#endif
|
||||
|
|
|
@ -34,11 +34,13 @@
|
|||
#include "lsquic_engine_public.h"
|
||||
#include "lsquic_sizes.h"
|
||||
#include "lsquic_handshake.h"
|
||||
#include "lsquic_xxhash.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_PRQ
|
||||
#include "lsquic_logger.h"
|
||||
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
|
||||
static const struct conn_iface evanescent_conn_iface;
|
||||
|
@ -46,14 +48,15 @@ static const struct conn_iface evanescent_conn_iface;
|
|||
|
||||
struct packet_req
|
||||
{
|
||||
STAILQ_ENTRY(packet_req) pr_next;
|
||||
struct lsquic_hash_elem pr_hash_el;
|
||||
lsquic_cid_t pr_scid;
|
||||
lsquic_cid_t pr_dcid;
|
||||
enum packet_req_type pr_type;
|
||||
enum {
|
||||
enum pr_flags {
|
||||
PR_GQUIC = 1 << 0,
|
||||
} pr_flags;
|
||||
enum lsquic_version pr_version;
|
||||
unsigned pr_rst_sz;
|
||||
struct network_path pr_path;
|
||||
};
|
||||
|
||||
|
@ -79,10 +82,10 @@ struct pr_queue
|
|||
{
|
||||
TAILQ_HEAD(, lsquic_conn) prq_free_conns,
|
||||
prq_returned_conns;
|
||||
STAILQ_HEAD(, packet_req) prq_reqs_queue;
|
||||
struct malo *prq_reqs_pool;
|
||||
const struct lsquic_engine_public
|
||||
*prq_enpub;
|
||||
struct lsquic_hash *prq_reqs_hash;
|
||||
unsigned prq_max_reqs;
|
||||
unsigned prq_nreqs;
|
||||
unsigned prq_max_conns;
|
||||
|
@ -103,6 +106,30 @@ struct pr_queue
|
|||
};
|
||||
|
||||
|
||||
static int
|
||||
comp_reqs (const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
const struct packet_req *a, *b;
|
||||
|
||||
a = s1;
|
||||
b = s2;
|
||||
if (a->pr_type == b->pr_type && LSQUIC_CIDS_EQ(&a->pr_dcid, &b->pr_dcid))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static unsigned
|
||||
hash_req (const void *p, size_t len, unsigned seed)
|
||||
{
|
||||
const struct packet_req *req;
|
||||
|
||||
req = p;
|
||||
return XXH32(req->pr_dcid.idbuf, req->pr_dcid.len, seed);
|
||||
}
|
||||
|
||||
|
||||
struct pr_queue *
|
||||
prq_create (unsigned max_elems, unsigned max_conns,
|
||||
const struct lsquic_engine_public *enpub)
|
||||
|
@ -110,6 +137,7 @@ prq_create (unsigned max_elems, unsigned max_conns,
|
|||
const struct parse_funcs *pf;
|
||||
struct pr_queue *prq;
|
||||
struct malo *malo;
|
||||
struct lsquic_hash *hash;
|
||||
unsigned verneg_g_sz;
|
||||
ssize_t prst_g_sz;
|
||||
int len;
|
||||
|
@ -121,11 +149,19 @@ prq_create (unsigned max_elems, unsigned max_conns,
|
|||
goto err0;
|
||||
}
|
||||
|
||||
|
||||
hash = lsquic_hash_create_ext(comp_reqs, hash_req);
|
||||
if (!hash)
|
||||
{
|
||||
LSQ_WARN("cannot create hash");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
prq = malloc(sizeof(*prq));
|
||||
if (!prq)
|
||||
{
|
||||
LSQ_WARN("malloc failed: %s", strerror(errno));
|
||||
goto err1;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
const lsquic_cid_t cid = { .len = 8, };
|
||||
|
@ -137,7 +173,7 @@ prq_create (unsigned max_elems, unsigned max_conns,
|
|||
if (len <= 0)
|
||||
{
|
||||
LSQ_ERROR("cannot generate version negotiation packet");
|
||||
goto err2;
|
||||
goto err3;
|
||||
}
|
||||
verneg_g_sz = (unsigned) len;
|
||||
|
||||
|
@ -146,12 +182,12 @@ prq_create (unsigned max_elems, unsigned max_conns,
|
|||
if (prst_g_sz < 0)
|
||||
{
|
||||
LSQ_ERROR("cannot generate public reset packet");
|
||||
goto err2;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
TAILQ_INIT(&prq->prq_free_conns);
|
||||
TAILQ_INIT(&prq->prq_returned_conns);
|
||||
STAILQ_INIT(&prq->prq_reqs_queue);
|
||||
prq->prq_reqs_hash = hash;
|
||||
prq->prq_reqs_pool = malo;
|
||||
prq->prq_max_reqs = max_elems;
|
||||
prq->prq_nreqs = 0;
|
||||
|
@ -165,8 +201,10 @@ prq_create (unsigned max_elems, unsigned max_conns,
|
|||
|
||||
return prq;
|
||||
|
||||
err2:
|
||||
err3:
|
||||
free(prq);
|
||||
err2:
|
||||
lsquic_hash_destroy(hash);
|
||||
err1:
|
||||
lsquic_malo_destroy(malo);
|
||||
err0:
|
||||
|
@ -185,6 +223,7 @@ prq_destroy (struct pr_queue *prq)
|
|||
TAILQ_REMOVE(&prq->prq_free_conns, conn, cn_next_pr);
|
||||
free(conn);
|
||||
}
|
||||
lsquic_hash_destroy(prq->prq_reqs_hash);
|
||||
lsquic_malo_destroy(prq->prq_reqs_pool);
|
||||
free(prq);
|
||||
}
|
||||
|
@ -223,6 +262,45 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
|
|||
{
|
||||
struct packet_req *req;
|
||||
lsquic_ver_tag_t ver_tag;
|
||||
enum lsquic_version version;
|
||||
enum pr_flags flags;
|
||||
unsigned max, size;
|
||||
|
||||
if (packet_in->pi_flags & PI_GQUIC)
|
||||
flags = PR_GQUIC;
|
||||
else
|
||||
flags = 0;
|
||||
|
||||
if (packet_in->pi_quic_ver)
|
||||
{
|
||||
memcpy(&ver_tag, packet_in->pi_data + packet_in->pi_quic_ver,
|
||||
sizeof(ver_tag));
|
||||
version = lsquic_tag2ver(ver_tag);
|
||||
}
|
||||
else /* Got to set it to something sensible... */
|
||||
version = LSQVER_ID23;
|
||||
|
||||
if (type == PACKET_REQ_PUBRES && !(flags & PR_GQUIC))
|
||||
{
|
||||
if (packet_in->pi_data_sz <= IQUIC_MIN_SRST_SIZE)
|
||||
{
|
||||
LSQ_DEBUGC("not scheduling public reset: incoming packet for CID "
|
||||
"%"CID_FMT" too small: %hu bytes",
|
||||
CID_BITS(&packet_in->pi_dcid), packet_in->pi_data_sz);
|
||||
return -1;
|
||||
}
|
||||
/* Use a random stateless reset size */
|
||||
max = MIN(IQUIC_MAX_SRST_SIZE, packet_in->pi_data_sz - 1u);
|
||||
if (max > IQUIC_MIN_SRST_SIZE)
|
||||
size = IQUIC_MIN_SRST_SIZE + rand() % (max - IQUIC_MIN_SRST_SIZE);
|
||||
else
|
||||
size = IQUIC_MIN_SRST_SIZE;
|
||||
LSQ_DEBUGC("selected %u-byte reset size for CID %"CID_FMT
|
||||
" (range is [%u, %u])", size, CID_BITS(&packet_in->pi_dcid),
|
||||
IQUIC_MIN_SRST_SIZE, max);
|
||||
}
|
||||
else
|
||||
size = 0;
|
||||
|
||||
req = get_req(prq);
|
||||
if (!req)
|
||||
|
@ -231,25 +309,34 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
|
|||
return -1;
|
||||
}
|
||||
|
||||
STAILQ_INSERT_TAIL(&prq->prq_reqs_queue, req, pr_next);
|
||||
|
||||
req->pr_type = type;
|
||||
req->pr_flags = packet_in->pi_flags & PI_GQUIC ? PR_GQUIC : 0;
|
||||
lsquic_scid_from_packet_in(packet_in, &req->pr_scid);
|
||||
req->pr_dcid = packet_in->pi_dcid;
|
||||
if (lsquic_hash_find(prq->prq_reqs_hash, req, sizeof(req)))
|
||||
{
|
||||
LSQ_DEBUG("request for this DCID and type already exists");
|
||||
put_req(prq, req);
|
||||
return -1;
|
||||
}
|
||||
|
||||
req->pr_hash_el.qhe_flags = 0;
|
||||
if (!lsquic_hash_insert(prq->prq_reqs_hash, req, sizeof(req),
|
||||
req, &req->pr_hash_el))
|
||||
{
|
||||
LSQ_DEBUG("could not insert req into hash");
|
||||
put_req(prq, req);
|
||||
return -1;
|
||||
}
|
||||
|
||||
req->pr_flags = flags;
|
||||
req->pr_rst_sz = size;
|
||||
req->pr_version = version;
|
||||
lsquic_scid_from_packet_in(packet_in, &req->pr_scid);
|
||||
req->pr_path.np_peer_ctx = peer_ctx;
|
||||
memcpy(NP_LOCAL_SA(&req->pr_path), local_addr,
|
||||
sizeof(req->pr_path.np_local_addr));
|
||||
memcpy(NP_PEER_SA(&req->pr_path), peer_addr,
|
||||
sizeof(req->pr_path.np_peer_addr));
|
||||
if (packet_in->pi_quic_ver)
|
||||
{
|
||||
memcpy(&ver_tag, packet_in->pi_data + packet_in->pi_quic_ver,
|
||||
sizeof(ver_tag));
|
||||
req->pr_version = lsquic_tag2ver(ver_tag);
|
||||
}
|
||||
else /* Got to set it to something sensible... */
|
||||
req->pr_version = LSQVER_ID22;
|
||||
|
||||
LSQ_DEBUGC("scheduled %s packet for connection %"CID_FMT,
|
||||
lsquic_preqt2str[type], CID_BITS(&req->pr_dcid));
|
||||
return 0;
|
||||
|
@ -316,6 +403,7 @@ prq_next_conn (struct pr_queue *prq)
|
|||
{
|
||||
struct evanescent_conn *evconn;
|
||||
struct lsquic_conn *lconn;
|
||||
struct lsquic_hash_elem *el;
|
||||
struct packet_req *req;
|
||||
struct lsquic_packet_out *packet_out;
|
||||
int (*gen_verneg) (unsigned char *, size_t, const lsquic_cid_t *,
|
||||
|
@ -329,14 +417,15 @@ prq_next_conn (struct pr_queue *prq)
|
|||
return lconn;
|
||||
}
|
||||
|
||||
req = STAILQ_FIRST(&prq->prq_reqs_queue);
|
||||
if (!req) /* Nothing is queued */
|
||||
el = lsquic_hash_first(prq->prq_reqs_hash);
|
||||
if (!el) /* Nothing is queued */
|
||||
return NULL;
|
||||
|
||||
evconn = get_evconn(prq);
|
||||
if (!evconn) /* Reached limit or malloc failed */
|
||||
return NULL;
|
||||
|
||||
req = lsquic_hashelem_getdata(el);
|
||||
packet_out = &evconn->evc_packet_out;
|
||||
switch ((req->pr_type << 29) | req->pr_flags)
|
||||
{
|
||||
|
@ -370,16 +459,16 @@ prq_next_conn (struct pr_queue *prq)
|
|||
break;
|
||||
default:
|
||||
packet_out->po_flags &= ~PO_VERNEG;
|
||||
packet_out->po_data_sz = IQUIC_MIN_SRST_SIZE;
|
||||
RAND_bytes(packet_out->po_data, IQUIC_MIN_SRST_RANDOM_BYTES);
|
||||
packet_out->po_data_sz = req->pr_rst_sz;
|
||||
RAND_bytes(packet_out->po_data, req->pr_rst_sz - IQUIC_SRESET_TOKEN_SZ);
|
||||
packet_out->po_data[0] &= ~0x80;
|
||||
packet_out->po_data[0] |= 0x40;
|
||||
lsquic_tg_generate_sreset(prq->prq_enpub->enp_tokgen, &req->pr_dcid,
|
||||
packet_out->po_data + IQUIC_MIN_SRST_RANDOM_BYTES);
|
||||
packet_out->po_data + req->pr_rst_sz - IQUIC_SRESET_TOKEN_SZ);
|
||||
break;
|
||||
}
|
||||
|
||||
STAILQ_REMOVE_HEAD(&prq->prq_reqs_queue, pr_next);
|
||||
lsquic_hash_erase(prq->prq_reqs_hash, el);
|
||||
evconn->evc_req = req;
|
||||
|
||||
lconn= &evconn->evc_conn;
|
||||
|
@ -394,7 +483,7 @@ prq_next_conn (struct pr_queue *prq)
|
|||
int
|
||||
prq_have_pending (const struct pr_queue *prq)
|
||||
{
|
||||
return !STAILQ_EMPTY(&prq->prq_reqs_queue);
|
||||
return lsquic_hash_count(prq->prq_reqs_hash) > 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#include "lsquic_conn_public.h"
|
||||
#include "lsquic_cong_ctl.h"
|
||||
#include "lsquic_enc_sess.h"
|
||||
#include "lsquic_h3_prio.h"
|
||||
#include "lsquic_hash.h"
|
||||
#include "lsquic_malo.h"
|
||||
|
||||
|
@ -312,12 +311,12 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
|
|||
ctl->sc_alset = alset;
|
||||
ctl->sc_ver_neg = ver_neg;
|
||||
ctl->sc_conn_pub = conn_pub;
|
||||
assert(!(flags & ~(SC_IETF|SC_NSTP)));
|
||||
assert(!(flags & ~(SC_IETF|SC_NSTP|SC_ECN)));
|
||||
ctl->sc_flags = flags;
|
||||
send_ctl_pick_initial_packno(ctl);
|
||||
if (enpub->enp_settings.es_pace_packets)
|
||||
ctl->sc_flags |= SC_PACE;
|
||||
if ((flags & SC_IETF) && enpub->enp_settings.es_ecn)
|
||||
if (flags & SC_ECN)
|
||||
ctl->sc_ecn = ECN_ECT0;
|
||||
else
|
||||
ctl->sc_ecn = ECN_NOT_ECT;
|
||||
|
@ -773,6 +772,16 @@ send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl,
|
|||
if (ctl->sc_ci->cci_lost)
|
||||
ctl->sc_ci->cci_lost(CGP(ctl), packet_out, packet_sz);
|
||||
|
||||
/* This is a client-only check, server check happens in mini conn */
|
||||
if (send_ctl_ecn_on(ctl)
|
||||
&& 0 == ctl->sc_ecn_total_acked[PNS_INIT]
|
||||
&& HETY_INITIAL == packet_out->po_header_type
|
||||
&& 3 == packet_out->po_packno)
|
||||
{
|
||||
LSQ_DEBUG("possible ECN black hole during handshake, disable ECN");
|
||||
ctl->sc_ecn = ECN_NOT_ECT;
|
||||
}
|
||||
|
||||
if (packet_out->po_frame_types & ctl->sc_retx_frames)
|
||||
{
|
||||
LSQ_DEBUG("lost retransmittable packet %"PRIu64,
|
||||
|
@ -1840,6 +1849,7 @@ update_for_resending (lsquic_send_ctl_t *ctl, lsquic_packet_out_t *packet_out)
|
|||
packet_out->po_frame_types &= ~GQUIC_FRAME_REGEN_MASK;
|
||||
assert(packet_out->po_frame_types);
|
||||
packet_out->po_packno = packno;
|
||||
lsquic_packet_out_set_ecn(packet_out, ctl->sc_ecn);
|
||||
|
||||
if (ctl->sc_ver_neg->vn_tag)
|
||||
{
|
||||
|
@ -2150,9 +2160,14 @@ lsquic_send_ctl_drop_scheduled (lsquic_send_ctl_t *ctl)
|
|||
}
|
||||
|
||||
|
||||
static enum buf_packet_type
|
||||
send_ctl_determine_gquic_bpt (struct lsquic_send_ctl *ctl,
|
||||
const struct lsquic_stream *stream)
|
||||
#ifdef NDEBUG
|
||||
static
|
||||
#elif __GNUC__
|
||||
__attribute__((weak))
|
||||
#endif
|
||||
enum buf_packet_type
|
||||
lsquic_send_ctl_determine_bpt (lsquic_send_ctl_t *ctl,
|
||||
const lsquic_stream_t *stream)
|
||||
{
|
||||
const lsquic_stream_t *other_stream;
|
||||
struct lsquic_hash_elem *el;
|
||||
|
@ -2173,35 +2188,6 @@ send_ctl_determine_gquic_bpt (struct lsquic_send_ctl *ctl,
|
|||
}
|
||||
|
||||
|
||||
static enum buf_packet_type
|
||||
send_ctl_determine_ietf_bpt (struct lsquic_send_ctl *ctl,
|
||||
const struct lsquic_stream *stream)
|
||||
{
|
||||
if (lsquic_stream_is_critical(stream)
|
||||
|| stream == lsquic_prio_tree_highest_non_crit(
|
||||
ctl->sc_conn_pub->u.ietf.prio_tree))
|
||||
return BPT_HIGHEST_PRIO;
|
||||
else
|
||||
return BPT_OTHER_PRIO;
|
||||
}
|
||||
|
||||
|
||||
#ifdef NDEBUG
|
||||
static
|
||||
#elif __GNUC__
|
||||
__attribute__((weak))
|
||||
#endif
|
||||
enum buf_packet_type
|
||||
lsquic_send_ctl_determine_bpt (lsquic_send_ctl_t *ctl,
|
||||
const lsquic_stream_t *stream)
|
||||
{
|
||||
if (ctl->sc_flags & SC_IETF)
|
||||
return send_ctl_determine_ietf_bpt(ctl, stream);
|
||||
else
|
||||
return send_ctl_determine_gquic_bpt(ctl, stream);
|
||||
}
|
||||
|
||||
|
||||
static enum buf_packet_type
|
||||
send_ctl_lookup_bpt (lsquic_send_ctl_t *ctl,
|
||||
const struct lsquic_stream *stream)
|
||||
|
|
|
@ -43,6 +43,7 @@ enum send_ctl_flags {
|
|||
SC_LOST_ACK_APP = SC_LOST_ACK_INIT << PNS_APP,
|
||||
SC_1RTT_ACKED = 1 << 11,
|
||||
SC_APP_LIMITED = 1 << 12,
|
||||
SC_ECN = 1 << 13,
|
||||
};
|
||||
|
||||
typedef struct lsquic_send_ctl {
|
||||
|
|
|
@ -4,10 +4,13 @@
|
|||
|
||||
#define IQUIC_SRESET_TOKEN_SZ 16u
|
||||
|
||||
#define IQUIC_MIN_SRST_RANDOM_BYTES (1 /* First byte: 01XX XXXX */ \
|
||||
+ 24 /* Random bytes */)
|
||||
#define IQUIC_MIN_SRST_RANDOM_BYTES (1u /* First byte: 01XX XXXX */ \
|
||||
+ 4u /* Random bytes */)
|
||||
|
||||
#define IQUIC_MIN_SRST_SIZE (IQUIC_MIN_SRST_RANDOM_BYTES \
|
||||
+ IQUIC_SRESET_TOKEN_SZ /* Token */)
|
||||
|
||||
/* Allow some wiggle room */
|
||||
#define IQUIC_MAX_SRST_SIZE (IQUIC_MIN_SRST_SIZE + 40u)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -64,7 +64,6 @@
|
|||
#include "lsquic_qdec_hdl.h"
|
||||
#include "lsquic_qenc_hdl.h"
|
||||
#include "lsquic_byteswap.h"
|
||||
#include "lsquic_h3_prio.h"
|
||||
#include "lsquic_ietf.h"
|
||||
#include "lsquic_push_promise.h"
|
||||
|
||||
|
@ -355,19 +354,6 @@ stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub,
|
|||
stream->sm_onnew_arg = stream_if_ctx;
|
||||
stream->sm_write_avail = stream_write_avail;
|
||||
|
||||
if ((ctor_flags & (SCF_IETF|SCF_CRITICAL)) == SCF_IETF)
|
||||
{
|
||||
if (0 == lsquic_prio_tree_add_stream(conn_pub->u.ietf.prio_tree,
|
||||
stream, H3ET_ROOT, 0, 0))
|
||||
stream->sm_qflags |= SMQF_H3_PRIO;
|
||||
else
|
||||
{
|
||||
stream->data_in->di_if->di_destroy(stream->data_in);
|
||||
free(stream);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
STAILQ_INIT(&stream->sm_hq_frames);
|
||||
|
||||
stream->sm_bflags |= ctor_flags & ((1 << (N_SMBF_FLAGS - 1)) - 1);
|
||||
|
@ -571,9 +557,6 @@ lsquic_stream_destroy (lsquic_stream_t *stream)
|
|||
TAILQ_REMOVE(&stream->conn_pub->service_streams, stream, next_service_stream);
|
||||
if (stream->sm_qflags & SMQF_QPACK_DEC)
|
||||
lsquic_qdh_unref_stream(stream->conn_pub->u.ietf.qdh, stream);
|
||||
if (stream->sm_qflags & SMQF_H3_PRIO)
|
||||
lsquic_prio_tree_remove_stream(stream->conn_pub->u.ietf.prio_tree,
|
||||
stream, lsquic_time_now() /* XXX: do we have to call this? */);
|
||||
drop_buffered_data(stream);
|
||||
lsquic_sfcw_consume_rem(&stream->fc);
|
||||
drop_frames_in(stream);
|
||||
|
@ -3881,7 +3864,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
|
|||
filter->hqfi_flags |= HQFI_FLAG_ERROR;
|
||||
LSQ_INFO("unexpected HTTP/3 frame sequence: %o",
|
||||
filter->hqfi_hist_buf);
|
||||
lconn->cn_if->ci_abort_error(lconn, 1, HEC_UNEXPECTED_FRAME,
|
||||
lconn->cn_if->ci_abort_error(lconn, 1, HEC_FRAME_UNEXPECTED,
|
||||
"unexpected HTTP/3 frame sequence on stream %"PRIu64,
|
||||
stream->id);
|
||||
goto end;
|
||||
|
@ -3905,7 +3888,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
|
|||
lconn = stream->conn_pub->lconn;
|
||||
if (stream->sm_bflags & SMBF_SERVER)
|
||||
lconn->cn_if->ci_abort_error(lconn, 1,
|
||||
HEC_UNEXPECTED_FRAME, "Received PUSH_PROMISE frame "
|
||||
HEC_FRAME_UNEXPECTED, "Received PUSH_PROMISE frame "
|
||||
"on stream %"PRIu64" (clients are not supposed to "
|
||||
"send those)", stream->id);
|
||||
else
|
||||
|
@ -3913,7 +3896,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
|
|||
* push.
|
||||
*/
|
||||
lconn->cn_if->ci_abort_error(lconn, 1,
|
||||
HEC_UNEXPECTED_FRAME, /* TODO: in ID-21 ID_ERROR? */
|
||||
HEC_FRAME_UNEXPECTED,
|
||||
"Received PUSH_PROMISE frame (not supported)"
|
||||
"on stream %"PRIu64, stream->id);
|
||||
goto end;
|
||||
|
@ -3927,7 +3910,6 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
|
|||
case HQFT_GOAWAY:
|
||||
case HQFT_HEADERS:
|
||||
case HQFT_MAX_PUSH_ID:
|
||||
case HQFT_PRIORITY:
|
||||
case HQFT_PUSH_PROMISE:
|
||||
case HQFT_SETTINGS:
|
||||
filter->hqfi_flags |= HQFI_FLAG_ERROR;
|
||||
|
|
|
@ -165,8 +165,6 @@ enum stream_q_flags
|
|||
SMQF_ABORT_CONN = 1 << 8, /* Unrecoverable error occurred */
|
||||
|
||||
SMQF_QPACK_DEC = 1 << 9, /* QPACK decoder is holding a reference to this stream */
|
||||
|
||||
SMQF_H3_PRIO = 1 <<10, /* Referenced from HTTP/3 priority tree */
|
||||
};
|
||||
|
||||
|
||||
|
@ -233,7 +231,6 @@ struct lsquic_stream
|
|||
enum stream_b_flags sm_bflags;
|
||||
enum stream_q_flags sm_qflags;
|
||||
unsigned n_unacked;
|
||||
unsigned sm_h3_prio_idx;
|
||||
|
||||
const struct lsquic_stream_if *stream_if;
|
||||
struct lsquic_stream_ctx *st_ctx;
|
||||
|
|
|
@ -130,7 +130,7 @@ lsquic_tp_encode (const struct transport_params *params,
|
|||
}
|
||||
}
|
||||
|
||||
if (params->tp_disable_migration != TP_DEF_DISABLE_MIGRATION)
|
||||
if (params->tp_disable_active_migration != TP_DEF_DISABLE_ACTIVE_MIGRATION)
|
||||
need += 4 + 0;
|
||||
|
||||
if (need > bufsz || need > UINT16_MAX)
|
||||
|
@ -240,10 +240,10 @@ lsquic_tp_encode (const struct transport_params *params,
|
|||
sizeof(params->tp_preferred_address.srst));
|
||||
}
|
||||
break;
|
||||
case TPI_DISABLE_MIGRATION:
|
||||
if (params->tp_disable_migration != TP_DEF_DISABLE_MIGRATION)
|
||||
case TPI_DISABLE_ACTIVE_MIGRATION:
|
||||
if (params->tp_disable_active_migration != TP_DEF_DISABLE_ACTIVE_MIGRATION)
|
||||
{
|
||||
WRITE_UINT_TO_P(TPI_DISABLE_MIGRATION, 16);
|
||||
WRITE_UINT_TO_P(TPI_DISABLE_ACTIVE_MIGRATION, 16);
|
||||
WRITE_UINT_TO_P(0, 16);
|
||||
}
|
||||
break;
|
||||
|
@ -360,9 +360,9 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
|
|||
{
|
||||
switch (param_id)
|
||||
{
|
||||
case TPI_DISABLE_MIGRATION:
|
||||
case TPI_DISABLE_ACTIVE_MIGRATION:
|
||||
EXPECT_LEN(0);
|
||||
params->tp_disable_migration = 1;
|
||||
params->tp_disable_active_migration = 1;
|
||||
break;
|
||||
case TPI_STATELESS_RESET_TOKEN:
|
||||
/* Client MUST not include reset token,
|
||||
|
@ -476,7 +476,7 @@ lsquic_tp_to_str (const struct transport_params *params, char *buf, size_t sz)
|
|||
WRITE_ONE_PARAM(max_packet_size, "%"PRIu64);
|
||||
WRITE_ONE_PARAM(ack_delay_exponent, "%"PRIu64);
|
||||
WRITE_ONE_PARAM(active_connection_id_limit, "%"PRIu64);
|
||||
WRITE_ONE_PARAM(disable_migration, "%hhd");
|
||||
WRITE_ONE_PARAM(disable_active_migration, "%hhd");
|
||||
#undef SEMICOLON
|
||||
#define SEMICOLON ""
|
||||
WRITE_ONE_PARAM(max_ack_delay, "%"PRIu64);
|
||||
|
|
|
@ -21,7 +21,7 @@ enum transport_param_id
|
|||
TPI_INIT_MAX_STREAMS_UNI = 9,
|
||||
TPI_ACK_DELAY_EXPONENT = 10,
|
||||
TPI_MAX_ACK_DELAY = 11,
|
||||
TPI_DISABLE_MIGRATION = 12,
|
||||
TPI_DISABLE_ACTIVE_MIGRATION = 12,
|
||||
TPI_PREFERRED_ADDRESS = 13,
|
||||
TPI_ACTIVE_CONNECTION_ID_LIMIT = 14,
|
||||
#define MAX_TPI TPI_ACTIVE_CONNECTION_ID_LIMIT
|
||||
|
@ -85,7 +85,7 @@ struct transport_params
|
|||
#define tp_max_ack_delay tp_numerics_u.s.max_ack_delay
|
||||
#define tp_active_connection_id_limit tp_numerics_u.s.active_connection_id_limit
|
||||
|
||||
signed char tp_disable_migration;
|
||||
signed char tp_disable_active_migration;
|
||||
uint8_t tp_stateless_reset_token[IQUIC_SRESET_TOKEN_SZ];
|
||||
struct {
|
||||
uint8_t ipv4_addr[4];
|
||||
|
@ -103,7 +103,7 @@ struct transport_params
|
|||
#define TP_DEF_INIT_MAX_STREAMS_UNI 0
|
||||
#define TP_DEF_INIT_MAX_STREAMS_BIDI 0
|
||||
#define TP_DEF_INIT_MAX_DATA 0
|
||||
#define TP_DEF_DISABLE_MIGRATION 0
|
||||
#define TP_DEF_DISABLE_ACTIVE_MIGRATION 0
|
||||
#define TP_DEF_INIT_MAX_STREAM_DATA_BIDI_LOCAL 0
|
||||
#define TP_DEF_INIT_MAX_STREAM_DATA_BIDI_REMOTE 0
|
||||
#define TP_DEF_INIT_MAX_STREAM_DATA_UNI 0
|
||||
|
@ -123,7 +123,7 @@ struct transport_params
|
|||
.tp_init_max_streams_bidi = TP_DEF_INIT_MAX_STREAMS_BIDI, \
|
||||
.tp_init_max_streams_uni = TP_DEF_INIT_MAX_STREAMS_UNI, \
|
||||
.tp_init_max_data = TP_DEF_INIT_MAX_DATA, \
|
||||
.tp_disable_migration = TP_DEF_DISABLE_MIGRATION, \
|
||||
.tp_disable_active_migration = TP_DEF_DISABLE_ACTIVE_MIGRATION, \
|
||||
.tp_init_max_stream_data_bidi_local = TP_DEF_INIT_MAX_STREAM_DATA_BIDI_LOCAL, \
|
||||
.tp_init_max_stream_data_bidi_remote = TP_DEF_INIT_MAX_STREAM_DATA_BIDI_REMOTE, \
|
||||
.tp_init_max_stream_data_uni = TP_DEF_INIT_MAX_STREAM_DATA_UNI
|
||||
|
|
|
@ -14,7 +14,7 @@ static const unsigned char version_tags[N_LSQVER][4] =
|
|||
#if LSQUIC_USE_Q098
|
||||
[LSQVER_098] = { 'Q', '0', '9', '8', },
|
||||
#endif
|
||||
[LSQVER_ID22] = { 0xFF, 0, 0, 22, },
|
||||
[LSQVER_ID23] = { 0xFF, 0, 0, 23, },
|
||||
[LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, },
|
||||
};
|
||||
|
||||
|
@ -51,7 +51,7 @@ const char *const lsquic_ver2str[N_LSQVER] = {
|
|||
#if LSQUIC_USE_Q098
|
||||
[LSQVER_098] = "Q098",
|
||||
#endif
|
||||
[LSQVER_ID22] = "FF000016",
|
||||
[LSQVER_ID23] = "FF000017",
|
||||
[LSQVER_VERNEG] = "FAFAFAFA",
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ static int
|
|||
select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen, void *arg)
|
||||
{
|
||||
const unsigned char alpn[] = "\x5h3-22";
|
||||
const unsigned char alpn[] = "\x5h3-23";
|
||||
int r;
|
||||
|
||||
r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen,
|
||||
|
|
|
@ -576,6 +576,17 @@ read_one_packet (struct read_iter *iter)
|
|||
, &packs_in->ecn[iter->ri_idx]
|
||||
#endif
|
||||
);
|
||||
#if LSQUIC_ECN_BLACK_HOLE && ECN_SUPPORTED
|
||||
{
|
||||
const char *s;
|
||||
s = getenv("LSQUIC_ECN_BLACK_HOLE");
|
||||
if (s && atoi(s) && packs_in->ecn[iter->ri_idx])
|
||||
{
|
||||
LSQ_NOTICE("ECN blackhole: drop packet");
|
||||
return ROP_OK;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if __linux__
|
||||
if (sport->drop_init)
|
||||
{
|
||||
|
@ -1789,11 +1800,6 @@ set_engine_option (struct lsquic_engine_settings *settings,
|
|||
}
|
||||
break;
|
||||
case 15:
|
||||
if (0 == strncmp(name, "h3_placeholders", 15))
|
||||
{
|
||||
settings->es_h3_placeholders = atoi(val);
|
||||
return 0;
|
||||
}
|
||||
if (0 == strncmp(name, "allow_migration", 15))
|
||||
{
|
||||
settings->es_allow_migration = atoi(val);
|
||||
|
|
|
@ -44,7 +44,6 @@ SET(TESTS
|
|||
frame_writer
|
||||
goaway_gquic_be
|
||||
h3_framing
|
||||
h3_prio
|
||||
hcsi_reader
|
||||
hkdf
|
||||
lsquic_hash
|
||||
|
@ -52,7 +51,6 @@ SET(TESTS
|
|||
packno_len
|
||||
parse
|
||||
parse_packet_in
|
||||
parse_prio
|
||||
purga
|
||||
qlog
|
||||
quic_be_floats
|
||||
|
|
|
@ -180,7 +180,7 @@ run_test (const struct test *test)
|
|||
size_t sz;
|
||||
unsigned char buf[0x1000];
|
||||
|
||||
pf = select_pf_by_ver(LSQVER_ID22);
|
||||
pf = select_pf_by_ver(LSQVER_ID23);
|
||||
if (!test->skip_gen)
|
||||
{
|
||||
rechist.acki = &test->acki;
|
||||
|
|
|
@ -33,7 +33,7 @@ struct test {
|
|||
static const struct test tests[] = {
|
||||
|
||||
{ .lineno = __LINE__,
|
||||
.pf = select_pf_by_ver(LSQVER_ID22),
|
||||
.pf = select_pf_by_ver(LSQVER_ID23),
|
||||
.offset = 0,
|
||||
.data_sz = 10,
|
||||
.data = "0123456789",
|
||||
|
@ -49,7 +49,7 @@ static const struct test tests[] = {
|
|||
},
|
||||
|
||||
{ .lineno = __LINE__,
|
||||
.pf = select_pf_by_ver(LSQVER_ID22),
|
||||
.pf = select_pf_by_ver(LSQVER_ID23),
|
||||
.offset = 500,
|
||||
.data_sz = 10,
|
||||
.data = "0123456789",
|
||||
|
|
|
@ -55,9 +55,8 @@
|
|||
#include "lsquic_varint.h"
|
||||
#include "lsquic_hq.h"
|
||||
#include "lsquic_data_in_if.h"
|
||||
#include "lsquic_h3_prio.h"
|
||||
|
||||
static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_ID22);
|
||||
static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_ID23);
|
||||
|
||||
struct test_ctl_settings
|
||||
{
|
||||
|
@ -298,7 +297,7 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
|
|||
memset(tobjs, 0, sizeof(*tobjs));
|
||||
LSCONN_INITIALIZE(&tobjs->lconn);
|
||||
tobjs->lconn.cn_pf = g_pf;
|
||||
tobjs->lconn.cn_version = LSQVER_ID22;
|
||||
tobjs->lconn.cn_version = LSQVER_ID23;
|
||||
tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1;
|
||||
network_path.np_pack_size = packet_sz;
|
||||
tobjs->lconn.cn_if = &our_conn_if;
|
||||
|
@ -331,9 +330,6 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
|
|||
assert(0 == s);
|
||||
tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh;
|
||||
}
|
||||
if (tobjs->ctor_flags & SCF_IETF)
|
||||
tobjs->conn_pub.u.ietf.prio_tree
|
||||
= lsquic_prio_tree_new(&tobjs->lconn, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -346,8 +342,6 @@ deinit_test_objs (struct test_objs *tobjs)
|
|||
lsquic_mm_cleanup(&tobjs->eng_pub.enp_mm);
|
||||
if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS)
|
||||
lsquic_qeh_cleanup(&tobjs->qeh);
|
||||
if (tobjs->ctor_flags & SCF_IETF)
|
||||
lsquic_prio_tree_destroy(tobjs->conn_pub.u.ietf.prio_tree);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,977 +0,0 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include "lsquic.h"
|
||||
#include "lsquic_types.h"
|
||||
#include "lsquic_int_types.h"
|
||||
#include "lsquic_hash.h"
|
||||
#include "lsquic_conn.h"
|
||||
#include "lsquic_h3_prio.h"
|
||||
#include "lsquic_sfcw.h"
|
||||
#include "lsquic_varint.h"
|
||||
#include "lsquic_hq.h"
|
||||
#include "lsquic_hash.h"
|
||||
#include "lsquic_stream.h"
|
||||
|
||||
|
||||
static struct conn_cid_elem cces[1] = {{ .cce_cid = { .len = 8, }, }};
|
||||
static struct lsquic_conn s_conn = { .cn_cces = cces, .cn_cces_mask = 1, };
|
||||
|
||||
static struct lsquic_stream s_streams[20];
|
||||
static struct lsquic_stream *s_next_stream;
|
||||
|
||||
struct set_rel_args
|
||||
{
|
||||
enum h3_elem_type parent_type;
|
||||
uint64_t parent_id;
|
||||
enum h3_elem_type child_type;
|
||||
uint64_t child_id;
|
||||
h3_weight_t child_weight;
|
||||
};
|
||||
|
||||
#define EXP_ARGS(ra) (ra)->child_type, (ra)->child_id, (ra)->child_weight, \
|
||||
(ra)->parent_type, (ra)->parent_id
|
||||
|
||||
struct add_stream_args
|
||||
{
|
||||
enum h3_elem_type parent_type;
|
||||
uint64_t parent_id;
|
||||
h3_weight_t weight;
|
||||
};
|
||||
|
||||
#define EXP_ADD_STREAM_ARGS(a) stream, (a)->parent_type, (a)->parent_id, \
|
||||
(a)->weight
|
||||
|
||||
struct remove_stream_args
|
||||
{
|
||||
struct lsquic_stream *stream;
|
||||
lsquic_time_t now;
|
||||
};
|
||||
|
||||
#define EXP_REMOVE_STREAM_ARGS(a) stream, (a)->now
|
||||
|
||||
struct prune_args
|
||||
{
|
||||
lsquic_time_t cutoff;
|
||||
};
|
||||
|
||||
#define EXP_PRUNE_ARGS(a) (a)->cutoff
|
||||
|
||||
enum tree_test_id {
|
||||
TEST_ID_UNSPECIFIED,
|
||||
TEST_ID_RFC7540_EXAMPLE,
|
||||
TEST_ID_PRE_PRUNE,
|
||||
TEST_ID_SIMPLE_TREE_1,
|
||||
TEST_ID_PRUNE,
|
||||
TEST_ID_TOP_LEVEL_PLACEHOLDERS,
|
||||
};
|
||||
|
||||
struct step
|
||||
{
|
||||
enum {
|
||||
LAST_STEP = 0,
|
||||
REPLAY_TEST,
|
||||
CALL_SET_REL,
|
||||
CALL_ADD_STREAM,
|
||||
CALL_REMOVE_STREAM,
|
||||
CALL_PRUNE,
|
||||
} call;
|
||||
int retval;
|
||||
union {
|
||||
struct set_rel_args set_rel;
|
||||
struct add_stream_args add_stream;
|
||||
struct remove_stream_args remove_stream;
|
||||
struct prune_args prune;
|
||||
enum tree_test_id test_id;
|
||||
} args;
|
||||
union {
|
||||
struct {
|
||||
lsquic_stream_id_t stream_id;
|
||||
} add_stream;
|
||||
struct {
|
||||
lsquic_stream_id_t stream_id;
|
||||
} remove_stream;
|
||||
} ctx;
|
||||
const char *result; /* Optional */
|
||||
};
|
||||
|
||||
struct tree_test
|
||||
{
|
||||
int lineno;
|
||||
enum tree_test_id test_id;
|
||||
unsigned n_placeholders;
|
||||
struct step steps[20];
|
||||
const char *result; /* Optional */
|
||||
};
|
||||
|
||||
static const struct tree_test tree_tests[] =
|
||||
{
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.result = "(t: R; id: 0; w: 0)",
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.test_id = TEST_ID_SIMPLE_TREE_1,
|
||||
.n_placeholders = 20,
|
||||
.steps = {
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_ROOT, 0, H3ET_REQ_STREAM, 1, 22, },
|
||||
.result = "(t: R; id: 0; w: 0; c: [(t: Q; id: 1; w: 22)])",
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .parent_type = H3ET_ROOT, .parent_id = 0, .weight = 77, },
|
||||
.ctx.add_stream.stream_id = 1,
|
||||
.result = "(t: R; id: 0; w: 0; c: [(t: Q; id: 1; w: 22)])",
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .parent_type = H3ET_ROOT, .parent_id = 0, .weight = 77, },
|
||||
.ctx.add_stream.stream_id = 1,
|
||||
.retval = -1,
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_ROOT, 0, H3ET_REQ_STREAM, 1, 23, },
|
||||
.result = "(t: R; id: 0; w: 0; c: [(t: Q; id: 1; w: 23)])",
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_PLACEHOLDER, 0, H3ET_REQ_STREAM, 1, 23, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: H; id: 0; w: 240; c: ["
|
||||
"(t: Q; id: 1; w: 23)"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .parent_type = H3ET_PLACEHOLDER, .parent_id = 0, .weight = 77, },
|
||||
.ctx.add_stream.stream_id = 2,
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: H; id: 0; w: 240; c: ["
|
||||
"(t: P; id: 2; w: 77),"
|
||||
"(t: Q; id: 1; w: 23)"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_REQ_STREAM, 1, H3ET_PUSH_STREAM, 2, 77, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: H; id: 0; w: 240; c: ["
|
||||
"(t: Q; id: 1; w: 23; c: ["
|
||||
"(t: P; id: 2; w: 77)"
|
||||
"])"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_REQ_STREAM, 1, H3ET_PLACEHOLDER, 2, 77, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: H; id: 0; w: 240; c: ["
|
||||
"(t: Q; id: 1; w: 23; c: ["
|
||||
"(t: H; id: 2; w: 77),"
|
||||
"(t: P; id: 2; w: 77)"
|
||||
"])"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_PLACEHOLDER, 0, H3ET_PUSH_STREAM, 3, 100, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: H; id: 0; w: 240; c: ["
|
||||
"(t: P; id: 3; w: 100),"
|
||||
"(t: Q; id: 1; w: 23; c: ["
|
||||
"(t: H; id: 2; w: 77),"
|
||||
"(t: P; id: 2; w: 77)"
|
||||
"])"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.n_placeholders = 20,
|
||||
.steps = {
|
||||
{
|
||||
.call = REPLAY_TEST,
|
||||
.args.test_id = TEST_ID_SIMPLE_TREE_1,
|
||||
},
|
||||
{
|
||||
.call = CALL_REMOVE_STREAM,
|
||||
.args.remove_stream = { .now = 100, },
|
||||
.ctx.remove_stream.stream_id = 2,
|
||||
},
|
||||
{
|
||||
.call = CALL_REMOVE_STREAM,
|
||||
.args.remove_stream = { .now = 100, },
|
||||
.ctx.remove_stream.stream_id = 2,
|
||||
.retval = -1,
|
||||
},
|
||||
{
|
||||
.call = CALL_PRUNE,
|
||||
.args.prune = { .cutoff = 101, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: H; id: 0; w: 240; c: ["
|
||||
"(t: P; id: 3; w: 100),"
|
||||
"(t: Q; id: 1; w: 23; c: ["
|
||||
"(t: H; id: 2; w: 77)"
|
||||
"])"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
/* From RFC 7540, Section 5.3.3:
|
||||
*
|
||||
* If a stream is made dependent on one of its own dependencies, the
|
||||
* formerly dependent stream is first moved to be dependent on the
|
||||
* reprioritized stream's previous parent. The moved dependency retains
|
||||
* its weight.
|
||||
*
|
||||
* For example, consider an original dependency tree where B and C
|
||||
* depend on A, D and E depend on C, and F depends on D. If A is made
|
||||
* dependent on D, then D takes the place of A. All other dependency
|
||||
* relationships stay the same, except for F, which becomes dependent on
|
||||
* A if the reprioritization is exclusive.
|
||||
*
|
||||
* x x x x
|
||||
* | / \ | |
|
||||
* A D A D D
|
||||
* / \ / / \ / \ |
|
||||
* B C ==> F B C ==> F A OR A
|
||||
* / \ | / \ /|\
|
||||
* D E E B C B C F
|
||||
* | | |
|
||||
* F E E
|
||||
* (intermediate) (non-exclusive) (exclusive)
|
||||
*
|
||||
* Figure 5: Example of Dependency Reordering
|
||||
*/
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.test_id = TEST_ID_RFC7540_EXAMPLE,
|
||||
.steps = {
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_ROOT, 0, H3ET_REQ_STREAM, 'A', 0, },
|
||||
.result = "(t: R; id: 0; w: 0; c: [(t: Q; id: 65; w: 0)])",
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_REQ_STREAM, 'A', H3ET_PUSH_STREAM, 'C', 0, },
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_REQ_STREAM, 'A', H3ET_PUSH_STREAM, 'B', 0, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: Q; id: 65; w: 0; c: ["
|
||||
"(t: P; id: 66; w: 0),"
|
||||
"(t: P; id: 67; w: 0)"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_PUSH_STREAM, 'C', H3ET_REQ_STREAM, 'E', 0, },
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_PUSH_STREAM, 'C', H3ET_REQ_STREAM, 'D', 0, },
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_REQ_STREAM, 'D', H3ET_PUSH_STREAM, 'F', 0, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: Q; id: 65; w: 0; c: ["
|
||||
"(t: P; id: 66; w: 0),"
|
||||
"(t: P; id: 67; w: 0; c: ["
|
||||
"(t: Q; id: 68; w: 0; c: ["
|
||||
"(t: P; id: 70; w: 0)"
|
||||
"]),"
|
||||
"(t: Q; id: 69; w: 0)"
|
||||
"])"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
/*
|
||||
* Now that the state corresponds to the picture on the left,
|
||||
* flip A and D. We want to have same structure as picture #3
|
||||
* (non-exclusive). The order of A and F is flipped, however,
|
||||
* as new elements are inserted at the head of the child list.
|
||||
*/
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_REQ_STREAM, 'D', H3ET_REQ_STREAM, 'A', 0, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: Q; id: 68; w: 0; c: [" /* D */
|
||||
"(t: Q; id: 65; w: 0; c: [" /* A */
|
||||
"(t: P; id: 66; w: 0)," /* B */
|
||||
"(t: P; id: 67; w: 0; c: [" /* C */
|
||||
"(t: Q; id: 69; w: 0)" /* E */
|
||||
"])"
|
||||
"]),"
|
||||
"(t: P; id: 70; w: 0)" /* F */
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.test_id = TEST_ID_PRE_PRUNE,
|
||||
.steps = {
|
||||
{
|
||||
.call = REPLAY_TEST,
|
||||
.args.test_id = TEST_ID_RFC7540_EXAMPLE,
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: Q; id: 68; w: 0; c: [" /* D */
|
||||
"(t: Q; id: 65; w: 0; c: [" /* A */
|
||||
"(t: P; id: 66; w: 0)," /* B */
|
||||
"(t: P; id: 67; w: 0; c: [" /* C */
|
||||
"(t: Q; id: 69; w: 0)" /* E */
|
||||
"])"
|
||||
"]),"
|
||||
"(t: P; id: 70; w: 0)" /* F */
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .parent_type = H3ET_ROOT, .parent_id = 0, .weight = 123, },
|
||||
.ctx.add_stream.stream_id = 71,
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: P; id: 71; w: 123),"
|
||||
"(t: Q; id: 68; w: 0; c: [" /* D */
|
||||
"(t: Q; id: 65; w: 0; c: [" /* A */
|
||||
"(t: P; id: 66; w: 0)," /* B */
|
||||
"(t: P; id: 67; w: 0; c: [" /* C */
|
||||
"(t: Q; id: 69; w: 0)" /* E */
|
||||
"])"
|
||||
"]),"
|
||||
"(t: P; id: 70; w: 0)" /* F */
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .parent_type = H3ET_REQ_STREAM, .parent_id = 65, .weight = 29, },
|
||||
.ctx.add_stream.stream_id = 72,
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .parent_type = H3ET_REQ_STREAM, .parent_id = 68, .weight = 6, },
|
||||
.ctx.add_stream.stream_id = 73,
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: P; id: 71; w: 123),"
|
||||
"(t: Q; id: 68; w: 0; c: [" /* D */
|
||||
"(t: Q; id: 73; w: 6),"
|
||||
"(t: Q; id: 65; w: 0; c: [" /* A */
|
||||
"(t: Q; id: 72; w: 29),"
|
||||
"(t: P; id: 66; w: 0)," /* B */
|
||||
"(t: P; id: 67; w: 0; c: [" /* C */
|
||||
"(t: Q; id: 69; w: 0)" /* E */
|
||||
"])"
|
||||
"]),"
|
||||
"(t: P; id: 70; w: 0)" /* F */
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .weight = 123, },
|
||||
.ctx.add_stream.stream_id = 65,
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .weight = 123, },
|
||||
.ctx.add_stream.stream_id = 66,
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .weight = 123, },
|
||||
.ctx.add_stream.stream_id = 67,
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .weight = 123, },
|
||||
.ctx.add_stream.stream_id = 68,
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .weight = 123, },
|
||||
.ctx.add_stream.stream_id = 69,
|
||||
},
|
||||
{
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .weight = 123, },
|
||||
.ctx.add_stream.stream_id = 70,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.test_id = TEST_ID_PRUNE,
|
||||
.steps = {
|
||||
{
|
||||
.call = REPLAY_TEST,
|
||||
.args.test_id = TEST_ID_PRE_PRUNE,
|
||||
},
|
||||
{
|
||||
.call = CALL_REMOVE_STREAM,
|
||||
.args.remove_stream = { .now = 100, },
|
||||
.ctx.remove_stream.stream_id = 65,
|
||||
},
|
||||
{
|
||||
.call = CALL_PRUNE,
|
||||
.args.prune = { .cutoff = 101, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: P; id: 71; w: 123),"
|
||||
"(t: Q; id: 68; w: 0; c: ["
|
||||
"(t: Q; id: 73; w: 6),"
|
||||
"(t: Q; id: 72; w: 29),"
|
||||
"(t: P; id: 66; w: 0),"
|
||||
"(t: P; id: 67; w: 0; c: ["
|
||||
"(t: Q; id: 69; w: 0)"
|
||||
"]),"
|
||||
"(t: P; id: 70; w: 0)"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_REMOVE_STREAM,
|
||||
.args.remove_stream = { .now = 200, },
|
||||
.ctx.remove_stream.stream_id = 66,
|
||||
},
|
||||
{
|
||||
.call = CALL_REMOVE_STREAM,
|
||||
.args.remove_stream = { .now = 210, },
|
||||
.ctx.remove_stream.stream_id = 67,
|
||||
},
|
||||
{
|
||||
.call = CALL_REMOVE_STREAM,
|
||||
.args.remove_stream = { .now = 220, },
|
||||
.ctx.remove_stream.stream_id = 73,
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: P; id: 71; w: 123),"
|
||||
"(t: Q; id: 68; w: 0; c: ["
|
||||
"(t: Q; id: 73; w: 6),"
|
||||
"(t: Q; id: 72; w: 29),"
|
||||
"(t: P; id: 66; w: 0),"
|
||||
"(t: P; id: 67; w: 0; c: ["
|
||||
"(t: Q; id: 69; w: 0)"
|
||||
"]),"
|
||||
"(t: P; id: 70; w: 0)"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_PRUNE,
|
||||
.args.prune = { .cutoff = 201, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: P; id: 71; w: 123),"
|
||||
"(t: Q; id: 68; w: 0; c: ["
|
||||
"(t: Q; id: 73; w: 6),"
|
||||
"(t: Q; id: 72; w: 29),"
|
||||
"(t: P; id: 67; w: 0; c: ["
|
||||
"(t: Q; id: 69; w: 0)"
|
||||
"]),"
|
||||
"(t: P; id: 70; w: 0)"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_PRUNE,
|
||||
.args.prune = { .cutoff = 211, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: P; id: 71; w: 123),"
|
||||
"(t: Q; id: 68; w: 0; c: ["
|
||||
"(t: Q; id: 73; w: 6),"
|
||||
"(t: Q; id: 72; w: 29),"
|
||||
"(t: Q; id: 69; w: 0),"
|
||||
"(t: P; id: 70; w: 0)"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_PRUNE,
|
||||
.args.prune = { .cutoff = 225, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: P; id: 71; w: 123),"
|
||||
"(t: Q; id: 68; w: 0; c: ["
|
||||
"(t: Q; id: 72; w: 29),"
|
||||
"(t: Q; id: 69; w: 0),"
|
||||
"(t: P; id: 70; w: 0)"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_REMOVE_STREAM,
|
||||
.args.remove_stream = { .now = 230, },
|
||||
.ctx.remove_stream.stream_id = 71,
|
||||
},
|
||||
{
|
||||
.call = CALL_PRUNE,
|
||||
.args.prune = { .cutoff = 235, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: Q; id: 68; w: 0; c: ["
|
||||
"(t: Q; id: 72; w: 29),"
|
||||
"(t: Q; id: 69; w: 0),"
|
||||
"(t: P; id: 70; w: 0)"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_REMOVE_STREAM,
|
||||
.args.remove_stream = { .now = 240, },
|
||||
.ctx.remove_stream.stream_id = 68,
|
||||
},
|
||||
{
|
||||
.call = CALL_PRUNE,
|
||||
.args.prune = { .cutoff = 245, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: Q; id: 72; w: 29),"
|
||||
"(t: Q; id: 69; w: 0),"
|
||||
"(t: P; id: 70; w: 0)"
|
||||
"])",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.n_placeholders = 20,
|
||||
.steps = {
|
||||
{
|
||||
.call = REPLAY_TEST,
|
||||
.args.test_id = TEST_ID_RFC7540_EXAMPLE,
|
||||
},
|
||||
{
|
||||
/* Test adding stream that depends on an non-existent
|
||||
* placeholder
|
||||
*/
|
||||
.call = CALL_ADD_STREAM,
|
||||
.args.add_stream = { .parent_type = H3ET_PLACEHOLDER, .parent_id = 9, .weight = 33, },
|
||||
.ctx.add_stream.stream_id = 71,
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: H; id: 9; w: 240; c: ["
|
||||
"(t: P; id: 71; w: 33)"
|
||||
"]),"
|
||||
"(t: Q; id: 68; w: 0; c: [" /* D */
|
||||
"(t: Q; id: 65; w: 0; c: [" /* A */
|
||||
"(t: P; id: 66; w: 0)," /* B */
|
||||
"(t: P; id: 67; w: 0; c: [" /* C */
|
||||
"(t: Q; id: 69; w: 0)" /* E */
|
||||
"])"
|
||||
"]),"
|
||||
"(t: P; id: 70; w: 0)" /* F */
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.n_placeholders = 20,
|
||||
.test_id = TEST_ID_TOP_LEVEL_PLACEHOLDERS,
|
||||
.steps = {
|
||||
{
|
||||
.call = REPLAY_TEST,
|
||||
.args.test_id = TEST_ID_RFC7540_EXAMPLE,
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_PLACEHOLDER, 1, H3ET_REQ_STREAM, 68, 0, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: H; id: 1; w: 240; c: ["
|
||||
"(t: Q; id: 68; w: 0; c: [" /* D */
|
||||
"(t: Q; id: 65; w: 0; c: [" /* A */
|
||||
"(t: P; id: 66; w: 0)," /* B */
|
||||
"(t: P; id: 67; w: 0; c: [" /* C */
|
||||
"(t: Q; id: 69; w: 0)" /* E */
|
||||
"])"
|
||||
"]),"
|
||||
"(t: P; id: 70; w: 0)" /* F */
|
||||
"])"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
{
|
||||
.call = CALL_SET_REL,
|
||||
.args.set_rel = { H3ET_PLACEHOLDER, 2, H3ET_PUSH_STREAM, 70, 0, },
|
||||
.result = "(t: R; id: 0; w: 0; c: ["
|
||||
"(t: H; id: 2; w: 240; c: ["
|
||||
"(t: P; id: 70; w: 0)" /* F */
|
||||
"]),"
|
||||
"(t: H; id: 1; w: 240; c: ["
|
||||
"(t: Q; id: 68; w: 0; c: [" /* D */
|
||||
"(t: Q; id: 65; w: 0; c: [" /* A */
|
||||
"(t: P; id: 66; w: 0)," /* B */
|
||||
"(t: P; id: 67; w: 0; c: [" /* C */
|
||||
"(t: Q; id: 69; w: 0)" /* E */
|
||||
"])"
|
||||
"])"
|
||||
"])"
|
||||
"])"
|
||||
"])",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
struct iter_test
|
||||
{
|
||||
int lineno;
|
||||
enum tree_test_id tree_test_id;
|
||||
unsigned n_streams;
|
||||
lsquic_stream_id_t in[20], out[20];
|
||||
};
|
||||
|
||||
static const struct iter_test iter_tests[] =
|
||||
{
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_RFC7540_EXAMPLE,
|
||||
.n_streams = 1,
|
||||
.in = { 70, },
|
||||
.out = { 70, },
|
||||
},
|
||||
|
||||
/* 65 and 70 have the same parent and weight: so the iterator should
|
||||
* return them in the order in which they were added to the iterator.
|
||||
*/
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_RFC7540_EXAMPLE,
|
||||
.n_streams = 2,
|
||||
.in = { 70, 65, },
|
||||
.out = { 70, 65, },
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_RFC7540_EXAMPLE,
|
||||
.n_streams = 2,
|
||||
.in = { 65, 70, },
|
||||
.out = { 65, 70, },
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_RFC7540_EXAMPLE,
|
||||
.n_streams = 6,
|
||||
.in = { 65, 66, 67, 68, 69, 70, },
|
||||
.out = { 68, 65, 70, 66, 67, 69, },
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_RFC7540_EXAMPLE,
|
||||
.n_streams = 2,
|
||||
.in = { 69, 68, },
|
||||
.out = { 68, 69, },
|
||||
},
|
||||
|
||||
/* Streams 1 and 3 are on the same level, but have different weight.
|
||||
* Order of interator insertion should not matter.
|
||||
*/
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_SIMPLE_TREE_1,
|
||||
.n_streams = 3,
|
||||
.in = { 1, 2, 3, },
|
||||
.out = { 1, 3, 2, },
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_SIMPLE_TREE_1,
|
||||
.n_streams = 3,
|
||||
.in = { 3, 2, 1, },
|
||||
.out = { 1, 3, 2, },
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
struct crit_test
|
||||
{
|
||||
int lineno;
|
||||
enum tree_test_id tree_test_id;
|
||||
lsquic_stream_id_t add_streams[10]; /* 0 means last */
|
||||
lsquic_stream_id_t expected;
|
||||
};
|
||||
|
||||
static const struct crit_test crit_tests[] =
|
||||
{
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_RFC7540_EXAMPLE,
|
||||
.add_streams = { 65, 66, 67, 68, 69, 70, },
|
||||
.expected = 68,
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_PRUNE,
|
||||
.expected = 69,
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_TOP_LEVEL_PLACEHOLDERS,
|
||||
.expected = ~0ull,
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_TOP_LEVEL_PLACEHOLDERS,
|
||||
.add_streams = { 69, },
|
||||
.expected = 69,
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_TOP_LEVEL_PLACEHOLDERS,
|
||||
.add_streams = { 69, 70, },
|
||||
.expected = 70,
|
||||
},
|
||||
|
||||
{
|
||||
.lineno = __LINE__,
|
||||
.tree_test_id = TEST_ID_TOP_LEVEL_PLACEHOLDERS,
|
||||
.add_streams = { 69, 68, },
|
||||
.expected = 68,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
enum run_step_flags
|
||||
{
|
||||
RUN_STEP_CHECK_RES = 1 << 0,
|
||||
};
|
||||
|
||||
static void
|
||||
replay_test (struct h3_prio_tree *tree, enum tree_test_id test_id);
|
||||
|
||||
static void
|
||||
run_step (struct h3_prio_tree *tree, const struct step *step,
|
||||
enum run_step_flags flags)
|
||||
{
|
||||
int s;
|
||||
struct lsquic_stream *stream;
|
||||
char buf[0x1000];
|
||||
|
||||
switch (step->call)
|
||||
{
|
||||
case CALL_SET_REL:
|
||||
s = lsquic_prio_tree_set_rel(tree, EXP_ARGS(&step->args.set_rel));
|
||||
break;
|
||||
case CALL_ADD_STREAM:
|
||||
stream = s_next_stream++;
|
||||
assert(stream < s_streams + sizeof(s_streams) / sizeof(s_streams[0]));
|
||||
memset(stream, 0, sizeof(*stream));
|
||||
stream->id = step->ctx.add_stream.stream_id;
|
||||
s = lsquic_prio_tree_add_stream(tree,
|
||||
EXP_ADD_STREAM_ARGS(&step->args.add_stream));
|
||||
break;
|
||||
case CALL_REMOVE_STREAM:
|
||||
for (stream = s_streams; stream < s_next_stream; ++stream)
|
||||
if (stream->id == step->ctx.remove_stream.stream_id)
|
||||
break;
|
||||
assert(stream);
|
||||
s = lsquic_prio_tree_remove_stream(tree,
|
||||
EXP_REMOVE_STREAM_ARGS(&step->args.remove_stream));
|
||||
break;
|
||||
case CALL_PRUNE:
|
||||
lsquic_prio_tree_prune(tree, EXP_PRUNE_ARGS(&step->args.prune));
|
||||
s = 0; /* prune is a void function call */
|
||||
break;
|
||||
case REPLAY_TEST:
|
||||
replay_test(tree, step->args.test_id);
|
||||
s = 0;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
assert(step->retval == s);
|
||||
if ((flags & RUN_STEP_CHECK_RES) && step->result)
|
||||
{
|
||||
memset(buf, 0, sizeof(buf));
|
||||
(void) lsquic_prio_tree_to_str(tree, buf, sizeof(buf));
|
||||
assert(0 == strcmp(step->result, buf));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
replay_test (struct h3_prio_tree *tree, enum tree_test_id test_id)
|
||||
{
|
||||
const struct tree_test *test;
|
||||
const struct step *step;
|
||||
|
||||
for (test = tree_tests; test < tree_tests + sizeof(tree_tests) / sizeof(tree_tests[0]); ++test)
|
||||
if (test->test_id == test_id)
|
||||
break;
|
||||
|
||||
assert(test);
|
||||
|
||||
for (step = test->steps; step->call != LAST_STEP; ++step)
|
||||
run_step(tree, step, 0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run_test (const struct tree_test *test)
|
||||
{
|
||||
struct h3_prio_tree *tree;
|
||||
const struct step *step;
|
||||
s_next_stream = s_streams;
|
||||
char buf[0x1000];
|
||||
|
||||
tree = lsquic_prio_tree_new(&s_conn, test->n_placeholders);
|
||||
|
||||
for (step = test->steps; step->call != LAST_STEP; ++step)
|
||||
run_step(tree, step, RUN_STEP_CHECK_RES);
|
||||
|
||||
if (test->result)
|
||||
{
|
||||
memset(buf, 0, sizeof(buf));
|
||||
(void) lsquic_prio_tree_to_str(tree, buf, sizeof(buf));
|
||||
assert(0 == strcmp(test->result, buf));
|
||||
}
|
||||
|
||||
lsquic_prio_tree_destroy(tree);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run_iter_test (const struct iter_test *test)
|
||||
{
|
||||
struct h3_prio_tree *tree;
|
||||
const struct tree_test *tree_test;
|
||||
const struct step *step;
|
||||
struct lsquic_stream *stream;
|
||||
unsigned n;
|
||||
int s;
|
||||
|
||||
for (tree_test = tree_tests; tree_test < tree_tests + sizeof(tree_tests)
|
||||
/ sizeof(tree_tests[0]); ++tree_test)
|
||||
if (tree_test->test_id == test->tree_test_id)
|
||||
break;
|
||||
|
||||
assert(tree_test);
|
||||
|
||||
s_next_stream = s_streams;
|
||||
tree = lsquic_prio_tree_new(&s_conn, tree_test->n_placeholders);
|
||||
for (step = tree_test->steps; step->call != LAST_STEP; ++step)
|
||||
run_step(tree, step, RUN_STEP_CHECK_RES);
|
||||
|
||||
lsquic_prio_tree_iter_reset(tree, "test");
|
||||
for (n = 0; n < test->n_streams; ++n)
|
||||
{
|
||||
for (stream = s_streams; stream < s_next_stream; ++stream)
|
||||
if (stream->id == test->in[n])
|
||||
break;
|
||||
if (stream >= s_next_stream)
|
||||
{
|
||||
stream = s_next_stream++;
|
||||
memset(stream, 0, sizeof(*stream));
|
||||
stream->id = test->in[n];
|
||||
/* We assume the relationship is already in there */
|
||||
s = lsquic_prio_tree_add_stream(tree, stream, 0, 0, 0);
|
||||
assert(0 == s);
|
||||
}
|
||||
lsquic_prio_tree_iter_add(tree, stream);
|
||||
}
|
||||
|
||||
for (n = 0; n < test->n_streams; ++n)
|
||||
{
|
||||
stream = lsquic_prio_tree_iter_next(tree);
|
||||
assert(stream);
|
||||
assert(stream->id == test->out[n]);
|
||||
}
|
||||
stream = lsquic_prio_tree_iter_next(tree);
|
||||
assert(NULL == stream);
|
||||
|
||||
lsquic_prio_tree_destroy(tree);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run_crit_test (const struct crit_test *test)
|
||||
{
|
||||
struct h3_prio_tree *tree;
|
||||
const struct tree_test *tree_test;
|
||||
const struct step *step;
|
||||
struct lsquic_stream *stream;
|
||||
const lsquic_stream_id_t *stream_id;
|
||||
int s;
|
||||
|
||||
for (tree_test = tree_tests; tree_test < tree_tests + sizeof(tree_tests)
|
||||
/ sizeof(tree_tests[0]); ++tree_test)
|
||||
if (tree_test->test_id == test->tree_test_id)
|
||||
break;
|
||||
|
||||
assert(tree_test);
|
||||
|
||||
s_next_stream = s_streams;
|
||||
tree = lsquic_prio_tree_new(&s_conn, tree_test->n_placeholders);
|
||||
for (step = tree_test->steps; step->call != LAST_STEP; ++step)
|
||||
run_step(tree, step, RUN_STEP_CHECK_RES);
|
||||
|
||||
for (stream_id = test->add_streams; *stream_id; ++stream_id)
|
||||
{
|
||||
stream = s_next_stream++;
|
||||
memset(stream, 0, sizeof(*stream));
|
||||
stream->id = *stream_id;
|
||||
/* We assume the relationship is already in there */
|
||||
s = lsquic_prio_tree_add_stream(tree, stream, 0, 0, 0);
|
||||
assert(0 == s);
|
||||
}
|
||||
|
||||
stream = lsquic_prio_tree_highest_non_crit(tree);
|
||||
if (test->expected != ~0ull)
|
||||
assert(stream->id == test->expected);
|
||||
else
|
||||
assert(!stream);
|
||||
|
||||
lsquic_prio_tree_destroy(tree);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
const struct tree_test *tree_test;
|
||||
const struct iter_test *iter_test;
|
||||
const struct crit_test *crit_test;
|
||||
|
||||
for (tree_test = tree_tests; tree_test < tree_tests + sizeof(tree_tests)
|
||||
/ sizeof(tree_tests[0]); ++tree_test)
|
||||
run_test(tree_test);
|
||||
for (iter_test = iter_tests; iter_test < iter_tests + sizeof(iter_tests)
|
||||
/ sizeof(iter_tests[0]); ++iter_test)
|
||||
run_iter_test(iter_test);
|
||||
for (crit_test = crit_tests; crit_test < crit_tests + sizeof(crit_tests)
|
||||
/ sizeof(crit_tests[0]); ++crit_test)
|
||||
run_crit_test(crit_test);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -28,71 +28,6 @@ struct test
|
|||
|
||||
static const struct test tests[] =
|
||||
{
|
||||
{
|
||||
__LINE__,
|
||||
{
|
||||
0x02,
|
||||
0x05,
|
||||
/* The `1' used to be the `exclusive' flag. It is left here to
|
||||
* test that the frame reader ignores these Emtpy bits.
|
||||
*/
|
||||
(H3PET_REQ_STREAM << HQ_PT_SHIFT) | (H3DET_PLACEHOLDER << HQ_DT_SHIFT) | 1,
|
||||
0x03,
|
||||
0x40, 0x33,
|
||||
0x77,
|
||||
},
|
||||
7,
|
||||
0,
|
||||
"on_priority: request stream #3 depends on placeholder #51; weight: 120\n",
|
||||
},
|
||||
|
||||
{
|
||||
__LINE__,
|
||||
{
|
||||
0x02,
|
||||
0x03,
|
||||
(H3PET_REQ_STREAM << HQ_PT_SHIFT) | (H3DET_ROOT << HQ_DT_SHIFT),
|
||||
0x03,
|
||||
0x77,
|
||||
},
|
||||
5,
|
||||
0,
|
||||
"on_priority: request stream #3 depends on root; weight: 120\n",
|
||||
},
|
||||
|
||||
{
|
||||
__LINE__,
|
||||
{
|
||||
0x02,
|
||||
0x04,
|
||||
/* The `1' used to be the `exclusive' flag. It is left here to
|
||||
* test that the frame reader ignores these Emtpy bits.
|
||||
*/
|
||||
(H3PET_CUR_STREAM << HQ_PT_SHIFT) | (H3DET_PLACEHOLDER << HQ_DT_SHIFT),
|
||||
0x40, 0x33,
|
||||
0x77,
|
||||
},
|
||||
6,
|
||||
0,
|
||||
"on_priority: current stream depends on placeholder #51; weight: 120\n",
|
||||
},
|
||||
|
||||
{
|
||||
__LINE__,
|
||||
{
|
||||
0x02,
|
||||
0x02,
|
||||
/* The `1' used to be the `exclusive' flag. It is left here to
|
||||
* test that the frame reader ignores these Emtpy bits.
|
||||
*/
|
||||
(H3PET_CUR_STREAM << HQ_PT_SHIFT) | (H3DET_ROOT << HQ_DT_SHIFT),
|
||||
0x77,
|
||||
},
|
||||
4,
|
||||
0,
|
||||
"on_priority: current stream depends on root; weight: 120\n",
|
||||
},
|
||||
|
||||
{
|
||||
__LINE__,
|
||||
{
|
||||
|
@ -161,41 +96,6 @@ static const struct test tests[] =
|
|||
};
|
||||
|
||||
|
||||
static void
|
||||
on_priority (void *ctx, const struct hq_priority *priority)
|
||||
{
|
||||
switch (((priority->hqp_prio_type == H3PET_CUR_STREAM) << 1)
|
||||
| (priority->hqp_dep_type == H3DET_ROOT))
|
||||
{
|
||||
case 0:
|
||||
fprintf(ctx, "%s: %s #%"PRIu64" depends on %s #%"PRIu64"; "
|
||||
"weight: %u\n", __func__,
|
||||
lsquic_h3pet2str[priority->hqp_prio_type], priority->hqp_prio_id,
|
||||
lsquic_h3det2str[priority->hqp_dep_type], priority->hqp_dep_id,
|
||||
HQP_WEIGHT(priority));
|
||||
break;
|
||||
case 1:
|
||||
fprintf(ctx, "%s: %s #%"PRIu64" depends on root; "
|
||||
"weight: %u\n", __func__,
|
||||
lsquic_h3pet2str[priority->hqp_prio_type], priority->hqp_prio_id,
|
||||
HQP_WEIGHT(priority));
|
||||
break;
|
||||
case 2:
|
||||
fprintf(ctx, "%s: current stream depends on %s #%"PRIu64"; "
|
||||
"weight: %u\n", __func__,
|
||||
lsquic_h3det2str[priority->hqp_dep_type], priority->hqp_dep_id,
|
||||
HQP_WEIGHT(priority));
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
case 3:
|
||||
fprintf(ctx, "%s: current stream depends on root; "
|
||||
"weight: %u\n", __func__,
|
||||
HQP_WEIGHT(priority));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_cancel_push (void *ctx, uint64_t push_id)
|
||||
{
|
||||
|
@ -234,7 +134,6 @@ on_unexpected_frame (void *ctx, uint64_t frame_type)
|
|||
|
||||
static const struct hcsi_callbacks callbacks =
|
||||
{
|
||||
.on_priority = on_priority,
|
||||
.on_cancel_push = on_cancel_push,
|
||||
.on_max_push_id = on_max_push_id,
|
||||
.on_settings_frame = on_settings_frame,
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include "lsquic.h"
|
||||
#include "lsquic_types.h"
|
||||
#include "lsquic_int_types.h"
|
||||
#include "lsquic_hash.h"
|
||||
#include "lsquic_conn.h"
|
||||
#include "lsquic_sfcw.h"
|
||||
#include "lsquic_varint.h"
|
||||
#include "lsquic_hq.h"
|
||||
|
||||
|
||||
struct test
|
||||
{
|
||||
int lineno;
|
||||
unsigned char buf[0x100];
|
||||
size_t buf_sz;
|
||||
};
|
||||
|
||||
|
||||
static const struct test tests[] =
|
||||
{
|
||||
{ .lineno = __LINE__,
|
||||
.buf = "\xb0\x00\x01",
|
||||
.buf_sz = 3,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
run_test (const struct test *test)
|
||||
{
|
||||
enum h3_prio_frame_read_status status;
|
||||
struct h3_prio_frame_read_state state;
|
||||
const unsigned char *p;
|
||||
|
||||
p = test->buf;
|
||||
state.h3pfrs_state = 0;
|
||||
status = lsquic_h3_prio_frame_read(&p, test->buf_sz, &state);
|
||||
assert(H3PFR_STATUS_DONE == status);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
const struct test *test;
|
||||
|
||||
for (test = tests; test < tests + sizeof(tests) / sizeof(tests[0]); ++test)
|
||||
run_test(test);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -56,7 +56,6 @@
|
|||
#include "lsquic_varint.h"
|
||||
#include "lsquic_hq.h"
|
||||
#include "lsquic_data_in_if.h"
|
||||
#include "lsquic_h3_prio.h"
|
||||
#include "lsquic_headers.h"
|
||||
#include "lsquic_push_promise.h"
|
||||
|
||||
|
@ -156,8 +155,8 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
|
|||
int s;
|
||||
memset(tobjs, 0, sizeof(*tobjs));
|
||||
LSCONN_INITIALIZE(&tobjs->lconn);
|
||||
tobjs->lconn.cn_pf = select_pf_by_ver(LSQVER_ID22);
|
||||
tobjs->lconn.cn_version = LSQVER_ID22;
|
||||
tobjs->lconn.cn_pf = select_pf_by_ver(LSQVER_ID23);
|
||||
tobjs->lconn.cn_version = LSQVER_ID23;
|
||||
tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1;
|
||||
network_path.np_pack_size = IQUIC_MAX_IPv4_PACKET_SZ;
|
||||
tobjs->lconn.cn_if = &our_conn_if;
|
||||
|
@ -191,9 +190,6 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
|
|||
assert(0 == s);
|
||||
tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh;
|
||||
}
|
||||
if (tobjs->ctor_flags & SCF_IETF)
|
||||
tobjs->conn_pub.u.ietf.prio_tree
|
||||
= lsquic_prio_tree_new(&tobjs->lconn, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -206,8 +202,6 @@ deinit_test_objs (struct test_objs *tobjs)
|
|||
lsquic_mm_cleanup(&tobjs->eng_pub.enp_mm);
|
||||
if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS)
|
||||
lsquic_qeh_cleanup(&tobjs->qeh);
|
||||
if (tobjs->ctor_flags & SCF_IETF)
|
||||
lsquic_prio_tree_destroy(tobjs->conn_pub.u.ietf.prio_tree);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
#include "lsquic_varint.h"
|
||||
#include "lsquic_hq.h"
|
||||
#include "lsquic_data_in_if.h"
|
||||
#include "lsquic_h3_prio.h"
|
||||
|
||||
static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_039);
|
||||
|
||||
|
@ -326,7 +325,7 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
|
|||
LSCONN_INITIALIZE(&tobjs->lconn);
|
||||
tobjs->lconn.cn_pf = pf ? pf : g_pf;
|
||||
tobjs->lconn.cn_version = tobjs->lconn.cn_pf == &lsquic_parse_funcs_ietf_v1 ?
|
||||
LSQVER_ID22 : LSQVER_043;
|
||||
LSQVER_ID23 : LSQVER_043;
|
||||
tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_gquic_1;
|
||||
network_path.np_pack_size = 1370;
|
||||
tobjs->lconn.cn_if = &our_conn_if;
|
||||
|
@ -358,8 +357,6 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
|
|||
s = lsquic_qeh_settings(&tobjs->qeh, 0, 0, 0, 0);
|
||||
assert(0 == s);
|
||||
tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh;
|
||||
tobjs->conn_pub.u.ietf.prio_tree
|
||||
= lsquic_prio_tree_new(&tobjs->lconn, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,7 +371,6 @@ deinit_test_objs (struct test_objs *tobjs)
|
|||
if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS)
|
||||
{
|
||||
lsquic_qeh_cleanup(&tobjs->qeh);
|
||||
lsquic_prio_tree_destroy(tobjs->conn_pub.u.ietf.prio_tree);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2168,7 +2164,7 @@ test_changing_pack_size (void)
|
|||
enum lsquic_version versions_to_test[3] =
|
||||
{
|
||||
LSQVER_046,
|
||||
LSQVER_ID22,
|
||||
LSQVER_ID23,
|
||||
};
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
|
@ -3097,7 +3093,7 @@ main (int argc, char **argv)
|
|||
|
||||
/* Redo some tests using crypto streams and frames */
|
||||
g_use_crypto_ctor = 1;
|
||||
g_pf = select_pf_by_ver(LSQVER_ID22);
|
||||
g_pf = select_pf_by_ver(LSQVER_ID23);
|
||||
main_test_packetization();
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -279,7 +279,7 @@ static const struct test tests[] = {
|
|||
*/
|
||||
|
||||
{ .lineno = __LINE__,
|
||||
.pf = select_pf_by_ver(LSQVER_ID22),
|
||||
.pf = select_pf_by_ver(LSQVER_ID23),
|
||||
.fin = { 0, 1, },
|
||||
.offset = 0x0807060504030201UL,
|
||||
.stream_id = 0x210,
|
||||
|
@ -299,7 +299,7 @@ static const struct test tests[] = {
|
|||
},
|
||||
|
||||
{ .lineno = __LINE__,
|
||||
.pf = select_pf_by_ver(LSQVER_ID22),
|
||||
.pf = select_pf_by_ver(LSQVER_ID23),
|
||||
.fin = { 0, 0, },
|
||||
.offset = 0,
|
||||
.stream_id = 0x210,
|
||||
|
@ -318,7 +318,7 @@ static const struct test tests[] = {
|
|||
},
|
||||
|
||||
{ .lineno = __LINE__,
|
||||
.pf = select_pf_by_ver(LSQVER_ID22),
|
||||
.pf = select_pf_by_ver(LSQVER_ID23),
|
||||
.fin = { 0, 0, },
|
||||
.offset = 0,
|
||||
.stream_id = 0x21,
|
||||
|
@ -336,7 +336,7 @@ static const struct test tests[] = {
|
|||
},
|
||||
|
||||
{ .lineno = __LINE__,
|
||||
.pf = select_pf_by_ver(LSQVER_ID22),
|
||||
.pf = select_pf_by_ver(LSQVER_ID23),
|
||||
.fin = { 0, 0, },
|
||||
.offset = 0x0807060504030201UL,
|
||||
.stream_id = 0x210,
|
||||
|
@ -356,7 +356,7 @@ static const struct test tests[] = {
|
|||
},
|
||||
|
||||
{ .lineno = __LINE__,
|
||||
.pf = select_pf_by_ver(LSQVER_ID22),
|
||||
.pf = select_pf_by_ver(LSQVER_ID23),
|
||||
.fin = { 1, 0, },
|
||||
.offset = 0x0807060504030201UL,
|
||||
.stream_id = 0x210,
|
||||
|
@ -374,7 +374,7 @@ static const struct test tests[] = {
|
|||
},
|
||||
|
||||
{ .lineno = __LINE__,
|
||||
.pf = select_pf_by_ver(LSQVER_ID22),
|
||||
.pf = select_pf_by_ver(LSQVER_ID23),
|
||||
.fin = { 1, 0, },
|
||||
.offset = 0x0807060504030201UL,
|
||||
.stream_id = 0x210,
|
||||
|
|
|
@ -243,7 +243,7 @@ static const struct test tests[] = {
|
|||
|
||||
{ "Balls to the wall: every possible bit is set",
|
||||
__LINE__,
|
||||
select_pf_by_ver(LSQVER_ID22),
|
||||
select_pf_by_ver(LSQVER_ID23),
|
||||
/* TYPE OFF DLEN FIN */
|
||||
{ 0x10 | 1<<2 | 1<<1 | 1<<0,
|
||||
0x41, 0x23, /* Stream ID */
|
||||
|
@ -262,7 +262,7 @@ static const struct test tests[] = {
|
|||
|
||||
{ "Balls to the wall #2: every possible bit is set except FIN",
|
||||
__LINE__,
|
||||
select_pf_by_ver(LSQVER_ID22),
|
||||
select_pf_by_ver(LSQVER_ID23),
|
||||
/* TYPE OFF DLEN FIN */
|
||||
{ 0x10 | 1<<2 | 1<<1 | 0<<0,
|
||||
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
|
||||
|
@ -281,7 +281,7 @@ static const struct test tests[] = {
|
|||
|
||||
{ "Data length is zero",
|
||||
__LINE__,
|
||||
select_pf_by_ver(LSQVER_ID22),
|
||||
select_pf_by_ver(LSQVER_ID23),
|
||||
/* TYPE OFF DLEN FIN */
|
||||
{ 0x10 | 1<<2 | 0<<1 | 0<<0,
|
||||
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
|
||||
|
@ -299,7 +299,7 @@ static const struct test tests[] = {
|
|||
|
||||
{ "Sanity check: what happens when data length is zero #1",
|
||||
__LINE__,
|
||||
select_pf_by_ver(LSQVER_ID22),
|
||||
select_pf_by_ver(LSQVER_ID23),
|
||||
/* TYPE OFF DLEN FIN */
|
||||
{ 0x10 | 1<<2 | 1<<1 | 0<<0,
|
||||
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
|
||||
|
@ -318,7 +318,7 @@ static const struct test tests[] = {
|
|||
|
||||
{ "Sanity check: what happens when data length is zero #2",
|
||||
__LINE__,
|
||||
select_pf_by_ver(LSQVER_ID22),
|
||||
select_pf_by_ver(LSQVER_ID23),
|
||||
/* TYPE OFF DLEN FIN */
|
||||
{ 0x10 | 1<<2 | 1<<1 | 0<<0,
|
||||
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
|
||||
|
@ -337,7 +337,7 @@ static const struct test tests[] = {
|
|||
|
||||
{ "Sanity check: what happens when data length is zero #3",
|
||||
__LINE__,
|
||||
select_pf_by_ver(LSQVER_ID22),
|
||||
select_pf_by_ver(LSQVER_ID23),
|
||||
/* TYPE OFF DLEN FIN */
|
||||
{ 0x10 | 0<<2 | 1<<1 | 0<<0,
|
||||
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
|
||||
|
@ -355,7 +355,7 @@ static const struct test tests[] = {
|
|||
|
||||
{ "Sanity check: what happens when data length is zero #3",
|
||||
__LINE__,
|
||||
select_pf_by_ver(LSQVER_ID22),
|
||||
select_pf_by_ver(LSQVER_ID23),
|
||||
/* TYPE OFF DLEN FIN */
|
||||
{ 0x10 | 1<<2 | 1<<1 | 1<<0,
|
||||
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
|
||||
|
@ -374,7 +374,7 @@ static const struct test tests[] = {
|
|||
|
||||
{ "Check data bounds #1",
|
||||
__LINE__,
|
||||
select_pf_by_ver(LSQVER_ID22),
|
||||
select_pf_by_ver(LSQVER_ID23),
|
||||
/* TYPE OFF DLEN FIN */
|
||||
{ 0x10 | 1<<2 | 1<<1 | 1<<0,
|
||||
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
|
||||
|
@ -393,7 +393,7 @@ static const struct test tests[] = {
|
|||
|
||||
{ "Check data bounds #2",
|
||||
__LINE__,
|
||||
select_pf_by_ver(LSQVER_ID22),
|
||||
select_pf_by_ver(LSQVER_ID23),
|
||||
/* TYPE OFF DLEN FIN */
|
||||
{ 0x10 | 1<<2 | 1<<1 | 1<<0,
|
||||
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
|
||||
|
|
|
@ -97,7 +97,7 @@ static const struct trapa_test tests[] =
|
|||
.tp_flags = TRAPA_SERVER,
|
||||
.tp_init_max_data = 0x123456,
|
||||
.tp_init_max_stream_data_bidi_local = 0xABCDEF88,
|
||||
.tp_disable_migration = 1,
|
||||
.tp_disable_active_migration = 1,
|
||||
.tp_max_packet_size = 0x333,
|
||||
},
|
||||
.enc_len = 32,
|
||||
|
@ -156,7 +156,7 @@ params_are_equal (const struct transport_params *a,
|
|||
#define MCMP(f) 0 == memcmp(&a->f, &b->f, sizeof(a->f))
|
||||
return a->tp_flags == b->tp_flags
|
||||
&& MCMP(tp_numerics_u)
|
||||
&& a->tp_disable_migration == b->tp_disable_migration
|
||||
&& a->tp_disable_active_migration == b->tp_disable_active_migration
|
||||
&& MCMP(tp_stateless_reset_token)
|
||||
&& MCMP(tp_preferred_address.ipv4_addr)
|
||||
&& MCMP(tp_preferred_address.ipv6_addr)
|
||||
|
|
Loading…
Reference in New Issue