Release 2.4.0

[FEATURE] QUIC and HTTP/3 Internet Draft 23 support
This commit is contained in:
Dmitri Tikhonov 2019-09-18 11:22:20 -04:00
parent 2718741832
commit 92f6e17bdc
45 changed files with 442 additions and 2674 deletions

View file

@ -1,3 +1,7 @@
2019-09-18
- 2.4.0
- [FEATURE] QUIC and HTTP/3 Internet Draft 23 support
2019-09-13 2019-09-13
- 2.3.1 - 2.3.1
- [BUGFIX] Fix memory leaks - [BUGFIX] Fix memory leaks

View file

@ -273,3 +273,9 @@ More Compilation Options
this flag is specified, sending of packets will randomly fail, about this flag is specified, sending of packets will randomly fail, about
one out of every 10 attempts. Set environment variable one out of every 10 attempts. Set environment variable
LSQUIC_RANDOM_SEND_FAILURE to change this frequency. 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.

View file

@ -24,8 +24,8 @@ extern "C" {
#endif #endif
#define LSQUIC_MAJOR_VERSION 2 #define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 3 #define LSQUIC_MINOR_VERSION 4
#define LSQUIC_PATCH_VERSION 1 #define LSQUIC_PATCH_VERSION 0
/** /**
* Engine flags: * Engine flags:
@ -125,9 +125,9 @@ enum lsquic_version
#endif #endif
/** /**
* IETF QUIC Draft-22 * IETF QUIC Draft-23
*/ */
LSQVER_ID22, LSQVER_ID23,
/** /**
* Special version to trigger version negotiation. * 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 * @see lsquic_version
*/ */
#define LSQUIC_SUPPORTED_VERSIONS ((1 << N_LSQVER) - 1) #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_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 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_BLOCKED 100
#define LSQUIC_DF_QPACK_ENC_MAX_SIZE 4096 #define LSQUIC_DF_QPACK_ENC_MAX_SIZE 4096
/** ECN is enabled by default */ /** ECN is disabled by default */
#define LSQUIC_DF_ECN 1 #define LSQUIC_DF_ECN 0
/**
* 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
/** Allow migration by default */ /** Allow migration by default */
#define LSQUIC_DF_ALLOW_MIGRATION 1 #define LSQUIC_DF_ALLOW_MIGRATION 1
@ -729,13 +723,6 @@ struct lsquic_engine_settings {
*/ */
int es_ecn; 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. * Allow peer to migrate connection.
* *

View file

@ -28,14 +28,12 @@ SET(lsquic_STAT_SRCS
lsquic_full_conn.c lsquic_full_conn.c
lsquic_full_conn_ietf.c lsquic_full_conn_ietf.c
lsquic_global.c lsquic_global.c
lsquic_h3_prio.c
lsquic_handshake.c lsquic_handshake.c
lsquic_hash.c lsquic_hash.c
lsquic_hcsi_reader.c lsquic_hcsi_reader.c
lsquic_hcso_writer.c lsquic_hcso_writer.c
lsquic_headers_stream.c lsquic_headers_stream.c
lsquic_hkdf.c lsquic_hkdf.c
lsquic_hq.c
lsquic_hspack_valid.c lsquic_hspack_valid.c
lsquic_http1x_if.c lsquic_http1x_if.c
lsquic_logger.c lsquic_logger.c

View file

@ -21,7 +21,6 @@ struct conn_stats;
#endif #endif
struct qpack_enc_hdl; struct qpack_enc_hdl;
struct qpack_dec_hdl; struct qpack_dec_hdl;
struct h3_prio_tree;
struct network_path; struct network_path;
struct lsquic_conn_public { struct lsquic_conn_public {
@ -44,7 +43,6 @@ struct lsquic_conn_public {
struct { struct {
struct qpack_enc_hdl *qeh; struct qpack_enc_hdl *qeh;
struct qpack_dec_hdl *qdh; struct qpack_dec_hdl *qdh;
struct h3_prio_tree *prio_tree;
struct lsquic_hash *promises; struct lsquic_hash *promises;
} ietf; } ietf;
} u; } u;

View file

@ -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; extern const struct enc_session_funcs_iquic lsquic_enc_session_iquic_ietf_v1;
#define select_esf_common_by_ver(ver) ( \ #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 : \ ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \
&lsquic_enc_session_common_gquic_1 ) &lsquic_enc_session_common_gquic_1 )

View file

@ -78,8 +78,8 @@ static const struct alpn_map {
enum lsquic_version version; enum lsquic_version version;
const unsigned char *alpn; const unsigned char *alpn;
} s_alpns[] = { } s_alpns[] = {
{ LSQVER_ID22, (unsigned char *) "\x05h3-22", }, { LSQVER_ID23, (unsigned char *) "\x05h3-23", },
{ LSQVER_VERNEG, (unsigned char *) "\x05h3-22", }, { LSQVER_VERNEG, (unsigned char *) "\x05h3-23", },
}; };
struct enc_sess_iquic; 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 */ - 1 /* One slot is used by peer's SCID */
- !!(params.tp_flags & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6)); - !!(params.tp_flags & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6));
if (!settings->es_allow_migration) if (!settings->es_allow_migration)
params.tp_disable_migration = 1; params.tp_disable_active_migration = 1;
len = lsquic_tp_encode(&params, buf, bufsz); len = lsquic_tp_encode(&params, buf, bufsz);
if (len >= 0) if (len >= 0)

View file

