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
- 2.3.1
- [BUGFIX] Fix memory leaks

View File

@ -273,3 +273,9 @@ More Compilation Options
this flag is specified, sending of packets will randomly fail, about
one out of every 10 attempts. Set environment variable
LSQUIC_RANDOM_SEND_FAILURE to change this frequency.
-DLSQUIC_ECN_BLACK_HOLE=1
When compiled with this flag, setting environment variable
LSQUIC_ECN_BLACK_HOLE to 1 will emulate ECN black hole: all received
packets with ECN markings are dropped on the floor.

View File

@ -24,8 +24,8 @@ extern "C" {
#endif
#define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 3
#define LSQUIC_PATCH_VERSION 1
#define LSQUIC_MINOR_VERSION 4
#define LSQUIC_PATCH_VERSION 0
/**
* Engine flags:
@ -125,9 +125,9 @@ enum lsquic_version
#endif
/**
* IETF QUIC Draft-22
* IETF QUIC Draft-23
*/
LSQVER_ID22,
LSQVER_ID23,
/**
* Special version to trigger version negotiation.
@ -139,7 +139,7 @@ enum lsquic_version
};
/**
* We currently support versions 39, 43, 46, and IETF Draft-22
* We currently support versions 39, 43, 46, and IETF Draft-23
* @see lsquic_version
*/
#define LSQUIC_SUPPORTED_VERSIONS ((1 << N_LSQVER) - 1)
@ -156,9 +156,9 @@ enum lsquic_version
#define LSQUIC_GQUIC_HEADER_VERSIONS ((1 << LSQVER_039) | (1 << LSQVER_043))
#define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID22) | (1 << LSQVER_VERNEG))
#define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID23) | (1 << LSQVER_VERNEG))
#define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID22) | (1 << LSQVER_VERNEG))
#define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID23) | (1 << LSQVER_VERNEG))
enum lsquic_hsk_status
{
@ -358,14 +358,8 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
#define LSQUIC_DF_QPACK_ENC_MAX_BLOCKED 100
#define LSQUIC_DF_QPACK_ENC_MAX_SIZE 4096
/** ECN is enabled by default */
#define LSQUIC_DF_ECN 1
/**
* The default number of the priority placeholders is higher than the
* recommended value of 16 to give the clients even more freedom.
*/
#define LSQUIC_DF_H3_PLACEHOLDERS 50
/** ECN is disabled by default */
#define LSQUIC_DF_ECN 0
/** Allow migration by default */
#define LSQUIC_DF_ALLOW_MIGRATION 1
@ -729,13 +723,6 @@ struct lsquic_engine_settings {
*/
int es_ecn;
/**
* Number of HTTP/3 priorify placeholders.
*
* The default is @ref LSQUIC_DF_H3_PLACEHOLDERS
*/
unsigned es_h3_placeholders;
/**
* Allow peer to migrate connection.
*

View File

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

View File

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

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;
#define select_esf_common_by_ver(ver) ( \
ver == LSQVER_ID22 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_ID23 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \
&lsquic_enc_session_common_gquic_1 )

View File

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

View File

@ -74,7 +74,6 @@
#include "lsquic_min_heap.h"
#include "lsquic_http1x_if.h"
#include "lsquic_parse_common.h"
#include "lsquic_h3_prio.h"
#include "lsquic_handshake.h"
#define LSQUIC_LOGGER_MODULE LSQLM_ENGINE
@ -320,7 +319,6 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
settings->es_qpack_dec_max_blocked = LSQUIC_DF_QPACK_DEC_MAX_BLOCKED;
settings->es_qpack_enc_max_size = LSQUIC_DF_QPACK_ENC_MAX_SIZE;
settings->es_qpack_enc_max_blocked = LSQUIC_DF_QPACK_ENC_MAX_BLOCKED;
settings->es_h3_placeholders = LSQUIC_DF_H3_PLACEHOLDERS;
settings->es_allow_migration = LSQUIC_DF_ALLOW_MIGRATION;
}
@ -331,8 +329,6 @@ lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
unsigned flags,
char *err_buf, size_t err_buf_sz)
{
unsigned sum;
if (settings->es_cfcw < LSQUIC_MIN_FCW ||
settings->es_sfcw < LSQUIC_MIN_FCW)
{
@ -384,19 +380,6 @@ lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
return -1;
}
sum = settings->es_init_max_streams_bidi
+ settings->es_init_max_streams_uni
+ settings->es_h3_placeholders;
if (sum > H3_PRIO_MAX_ELEMS)
{
if (err_buf)
snprintf(err_buf, err_buf_sz, "Combined number of streams and "
"placeholders (%u) is greater than the maximum supported "
"number of elements in the HTTP/3 priority tree (%u)",
sum, H3_PRIO_MAX_ELEMS);
return -1;
}
if (settings->es_cc_algo > 2)
{
if (err_buf)
@ -955,8 +938,7 @@ schedule_req_packet (struct lsquic_engine *engine, enum packet_req_type type,
LSQ_DEBUGC("scheduled %s packet for cid %"CID_FMT,
lsquic_preqt2str[type], CID_BITS(&packet_in->pi_conn_id));
else
LSQ_DEBUG("cannot schedule %s packet: out of packet request objects",
lsquic_preqt2str[type]);
LSQ_DEBUG("cannot schedule %s packet", lsquic_preqt2str[type]);
}

View File

@ -61,7 +61,7 @@
#include "lsquic_mini_conn_ietf.h"
#include "lsquic_tokgen.h"
#include "lsquic_full_conn.h"
#include "lsquic_h3_prio.h"
#include "lsquic_spi.h"
#include "lsquic_ietf.h"
#include "lsquic_push_promise.h"
#include "lsquic_headers.h"
@ -70,7 +70,6 @@
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&conn->ifc_conn)
#include "lsquic_logger.h"
#define MAX_ANY_PACKETS_SINCE_LAST_ACK 20
#define MAX_RETR_PACKETS_SINCE_LAST_ACK 2
#define ACK_TIMEOUT (TP_DEF_MAX_ACK_DELAY * 1000)
#define INITIAL_CHAL_TIMEOUT 25000
@ -873,7 +872,7 @@ ietf_full_conn_add_scid (struct ietf_full_conn *conn,
static int
ietf_full_conn_init (struct ietf_full_conn *conn,
struct lsquic_engine_public *enpub, unsigned flags)
struct lsquic_engine_public *enpub, unsigned flags, int ecn)
{
conn->ifc_conn.cn_if = ietf_full_conn_iface_ptr;
if (enpub->enp_settings.es_scid_len)
@ -907,21 +906,16 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
lsquic_rechist_init(&conn->ifc_rechist[PNS_APP], &conn->ifc_conn, 1);
lsquic_send_ctl_init(&conn->ifc_send_ctl, &conn->ifc_alset, enpub,
flags & IFC_SERVER ? &server_ver_neg : &conn->ifc_u.cli.ifcli_ver_neg,
&conn->ifc_pub, SC_IETF|SC_NSTP);
&conn->ifc_pub, SC_IETF|SC_NSTP|(ecn ? SC_ECN : 0));
lsquic_cfcw_init(&conn->ifc_pub.cfcw, &conn->ifc_pub,
conn->ifc_settings->es_cfcw);
conn->ifc_pub.all_streams = lsquic_hash_create();
if (!conn->ifc_pub.all_streams)
goto err0;
conn->ifc_pub.u.ietf.prio_tree = lsquic_prio_tree_new(&conn->ifc_conn,
flags & IFC_SERVER ? conn->ifc_settings->es_h3_placeholders : 0);
if (!conn->ifc_pub.u.ietf.prio_tree)
goto err1;
return -1;
conn->ifc_pub.u.ietf.qeh = &conn->ifc_qeh;
conn->ifc_pub.u.ietf.qdh = &conn->ifc_qdh;
conn->ifc_peer_hq_settings.header_table_size = HQ_DF_QPACK_MAX_TABLE_CAPACITY;
conn->ifc_peer_hq_settings.num_placeholders = conn->ifc_settings->es_h3_placeholders;
conn->ifc_peer_hq_settings.max_header_list_size = HQ_DF_MAX_HEADER_LIST_SIZE;
conn->ifc_peer_hq_settings.qpack_blocked_streams = HQ_DF_QPACK_BLOCKED_STREAMS;
@ -936,11 +930,6 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
conn->ifc_idle_to = enpub->enp_settings.es_idle_timeout * 1000 * 1000;
conn->ifc_ping_period = enpub->enp_settings.es_ping_period * 1000 * 1000;
return 0;
err1:
lsquic_hash_destroy(conn->ifc_pub.all_streams);
err0:
return -1;
}
@ -990,7 +979,8 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
}
conn->ifc_paths[0].cop_path.np_pack_size = max_packet_size;
if (0 != ietf_full_conn_init(conn, enpub, flags))
if (0 != ietf_full_conn_init(conn, enpub, flags,
enpub->enp_settings.es_ecn))
{
free(conn);
return NULL;
@ -1139,7 +1129,8 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
/* Set the flags early so that correct CID is used for logging */
conn->ifc_conn.cn_flags |= LSCONN_IETF | LSCONN_SERVER;
if (0 != ietf_full_conn_init(conn, enpub, flags))
if (0 != ietf_full_conn_init(conn, enpub, flags,
lsquic_mini_conn_ietf_ecn_ok(imc)))
{
free(conn);
return NULL;
@ -2197,16 +2188,18 @@ process_stream_ready_to_send (struct ietf_full_conn *conn,
static void
process_streams_ready_to_send (struct ietf_full_conn *conn)
{
lsquic_stream_t *stream;
struct lsquic_stream *stream;
struct stream_prio_iter spi;
assert(!TAILQ_EMPTY(&conn->ifc_pub.sending_streams));
lsquic_prio_tree_iter_reset(conn->ifc_pub.u.ietf.prio_tree, "send");
TAILQ_FOREACH(stream, &conn->ifc_pub.sending_streams, next_send_stream)
lsquic_prio_tree_iter_add(conn->ifc_pub.u.ietf.prio_tree, stream);
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.sending_streams),
TAILQ_LAST(&conn->ifc_pub.sending_streams, lsquic_streams_tailq),
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_send_stream),
SMQF_SENDING_FLAGS, &conn->ifc_conn, "send", NULL, NULL);
while ((stream = lsquic_prio_tree_iter_next(
conn->ifc_pub.u.ietf.prio_tree)))
for (stream = lsquic_spi_first(&spi); stream;
stream = lsquic_spi_next(&spi))
if (!process_stream_ready_to_send(conn, stream))
break;
}
@ -2375,8 +2368,6 @@ ietf_full_conn_ci_destroy (struct lsquic_conn *lconn)
lsquic_malo_destroy(conn->ifc_pub.packet_out_malo);
if (conn->ifc_flags & IFC_CREATED_OK)
conn->ifc_enpub->enp_stream_if->on_conn_closed(&conn->ifc_conn);
if (conn->ifc_pub.u.ietf.prio_tree)
lsquic_prio_tree_destroy(conn->ifc_pub.u.ietf.prio_tree);
if (conn->ifc_conn.cn_enc_session)
conn->ifc_conn.cn_esf.i->esfi_destroy(conn->ifc_conn.cn_enc_session);
while (!STAILQ_EMPTY(&conn->ifc_stream_ids_to_ss))
@ -2533,7 +2524,7 @@ begin_migra_or_retire_cid (struct ietf_full_conn *conn,
struct sockaddr_in6 v6;
} sockaddr;
if (params->tp_disable_migration)
if (params->tp_disable_active_migration)
{
LSQ_DEBUG("TP disables migration: retire PreferredAddress CID");
retire_cid_from_tp(conn, params);
@ -3337,39 +3328,32 @@ static void
process_streams_read_events (struct ietf_full_conn *conn)
{
struct lsquic_stream *stream;
int have_streams, iters;
int iters;
enum stream_q_flags q_flags, needs_service;
struct stream_prio_iter spi;
static const char *const labels[2] = { "read-0", "read-1", };
if (TAILQ_EMPTY(&conn->ifc_pub.read_streams))
return;
conn->ifc_pub.cp_flags &= ~CP_STREAM_UNBLOCKED;
iters = 0;
do
{
have_streams = 0;
TAILQ_FOREACH(stream, &conn->ifc_pub.read_streams, next_read_stream)
if (lsquic_stream_readable(stream))
{
if (!have_streams)
{
++have_streams;
lsquic_prio_tree_iter_reset(conn->ifc_pub.u.ietf.prio_tree,
labels[iters]);
}
lsquic_prio_tree_iter_add(conn->ifc_pub.u.ietf.prio_tree,
stream);
}
if (!have_streams)
break;
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.read_streams),
TAILQ_LAST(&conn->ifc_pub.read_streams, lsquic_streams_tailq),
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_read_stream),
SMQF_WANT_READ, &conn->ifc_conn, labels[iters], NULL, NULL);
needs_service = 0;
while ((stream = lsquic_prio_tree_iter_next(
conn->ifc_pub.u.ietf.prio_tree)))
for (stream = lsquic_spi_first(&spi); stream;
stream = lsquic_spi_next(&spi))
{
q_flags = stream->sm_qflags & SMQF_SERVICE_FLAGS;
lsquic_stream_dispatch_read_events(stream);
needs_service |= q_flags ^ (stream->sm_qflags & SMQF_SERVICE_FLAGS);
}
if (needs_service)
service_streams(conn);
}
@ -3435,31 +3419,25 @@ write_is_possible (struct ietf_full_conn *conn)
}
/* Write events are dispatched in two steps. First, only the high-priority
* streams are processed. High-priority streams are critical streams plus
* one non-critical streams with the highest priority. In the second step,
* all other streams are processed.
*/
static void
process_streams_write_events (struct ietf_full_conn *conn, int high_prio,
const struct lsquic_stream **highest_non_crit)
process_streams_write_events (struct ietf_full_conn *conn, int high_prio)
{
struct lsquic_stream *stream;
struct h3_prio_tree *const prio_tree = conn->ifc_pub.u.ietf.prio_tree;
struct stream_prio_iter spi;
lsquic_spi_init(&spi, TAILQ_FIRST(&conn->ifc_pub.write_streams),
TAILQ_LAST(&conn->ifc_pub.write_streams, lsquic_streams_tailq),
(uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_write_stream),
SMQF_WANT_WRITE|SMQF_WANT_FLUSH, &conn->ifc_conn,
high_prio ? "write-high" : "write-low", NULL, NULL);
if (high_prio)
*highest_non_crit = lsquic_prio_tree_highest_non_crit(prio_tree);
lsquic_prio_tree_iter_reset(prio_tree, high_prio ? "write-high" :
"write-low");
TAILQ_FOREACH(stream, &conn->ifc_pub.write_streams, next_write_stream)
if (high_prio ==
(stream == *highest_non_crit ||
lsquic_stream_is_critical(stream)))
lsquic_prio_tree_iter_add(prio_tree, stream);
lsquic_spi_drop_non_high(&spi);
else
lsquic_spi_drop_high(&spi);
while ((stream = lsquic_prio_tree_iter_next(
conn->ifc_pub.u.ietf.prio_tree)))
for (stream = lsquic_spi_first(&spi); stream && write_is_possible(conn);
stream = lsquic_spi_next(&spi))
if (stream->sm_qflags & SMQF_WRITE_Q_FLAGS)
lsquic_stream_dispatch_write_events(stream);
@ -3920,6 +3898,9 @@ switch_path_to (struct ietf_full_conn *conn, unsigned char path_id)
assert(conn->ifc_cur_path_id != path_id);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "switched paths");
/* TODO: reset cwnd and RTT estimate.
* See [draft-ietf-quic-transport-23] Section 9.4.
*/
lsquic_send_ctl_repath(&conn->ifc_send_ctl,
CUR_NPATH(conn), &conn->ifc_paths[path_id].cop_path);
maybe_retire_dcid(conn, &CUR_NPATH(conn)->np_dcid);
@ -5550,6 +5531,7 @@ process_regular_packet (struct ietf_full_conn *conn,
switch (dec_packin)
{
case DECPI_BADCRYPT:
case DECPI_TOO_SHORT:
if (conn->ifc_enpub->enp_settings.es_honor_prst
/* In server mode, even if we do support stateless reset packets,
* they are handled in lsquic_engine.c. No need to have this
@ -5562,22 +5544,21 @@ process_regular_packet (struct ietf_full_conn *conn,
conn->ifc_flags |= IFC_GOT_PRST;
return -1;
}
else
else if (dec_packin == DECPI_BADCRYPT)
{
LSQ_INFO("could not decrypt packet (type %s)",
lsquic_hety2str[packet_in->pi_header_type]);
return 0;
}
else
{
LSQ_INFO("packet is too short to be decrypted");
return 0;
}
case DECPI_NOT_YET:
return 0;
case DECPI_NOMEM:
return 0;
case DECPI_TOO_SHORT:
/* We should not hit this path: packets that are too short should
* not be parsed correctly.
*/
LSQ_INFO("packet is too short to be decrypted");
return 0;
case DECPI_VIOLATION:
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION,
"decrypter reports protocol violation");
@ -5853,7 +5834,6 @@ static enum tick_st
ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
const struct lsquic_stream *highest_non_crit;
int have_delayed_packets, s;
enum tick_st tick = 0;
unsigned n;
@ -6014,7 +5994,7 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
if (!TAILQ_EMPTY(&conn->ifc_pub.write_streams))
{
process_streams_write_events(conn, 1, &highest_non_crit);
process_streams_write_events(conn, 1);
if (!write_is_possible(conn))
goto end_write;
}
@ -6025,7 +6005,7 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
goto end_write;
if (!TAILQ_EMPTY(&conn->ifc_pub.write_streams))
process_streams_write_events(conn, 0, &highest_non_crit);
process_streams_write_events(conn, 0);
lsquic_send_ctl_maybe_app_limited(&conn->ifc_send_ctl, CUR_NPATH(conn));
@ -6448,59 +6428,6 @@ static const struct conn_iface *ietf_full_conn_iface_ptr =
&ietf_full_conn_iface;
static void
on_priority (void *ctx, const struct hq_priority *priority)
{
struct ietf_full_conn *const conn = ctx;
ABORT_QUIETLY(1, HEC_UNEXPECTED_FRAME,
"server should not send PRIORITY frames");
}
static void
on_priority_server (void *ctx, const struct hq_priority *priority)
{
struct ietf_full_conn *const conn = ctx;
enum h3_elem_type child_type, parent_type;
int s;
LSQ_DEBUG("%s: %s #%"PRIu64" depends on %s #%"PRIu64"; "
"weight: %u", __func__,
lsquic_h3pet2str[priority->hqp_prio_type], priority->hqp_prio_id,
lsquic_h3det2str[priority->hqp_dep_type], priority->hqp_dep_id,
HQP_WEIGHT(priority));
switch (priority->hqp_dep_type)
{
case H3DET_REQ_STREAM: parent_type = H3ET_REQ_STREAM; break;
case H3DET_PUSH_STREAM: parent_type = H3ET_PUSH_STREAM; break;
case H3DET_PLACEHOLDER: parent_type = H3ET_PLACEHOLDER; break;
default: parent_type = H3ET_ROOT; break;
}
switch (priority->hqp_prio_type)
{
case H3PET_REQ_STREAM: child_type = H3ET_REQ_STREAM; break;
case H3PET_PUSH_STREAM: child_type = H3ET_PUSH_STREAM; break;
case H3PET_PLACEHOLDER: child_type = H3ET_PLACEHOLDER; break;
default:
ABORT_QUIETLY(1, HEC_MALFORMED_FRAME + HQFT_PRIORITY,
"Priority frame on control stream specified current stream "
"prioritized type");
return;
}
s = lsquic_prio_tree_set_rel(conn->ifc_pub.u.ietf.prio_tree, child_type,
priority->hqp_prio_id, priority->hqp_weight, parent_type,
priority->hqp_dep_id);
/* TODO: differentiate between internal and protocol errors -- perhaps
* via a special callback to be called in prio_tree.
*/
if (s != 0)
ABORT_QUIETLY(1, HEC_GENERAL_PROTOCOL_ERROR, "error setting priority");
}
static void
on_cancel_push (void *ctx, uint64_t push_id)
{
@ -6514,7 +6441,7 @@ static void
on_max_push_id_client (void *ctx, uint64_t push_id)
{
struct ietf_full_conn *const conn = ctx;
ABORT_QUIETLY(1, HEC_WRONG_STREAM, "client does not expect the server "
ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED, "client does not expect the server "
"to send MAX_PUSH_ID frame");
}
@ -6586,19 +6513,6 @@ on_setting (void *ctx, uint64_t setting_id, uint64_t value)
LSQ_DEBUG("Peer's SETTINGS_QPACK_BLOCKED_STREAMS=%"PRIu64, value);
conn->ifc_peer_hq_settings.qpack_blocked_streams = value;
break;
case HQSID_NUM_PLACEHOLDERS:
LSQ_DEBUG("Peer's SETTINGS_NUM_PLACEHOLDERS=%"PRIu64, value);
if (conn->ifc_flags & IFC_SERVER)
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION,
"HTTP_WRONG_SETTING_DIRECTION");
else
{
conn->ifc_peer_hq_settings.num_placeholders
= MIN(value, conn->ifc_settings->es_h3_placeholders);
lsquic_prio_tree_set_ph(conn->ifc_pub.u.ietf.prio_tree,
conn->ifc_peer_hq_settings.num_placeholders);
}
break;
case HQSID_QPACK_MAX_TABLE_CAPACITY:
LSQ_DEBUG("Peer's SETTINGS_QPACK_MAX_TABLE_CAPACITY=%"PRIu64, value);
conn->ifc_peer_hq_settings.header_table_size = value;
@ -6620,7 +6534,7 @@ static void
on_goaway_server (void *ctx, uint64_t stream_id)
{
struct ietf_full_conn *const conn = ctx;
ABORT_QUIETLY(1, HEC_UNEXPECTED_FRAME,
ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED,
"client should not send GOAWAY frames");
}
@ -6636,7 +6550,7 @@ on_goaway (void *ctx, uint64_t stream_id)
sit = stream_id & SIT_MASK;
if (sit != SIT_BIDI_CLIENT)
{
ABORT_QUIETLY(1, HEC_MALFORMED_FRAME,
ABORT_QUIETLY(1, HEC_ID_ERROR,
"stream ID %"PRIu64" in GOAWAY frame", stream_id);
return;
}
@ -6669,14 +6583,13 @@ static void
on_unexpected_frame (void *ctx, uint64_t frame_type)
{
struct ietf_full_conn *const conn = ctx;
ABORT_QUIETLY(1, HEC_WRONG_STREAM, "Frame type %"PRIu64" is not allowed "
"on the control stream", frame_type);
ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED, "Frame type %"PRIu64" is not "
"allowed on the control stream", frame_type);
}
static const struct hcsi_callbacks hcsi_callbacks_server =
{
.on_priority = on_priority_server,
.on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id,
.on_settings_frame = on_settings_frame,
@ -6687,7 +6600,6 @@ static const struct hcsi_callbacks hcsi_callbacks_server =
static const struct hcsi_callbacks hcsi_callbacks =
{
.on_priority = on_priority,
.on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id_client,
.on_settings_frame = on_settings_frame,
@ -6815,8 +6727,10 @@ apply_uni_stream_class (struct ietf_full_conn *conn,
}
else
{
ABORT_WARN("Incoming QPACK encoder stream already exists");
/* TODO: special error code? */
ABORT_QUIETLY(1, HEC_STREAM_CREATION_ERROR,
"Incoming QPACK encoder stream %"PRIu64" already exists: "
"cannot create second stream %"PRIu64,
conn->ifc_qdh.qdh_enc_sm_in->id, stream->id);
lsquic_stream_close(stream);
}
break;
@ -6830,8 +6744,10 @@ apply_uni_stream_class (struct ietf_full_conn *conn,
}
else
{
ABORT_WARN("Incoming QPACK decoder stream already exists");
/* TODO: special error code? */
ABORT_QUIETLY(1, HEC_STREAM_CREATION_ERROR,
"Incoming QPACK decoder stream %"PRIu64" already exists: "
"cannot create second stream %"PRIu64,
conn->ifc_qeh.qeh_dec_sm_in->id, stream->id);
lsquic_stream_close(stream);
}
break;

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,
qh_all;
struct lsquic_hash_elem *qh_iter_next;
int (*qh_cmp)(const void *, const void *, size_t);
unsigned (*qh_hash)(const void *, size_t, unsigned seed);
unsigned qh_count;
unsigned qh_nbits;
};
struct lsquic_hash *
lsquic_hash_create (void)
lsquic_hash_create_ext (int (*cmp)(const void *, const void *, size_t),
unsigned (*hashf)(const void *, size_t, unsigned seed))
{
struct hels_head *buckets;
struct lsquic_hash *hash;
@ -53,6 +56,8 @@ lsquic_hash_create (void)
TAILQ_INIT(&buckets[i]);
TAILQ_INIT(&hash->qh_all);
hash->qh_cmp = cmp;
hash->qh_hash = hashf;
hash->qh_buckets = buckets;
hash->qh_nbits = nbits;
hash->qh_iter_next = NULL;
@ -61,6 +66,13 @@ lsquic_hash_create (void)
}
struct lsquic_hash *
lsquic_hash_create (void)
{
return lsquic_hash_create_ext(memcmp, XXH32);
}
void
lsquic_hash_destroy (struct lsquic_hash *hash)
{
@ -116,7 +128,7 @@ lsquic_hash_insert (struct lsquic_hash *hash, const void *key,
0 != lsquic_hash_grow(hash))
return NULL;
hash_val = XXH64(key, key_sz, (uintptr_t) hash);
hash_val = hash->qh_hash(key, key_sz, (uintptr_t) hash);
buckno = BUCKNO(hash->qh_nbits, hash_val);
TAILQ_INSERT_TAIL(&hash->qh_all, el, qhe_next_all);
TAILQ_INSERT_TAIL(&hash->qh_buckets[buckno], el, qhe_next_bucket);
@ -136,12 +148,12 @@ lsquic_hash_find (struct lsquic_hash *hash, const void *key, unsigned key_sz)
unsigned buckno, hash_val;
struct lsquic_hash_elem *el;
hash_val = XXH64(key, key_sz, (uintptr_t) hash);
hash_val = hash->qh_hash(key, key_sz, (uintptr_t) hash);
buckno = BUCKNO(hash->qh_nbits, hash_val);
TAILQ_FOREACH(el, &hash->qh_buckets[buckno], qhe_next_bucket)
if (hash_val == el->qhe_hash_val &&
key_sz == el->qhe_key_len &&
0 == memcmp(key, el->qhe_key_data, key_sz))
0 == hash->qh_cmp(key, el->qhe_key_data, key_sz))
{
return el;
}

View File

@ -25,6 +25,10 @@ struct lsquic_hash_elem
struct lsquic_hash *
lsquic_hash_create (void);
struct lsquic_hash *
lsquic_hash_create_ext (int (*cmp)(const void *, const void *, size_t),
unsigned (*hash)(const void *, size_t, unsigned seed));
void
lsquic_hash_destroy (struct lsquic_hash *);

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

View File

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

View File

@ -131,10 +131,10 @@ lsquic_hcso_write_settings (struct hcso_writer *writer,
const unsigned frame_size_len = 2;
#endif
unsigned char buf[1 /* Frame type */ + /* Frame size */ frame_size_len
/* There are maximum four settings that need to be written out and
/* There are maximum three settings that need to be written out and
* each value can be encoded in maximum 8 bytes:
*/
+ 4 * (
+ 3 * (
#ifdef NDEBUG
1 /* Each setting needs 1-byte varint number, */
#else
@ -146,18 +146,6 @@ lsquic_hcso_write_settings (struct hcso_writer *writer,
*p++ = HQFT_SETTINGS;
p += frame_size_len;
if (is_server)
if (settings->es_h3_placeholders != HQ_DF_NUM_PLACEHOLDERS)
{
/* Write out SETTINGS_NUM_PLACEHOLDERS */
bits = hcso_setting_type2bits(writer, HQSID_NUM_PLACEHOLDERS);
vint_write(p, HQSID_NUM_PLACEHOLDERS, bits, 1 << bits);
p += 1 << bits;
bits = vint_val2bits(settings->es_h3_placeholders);
vint_write(p, settings->es_h3_placeholders, bits, 1 << bits);
p += 1 << bits;
}
if (settings->es_max_header_list_size != HQ_DF_MAX_HEADER_LIST_SIZE)
{
/* Write out SETTINGS_MAX_HEADER_LIST_SIZE */

View File

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

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_HEADERS = 1,
HQFT_PRIORITY = 2,
HQFT_CANCEL_PUSH = 3,
HQFT_SETTINGS = 4,
HQFT_PUSH_PROMISE = 5,
@ -26,52 +25,18 @@ enum hq_frame_type
};
enum h3_prio_el_type
{
H3PET_REQ_STREAM = 0,
H3PET_PUSH_STREAM = 1,
H3PET_PLACEHOLDER = 2,
H3PET_CUR_STREAM = 3,
};
enum h3_dep_el_type
{
H3DET_REQ_STREAM = 0,
H3DET_PUSH_STREAM = 1,
H3DET_PLACEHOLDER = 2,
H3DET_ROOT = 3,
};
#define HQ_PT_SHIFT 6
#define HQ_DT_SHIFT 4
enum hq_setting_id
{
HQSID_QPACK_MAX_TABLE_CAPACITY = 1,
HQSID_MAX_HEADER_LIST_SIZE = 6,
HQSID_QPACK_BLOCKED_STREAMS = 7,
HQSID_NUM_PLACEHOLDERS = 9,
};
/* As of 12/18/2018: */
#define HQ_DF_QPACK_MAX_TABLE_CAPACITY 0
#define HQ_DF_NUM_PLACEHOLDERS 0
#define HQ_DF_MAX_HEADER_LIST_SIZE 0
#define HQ_DF_QPACK_BLOCKED_STREAMS 0
struct hq_priority
{
lsquic_stream_id_t hqp_prio_id;
lsquic_stream_id_t hqp_dep_id;
enum h3_prio_el_type hqp_prio_type:8;
enum h3_dep_el_type hqp_dep_type:8;
uint8_t hqp_weight;
};
#define HQP_WEIGHT(p) ((p)->hqp_weight + 1)
/* [draft-ietf-quic-http-19] Section 10.6,
* [draft-ietf-quic-qpack-07] Section 8.2
@ -84,69 +49,33 @@ enum hq_uni_stream_type
HQUST_QPACK_DEC = 3,
};
extern const char *const lsquic_h3det2str[];
extern const char *const lsquic_h3pet2str[];
/* [draft-ietf-quic-http-22] Section 8.1 and
/* [draft-ietf-quic-http-23] Section 8.1 and
* [draft-ietf-quic-qpack-08], Section 8.3
*/
enum http_error_code
{
HEC_NO_ERROR = 0x00,
HEC_GENERAL_PROTOCOL_ERROR = 0x01,
/* Error code 0x2 is reserved and has no meaning */
HEC_INTERNAL_ERROR = 0x03,
/* Error code 0x4 is reserved and has no meaning */
HEC_REQUEST_CANCELLED = 0x05,
HEC_INCOMPLETE_REQUEST = 0x06,
HEC_CONNECT_ERROR = 0x07,
HEC_EXCESSIVE_LOAD = 0x08,
HEC_VERSION_FALLBACK = 0x09,
HEC_WRONG_STREAM = 0x0A,
HEC_ID_ERROR = 0x0B,
/* Error code 0xC is reserved and has no meaning */
HEC_STREAM_CREATION_ERROR = 0x0D,
/* Error code 0xE is reserved and has no meaning */
HEC_CLOSED_CRITICAL_STREAM = 0x0F,
/* Error code 0x10 is reserved and has no meaning */
HEC_EARLY_RESPONSE = 0x0011,
HEC_MISSING_SETTINGS = 0x0012,
HEC_UNEXPECTED_FRAME = 0x0013,
HEC_REQUEST_REJECTED = 0x14,
HEC_SETTINGS_ERROR = 0x00FF,
HEC_MALFORMED_FRAME = 0x0100, /* add frame type */
HEC_NO_ERROR = 0x100,
HEC_GENERAL_PROTOCOL_ERROR = 0x101,
HEC_INTERNAL_ERROR = 0x102,
HEC_STREAM_CREATION_ERROR = 0x103,
HEC_CLOSED_CRITICAL_STREAM = 0x104,
HEC_FRAME_UNEXPECTED = 0x105,
HEC_FRAME_ERROR = 0x106,
HEC_EXCESSIVE_LOAD = 0x107,
HEC_ID_ERROR = 0x108,
HEC_SETTINGS_ERROR = 0x109,
HEC_MISSING_SETTINGS = 0x10A,
HEC_REQUEST_REJECTED = 0x10B,
HEC_REQUEST_CANCELLED = 0x10C,
HEC_REQUEST_INCOMPLETE = 0x10D,
HEC_EARLY_RESPONSE = 0x10E,
HEC_CONNECT_ERROR = 0x10F,
HEC_VERSION_FALLBACK = 0x110,
HEC_QPACK_DECOMPRESSION_FAILED = 0x200,
HEC_QPACK_ENCODER_STREAM_ERROR = 0x201,
HEC_QPACK_DECODER_STREAM_ERROR = 0x202,
};
struct h3_prio_frame_read_state
{
struct varint_read_state h3pfrs_vint;
struct hq_priority h3pfrs_prio;
enum {
H3PFRS_STATE_TYPE = 0,
H3PFRS_STATE_VINT_BEGIN,
H3PFRS_STATE_VINT_CONTINUE,
H3PFRS_STATE_WEIGHT,
} h3pfrs_state;
enum {
H3PFRS_FLAG_HAVE_PRIO_ID = 1 << 0,
} h3pfrs_flags;
};
enum h3_prio_frame_read_status
{
H3PFR_STATUS_DONE,
H3PFR_STATUS_NEED,
};
/* When first called, h3pfrs_state should be set to 0 */
enum h3_prio_frame_read_status
lsquic_h3_prio_frame_read (const unsigned char **, size_t,
struct h3_prio_frame_read_state *);
#endif

View File

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

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 *
imico_get_packet_out (struct ietf_mini_conn *conn,
enum header_type header_type, size_t need)
{
struct lsquic_packet_out *packet_out;
enum ecn ecn;
if (need)
TAILQ_FOREACH(packet_out, &conn->imc_packets_out, po_next)
@ -94,8 +124,8 @@ imico_get_packet_out (struct ietf_mini_conn *conn,
packet_out->po_packno = conn->imc_next_packno++;
packet_out->po_flags |= PO_MINI;
lsquic_packet_out_set_pns(packet_out, lsquic_hety2pns[header_type]);
if (conn->imc_enpub->enp_settings.es_ecn)
packet_out->po_lflags |= ECN_ECT0 << POECN_SHIFT;
ecn = imico_get_ecn(conn);
packet_out->po_lflags |= ecn << POECN_SHIFT;
TAILQ_INSERT_TAIL(&conn->imc_packets_out, packet_out, po_next);
packet_out->po_loss_chain = packet_out;
return packet_out;
@ -903,6 +933,11 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
enum dec_packin dec_packin;
enum packnum_space pns;
if (conn->imc_flags & IMC_ERROR)
{
LSQ_DEBUG("ignore incoming packet: connection is in error state");
return;
}
pns = lsquic_hety2pns[ packet_in->pi_header_type ];
if (pns == PNS_INIT && (conn->imc_flags & IMC_IGNORE_INIT))
@ -957,11 +992,15 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
packet_in->pi_packno > highest_bit_set(conn->imc_recvd_packnos[pns]))
conn->imc_largest_recvd[pns] = packet_in->pi_received;
conn->imc_recvd_packnos[pns] |= 1ULL << packet_in->pi_packno;
conn->imc_flags |= IMC_QUEUED_ACK_INIT << pns;
if (0 != imico_parse_regular_packet(conn, packet_in))
{
LSQ_DEBUG("connection is now in error state");
conn->imc_flags |= IMC_ERROR;
return;
}
conn->imc_flags |= IMC_QUEUED_ACK_INIT << pns;
++conn->imc_ecn_counts_in[pns][ lsquic_packet_in_ecn(packet_in) ];
conn->imc_incoming_ecn <<= 1;
conn->imc_incoming_ecn |= lsquic_packet_in_ecn(packet_in) != ECN_NOT_ECT;
@ -974,6 +1013,8 @@ ietf_mini_conn_ci_packet_sent (struct lsquic_conn *lconn,
{
struct ietf_mini_conn *conn = (struct ietf_mini_conn *) lconn;
conn->imc_sent_packnos |= 1ULL << packet_out->po_packno;
conn->imc_ecn_packnos |= !!lsquic_packet_out_ecn(packet_out)
<< packet_out->po_packno;
#if 0
if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
{
@ -983,6 +1024,8 @@ ietf_mini_conn_ci_packet_sent (struct lsquic_conn *lconn,
#endif
++conn->imc_ecn_counts_out[ lsquic_packet_out_pns(packet_out) ]
[ lsquic_packet_out_ecn(packet_out) ];
if (packet_out->po_header_type == HETY_HANDSHAKE)
conn->imc_flags |= IMC_HSK_PACKET_SENT;
LSQ_DEBUG("%s: packet %"PRIu64" sent", __func__, packet_out->po_packno);
}
@ -1028,6 +1071,7 @@ imico_repackage_packet (struct ietf_mini_conn *conn,
"resending as packet %"PRIu64, oldno, packno);
packet_out->po_packno = packno;
packet_out->po_flags &= ~PO_SENT;
lsquic_packet_out_set_ecn(packet_out, imico_get_ecn(conn));
if (packet_out->po_flags & PO_ENCRYPTED)
imico_return_enc_data(conn, packet_out);
TAILQ_INSERT_TAIL(&conn->imc_packets_out, packet_out, po_next);
@ -1245,20 +1289,13 @@ imico_generate_conn_close (struct ietf_mini_conn *conn)
{
struct lsquic_packet_out *packet_out;
enum header_type header_type;
enum packnum_space pns;
enum packnum_space pns, pns_max;
unsigned error_code;
const char *reason;
size_t need;
int sz, rlen, is_app;
char reason_buf[0x20];
pns = (conn->imc_flags >> IMCBIT_PNS_BIT_SHIFT) & 3;
header_type = pns2hety[pns];
need = 30; /* Guess */ /* TODO: calculate, don't guess */
packet_out = imico_get_packet_out(conn, header_type, need);
if (!packet_out)
return;
if (conn->imc_flags & IMC_ABORT_ERROR)
{
is_app = !!(conn->imc_flags & IMC_ABORT_ISAPP);
@ -1302,18 +1339,59 @@ imico_generate_conn_close (struct ietf_mini_conn *conn)
rlen = 0;
}
sz = conn->imc_conn.cn_pf->pf_gen_connect_close_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), is_app, error_code, reason,
rlen);
if (sz >= 0)
/* [draft-ietf-quic-transport-23] Section 12.2:
*
" A client will always know whether the server has Handshake keys (see
" Section 17.2.2.1), but it is possible that a server does not know
" whether the client has Handshake keys. Under these circumstances, a
" server SHOULD send a CONNECTION_CLOSE frame in both Handshake and
" Initial packets to ensure that at least one of them is processable by
" the client.
*/
pns = (conn->imc_flags >> IMCBIT_PNS_BIT_SHIFT) & 3;
switch ((!!(conn->imc_flags & IMC_HSK_PACKET_SENT) << 1)
| (pns == PNS_HSK) /* Handshake packet received */)
{
packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
packet_out->po_data_sz += sz;
LSQ_DEBUG("generated CONNECTION_CLOSE frame");
case (0 << 1) | 0:
pns = PNS_INIT;
pns_max = PNS_INIT;
break;
case (1 << 1) | 0:
pns = PNS_INIT;
pns_max = PNS_HSK;
break;
default:
pns = PNS_HSK;
pns_max = PNS_HSK;
break;
}
else
LSQ_WARN("could not generate CONNECTION_CLOSE frame");
LSQ_DEBUG("will generate %u CONNECTION_CLOSE frame%.*s",
pns_max - pns + 1, pns_max > pns, "s");
do
{
header_type = pns2hety[pns];
need = 30; /* Guess */ /* TODO: calculate, don't guess */
packet_out = imico_get_packet_out(conn, header_type, need);
if (!packet_out)
return;
sz = conn->imc_conn.cn_pf->pf_gen_connect_close_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), is_app, error_code, reason,
rlen);
if (sz >= 0)
{
packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
packet_out->po_data_sz += sz;
LSQ_DEBUG("generated CONNECTION_CLOSE frame");
}
else
LSQ_WARN("could not generate CONNECTION_CLOSE frame");
++pns;
}
while (pns <= pns_max);
}

View File

@ -53,6 +53,7 @@ struct ietf_mini_conn
IMC_ABORT_ISAPP = 1 << 15,
IMC_BAD_TRANS_PARAMS = 1 << 16,
IMC_ADDR_VALIDATED = 1 << 17,
IMC_HSK_PACKET_SENT = 1 << 18,
} imc_flags;
struct mini_crypto_stream imc_streams[N_ENC_LEVS];
void *imc_stream_ps[N_ENC_LEVS];
@ -72,6 +73,12 @@ struct ietf_mini_conn
unsigned imc_bytes_out;
unsigned char imc_next_packno;
unsigned char imc_hsk_count;
/* We don't send more than eight in the first flight, and so it's OK to
* use uint8_t. This value is also used as a boolean: when ECN black
* hole is detected, it is set to zero to indicate that black hole
* detection is no longer active.
*/
uint8_t imc_ecn_packnos;
uint8_t imc_ack_exp;
uint8_t imc_ecn_counts_in[N_PNS][4];
uint8_t imc_ecn_counts_out[N_PNS][4];
@ -85,4 +92,6 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *,
const struct lsquic_packet_in *,
enum lsquic_version, int is_ipv4, const struct lsquic_cid *);
int
lsquic_mini_conn_ietf_ecn_ok (const struct ietf_mini_conn *);
#endif

View File

@ -34,11 +34,13 @@
#include "lsquic_engine_public.h"
#include "lsquic_sizes.h"
#include "lsquic_handshake.h"
#include "lsquic_xxhash.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PRQ
#include "lsquic_logger.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static const struct conn_iface evanescent_conn_iface;
@ -46,14 +48,15 @@ static const struct conn_iface evanescent_conn_iface;
struct packet_req
{
STAILQ_ENTRY(packet_req) pr_next;
struct lsquic_hash_elem pr_hash_el;
lsquic_cid_t pr_scid;
lsquic_cid_t pr_dcid;
enum packet_req_type pr_type;
enum {
enum pr_flags {
PR_GQUIC = 1 << 0,
} pr_flags;
enum lsquic_version pr_version;
unsigned pr_rst_sz;
struct network_path pr_path;
};
@ -79,10 +82,10 @@ struct pr_queue
{
TAILQ_HEAD(, lsquic_conn) prq_free_conns,
prq_returned_conns;
STAILQ_HEAD(, packet_req) prq_reqs_queue;
struct malo *prq_reqs_pool;
const struct lsquic_engine_public
*prq_enpub;
struct lsquic_hash *prq_reqs_hash;
unsigned prq_max_reqs;
unsigned prq_nreqs;
unsigned prq_max_conns;
@ -103,6 +106,30 @@ struct pr_queue
};
static int
comp_reqs (const void *s1, const void *s2, size_t n)
{
const struct packet_req *a, *b;
a = s1;
b = s2;
if (a->pr_type == b->pr_type && LSQUIC_CIDS_EQ(&a->pr_dcid, &b->pr_dcid))
return 0;
else
return -1;
}
static unsigned
hash_req (const void *p, size_t len, unsigned seed)
{
const struct packet_req *req;
req = p;
return XXH32(req->pr_dcid.idbuf, req->pr_dcid.len, seed);
}
struct pr_queue *
prq_create (unsigned max_elems, unsigned max_conns,
const struct lsquic_engine_public *enpub)
@ -110,6 +137,7 @@ prq_create (unsigned max_elems, unsigned max_conns,
const struct parse_funcs *pf;
struct pr_queue *prq;
struct malo *malo;
struct lsquic_hash *hash;
unsigned verneg_g_sz;
ssize_t prst_g_sz;
int len;
@ -121,11 +149,19 @@ prq_create (unsigned max_elems, unsigned max_conns,
goto err0;
}
hash = lsquic_hash_create_ext(comp_reqs, hash_req);
if (!hash)
{
LSQ_WARN("cannot create hash");
goto err1;
}
prq = malloc(sizeof(*prq));
if (!prq)
{
LSQ_WARN("malloc failed: %s", strerror(errno));
goto err1;
goto err2;
}
const lsquic_cid_t cid = { .len = 8, };
@ -137,7 +173,7 @@ prq_create (unsigned max_elems, unsigned max_conns,
if (len <= 0)
{
LSQ_ERROR("cannot generate version negotiation packet");
goto err2;
goto err3;
}
verneg_g_sz = (unsigned) len;
@ -146,12 +182,12 @@ prq_create (unsigned max_elems, unsigned max_conns,
if (prst_g_sz < 0)
{
LSQ_ERROR("cannot generate public reset packet");
goto err2;
goto err3;
}
TAILQ_INIT(&prq->prq_free_conns);
TAILQ_INIT(&prq->prq_returned_conns);
STAILQ_INIT(&prq->prq_reqs_queue);
prq->prq_reqs_hash = hash;
prq->prq_reqs_pool = malo;
prq->prq_max_reqs = max_elems;
prq->prq_nreqs = 0;
@ -165,8 +201,10 @@ prq_create (unsigned max_elems, unsigned max_conns,
return prq;
err2:
err3:
free(prq);
err2:
lsquic_hash_destroy(hash);
err1:
lsquic_malo_destroy(malo);
err0:
@ -185,6 +223,7 @@ prq_destroy (struct pr_queue *prq)
TAILQ_REMOVE(&prq->prq_free_conns, conn, cn_next_pr);
free(conn);
}
lsquic_hash_destroy(prq->prq_reqs_hash);
lsquic_malo_destroy(prq->prq_reqs_pool);
free(prq);
}
@ -223,6 +262,45 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
{
struct packet_req *req;
lsquic_ver_tag_t ver_tag;
enum lsquic_version version;
enum pr_flags flags;
unsigned max, size;
if (packet_in->pi_flags & PI_GQUIC)
flags = PR_GQUIC;
else
flags = 0;
if (packet_in->pi_quic_ver)
{
memcpy(&ver_tag, packet_in->pi_data + packet_in->pi_quic_ver,
sizeof(ver_tag));
version = lsquic_tag2ver(ver_tag);
}
else /* Got to set it to something sensible... */
version = LSQVER_ID23;
if (type == PACKET_REQ_PUBRES && !(flags & PR_GQUIC))
{
if (packet_in->pi_data_sz <= IQUIC_MIN_SRST_SIZE)
{
LSQ_DEBUGC("not scheduling public reset: incoming packet for CID "
"%"CID_FMT" too small: %hu bytes",
CID_BITS(&packet_in->pi_dcid), packet_in->pi_data_sz);
return -1;
}
/* Use a random stateless reset size */
max = MIN(IQUIC_MAX_SRST_SIZE, packet_in->pi_data_sz - 1u);
if (max > IQUIC_MIN_SRST_SIZE)
size = IQUIC_MIN_SRST_SIZE + rand() % (max - IQUIC_MIN_SRST_SIZE);
else
size = IQUIC_MIN_SRST_SIZE;
LSQ_DEBUGC("selected %u-byte reset size for CID %"CID_FMT
" (range is [%u, %u])", size, CID_BITS(&packet_in->pi_dcid),
IQUIC_MIN_SRST_SIZE, max);
}
else
size = 0;
req = get_req(prq);
if (!req)
@ -231,25 +309,34 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
return -1;
}
STAILQ_INSERT_TAIL(&prq->prq_reqs_queue, req, pr_next);
req->pr_type = type;
req->pr_flags = packet_in->pi_flags & PI_GQUIC ? PR_GQUIC : 0;
lsquic_scid_from_packet_in(packet_in, &req->pr_scid);
req->pr_dcid = packet_in->pi_dcid;
if (lsquic_hash_find(prq->prq_reqs_hash, req, sizeof(req)))
{
LSQ_DEBUG("request for this DCID and type already exists");
put_req(prq, req);
return -1;
}
req->pr_hash_el.qhe_flags = 0;
if (!lsquic_hash_insert(prq->prq_reqs_hash, req, sizeof(req),
req, &req->pr_hash_el))
{
LSQ_DEBUG("could not insert req into hash");
put_req(prq, req);
return -1;
}
req->pr_flags = flags;
req->pr_rst_sz = size;
req->pr_version = version;
lsquic_scid_from_packet_in(packet_in, &req->pr_scid);
req->pr_path.np_peer_ctx = peer_ctx;
memcpy(NP_LOCAL_SA(&req->pr_path), local_addr,
sizeof(req->pr_path.np_local_addr));
memcpy(NP_PEER_SA(&req->pr_path), peer_addr,
sizeof(req->pr_path.np_peer_addr));
if (packet_in->pi_quic_ver)
{
memcpy(&ver_tag, packet_in->pi_data + packet_in->pi_quic_ver,
sizeof(ver_tag));
req->pr_version = lsquic_tag2ver(ver_tag);
}
else /* Got to set it to something sensible... */
req->pr_version = LSQVER_ID22;
LSQ_DEBUGC("scheduled %s packet for connection %"CID_FMT,
lsquic_preqt2str[type], CID_BITS(&req->pr_dcid));
return 0;
@ -316,6 +403,7 @@ prq_next_conn (struct pr_queue *prq)
{
struct evanescent_conn *evconn;
struct lsquic_conn *lconn;
struct lsquic_hash_elem *el;
struct packet_req *req;
struct lsquic_packet_out *packet_out;
int (*gen_verneg) (unsigned char *, size_t, const lsquic_cid_t *,
@ -329,14 +417,15 @@ prq_next_conn (struct pr_queue *prq)
return lconn;
}
req = STAILQ_FIRST(&prq->prq_reqs_queue);
if (!req) /* Nothing is queued */
el = lsquic_hash_first(prq->prq_reqs_hash);
if (!el) /* Nothing is queued */
return NULL;
evconn = get_evconn(prq);
if (!evconn) /* Reached limit or malloc failed */
return NULL;
req = lsquic_hashelem_getdata(el);
packet_out = &evconn->evc_packet_out;
switch ((req->pr_type << 29) | req->pr_flags)
{
@ -370,16 +459,16 @@ prq_next_conn (struct pr_queue *prq)
break;
default:
packet_out->po_flags &= ~PO_VERNEG;
packet_out->po_data_sz = IQUIC_MIN_SRST_SIZE;
RAND_bytes(packet_out->po_data, IQUIC_MIN_SRST_RANDOM_BYTES);
packet_out->po_data_sz = req->pr_rst_sz;
RAND_bytes(packet_out->po_data, req->pr_rst_sz - IQUIC_SRESET_TOKEN_SZ);
packet_out->po_data[0] &= ~0x80;
packet_out->po_data[0] |= 0x40;
lsquic_tg_generate_sreset(prq->prq_enpub->enp_tokgen, &req->pr_dcid,
packet_out->po_data + IQUIC_MIN_SRST_RANDOM_BYTES);
packet_out->po_data + req->pr_rst_sz - IQUIC_SRESET_TOKEN_SZ);
break;
}
STAILQ_REMOVE_HEAD(&prq->prq_reqs_queue, pr_next);
lsquic_hash_erase(prq->prq_reqs_hash, el);
evconn->evc_req = req;
lconn= &evconn->evc_conn;
@ -394,7 +483,7 @@ prq_next_conn (struct pr_queue *prq)
int
prq_have_pending (const struct pr_queue *prq)
{
return !STAILQ_EMPTY(&prq->prq_reqs_queue);
return lsquic_hash_count(prq->prq_reqs_hash) > 0;
}

View File

@ -40,7 +40,6 @@
#include "lsquic_conn_public.h"
#include "lsquic_cong_ctl.h"
#include "lsquic_enc_sess.h"
#include "lsquic_h3_prio.h"
#include "lsquic_hash.h"
#include "lsquic_malo.h"
@ -312,12 +311,12 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
ctl->sc_alset = alset;
ctl->sc_ver_neg = ver_neg;
ctl->sc_conn_pub = conn_pub;
assert(!(flags & ~(SC_IETF|SC_NSTP)));
assert(!(flags & ~(SC_IETF|SC_NSTP|SC_ECN)));
ctl->sc_flags = flags;
send_ctl_pick_initial_packno(ctl);
if (enpub->enp_settings.es_pace_packets)
ctl->sc_flags |= SC_PACE;
if ((flags & SC_IETF) && enpub->enp_settings.es_ecn)
if (flags & SC_ECN)
ctl->sc_ecn = ECN_ECT0;
else
ctl->sc_ecn = ECN_NOT_ECT;
@ -773,6 +772,16 @@ send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl,
if (ctl->sc_ci->cci_lost)
ctl->sc_ci->cci_lost(CGP(ctl), packet_out, packet_sz);
/* This is a client-only check, server check happens in mini conn */
if (send_ctl_ecn_on(ctl)
&& 0 == ctl->sc_ecn_total_acked[PNS_INIT]
&& HETY_INITIAL == packet_out->po_header_type
&& 3 == packet_out->po_packno)
{
LSQ_DEBUG("possible ECN black hole during handshake, disable ECN");
ctl->sc_ecn = ECN_NOT_ECT;
}
if (packet_out->po_frame_types & ctl->sc_retx_frames)
{
LSQ_DEBUG("lost retransmittable packet %"PRIu64,
@ -1840,6 +1849,7 @@ update_for_resending (lsquic_send_ctl_t *ctl, lsquic_packet_out_t *packet_out)
packet_out->po_frame_types &= ~GQUIC_FRAME_REGEN_MASK;
assert(packet_out->po_frame_types);
packet_out->po_packno = packno;
lsquic_packet_out_set_ecn(packet_out, ctl->sc_ecn);
if (ctl->sc_ver_neg->vn_tag)
{
@ -2150,9 +2160,14 @@ lsquic_send_ctl_drop_scheduled (lsquic_send_ctl_t *ctl)
}
static enum buf_packet_type
send_ctl_determine_gquic_bpt (struct lsquic_send_ctl *ctl,
const struct lsquic_stream *stream)
#ifdef NDEBUG
static
#elif __GNUC__
__attribute__((weak))
#endif
enum buf_packet_type
lsquic_send_ctl_determine_bpt (lsquic_send_ctl_t *ctl,
const lsquic_stream_t *stream)
{
const lsquic_stream_t *other_stream;
struct lsquic_hash_elem *el;
@ -2173,35 +2188,6 @@ send_ctl_determine_gquic_bpt (struct lsquic_send_ctl *ctl,
}
static enum buf_packet_type
send_ctl_determine_ietf_bpt (struct lsquic_send_ctl *ctl,
const struct lsquic_stream *stream)
{
if (lsquic_stream_is_critical(stream)
|| stream == lsquic_prio_tree_highest_non_crit(
ctl->sc_conn_pub->u.ietf.prio_tree))
return BPT_HIGHEST_PRIO;
else
return BPT_OTHER_PRIO;
}
#ifdef NDEBUG
static
#elif __GNUC__
__attribute__((weak))
#endif
enum buf_packet_type
lsquic_send_ctl_determine_bpt (lsquic_send_ctl_t *ctl,
const lsquic_stream_t *stream)
{
if (ctl->sc_flags & SC_IETF)
return send_ctl_determine_ietf_bpt(ctl, stream);
else
return send_ctl_determine_gquic_bpt(ctl, stream);
}
static enum buf_packet_type
send_ctl_lookup_bpt (lsquic_send_ctl_t *ctl,
const struct lsquic_stream *stream)

View File

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

View File

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

View File

@ -64,7 +64,6 @@
#include "lsquic_qdec_hdl.h"
#include "lsquic_qenc_hdl.h"
#include "lsquic_byteswap.h"
#include "lsquic_h3_prio.h"
#include "lsquic_ietf.h"
#include "lsquic_push_promise.h"
@ -355,19 +354,6 @@ stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub,
stream->sm_onnew_arg = stream_if_ctx;
stream->sm_write_avail = stream_write_avail;
if ((ctor_flags & (SCF_IETF|SCF_CRITICAL)) == SCF_IETF)
{
if (0 == lsquic_prio_tree_add_stream(conn_pub->u.ietf.prio_tree,
stream, H3ET_ROOT, 0, 0))
stream->sm_qflags |= SMQF_H3_PRIO;
else
{
stream->data_in->di_if->di_destroy(stream->data_in);
free(stream);
return NULL;
}
}
STAILQ_INIT(&stream->sm_hq_frames);
stream->sm_bflags |= ctor_flags & ((1 << (N_SMBF_FLAGS - 1)) - 1);
@ -571,9 +557,6 @@ lsquic_stream_destroy (lsquic_stream_t *stream)
TAILQ_REMOVE(&stream->conn_pub->service_streams, stream, next_service_stream);
if (stream->sm_qflags & SMQF_QPACK_DEC)
lsquic_qdh_unref_stream(stream->conn_pub->u.ietf.qdh, stream);
if (stream->sm_qflags & SMQF_H3_PRIO)
lsquic_prio_tree_remove_stream(stream->conn_pub->u.ietf.prio_tree,
stream, lsquic_time_now() /* XXX: do we have to call this? */);
drop_buffered_data(stream);
lsquic_sfcw_consume_rem(&stream->fc);
drop_frames_in(stream);
@ -3881,7 +3864,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
filter->hqfi_flags |= HQFI_FLAG_ERROR;
LSQ_INFO("unexpected HTTP/3 frame sequence: %o",
filter->hqfi_hist_buf);
lconn->cn_if->ci_abort_error(lconn, 1, HEC_UNEXPECTED_FRAME,
lconn->cn_if->ci_abort_error(lconn, 1, HEC_FRAME_UNEXPECTED,
"unexpected HTTP/3 frame sequence on stream %"PRIu64,
stream->id);
goto end;
@ -3905,7 +3888,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
lconn = stream->conn_pub->lconn;
if (stream->sm_bflags & SMBF_SERVER)
lconn->cn_if->ci_abort_error(lconn, 1,
HEC_UNEXPECTED_FRAME, "Received PUSH_PROMISE frame "
HEC_FRAME_UNEXPECTED, "Received PUSH_PROMISE frame "
"on stream %"PRIu64" (clients are not supposed to "
"send those)", stream->id);
else
@ -3913,7 +3896,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
* push.
*/
lconn->cn_if->ci_abort_error(lconn, 1,
HEC_UNEXPECTED_FRAME, /* TODO: in ID-21 ID_ERROR? */
HEC_FRAME_UNEXPECTED,
"Received PUSH_PROMISE frame (not supported)"
"on stream %"PRIu64, stream->id);
goto end;
@ -3927,7 +3910,6 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
case HQFT_GOAWAY:
case HQFT_HEADERS:
case HQFT_MAX_PUSH_ID:
case HQFT_PRIORITY:
case HQFT_PUSH_PROMISE:
case HQFT_SETTINGS:
filter->hqfi_flags |= HQFI_FLAG_ERROR;

View File

@ -165,8 +165,6 @@ enum stream_q_flags
SMQF_ABORT_CONN = 1 << 8, /* Unrecoverable error occurred */
SMQF_QPACK_DEC = 1 << 9, /* QPACK decoder is holding a reference to this stream */
SMQF_H3_PRIO = 1 <<10, /* Referenced from HTTP/3 priority tree */
};
@ -233,7 +231,6 @@ struct lsquic_stream
enum stream_b_flags sm_bflags;
enum stream_q_flags sm_qflags;
unsigned n_unacked;
unsigned sm_h3_prio_idx;
const struct lsquic_stream_if *stream_if;
struct lsquic_stream_ctx *st_ctx;

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

View File

@ -21,7 +21,7 @@ enum transport_param_id
TPI_INIT_MAX_STREAMS_UNI = 9,
TPI_ACK_DELAY_EXPONENT = 10,
TPI_MAX_ACK_DELAY = 11,
TPI_DISABLE_MIGRATION = 12,
TPI_DISABLE_ACTIVE_MIGRATION = 12,
TPI_PREFERRED_ADDRESS = 13,
TPI_ACTIVE_CONNECTION_ID_LIMIT = 14,
#define MAX_TPI TPI_ACTIVE_CONNECTION_ID_LIMIT
@ -85,7 +85,7 @@ struct transport_params
#define tp_max_ack_delay tp_numerics_u.s.max_ack_delay
#define tp_active_connection_id_limit tp_numerics_u.s.active_connection_id_limit
signed char tp_disable_migration;
signed char tp_disable_active_migration;
uint8_t tp_stateless_reset_token[IQUIC_SRESET_TOKEN_SZ];
struct {
uint8_t ipv4_addr[4];
@ -103,7 +103,7 @@ struct transport_params
#define TP_DEF_INIT_MAX_STREAMS_UNI 0
#define TP_DEF_INIT_MAX_STREAMS_BIDI 0
#define TP_DEF_INIT_MAX_DATA 0
#define TP_DEF_DISABLE_MIGRATION 0
#define TP_DEF_DISABLE_ACTIVE_MIGRATION 0
#define TP_DEF_INIT_MAX_STREAM_DATA_BIDI_LOCAL 0
#define TP_DEF_INIT_MAX_STREAM_DATA_BIDI_REMOTE 0
#define TP_DEF_INIT_MAX_STREAM_DATA_UNI 0
@ -123,7 +123,7 @@ struct transport_params
.tp_init_max_streams_bidi = TP_DEF_INIT_MAX_STREAMS_BIDI, \
.tp_init_max_streams_uni = TP_DEF_INIT_MAX_STREAMS_UNI, \
.tp_init_max_data = TP_DEF_INIT_MAX_DATA, \
.tp_disable_migration = TP_DEF_DISABLE_MIGRATION, \
.tp_disable_active_migration = TP_DEF_DISABLE_ACTIVE_MIGRATION, \
.tp_init_max_stream_data_bidi_local = TP_DEF_INIT_MAX_STREAM_DATA_BIDI_LOCAL, \
.tp_init_max_stream_data_bidi_remote = TP_DEF_INIT_MAX_STREAM_DATA_BIDI_REMOTE, \
.tp_init_max_stream_data_uni = TP_DEF_INIT_MAX_STREAM_DATA_UNI

View File

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

View File

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

View File

@ -576,6 +576,17 @@ read_one_packet (struct read_iter *iter)
, &packs_in->ecn[iter->ri_idx]
#endif
);
#if LSQUIC_ECN_BLACK_HOLE && ECN_SUPPORTED
{
const char *s;
s = getenv("LSQUIC_ECN_BLACK_HOLE");
if (s && atoi(s) && packs_in->ecn[iter->ri_idx])
{
LSQ_NOTICE("ECN blackhole: drop packet");
return ROP_OK;
}
}
#endif
#if __linux__
if (sport->drop_init)
{
@ -1789,11 +1800,6 @@ set_engine_option (struct lsquic_engine_settings *settings,
}
break;
case 15:
if (0 == strncmp(name, "h3_placeholders", 15))
{
settings->es_h3_placeholders = atoi(val);
return 0;
}
if (0 == strncmp(name, "allow_migration", 15))
{
settings->es_allow_migration = atoi(val);

View File

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

View File

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

View File

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

View File

@ -55,9 +55,8 @@
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_data_in_if.h"
#include "lsquic_h3_prio.h"
static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_ID22);
static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_ID23);
struct test_ctl_settings
{
@ -298,7 +297,7 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
memset(tobjs, 0, sizeof(*tobjs));
LSCONN_INITIALIZE(&tobjs->lconn);
tobjs->lconn.cn_pf = g_pf;
tobjs->lconn.cn_version = LSQVER_ID22;
tobjs->lconn.cn_version = LSQVER_ID23;
tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1;
network_path.np_pack_size = packet_sz;
tobjs->lconn.cn_if = &our_conn_if;
@ -331,9 +330,6 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
assert(0 == s);
tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh;
}
if (tobjs->ctor_flags & SCF_IETF)
tobjs->conn_pub.u.ietf.prio_tree
= lsquic_prio_tree_new(&tobjs->lconn, 0);
}
@ -346,8 +342,6 @@ deinit_test_objs (struct test_objs *tobjs)
lsquic_mm_cleanup(&tobjs->eng_pub.enp_mm);
if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS)
lsquic_qeh_cleanup(&tobjs->qeh);
if (tobjs->ctor_flags & SCF_IETF)
lsquic_prio_tree_destroy(tobjs->conn_pub.u.ietf.prio_tree);
}

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[] =
{
{
__LINE__,
{
0x02,
0x05,
/* The `1' used to be the `exclusive' flag. It is left here to
* test that the frame reader ignores these Emtpy bits.
*/
(H3PET_REQ_STREAM << HQ_PT_SHIFT) | (H3DET_PLACEHOLDER << HQ_DT_SHIFT) | 1,
0x03,
0x40, 0x33,
0x77,
},
7,
0,
"on_priority: request stream #3 depends on placeholder #51; weight: 120\n",
},
{
__LINE__,
{
0x02,
0x03,
(H3PET_REQ_STREAM << HQ_PT_SHIFT) | (H3DET_ROOT << HQ_DT_SHIFT),
0x03,
0x77,
},
5,
0,
"on_priority: request stream #3 depends on root; weight: 120\n",
},
{
__LINE__,
{
0x02,
0x04,
/* The `1' used to be the `exclusive' flag. It is left here to
* test that the frame reader ignores these Emtpy bits.
*/
(H3PET_CUR_STREAM << HQ_PT_SHIFT) | (H3DET_PLACEHOLDER << HQ_DT_SHIFT),
0x40, 0x33,
0x77,
},
6,
0,
"on_priority: current stream depends on placeholder #51; weight: 120\n",
},
{
__LINE__,
{
0x02,
0x02,
/* The `1' used to be the `exclusive' flag. It is left here to
* test that the frame reader ignores these Emtpy bits.
*/
(H3PET_CUR_STREAM << HQ_PT_SHIFT) | (H3DET_ROOT << HQ_DT_SHIFT),
0x77,
},
4,
0,
"on_priority: current stream depends on root; weight: 120\n",
},
{
__LINE__,
{
@ -161,41 +96,6 @@ static const struct test tests[] =
};
static void
on_priority (void *ctx, const struct hq_priority *priority)
{
switch (((priority->hqp_prio_type == H3PET_CUR_STREAM) << 1)
| (priority->hqp_dep_type == H3DET_ROOT))
{
case 0:
fprintf(ctx, "%s: %s #%"PRIu64" depends on %s #%"PRIu64"; "
"weight: %u\n", __func__,
lsquic_h3pet2str[priority->hqp_prio_type], priority->hqp_prio_id,
lsquic_h3det2str[priority->hqp_dep_type], priority->hqp_dep_id,
HQP_WEIGHT(priority));
break;
case 1:
fprintf(ctx, "%s: %s #%"PRIu64" depends on root; "
"weight: %u\n", __func__,
lsquic_h3pet2str[priority->hqp_prio_type], priority->hqp_prio_id,
HQP_WEIGHT(priority));
break;
case 2:
fprintf(ctx, "%s: current stream depends on %s #%"PRIu64"; "
"weight: %u\n", __func__,
lsquic_h3det2str[priority->hqp_dep_type], priority->hqp_dep_id,
HQP_WEIGHT(priority));
break;
default:
assert(0);
case 3:
fprintf(ctx, "%s: current stream depends on root; "
"weight: %u\n", __func__,
HQP_WEIGHT(priority));
break;
}
}
static void
on_cancel_push (void *ctx, uint64_t push_id)
{
@ -234,7 +134,6 @@ on_unexpected_frame (void *ctx, uint64_t frame_type)
static const struct hcsi_callbacks callbacks =
{
.on_priority = on_priority,
.on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id,
.on_settings_frame = on_settings_frame,

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

View File

@ -50,7 +50,6 @@
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_data_in_if.h"
#include "lsquic_h3_prio.h"
static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_039);
@ -326,7 +325,7 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
LSCONN_INITIALIZE(&tobjs->lconn);
tobjs->lconn.cn_pf = pf ? pf : g_pf;
tobjs->lconn.cn_version = tobjs->lconn.cn_pf == &lsquic_parse_funcs_ietf_v1 ?
LSQVER_ID22 : LSQVER_043;
LSQVER_ID23 : LSQVER_043;
tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_gquic_1;
network_path.np_pack_size = 1370;
tobjs->lconn.cn_if = &our_conn_if;
@ -358,8 +357,6 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
s = lsquic_qeh_settings(&tobjs->qeh, 0, 0, 0, 0);
assert(0 == s);
tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh;
tobjs->conn_pub.u.ietf.prio_tree
= lsquic_prio_tree_new(&tobjs->lconn, 0);
}
}
@ -374,7 +371,6 @@ deinit_test_objs (struct test_objs *tobjs)
if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS)
{
lsquic_qeh_cleanup(&tobjs->qeh);
lsquic_prio_tree_destroy(tobjs->conn_pub.u.ietf.prio_tree);
}
}
@ -2168,7 +2164,7 @@ test_changing_pack_size (void)
enum lsquic_version versions_to_test[3] =
{
LSQVER_046,
LSQVER_ID22,
LSQVER_ID23,
};
for (i = 0; i < 3; i++)
@ -3097,7 +3093,7 @@ main (int argc, char **argv)
/* Redo some tests using crypto streams and frames */
g_use_crypto_ctor = 1;
g_pf = select_pf_by_ver(LSQVER_ID22);
g_pf = select_pf_by_ver(LSQVER_ID23);
main_test_packetization();
return 0;

View File

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

View File

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

View File

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