@ -74,7 +74,6 @@
#include "lsquic_min_heap.h" #include "lsquic_min_heap.h"
#include "lsquic_http1x_if.h" #include "lsquic_http1x_if.h"
#include "lsquic_parse_common.h" #include "lsquic_parse_common.h"
#include "lsquic_h3_prio.h"
#include "lsquic_handshake.h" #include "lsquic_handshake.h"
#define LSQUIC_LOGGER_MODULE LSQLM_ENGINE #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_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_size = LSQUIC_DF_QPACK_ENC_MAX_SIZE;
settings->es_qpack_enc_max_blocked = LSQUIC_DF_QPACK_ENC_MAX_BLOCKED; 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; settings->es_allow_migration = LSQUIC_DF_ALLOW_MIGRATION;
} }
@ -331,8 +329,6 @@ lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
unsigned flags, unsigned flags,
char *err_buf, size_t err_buf_sz) char *err_buf, size_t err_buf_sz)
{ {
unsigned sum;
if (settings->es_cfcw < LSQUIC_MIN_FCW || if (settings->es_cfcw < LSQUIC_MIN_FCW ||
settings->es_sfcw < 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; 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 (settings->es_cc_algo > 2)
{ {
if (err_buf) 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, LSQ_DEBUGC("scheduled %s packet for cid %"CID_FMT,
lsquic_preqt2str[type], CID_BITS(&packet_in->pi_conn_id)); lsquic_preqt2str[type], CID_BITS(&packet_in->pi_conn_id));
else else
LSQ_DEBUG("cannot schedule %s packet: out of packet request objects", LSQ_DEBUG("cannot schedule %s packet", lsquic_preqt2str[type]);
lsquic_preqt2str[type]);
} }

View file

@ -61,7 +61,7 @@
#include "lsquic_mini_conn_ietf.h" #include "lsquic_mini_conn_ietf.h"
#include "lsquic_tokgen.h" #include "lsquic_tokgen.h"
#include "lsquic_full_conn.h" #include "lsquic_full_conn.h"
#include "lsquic_h3_prio.h" #include "lsquic_spi.h"
#include "lsquic_ietf.h" #include "lsquic_ietf.h"
#include "lsquic_push_promise.h" #include "lsquic_push_promise.h"
#include "lsquic_headers.h" #include "lsquic_headers.h"
@ -70,7 +70,6 @@
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&conn->ifc_conn) #define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&conn->ifc_conn)
#include "lsquic_logger.h" #include "lsquic_logger.h"
#define MAX_ANY_PACKETS_SINCE_LAST_ACK 20
#define MAX_RETR_PACKETS_SINCE_LAST_ACK 2 #define MAX_RETR_PACKETS_SINCE_LAST_ACK 2
#define ACK_TIMEOUT (TP_DEF_MAX_ACK_DELAY * 1000) #define ACK_TIMEOUT (TP_DEF_MAX_ACK_DELAY * 1000)
#define INITIAL_CHAL_TIMEOUT 25000 #define INITIAL_CHAL_TIMEOUT 25000
@ -873,7 +872,7 @@ ietf_full_conn_add_scid (struct ietf_full_conn *conn,
static int static int
ietf_full_conn_init (struct ietf_full_conn *conn, 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; conn->ifc_conn.cn_if = ietf_full_conn_iface_ptr;
if (enpub->enp_settings.es_scid_len) 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_rechist_init(&conn->ifc_rechist[PNS_APP], &conn->ifc_conn, 1);
lsquic_send_ctl_init(&conn->ifc_send_ctl, &conn->ifc_alset, enpub, 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, 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, lsquic_cfcw_init(&conn->ifc_pub.cfcw, &conn->ifc_pub,
conn->ifc_settings->es_cfcw); conn->ifc_settings->es_cfcw);
conn->ifc_pub.all_streams = lsquic_hash_create(); conn->ifc_pub.all_streams = lsquic_hash_create();
if (!conn->ifc_pub.all_streams) if (!conn->ifc_pub.all_streams)
goto err0; return -1;
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;
conn->ifc_pub.u.ietf.qeh = &conn->ifc_qeh; conn->ifc_pub.u.ietf.qeh = &conn->ifc_qeh;
conn->ifc_pub.u.ietf.qdh = &conn->ifc_qdh; 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.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.max_header_list_size = HQ_DF_MAX_HEADER_LIST_SIZE;
conn->ifc_peer_hq_settings.qpack_blocked_streams = HQ_DF_QPACK_BLOCKED_STREAMS; 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_idle_to = enpub->enp_settings.es_idle_timeout * 1000 * 1000;
conn->ifc_ping_period = enpub->enp_settings.es_ping_period * 1000 * 1000; conn->ifc_ping_period = enpub->enp_settings.es_ping_period * 1000 * 1000;
return 0; 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; 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); free(conn);
return NULL; 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 */ /* Set the flags early so that correct CID is used for logging */
conn->ifc_conn.cn_flags |= LSCONN_IETF | LSCONN_SERVER; 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); free(conn);
return NULL; return NULL;
@ -2197,16 +2188,18 @@ process_stream_ready_to_send (struct ietf_full_conn *conn,
static void static void
process_streams_ready_to_send (struct ietf_full_conn *conn) 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)); assert(!TAILQ_EMPTY(&conn->ifc_pub.sending_streams));
lsquic_prio_tree_iter_reset(conn->ifc_pub.u.ietf.prio_tree, "send"); lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.sending_streams),
TAILQ_FOREACH(stream, &conn->ifc_pub.sending_streams, next_send_stream) TAILQ_LAST(&conn->ifc_pub.sending_streams, lsquic_streams_tailq),
lsquic_prio_tree_iter_add(conn->ifc_pub.u.ietf.prio_tree, stream); (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( for (stream = lsquic_spi_first(&spi); stream;
conn->ifc_pub.u.ietf.prio_tree))) stream = lsquic_spi_next(&spi))
if (!process_stream_ready_to_send(conn, stream)) if (!process_stream_ready_to_send(conn, stream))
break; break;
} }
@ -2375,8 +2368,6 @@ ietf_full_conn_ci_destroy (struct lsquic_conn *lconn)
lsquic_malo_destroy(conn->ifc_pub.packet_out_malo); lsquic_malo_destroy(conn->ifc_pub.packet_out_malo);
if (conn->ifc_flags & IFC_CREATED_OK) if (conn->ifc_flags & IFC_CREATED_OK)
conn->ifc_enpub->enp_stream_if->on_conn_closed(&conn->ifc_conn); 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) if (conn->ifc_conn.cn_enc_session)
conn->ifc_conn.cn_esf.i->esfi_destroy(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)) 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; struct sockaddr_in6 v6;
} sockaddr; } sockaddr;
if (params->tp_disable_migration) if (params->tp_disable_active_migration)
{ {
LSQ_DEBUG("TP disables migration: retire PreferredAddress CID"); LSQ_DEBUG("TP disables migration: retire PreferredAddress CID");
retire_cid_from_tp(conn, params); retire_cid_from_tp(conn, params);
@ -3337,39 +3328,32 @@ static void
process_streams_read_events (struct ietf_full_conn *conn) process_streams_read_events (struct ietf_full_conn *conn)
{ {
struct lsquic_stream *stream; struct lsquic_stream *stream;
int have_streams, iters; int iters;
enum stream_q_flags q_flags, needs_service; enum stream_q_flags q_flags, needs_service;
struct stream_prio_iter spi;
static const char *const labels[2] = { "read-0", "read-1", }; 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; conn->ifc_pub.cp_flags &= ~CP_STREAM_UNBLOCKED;
iters = 0; iters = 0;
do do
{ {
have_streams = 0; lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.read_streams),
TAILQ_FOREACH(stream, &conn->ifc_pub.read_streams, next_read_stream) TAILQ_LAST(&conn->ifc_pub.read_streams, lsquic_streams_tailq),
if (lsquic_stream_readable(stream)) (uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
{ SMQF_WANT_READ, &conn->ifc_conn, labels[iters], NULL, NULL);
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;
needs_service = 0; needs_service = 0;
while ((stream = lsquic_prio_tree_iter_next( for (stream = lsquic_spi_first(&spi); stream;
conn->ifc_pub.u.ietf.prio_tree))) stream = lsquic_spi_next(&spi))
{ {
q_flags = stream->sm_qflags & SMQF_SERVICE_FLAGS; q_flags = stream->sm_qflags & SMQF_SERVICE_FLAGS;
lsquic_stream_dispatch_read_events(stream); lsquic_stream_dispatch_read_events(stream);
needs_service |= q_flags ^ (stream->sm_qflags & SMQF_SERVICE_FLAGS); needs_service |= q_flags ^ (stream->sm_qflags & SMQF_SERVICE_FLAGS);
} }
if (needs_service) if (needs_service)
service_streams(conn); 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 static void
process_streams_write_events (struct ietf_full_conn *conn, int high_prio, process_streams_write_events (struct ietf_full_conn *conn, int high_prio)
const struct lsquic_stream **highest_non_crit)
{ {
struct lsquic_stream *stream; 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) if (high_prio)
*highest_non_crit = lsquic_prio_tree_highest_non_crit(prio_tree); lsquic_spi_drop_non_high(&spi);
lsquic_prio_tree_iter_reset(prio_tree, high_prio ? "write-high" : else
"write-low"); lsquic_spi_drop_high(&spi);
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);
for (stream = lsquic_spi_first(&spi); stream && write_is_possible(conn);
while ((stream = lsquic_prio_tree_iter_next( stream = lsquic_spi_next(&spi))
conn->ifc_pub.u.ietf.prio_tree)))
if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS) if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
lsquic_stream_dispatch_write_events(stream); 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); assert(conn->ifc_cur_path_id != path_id);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "switched paths"); 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, lsquic_send_ctl_repath(&conn->ifc_send_ctl,
CUR_NPATH(conn), &conn->ifc_paths[path_id].cop_path); CUR_NPATH(conn), &conn->ifc_paths[path_id].cop_path);
maybe_retire_dcid(conn, &CUR_NPATH(conn)->np_dcid); maybe_retire_dcid(conn, &CUR_NPATH(conn)->np_dcid);
@ -5550,6 +5531,7 @@ process_regular_packet (struct ietf_full_conn *conn,
switch (dec_packin) switch (dec_packin)
{ {
case DECPI_BADCRYPT: case DECPI_BADCRYPT:
case DECPI_TOO_SHORT:
if (conn->ifc_enpub->enp_settings.es_honor_prst if (conn->ifc_enpub->enp_settings.es_honor_prst
/* In server mode, even if we do support stateless reset packets, /* In server mode, even if we do support stateless reset packets,
* they are handled in lsquic_engine.c. No need to have this * 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; conn->ifc_flags |= IFC_GOT_PRST;
return -1; return -1;
} }
else else if (dec_packin == DECPI_BADCRYPT)
{ {
LSQ_INFO("could not decrypt packet (type %s)", LSQ_INFO("could not decrypt packet (type %s)",
lsquic_hety2str[packet_in->pi_header_type]); lsquic_hety2str[packet_in->pi_header_type]);
return 0; return 0;
} }
else
{
LSQ_INFO("packet is too short to be decrypted");
return 0;
}
case DECPI_NOT_YET: case DECPI_NOT_YET:
return 0; return 0;
case DECPI_NOMEM: case DECPI_NOMEM:
return 0; 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: case DECPI_VIOLATION:
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION, ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION,
"decrypter reports 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) ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
{ {
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn; struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
const struct lsquic_stream *highest_non_crit;
int have_delayed_packets, s; int have_delayed_packets, s;
enum tick_st tick = 0; enum tick_st tick = 0;
unsigned n; 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)) 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)) if (!write_is_possible(conn))
goto end_write; goto end_write;
} }
@ -6025,7 +6005,7 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
goto end_write; goto end_write;
if (!TAILQ_EMPTY(&conn->ifc_pub.write_streams)) 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)); 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; &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 static void
on_cancel_push (void *ctx, uint64_t push_id) 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) on_max_push_id_client (void *ctx, uint64_t push_id)
{ {
struct ietf_full_conn *const conn = ctx; 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"); "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); LSQ_DEBUG("Peer's SETTINGS_QPACK_BLOCKED_STREAMS=%"PRIu64, value);
conn->ifc_peer_hq_settings.qpack_blocked_streams = value; conn->ifc_peer_hq_settings.qpack_blocked_streams = value;
break; 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: case HQSID_QPACK_MAX_TABLE_CAPACITY:
LSQ_DEBUG("Peer's SETTINGS_QPACK_MAX_TABLE_CAPACITY=%"PRIu64, value); LSQ_DEBUG("Peer's SETTINGS_QPACK_MAX_TABLE_CAPACITY=%"PRIu64, value);
conn->ifc_peer_hq_settings.header_table_size = 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) on_goaway_server (void *ctx, uint64_t stream_id)
{ {
struct ietf_full_conn *const conn = ctx; 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"); "client should not send GOAWAY frames");
} }
@ -6636,7 +6550,7 @@ on_goaway (void *ctx, uint64_t stream_id)
sit = stream_id & SIT_MASK; sit = stream_id & SIT_MASK;
if (sit != SIT_BIDI_CLIENT) 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); "stream ID %"PRIu64" in GOAWAY frame", stream_id);
return; return;
} }
@ -6669,14 +6583,13 @@ static void
on_unexpected_frame (void *ctx, uint64_t frame_type) on_unexpected_frame (void *ctx, uint64_t frame_type)
{ {
struct ietf_full_conn *const conn = ctx; struct ietf_full_conn *const conn = ctx;
ABORT_QUIETLY(1, HEC_WRONG_STREAM, "Frame type %"PRIu64" is not allowed " ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED, "Frame type %"PRIu64" is not "
"on the control stream", frame_type); "allowed on the control stream", frame_type);
} }
static const struct hcsi_callbacks hcsi_callbacks_server = static const struct hcsi_callbacks hcsi_callbacks_server =
{ {
.on_priority = on_priority_server,
.on_cancel_push = on_cancel_push, .on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id, .on_max_push_id = on_max_push_id,
.on_settings_frame = on_settings_frame, .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 = static const struct hcsi_callbacks hcsi_callbacks =
{ {
.on_priority = on_priority,
.on_cancel_push = on_cancel_push, .on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id_client, .on_max_push_id = on_max_push_id_client,
.on_settings_frame = on_settings_frame, .on_settings_frame = on_settings_frame,
@ -6815,8 +6727,10 @@ apply_uni_stream_class (struct ietf_full_conn *conn,
} }
else else
{ {
ABORT_WARN("Incoming QPACK encoder stream already exists"); ABORT_QUIETLY(1, HEC_STREAM_CREATION_ERROR,
/* TODO: special error code? */ "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); lsquic_stream_close(stream);
} }
break; break;
@ -6830,8 +6744,10 @@ apply_uni_stream_class (struct ietf_full_conn *conn,
} }
else else
{ {
ABORT_WARN("Incoming QPACK decoder stream already exists"); ABORT_QUIETLY(1, HEC_STREAM_CREATION_ERROR,
/* TODO: special error code? */ "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); lsquic_stream_close(stream);
} }
break; break;

View file

@ -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;
}

View file

@ -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

View file

@ -25,13 +25,16 @@ struct lsquic_hash
struct hels_head *qh_buckets, struct hels_head *qh_buckets,
qh_all; qh_all;
struct lsquic_hash_elem *qh_iter_next; 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_count;
unsigned qh_nbits; unsigned qh_nbits;
}; };
struct lsquic_hash * 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 hels_head *buckets;
struct lsquic_hash *hash; struct lsquic_hash *hash;
@ -53,6 +56,8 @@ lsquic_hash_create (void)
TAILQ_INIT(&buckets[i]); TAILQ_INIT(&buckets[i]);
TAILQ_INIT(&hash->qh_all); TAILQ_INIT(&hash->qh_all);
hash->qh_cmp = cmp;
hash->qh_hash = hashf;
hash->qh_buckets = buckets; hash->qh_buckets = buckets;
hash->qh_nbits = nbits; hash->qh_nbits = nbits;
hash->qh_iter_next = NULL; 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 void
lsquic_hash_destroy (struct lsquic_hash *hash) 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)) 0 != lsquic_hash_grow(hash))
return NULL; 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); buckno = BUCKNO(hash->qh_nbits, hash_val);
TAILQ_INSERT_TAIL(&hash->qh_all, el, qhe_next_all); TAILQ_INSERT_TAIL(&hash->qh_all, el, qhe_next_all);
TAILQ_INSERT_TAIL(&hash->qh_buckets[buckno], el, qhe_next_bucket); 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; unsigned buckno, hash_val;
struct lsquic_hash_elem *el; 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); buckno = BUCKNO(hash->qh_nbits, hash_val);
TAILQ_FOREACH(el, &hash->qh_buckets[buckno], qhe_next_bucket) TAILQ_FOREACH(el, &hash->qh_buckets[buckno], qhe_next_bucket)
if (hash_val == el->qhe_hash_val && if (hash_val == el->qhe_hash_val &&
key_sz == el->qhe_key_len && 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; return el;
} }

View file

@ -25,6 +25,10 @@ struct lsquic_hash_elem
struct lsquic_hash * struct lsquic_hash *
lsquic_hash_create (void); 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 void
lsquic_hash_destroy (struct lsquic_hash *); lsquic_hash_destroy (struct lsquic_hash *);

View file

@ -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 *const end = p + bufsz;
const unsigned char *orig_p; const unsigned char *orig_p;
enum h3_prio_frame_read_status prio_status;
uint64_t len; uint64_t len;
int s; int s;
@ -75,9 +74,6 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
reader->hr_state = HR_READ_FRAME_BEGIN; reader->hr_state = HR_READ_FRAME_BEGIN;
} }
break; break;
case HQFT_PRIORITY:
reader->hr_state = HR_READ_PRIORITY_BEGIN;
break;
case HQFT_GOAWAY: case HQFT_GOAWAY:
reader->hr_state = HR_READ_VARINT; reader->hr_state = HR_READ_VARINT;
break; break;
@ -118,7 +114,7 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
if (reader->hr_nread != reader->hr_frame_length) if (reader->hr_nread != reader->hr_frame_length)
{ {
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1, 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"); "Frame length does not match actual payload length");
reader->hr_state = HR_ERROR; reader->hr_state = HR_ERROR;
return -1; 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) if (reader->hr_nread > reader->hr_frame_length)
{ {
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1, reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
HEC_MALFORMED_FRAME + HQFT_SETTINGS, HEC_FRAME_ERROR, "SETTING frame contents too long");
"SETTING frame contents too long");
reader->hr_state = HR_ERROR; reader->hr_state = HR_ERROR;
return -1; return -1;
} }
@ -184,31 +179,6 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
else else
reader->hr_state = HR_READ_SETTING_BEGIN; reader->hr_state = HR_READ_SETTING_BEGIN;
break; 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: default:
assert(0); assert(0);
/* fall-through */ /* fall-through */

View file

@ -11,7 +11,6 @@ struct lsquic_conn;
struct hcsi_callbacks struct hcsi_callbacks
{ {
void (*on_priority)(void *ctx, const struct hq_priority *);
void (*on_cancel_push)(void *ctx, uint64_t push_id); void (*on_cancel_push)(void *ctx, uint64_t push_id);
void (*on_max_push_id)(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 */ /* Gets called at the *end* of the SETTING frame */
@ -30,8 +29,6 @@ struct hcsi_reader
HR_SKIPPING, HR_SKIPPING,
HR_READ_SETTING_BEGIN, HR_READ_SETTING_BEGIN,
HR_READ_SETTING_CONTINUE, HR_READ_SETTING_CONTINUE,
HR_READ_PRIORITY_BEGIN,
HR_READ_PRIORITY_CONTINUE,
HR_READ_VARINT, HR_READ_VARINT,
HR_READ_VARINT_CONTINUE, HR_READ_VARINT_CONTINUE,
HR_ERROR, HR_ERROR,
@ -43,7 +40,6 @@ struct hcsi_reader
{ {
struct varint_read_state vint_state; struct varint_read_state vint_state;
struct varint_read2_state vint2_state; struct varint_read2_state vint2_state;
struct h3_prio_frame_read_state prio;
} hr_u; } hr_u;
const struct hcsi_callbacks *hr_cb; const struct hcsi_callbacks *hr_cb;
void *hr_ctx; void *hr_ctx;

View file

@ -131,10 +131,10 @@ lsquic_hcso_write_settings (struct hcso_writer *writer,
const unsigned frame_size_len = 2; const unsigned frame_size_len = 2;
#endif #endif
unsigned char buf[1 /* Frame type */ + /* Frame size */ frame_size_len 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: * each value can be encoded in maximum 8 bytes:
*/ */
+ 4 * ( + 3 * (
#ifdef NDEBUG #ifdef NDEBUG
1 /* Each setting needs 1-byte varint number, */ 1 /* Each setting needs 1-byte varint number, */
#else #else
@ -146,18 +146,6 @@ lsquic_hcso_write_settings (struct hcso_writer *writer,
*p++ = HQFT_SETTINGS; *p++ = HQFT_SETTINGS;
p += frame_size_len; 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) if (settings->es_max_header_list_size != HQ_DF_MAX_HEADER_LIST_SIZE)
{ {
/* Write out SETTINGS_MAX_HEADER_LIST_SIZE */ /* Write out SETTINGS_MAX_HEADER_LIST_SIZE */

View file

@ -2,9 +2,9 @@
#ifndef LSQUIC_HKDF_H #ifndef LSQUIC_HKDF_H
#define LSQUIC_HKDF_H 1 #define LSQUIC_HKDF_H 1
/* [draft-ietf-quic-tls-22] Section 5.2 */ /* [draft-ietf-quic-tls-23] Section 5.2 */
#define HSK_SALT_BUF "\x7f\xbc\xdb\x0e\x7c\x66\xbb\xe9\x19\x3a" \ #define HSK_SALT_BUF "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" \
"\x96\xcd\x21\x51\x9e\xbd\x7a\x02\x64\x4a" "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"
#define HSK_SALT ((unsigned char *) HSK_SALT_BUF) #define HSK_SALT ((unsigned char *) HSK_SALT_BUF)
#define HSK_SALT_SZ (sizeof(HSK_SALT_BUF) - 1) #define HSK_SALT_SZ (sizeof(HSK_SALT_BUF) - 1)

View file

@ -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;
}

View file

@ -11,7 +11,6 @@ enum hq_frame_type
{ {
HQFT_DATA = 0, HQFT_DATA = 0,
HQFT_HEADERS = 1, HQFT_HEADERS = 1,
HQFT_PRIORITY = 2,
HQFT_CANCEL_PUSH = 3, HQFT_CANCEL_PUSH = 3,
HQFT_SETTINGS = 4, HQFT_SETTINGS = 4,
HQFT_PUSH_PROMISE = 5, 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 enum hq_setting_id
{ {
HQSID_QPACK_MAX_TABLE_CAPACITY = 1, HQSID_QPACK_MAX_TABLE_CAPACITY = 1,
HQSID_MAX_HEADER_LIST_SIZE = 6, HQSID_MAX_HEADER_LIST_SIZE = 6,
HQSID_QPACK_BLOCKED_STREAMS = 7, HQSID_QPACK_BLOCKED_STREAMS = 7,
HQSID_NUM_PLACEHOLDERS = 9,
}; };
/* As of 12/18/2018: */ /* As of 12/18/2018: */
#define HQ_DF_QPACK_MAX_TABLE_CAPACITY 0 #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_MAX_HEADER_LIST_SIZE 0
#define HQ_DF_QPACK_BLOCKED_STREAMS 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-http-19] Section 10.6,
* [draft-ietf-quic-qpack-07] Section 8.2 * [draft-ietf-quic-qpack-07] Section 8.2
@ -84,69 +49,33 @@ enum hq_uni_stream_type
HQUST_QPACK_DEC = 3, 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 * [draft-ietf-quic-qpack-08], Section 8.3
*/ */
enum http_error_code enum http_error_code
{ {
HEC_NO_ERROR = 0x00, HEC_NO_ERROR = 0x100,
HEC_GENERAL_PROTOCOL_ERROR = 0x01, HEC_GENERAL_PROTOCOL_ERROR = 0x101,
/* Error code 0x2 is reserved and has no meaning */ HEC_INTERNAL_ERROR = 0x102,
HEC_INTERNAL_ERROR = 0x03, HEC_STREAM_CREATION_ERROR = 0x103,
/* Error code 0x4 is reserved and has no meaning */ HEC_CLOSED_CRITICAL_STREAM = 0x104,
HEC_REQUEST_CANCELLED = 0x05, HEC_FRAME_UNEXPECTED = 0x105,
HEC_INCOMPLETE_REQUEST = 0x06, HEC_FRAME_ERROR = 0x106,
HEC_CONNECT_ERROR = 0x07, HEC_EXCESSIVE_LOAD = 0x107,
HEC_EXCESSIVE_LOAD = 0x08, HEC_ID_ERROR = 0x108,
HEC_VERSION_FALLBACK = 0x09, HEC_SETTINGS_ERROR = 0x109,
HEC_WRONG_STREAM = 0x0A, HEC_MISSING_SETTINGS = 0x10A,
HEC_ID_ERROR = 0x0B, HEC_REQUEST_REJECTED = 0x10B,
/* Error code 0xC is reserved and has no meaning */ HEC_REQUEST_CANCELLED = 0x10C,
HEC_STREAM_CREATION_ERROR = 0x0D, HEC_REQUEST_INCOMPLETE = 0x10D,
/* Error code 0xE is reserved and has no meaning */ HEC_EARLY_RESPONSE = 0x10E,
HEC_CLOSED_CRITICAL_STREAM = 0x0F, HEC_CONNECT_ERROR = 0x10F,
/* Error code 0x10 is reserved and has no meaning */ HEC_VERSION_FALLBACK = 0x110,
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_QPACK_DECOMPRESSION_FAILED = 0x200, HEC_QPACK_DECOMPRESSION_FAILED = 0x200,
HEC_QPACK_ENCODER_STREAM_ERROR = 0x201, HEC_QPACK_ENCODER_STREAM_ERROR = 0x201,
HEC_QPACK_DECODER_STREAM_ERROR = 0x202, 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 #endif

View file

@ -18,7 +18,6 @@ enum trans_error_code
TEC_TRANSPORT_PARAMETER_ERROR = 0x8, TEC_TRANSPORT_PARAMETER_ERROR = 0x8,
TEC_VERSION_NEGOTIATION_ERROR = 0x9, TEC_VERSION_NEGOTIATION_ERROR = 0x9,
TEC_PROTOCOL_VIOLATION = 0xA, TEC_PROTOCOL_VIOLATION = 0xA,
TEC_INVALID_MIGRATION = 0xC,
TEC_CRYPTO_BUFFER_EXCEEDED = 0xD, TEC_CRYPTO_BUFFER_EXCEEDED = 0xD,
}; };

View file

@ -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 * static struct lsquic_packet_out *
imico_get_packet_out (struct ietf_mini_conn *conn, imico_get_packet_out (struct ietf_mini_conn *conn,
enum header_type header_type, size_t need) enum header_type header_type, size_t need)
{ {
struct lsquic_packet_out *packet_out; struct lsquic_packet_out *packet_out;
enum ecn ecn;
if (need) if (need)
TAILQ_FOREACH(packet_out, &conn->imc_packets_out, po_next) 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_packno = conn->imc_next_packno++;
packet_out->po_flags |= PO_MINI; packet_out->po_flags |= PO_MINI;
lsquic_packet_out_set_pns(packet_out, lsquic_hety2pns[header_type]); lsquic_packet_out_set_pns(packet_out, lsquic_hety2pns[header_type]);
if (conn->imc_enpub->enp_settings.es_ecn) ecn = imico_get_ecn(conn);
packet_out->po_lflags |= ECN_ECT0 << POECN_SHIFT; packet_out->po_lflags |= ecn << POECN_SHIFT;
TAILQ_INSERT_TAIL(&conn->imc_packets_out, packet_out, po_next); TAILQ_INSERT_TAIL(&conn->imc_packets_out, packet_out, po_next);
packet_out->po_loss_chain = packet_out; packet_out->po_loss_chain = packet_out;
return 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 dec_packin dec_packin;
enum packnum_space pns; 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 ]; pns = lsquic_hety2pns[ packet_in->pi_header_type ];
if (pns == PNS_INIT && (conn->imc_flags & IMC_IGNORE_INIT)) 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])) packet_in->pi_packno > highest_bit_set(conn->imc_recvd_packnos[pns]))
conn->imc_largest_recvd[pns] = packet_in->pi_received; conn->imc_largest_recvd[pns] = packet_in->pi_received;
conn->imc_recvd_packnos[pns] |= 1ULL << packet_in->pi_packno; 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)) if (0 != imico_parse_regular_packet(conn, packet_in))
{
LSQ_DEBUG("connection is now in error state");
conn->imc_flags |= IMC_ERROR; 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_ecn_counts_in[pns][ lsquic_packet_in_ecn(packet_in) ];
conn->imc_incoming_ecn <<= 1; conn->imc_incoming_ecn <<= 1;
conn->imc_incoming_ecn |= lsquic_packet_in_ecn(packet_in) != ECN_NOT_ECT; 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; struct ietf_mini_conn *conn = (struct ietf_mini_conn *) lconn;
conn->imc_sent_packnos |= 1ULL << packet_out->po_packno; 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 0
if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK)) 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 #endif
++conn->imc_ecn_counts_out[ lsquic_packet_out_pns(packet_out) ] ++conn->imc_ecn_counts_out[ lsquic_packet_out_pns(packet_out) ]
[ lsquic_packet_out_ecn(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); 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); "resending as packet %"PRIu64, oldno, packno);
packet_out->po_packno = packno; packet_out->po_packno = packno;
packet_out->po_flags &= ~PO_SENT; packet_out->po_flags &= ~PO_SENT;
lsquic_packet_out_set_ecn(packet_out, imico_get_ecn(conn));
if (packet_out->po_flags & PO_ENCRYPTED) if (packet_out->po_flags & PO_ENCRYPTED)
imico_return_enc_data(conn, packet_out); imico_return_enc_data(conn, packet_out);
TAILQ_INSERT_TAIL(&conn->imc_packets_out, packet_out, po_next); 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; struct lsquic_packet_out *packet_out;
enum header_type header_type; enum header_type header_type;
enum packnum_space pns; enum packnum_space pns, pns_max;
unsigned error_code; unsigned error_code;
const char *reason; const char *reason;
size_t need; size_t need;
int sz, rlen, is_app; int sz, rlen, is_app;
char reason_buf[0x20]; 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) if (conn->imc_flags & IMC_ABORT_ERROR)
{ {
is_app = !!(conn->imc_flags & IMC_ABORT_ISAPP); is_app = !!(conn->imc_flags & IMC_ABORT_ISAPP);
@ -1302,18 +1339,59 @@ imico_generate_conn_close (struct ietf_mini_conn *conn)
rlen = 0; rlen = 0;
} }
sz = conn->imc_conn.cn_pf->pf_gen_connect_close_frame(
packet_out->po_data + packet_out->po_data_sz, /* [draft-ietf-quic-transport-23] Section 12.2:
lsquic_packet_out_avail(packet_out), is_app, error_code, reason, *
rlen); " A client will always know whether the server has Handshake keys (see
if (sz >= 0) " 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; case (0 << 1) | 0:
packet_out->po_data_sz += sz; pns = PNS_INIT;
LSQ_DEBUG("generated CONNECTION_CLOSE frame"); 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);
} }

View file

@ -53,6 +53,7 @@ struct ietf_mini_conn
IMC_ABORT_ISAPP = 1 << 15, IMC_ABORT_ISAPP = 1 << 15,
IMC_BAD_TRANS_PARAMS = 1 << 16, IMC_BAD_TRANS_PARAMS = 1 << 16,
IMC_ADDR_VALIDATED = 1 << 17, IMC_ADDR_VALIDATED = 1 << 17,
IMC_HSK_PACKET_SENT = 1 << 18,
} imc_flags; } imc_flags;
struct mini_crypto_stream imc_streams[N_ENC_LEVS]; struct mini_crypto_stream imc_streams[N_ENC_LEVS];
void *imc_stream_ps[N_ENC_LEVS]; void *imc_stream_ps[N_ENC_LEVS];
@ -72,6 +73,12 @@ struct ietf_mini_conn
unsigned imc_bytes_out; unsigned imc_bytes_out;
unsigned char imc_next_packno; unsigned char imc_next_packno;
unsigned char imc_hsk_count; 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_ack_exp;
uint8_t imc_ecn_counts_in[N_PNS][4]; uint8_t imc_ecn_counts_in[N_PNS][4];
uint8_t imc_ecn_counts_out[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 *, const struct lsquic_packet_in *,
enum lsquic_version, int is_ipv4, const struct lsquic_cid *); enum lsquic_version, int is_ipv4, const struct lsquic_cid *);
int
lsquic_mini_conn_ietf_ecn_ok (const struct ietf_mini_conn *);
#endif #endif

View file

@ -34,11 +34,13 @@
#include "lsquic_engine_public.h" #include "lsquic_engine_public.h"
#include "lsquic_sizes.h" #include "lsquic_sizes.h"
#include "lsquic_handshake.h" #include "lsquic_handshake.h"
#include "lsquic_xxhash.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PRQ #define LSQUIC_LOGGER_MODULE LSQLM_PRQ
#include "lsquic_logger.h" #include "lsquic_logger.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static const struct conn_iface evanescent_conn_iface; static const struct conn_iface evanescent_conn_iface;
@ -46,14 +48,15 @@ static const struct conn_iface evanescent_conn_iface;
struct packet_req 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_scid;
lsquic_cid_t pr_dcid; lsquic_cid_t pr_dcid;
enum packet_req_type pr_type; enum packet_req_type pr_type;
enum { enum pr_flags {
PR_GQUIC = 1 << 0, PR_GQUIC = 1 << 0,
} pr_flags; } pr_flags;
enum lsquic_version pr_version; enum lsquic_version pr_version;
unsigned pr_rst_sz;
struct network_path pr_path; struct network_path pr_path;
}; };
@ -79,10 +82,10 @@ struct pr_queue
{ {
TAILQ_HEAD(, lsquic_conn) prq_free_conns, TAILQ_HEAD(, lsquic_conn) prq_free_conns,
prq_returned_conns; prq_returned_conns;
STAILQ_HEAD(, packet_req) prq_reqs_queue;
struct malo *prq_reqs_pool; struct malo *prq_reqs_pool;
const struct lsquic_engine_public const struct lsquic_engine_public
*prq_enpub; *prq_enpub;
struct lsquic_hash *prq_reqs_hash;
unsigned prq_max_reqs; unsigned prq_max_reqs;
unsigned prq_nreqs; unsigned prq_nreqs;
unsigned prq_max_conns; 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 * struct pr_queue *
prq_create (unsigned max_elems, unsigned max_conns, prq_create (unsigned max_elems, unsigned max_conns,
const struct lsquic_engine_public *enpub) const struct lsquic_engine_public *enpub)
@ -110,6 +137,7 @@ prq_create (unsigned max_elems, unsigned max_conns,
const struct parse_funcs *pf; const struct parse_funcs *pf;
struct pr_queue *prq; struct pr_queue *prq;
struct malo *malo; struct malo *malo;
struct lsquic_hash *hash;
unsigned verneg_g_sz; unsigned verneg_g_sz;
ssize_t prst_g_sz; ssize_t prst_g_sz;
int len; int len;
@ -121,11 +149,19 @@ prq_create (unsigned max_elems, unsigned max_conns,
goto err0; goto err0;
} }
hash = lsquic_hash_create_ext(comp_reqs, hash_req);
if (!hash)
{
LSQ_WARN("cannot create hash");
goto err1;
}
prq = malloc(sizeof(*prq)); prq = malloc(sizeof(*prq));
if (!prq) if (!prq)
{ {
LSQ_WARN("malloc failed: %s", strerror(errno)); LSQ_WARN("malloc failed: %s", strerror(errno));
goto err1; goto err2;
} }
const lsquic_cid_t cid = { .len = 8, }; const lsquic_cid_t cid = { .len = 8, };
@ -137,7 +173,7 @@ prq_create (unsigned max_elems, unsigned max_conns,
if (len <= 0) if (len <= 0)
{ {
LSQ_ERROR("cannot generate version negotiation packet"); LSQ_ERROR("cannot generate version negotiation packet");
goto err2; goto err3;
} }
verneg_g_sz = (unsigned) len; verneg_g_sz = (unsigned) len;
@ -146,12 +182,12 @@ prq_create (unsigned max_elems, unsigned max_conns,
if (prst_g_sz < 0) if (prst_g_sz < 0)
{ {
LSQ_ERROR("cannot generate public reset packet"); LSQ_ERROR("cannot generate public reset packet");
goto err2; goto err3;
} }
TAILQ_INIT(&prq->prq_free_conns); TAILQ_INIT(&prq->prq_free_conns);
TAILQ_INIT(&prq->prq_returned_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_reqs_pool = malo;
prq->prq_max_reqs = max_elems; prq->prq_max_reqs = max_elems;
prq->prq_nreqs = 0; prq->prq_nreqs = 0;
@ -165,8 +201,10 @@ prq_create (unsigned max_elems, unsigned max_conns,
return prq; return prq;
err2: err3:
free(prq); free(prq);
err2:
lsquic_hash_destroy(hash);
err1: err1:
lsquic_malo_destroy(malo); lsquic_malo_destroy(malo);
err0: err0:
@ -185,6 +223,7 @@ prq_destroy (struct pr_queue *prq)
TAILQ_REMOVE(&prq->prq_free_conns, conn, cn_next_pr); TAILQ_REMOVE(&prq->prq_free_conns, conn, cn_next_pr);
free(conn); free(conn);
} }
lsquic_hash_destroy(prq->prq_reqs_hash);
lsquic_malo_destroy(prq->prq_reqs_pool); lsquic_malo_destroy(prq->prq_reqs_pool);
free(prq); free(prq);
} }
@ -223,6 +262,45 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
{ {
struct packet_req *req; struct packet_req *req;
lsquic_ver_tag_t ver_tag; 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); req = get_req(prq);
if (!req) if (!req)
@ -231,25 +309,34 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
return -1; return -1;
} }
STAILQ_INSERT_TAIL(&prq->prq_reqs_queue, req, pr_next);
req->pr_type = type; 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; 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; req->pr_path.np_peer_ctx = peer_ctx;
memcpy(NP_LOCAL_SA(&req->pr_path), local_addr, memcpy(NP_LOCAL_SA(&req->pr_path), local_addr,
sizeof(req->pr_path.np_local_addr)); sizeof(req->pr_path.np_local_addr));
memcpy(NP_PEER_SA(&req->pr_path), peer_addr, memcpy(NP_PEER_SA(&req->pr_path), peer_addr,
sizeof(req->pr_path.np_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, LSQ_DEBUGC("scheduled %s packet for connection %"CID_FMT,
lsquic_preqt2str[type], CID_BITS(&req->pr_dcid)); lsquic_preqt2str[type], CID_BITS(&req->pr_dcid));
return 0; return 0;
@ -316,6 +403,7 @@ prq_next_conn (struct pr_queue *prq)
{ {
struct evanescent_conn *evconn; struct evanescent_conn *evconn;
struct lsquic_conn *lconn; struct lsquic_conn *lconn;
struct lsquic_hash_elem *el;
struct packet_req *req; struct packet_req *req;
struct lsquic_packet_out *packet_out; struct lsquic_packet_out *packet_out;
int (*gen_verneg) (unsigned char *, size_t, const lsquic_cid_t *, int (*gen_verneg) (unsigned char *, size_t, const lsquic_cid_t *,
@ -329,14 +417,15 @@ prq_next_conn (struct pr_queue *prq)
return lconn; return lconn;
} }
req = STAILQ_FIRST(&prq->prq_reqs_queue); el = lsquic_hash_first(prq->prq_reqs_hash);
if (!req) /* Nothing is queued */ if (!el) /* Nothing is queued */
return NULL; return NULL;
evconn = get_evconn(prq); evconn = get_evconn(prq);
if (!evconn) /* Reached limit or malloc failed */ if (!evconn) /* Reached limit or malloc failed */
return NULL; return NULL;
req = lsquic_hashelem_getdata(el);
packet_out = &evconn->evc_packet_out; packet_out = &evconn->evc_packet_out;
switch ((req->pr_type << 29) | req->pr_flags) switch ((req->pr_type << 29) | req->pr_flags)
{ {
@ -370,16 +459,16 @@ prq_next_conn (struct pr_queue *prq)
break; break;
default: default:
packet_out->po_flags &= ~PO_VERNEG; packet_out->po_flags &= ~PO_VERNEG;
packet_out->po_data_sz = IQUIC_MIN_SRST_SIZE; packet_out->po_data_sz = req->pr_rst_sz;
RAND_bytes(packet_out->po_data, IQUIC_MIN_SRST_RANDOM_BYTES); 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] &= ~0x80;
packet_out->po_data[0] |= 0x40; packet_out->po_data[0] |= 0x40;
lsquic_tg_generate_sreset(prq->prq_enpub->enp_tokgen, &req->pr_dcid, 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; break;
} }
STAILQ_REMOVE_HEAD(&prq->prq_reqs_queue, pr_next); lsquic_hash_erase(prq->prq_reqs_hash, el);
evconn->evc_req = req; evconn->evc_req = req;
lconn= &evconn->evc_conn; lconn= &evconn->evc_conn;
@ -394,7 +483,7 @@ prq_next_conn (struct pr_queue *prq)
int int
prq_have_pending (const struct pr_queue *prq) prq_have_pending (const struct pr_queue *prq)
{ {
return !STAILQ_EMPTY(&prq->prq_reqs_queue); return lsquic_hash_count(prq->prq_reqs_hash) > 0;
} }

View file

@ -40,7 +40,6 @@
#include "lsquic_conn_public.h" #include "lsquic_conn_public.h"
#include "lsquic_cong_ctl.h" #include "lsquic_cong_ctl.h"
#include "lsquic_enc_sess.h" #include "lsquic_enc_sess.h"
#include "lsquic_h3_prio.h"
#include "lsquic_hash.h" #include "lsquic_hash.h"
#include "lsquic_malo.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_alset = alset;
ctl->sc_ver_neg = ver_neg; ctl->sc_ver_neg = ver_neg;
ctl->sc_conn_pub = conn_pub; ctl->sc_conn_pub = conn_pub;
assert(!(flags & ~(SC_IETF|SC_NSTP))); assert(!(flags & ~(SC_IETF|SC_NSTP|SC_ECN)));
ctl->sc_flags = flags; ctl->sc_flags = flags;
send_ctl_pick_initial_packno(ctl); send_ctl_pick_initial_packno(ctl);
if (enpub->enp_settings.es_pace_packets) if (enpub->enp_settings.es_pace_packets)
ctl->sc_flags |= SC_PACE; ctl->sc_flags |= SC_PACE;
if ((flags & SC_IETF) && enpub->enp_settings.es_ecn) if (flags & SC_ECN)
ctl->sc_ecn = ECN_ECT0; ctl->sc_ecn = ECN_ECT0;
else else
ctl->sc_ecn = ECN_NOT_ECT; 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) if (ctl->sc_ci->cci_lost)
ctl->sc_ci->cci_lost(CGP(ctl), packet_out, packet_sz); 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) if (packet_out->po_frame_types & ctl->sc_retx_frames)
{ {
LSQ_DEBUG("lost retransmittable packet %"PRIu64, 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; packet_out->po_frame_types &= ~GQUIC_FRAME_REGEN_MASK;
assert(packet_out->po_frame_types); assert(packet_out->po_frame_types);
packet_out->po_packno = packno; packet_out->po_packno = packno;
lsquic_packet_out_set_ecn(packet_out, ctl->sc_ecn);
if (ctl->sc_ver_neg->vn_tag) 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 #ifdef NDEBUG
send_ctl_determine_gquic_bpt (struct lsquic_send_ctl *ctl, static
const struct lsquic_stream *stream) #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; const lsquic_stream_t *other_stream;
struct lsquic_hash_elem *el; 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 static enum buf_packet_type
send_ctl_lookup_bpt (lsquic_send_ctl_t *ctl, send_ctl_lookup_bpt (lsquic_send_ctl_t *ctl,
const struct lsquic_stream *stream) const struct lsquic_stream *stream)

View file

@ -43,6 +43,7 @@ enum send_ctl_flags {
SC_LOST_ACK_APP = SC_LOST_ACK_INIT << PNS_APP, SC_LOST_ACK_APP = SC_LOST_ACK_INIT << PNS_APP,
SC_1RTT_ACKED = 1 << 11, SC_1RTT_ACKED = 1 << 11,
SC_APP_LIMITED = 1 << 12, SC_APP_LIMITED = 1 << 12,
SC_ECN = 1 << 13,
}; };
typedef struct lsquic_send_ctl { typedef struct lsquic_send_ctl {

View file

@ -4,10 +4,13 @@
#define IQUIC_SRESET_TOKEN_SZ 16u #define IQUIC_SRESET_TOKEN_SZ 16u
#define IQUIC_MIN_SRST_RANDOM_BYTES (1 /* First byte: 01XX XXXX */ \ #define IQUIC_MIN_SRST_RANDOM_BYTES (1u /* First byte: 01XX XXXX */ \
+ 24 /* Random bytes */) + 4u /* Random bytes */)
#define IQUIC_MIN_SRST_SIZE (IQUIC_MIN_SRST_RANDOM_BYTES \ #define IQUIC_MIN_SRST_SIZE (IQUIC_MIN_SRST_RANDOM_BYTES \
+ IQUIC_SRESET_TOKEN_SZ /* Token */) + IQUIC_SRESET_TOKEN_SZ /* Token */)
/* Allow some wiggle room */
#define IQUIC_MAX_SRST_SIZE (IQUIC_MIN_SRST_SIZE + 40u)
#endif #endif

View file

@ -64,7 +64,6 @@
#include "lsquic_qdec_hdl.h" #include "lsquic_qdec_hdl.h"
#include "lsquic_qenc_hdl.h" #include "lsquic_qenc_hdl.h"
#include "lsquic_byteswap.h" #include "lsquic_byteswap.h"
#include "lsquic_h3_prio.h"
#include "lsquic_ietf.h" #include "lsquic_ietf.h"
#include "lsquic_push_promise.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_onnew_arg = stream_if_ctx;
stream->sm_write_avail = stream_write_avail; 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); STAILQ_INIT(&stream->sm_hq_frames);
stream->sm_bflags |= ctor_flags & ((1 << (N_SMBF_FLAGS - 1)) - 1); 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); TAILQ_REMOVE(&stream->conn_pub->service_streams, stream, next_service_stream);
if (stream->sm_qflags & SMQF_QPACK_DEC) if (stream->sm_qflags & SMQF_QPACK_DEC)
lsquic_qdh_unref_stream(stream->conn_pub->u.ietf.qdh, stream); 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); drop_buffered_data(stream);
lsquic_sfcw_consume_rem(&stream->fc); lsquic_sfcw_consume_rem(&stream->fc);
drop_frames_in(stream); 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; filter->hqfi_flags |= HQFI_FLAG_ERROR;
LSQ_INFO("unexpected HTTP/3 frame sequence: %o", LSQ_INFO("unexpected HTTP/3 frame sequence: %o",
filter->hqfi_hist_buf); 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, "unexpected HTTP/3 frame sequence on stream %"PRIu64,
stream->id); stream->id);
goto end; goto end;
@ -3905,7 +3888,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
lconn = stream->conn_pub->lconn; lconn = stream->conn_pub->lconn;
if (stream->sm_bflags & SMBF_SERVER) if (stream->sm_bflags & SMBF_SERVER)
lconn->cn_if->ci_abort_error(lconn, 1, 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 " "on stream %"PRIu64" (clients are not supposed to "
"send those)", stream->id); "send those)", stream->id);
else else
@ -3913,7 +3896,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
* push. * push.
*/ */
lconn->cn_if->ci_abort_error(lconn, 1, 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)" "Received PUSH_PROMISE frame (not supported)"
"on stream %"PRIu64, stream->id); "on stream %"PRIu64, stream->id);
goto end; goto end;
@ -3927,7 +3910,6 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
case HQFT_GOAWAY: case HQFT_GOAWAY:
case HQFT_HEADERS: case HQFT_HEADERS:
case HQFT_MAX_PUSH_ID: case HQFT_MAX_PUSH_ID:
case HQFT_PRIORITY:
case HQFT_PUSH_PROMISE: case HQFT_PUSH_PROMISE:
case HQFT_SETTINGS: case HQFT_SETTINGS:
filter->hqfi_flags |= HQFI_FLAG_ERROR; filter->hqfi_flags |= HQFI_FLAG_ERROR;

View file

@ -165,8 +165,6 @@ enum stream_q_flags
SMQF_ABORT_CONN = 1 << 8, /* Unrecoverable error occurred */ SMQF_ABORT_CONN = 1 << 8, /* Unrecoverable error occurred */
SMQF_QPACK_DEC = 1 << 9, /* QPACK decoder is holding a reference to this stream */ 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_b_flags sm_bflags;
enum stream_q_flags sm_qflags; enum stream_q_flags sm_qflags;
unsigned n_unacked; unsigned n_unacked;
unsigned sm_h3_prio_idx;
const struct lsquic_stream_if *stream_if; const struct lsquic_stream_if *stream_if;
struct lsquic_stream_ctx *st_ctx; struct lsquic_stream_ctx *st_ctx;

View file

@ -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; need += 4 + 0;
if (need > bufsz || need > UINT16_MAX) if (need > bufsz || need > UINT16_MAX)
@ -240,10 +240,10 @@ lsquic_tp_encode (const struct transport_params *params,
sizeof(params->tp_preferred_address.srst)); sizeof(params->tp_preferred_address.srst));
} }
break; break;
case TPI_DISABLE_MIGRATION: case TPI_DISABLE_ACTIVE_MIGRATION:
if (params->tp_disable_migration != TP_DEF_DISABLE_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); WRITE_UINT_TO_P(0, 16);
} }
break; break;
@ -360,9 +360,9 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
{ {
switch (param_id) switch (param_id)
{ {
case TPI_DISABLE_MIGRATION: case TPI_DISABLE_ACTIVE_MIGRATION:
EXPECT_LEN(0); EXPECT_LEN(0);
params->tp_disable_migration = 1; params->tp_disable_active_migration = 1;
break; break;
case TPI_STATELESS_RESET_TOKEN: case TPI_STATELESS_RESET_TOKEN:
/* Client MUST not include 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(max_packet_size, "%"PRIu64);
WRITE_ONE_PARAM(ack_delay_exponent, "%"PRIu64); WRITE_ONE_PARAM(ack_delay_exponent, "%"PRIu64);
WRITE_ONE_PARAM(active_connection_id_limit, "%"PRIu64); WRITE_ONE_PARAM(active_connection_id_limit, "%"PRIu64);
WRITE_ONE_PARAM(disable_migration, "%hhd"); WRITE_ONE_PARAM(disable_active_migration, "%hhd");
#undef SEMICOLON #undef SEMICOLON
#define SEMICOLON "" #define SEMICOLON ""
WRITE_ONE_PARAM(max_ack_delay, "%"PRIu64); WRITE_ONE_PARAM(max_ack_delay, "%"PRIu64);

View file

@ -21,7 +21,7 @@ enum transport_param_id
TPI_INIT_MAX_STREAMS_UNI = 9, TPI_INIT_MAX_STREAMS_UNI = 9,
TPI_ACK_DELAY_EXPONENT = 10, TPI_ACK_DELAY_EXPONENT = 10,
TPI_MAX_ACK_DELAY = 11, TPI_MAX_ACK_DELAY = 11,
TPI_DISABLE_MIGRATION = 12, TPI_DISABLE_ACTIVE_MIGRATION = 12,
TPI_PREFERRED_ADDRESS = 13, TPI_PREFERRED_ADDRESS = 13,
TPI_ACTIVE_CONNECTION_ID_LIMIT = 14, TPI_ACTIVE_CONNECTION_ID_LIMIT = 14,
#define MAX_TPI TPI_ACTIVE_CONNECTION_ID_LIMIT #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_max_ack_delay tp_numerics_u.s.max_ack_delay
#define tp_active_connection_id_limit tp_numerics_u.s.active_connection_id_limit #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]; uint8_t tp_stateless_reset_token[IQUIC_SRESET_TOKEN_SZ];
struct { struct {
uint8_t ipv4_addr[4]; 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_UNI 0
#define TP_DEF_INIT_MAX_STREAMS_BIDI 0 #define TP_DEF_INIT_MAX_STREAMS_BIDI 0
#define TP_DEF_INIT_MAX_DATA 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_LOCAL 0
#define TP_DEF_INIT_MAX_STREAM_DATA_BIDI_REMOTE 0 #define TP_DEF_INIT_MAX_STREAM_DATA_BIDI_REMOTE 0
#define TP_DEF_INIT_MAX_STREAM_DATA_UNI 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_bidi = TP_DEF_INIT_MAX_STREAMS_BIDI, \
.tp_init_max_streams_uni = TP_DEF_INIT_MAX_STREAMS_UNI, \ .tp_init_max_streams_uni = TP_DEF_INIT_MAX_STREAMS_UNI, \
.tp_init_max_data = TP_DEF_INIT_MAX_DATA, \ .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_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_bidi_remote = TP_DEF_INIT_MAX_STREAM_DATA_BIDI_REMOTE, \
.tp_init_max_stream_data_uni = TP_DEF_INIT_MAX_STREAM_DATA_UNI .tp_init_max_stream_data_uni = TP_DEF_INIT_MAX_STREAM_DATA_UNI

View file

@ -14,7 +14,7 @@ static const unsigned char version_tags[N_LSQVER][4] =
#if LSQUIC_USE_Q098 #if LSQUIC_USE_Q098
[LSQVER_098] = { 'Q', '0', '9', '8', }, [LSQVER_098] = { 'Q', '0', '9', '8', },
#endif #endif
[LSQVER_ID22] = { 0xFF, 0, 0, 22, }, [LSQVER_ID23] = { 0xFF, 0, 0, 23, },
[LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, }, [LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, },
}; };
@ -51,7 +51,7 @@ const char *const lsquic_ver2str[N_LSQVER] = {
#if LSQUIC_USE_Q098 #if LSQUIC_USE_Q098
[LSQVER_098] = "Q098", [LSQVER_098] = "Q098",
#endif #endif
[LSQVER_ID22] = "FF000016", [LSQVER_ID23] = "FF000017",
[LSQVER_VERNEG] = "FAFAFAFA", [LSQVER_VERNEG] = "FAFAFAFA",
}; };

View file

@ -21,7 +21,7 @@ static int
select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen, select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg) const unsigned char *in, unsigned int inlen, void *arg)
{ {
const unsigned char alpn[] = "\x5h3-22"; const unsigned char alpn[] = "\x5h3-23";
int r; int r;
r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen, r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen,

View file

@ -576,6 +576,17 @@ read_one_packet (struct read_iter *iter)
, &packs_in->ecn[iter->ri_idx] , &packs_in->ecn[iter->ri_idx]
#endif #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 __linux__
if (sport->drop_init) if (sport->drop_init)
{ {
@ -1789,11 +1800,6 @@ set_engine_option (struct lsquic_engine_settings *settings,
} }
break; break;
case 15: case 15:
if (0 == strncmp(name, "h3_placeholders", 15))
{
settings->es_h3_placeholders = atoi(val);
return 0;
}
if (0 == strncmp(name, "allow_migration", 15)) if (0 == strncmp(name, "allow_migration", 15))
{ {
settings->es_allow_migration = atoi(val); settings->es_allow_migration = atoi(val);

View file

@ -44,7 +44,6 @@ SET(TESTS
frame_writer frame_writer
goaway_gquic_be goaway_gquic_be
h3_framing h3_framing
h3_prio
hcsi_reader hcsi_reader
hkdf hkdf
lsquic_hash lsquic_hash
@ -52,7 +51,6 @@ SET(TESTS
packno_len packno_len
parse parse
parse_packet_in parse_packet_in
parse_prio
purga purga
qlog qlog
quic_be_floats quic_be_floats

View file

@ -180,7 +180,7 @@ run_test (const struct test *test)
size_t sz; size_t sz;
unsigned char buf[0x1000]; unsigned char buf[0x1000];
pf = select_pf_by_ver(LSQVER_ID22); pf = select_pf_by_ver(LSQVER_ID23);
if (!test->skip_gen) if (!test->skip_gen)
{ {
rechist.acki = &test->acki; rechist.acki = &test->acki;

View file

@ -33,7 +33,7 @@ struct test {
static const struct test tests[] = { static const struct test tests[] = {
{ .lineno = __LINE__, { .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID22), .pf = select_pf_by_ver(LSQVER_ID23),
.offset = 0, .offset = 0,
.data_sz = 10, .data_sz = 10,
.data = "0123456789", .data = "0123456789",
@ -49,7 +49,7 @@ static const struct test tests[] = {
}, },
{ .lineno = __LINE__, { .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID22), .pf = select_pf_by_ver(LSQVER_ID23),
.offset = 500, .offset = 500,
.data_sz = 10, .data_sz = 10,
.data = "0123456789", .data = "0123456789",

View file

@ -55,9 +55,8 @@
#include "lsquic_varint.h" #include "lsquic_varint.h"
#include "lsquic_hq.h" #include "lsquic_hq.h"
#include "lsquic_data_in_if.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 struct test_ctl_settings
{ {
@ -298,7 +297,7 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
memset(tobjs, 0, sizeof(*tobjs)); memset(tobjs, 0, sizeof(*tobjs));
LSCONN_INITIALIZE(&tobjs->lconn); LSCONN_INITIALIZE(&tobjs->lconn);
tobjs->lconn.cn_pf = g_pf; 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; tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1;
network_path.np_pack_size = packet_sz; network_path.np_pack_size = packet_sz;
tobjs->lconn.cn_if = &our_conn_if; 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); assert(0 == s);
tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh; 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); lsquic_mm_cleanup(&tobjs->eng_pub.enp_mm);
if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS) if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS)
lsquic_qeh_cleanup(&tobjs->qeh); lsquic_qeh_cleanup(&tobjs->qeh);
if (tobjs->ctor_flags & SCF_IETF)
lsquic_prio_tree_destroy(tobjs->conn_pub.u.ietf.prio_tree);
} }

View file

@ -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;
}

View file

@ -28,71 +28,6 @@ struct test
static const struct test tests[] = 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__, __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 static void
on_cancel_push (void *ctx, uint64_t push_id) 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 = static const struct hcsi_callbacks callbacks =
{ {
.on_priority = on_priority,
.on_cancel_push = on_cancel_push, .on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id, .on_max_push_id = on_max_push_id,
.on_settings_frame = on_settings_frame, .on_settings_frame = on_settings_frame,

View file

@ -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;
}

View file

@ -56,7 +56,6 @@
#include "lsquic_varint.h" #include "lsquic_varint.h"
#include "lsquic_hq.h" #include "lsquic_hq.h"
#include "lsquic_data_in_if.h" #include "lsquic_data_in_if.h"
#include "lsquic_h3_prio.h"
#include "lsquic_headers.h" #include "lsquic_headers.h"
#include "lsquic_push_promise.h" #include "lsquic_push_promise.h"
@ -156,8 +155,8 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
int s; int s;
memset(tobjs, 0, sizeof(*tobjs)); memset(tobjs, 0, sizeof(*tobjs));
LSCONN_INITIALIZE(&tobjs->lconn); LSCONN_INITIALIZE(&tobjs->lconn);
tobjs->lconn.cn_pf = select_pf_by_ver(LSQVER_ID22); tobjs->lconn.cn_pf = select_pf_by_ver(LSQVER_ID23);
tobjs->lconn.cn_version = LSQVER_ID22; tobjs->lconn.cn_version = LSQVER_ID23;
tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1; tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1;
network_path.np_pack_size = IQUIC_MAX_IPv4_PACKET_SZ; network_path.np_pack_size = IQUIC_MAX_IPv4_PACKET_SZ;
tobjs->lconn.cn_if = &our_conn_if; 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); assert(0 == s);
tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh; 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); lsquic_mm_cleanup(&tobjs->eng_pub.enp_mm);
if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS) if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS)
lsquic_qeh_cleanup(&tobjs->qeh); lsquic_qeh_cleanup(&tobjs->qeh);
if (tobjs->ctor_flags & SCF_IETF)
lsquic_prio_tree_destroy(tobjs->conn_pub.u.ietf.prio_tree);
} }

View file

@ -50,7 +50,6 @@
#include "lsquic_varint.h" #include "lsquic_varint.h"
#include "lsquic_hq.h" #include "lsquic_hq.h"
#include "lsquic_data_in_if.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); 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); LSCONN_INITIALIZE(&tobjs->lconn);
tobjs->lconn.cn_pf = pf ? pf : g_pf; tobjs->lconn.cn_pf = pf ? pf : g_pf;
tobjs->lconn.cn_version = tobjs->lconn.cn_pf == &lsquic_parse_funcs_ietf_v1 ? 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; tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_gquic_1;
network_path.np_pack_size = 1370; network_path.np_pack_size = 1370;
tobjs->lconn.cn_if = &our_conn_if; 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); s = lsquic_qeh_settings(&tobjs->qeh, 0, 0, 0, 0);
assert(0 == s); assert(0 == s);
tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh; 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) if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS)
{ {
lsquic_qeh_cleanup(&tobjs->qeh); 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] = enum lsquic_version versions_to_test[3] =
{ {
LSQVER_046, LSQVER_046,
LSQVER_ID22, LSQVER_ID23,
}; };
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
@ -3097,7 +3093,7 @@ main (int argc, char **argv)
/* Redo some tests using crypto streams and frames */ /* Redo some tests using crypto streams and frames */
g_use_crypto_ctor = 1; g_use_crypto_ctor = 1;
g_pf = select_pf_by_ver(LSQVER_ID22); g_pf = select_pf_by_ver(LSQVER_ID23);
main_test_packetization(); main_test_packetization();
return 0; return 0;

View file

@ -279,7 +279,7 @@ static const struct test tests[] = {
*/ */
{ .lineno = __LINE__, { .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID22), .pf = select_pf_by_ver(LSQVER_ID23),
.fin = { 0, 1, }, .fin = { 0, 1, },
.offset = 0x0807060504030201UL, .offset = 0x0807060504030201UL,
.stream_id = 0x210, .stream_id = 0x210,
@ -299,7 +299,7 @@ static const struct test tests[] = {
}, },
{ .lineno = __LINE__, { .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID22), .pf = select_pf_by_ver(LSQVER_ID23),
.fin = { 0, 0, }, .fin = { 0, 0, },
.offset = 0, .offset = 0,
.stream_id = 0x210, .stream_id = 0x210,
@ -318,7 +318,7 @@ static const struct test tests[] = {
}, },
{ .lineno = __LINE__, { .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID22), .pf = select_pf_by_ver(LSQVER_ID23),
.fin = { 0, 0, }, .fin = { 0, 0, },
.offset = 0, .offset = 0,
.stream_id = 0x21, .stream_id = 0x21,
@ -336,7 +336,7 @@ static const struct test tests[] = {
}, },
{ .lineno = __LINE__, { .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID22), .pf = select_pf_by_ver(LSQVER_ID23),
.fin = { 0, 0, }, .fin = { 0, 0, },
.offset = 0x0807060504030201UL, .offset = 0x0807060504030201UL,
.stream_id = 0x210, .stream_id = 0x210,
@ -356,7 +356,7 @@ static const struct test tests[] = {
}, },
{ .lineno = __LINE__, { .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID22), .pf = select_pf_by_ver(LSQVER_ID23),
.fin = { 1, 0, }, .fin = { 1, 0, },
.offset = 0x0807060504030201UL, .offset = 0x0807060504030201UL,
.stream_id = 0x210, .stream_id = 0x210,
@ -374,7 +374,7 @@ static const struct test tests[] = {
}, },
{ .lineno = __LINE__, { .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID22), .pf = select_pf_by_ver(LSQVER_ID23),
.fin = { 1, 0, }, .fin = { 1, 0, },
.offset = 0x0807060504030201UL, .offset = 0x0807060504030201UL,
.stream_id = 0x210, .stream_id = 0x210,

View file

@ -243,7 +243,7 @@ static const struct test tests[] = {
{ "Balls to the wall: every possible bit is set", { "Balls to the wall: every possible bit is set",
__LINE__, __LINE__,
select_pf_by_ver(LSQVER_ID22), select_pf_by_ver(LSQVER_ID23),
/* TYPE OFF DLEN FIN */ /* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 1<<0, { 0x10 | 1<<2 | 1<<1 | 1<<0,
0x41, 0x23, /* Stream ID */ 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", { "Balls to the wall #2: every possible bit is set except FIN",
__LINE__, __LINE__,
select_pf_by_ver(LSQVER_ID22), select_pf_by_ver(LSQVER_ID23),
/* TYPE OFF DLEN FIN */ /* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 0<<0, { 0x10 | 1<<2 | 1<<1 | 0<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */ 0x81, 0x23, 0x00, 0xE4, /* Stream ID */
@ -281,7 +281,7 @@ static const struct test tests[] = {
{ "Data length is zero", { "Data length is zero",
__LINE__, __LINE__,
select_pf_by_ver(LSQVER_ID22), select_pf_by_ver(LSQVER_ID23),
/* TYPE OFF DLEN FIN */ /* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 0<<1 | 0<<0, { 0x10 | 1<<2 | 0<<1 | 0<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */ 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", { "Sanity check: what happens when data length is zero #1",
__LINE__, __LINE__,
select_pf_by_ver(LSQVER_ID22), select_pf_by_ver(LSQVER_ID23),
/* TYPE OFF DLEN FIN */ /* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 0<<0, { 0x10 | 1<<2 | 1<<1 | 0<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */ 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", { "Sanity check: what happens when data length is zero #2",
__LINE__, __LINE__,
select_pf_by_ver(LSQVER_ID22), select_pf_by_ver(LSQVER_ID23),
/* TYPE OFF DLEN FIN */ /* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 0<<0, { 0x10 | 1<<2 | 1<<1 | 0<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */ 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", { "Sanity check: what happens when data length is zero #3",
__LINE__, __LINE__,
select_pf_by_ver(LSQVER_ID22), select_pf_by_ver(LSQVER_ID23),
/* TYPE OFF DLEN FIN */ /* TYPE OFF DLEN FIN */
{ 0x10 | 0<<2 | 1<<1 | 0<<0, { 0x10 | 0<<2 | 1<<1 | 0<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */ 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", { "Sanity check: what happens when data length is zero #3",
__LINE__, __LINE__,
select_pf_by_ver(LSQVER_ID22), select_pf_by_ver(LSQVER_ID23),
/* TYPE OFF DLEN FIN */ /* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 1<<0, { 0x10 | 1<<2 | 1<<1 | 1<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */ 0x81, 0x23, 0x00, 0xE4, /* Stream ID */
@ -374,7 +374,7 @@ static const struct test tests[] = {
{ "Check data bounds #1", { "Check data bounds #1",
__LINE__, __LINE__,
select_pf_by_ver(LSQVER_ID22), select_pf_by_ver(LSQVER_ID23),
/* TYPE OFF DLEN FIN */ /* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 1<<0, { 0x10 | 1<<2 | 1<<1 | 1<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */ 0x81, 0x23, 0x00, 0xE4, /* Stream ID */
@ -393,7 +393,7 @@ static const struct test tests[] = {
{ "Check data bounds #2", { "Check data bounds #2",
__LINE__, __LINE__,
select_pf_by_ver(LSQVER_ID22), select_pf_by_ver(LSQVER_ID23),
/* TYPE OFF DLEN FIN */ /* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 1<<0, { 0x10 | 1<<2 | 1<<1 | 1<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */ 0x81, 0x23, 0x00, 0xE4, /* Stream ID */

View file

@ -97,7 +97,7 @@ static const struct trapa_test tests[] =
.tp_flags = TRAPA_SERVER, .tp_flags = TRAPA_SERVER,
.tp_init_max_data = 0x123456, .tp_init_max_data = 0x123456,
.tp_init_max_stream_data_bidi_local = 0xABCDEF88, .tp_init_max_stream_data_bidi_local = 0xABCDEF88,
.tp_disable_migration = 1, .tp_disable_active_migration = 1,
.tp_max_packet_size = 0x333, .tp_max_packet_size = 0x333,
}, },
.enc_len = 32, .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)) #define MCMP(f) 0 == memcmp(&a->f, &b->f, sizeof(a->f))
return a->tp_flags == b->tp_flags return a->tp_flags == b->tp_flags
&& MCMP(tp_numerics_u) && 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_stateless_reset_token)
&& MCMP(tp_preferred_address.ipv4_addr) && MCMP(tp_preferred_address.ipv4_addr)
&& MCMP(tp_preferred_address.ipv6_addr) && MCMP(tp_preferred_address.ipv6_addr)